datalist-polyfill
Advanced tools
Comparing version 1.15.1 to 1.16.0
{ | ||
"name": "datalist-polyfill", | ||
"description": "An extremely lightweight and library-dependency-free vanilla JavaScript datalist polyfill.", | ||
"version": "1.15.1", | ||
"version": "1.16.0", | ||
"homepage": "https://github.com/mfranzke/datalist-polyfill", | ||
@@ -6,0 +6,0 @@ "authors": [{ |
@@ -9,2 +9,13 @@ # Changelog | ||
## [1.16.0] - 2018-06-27 | ||
### Added | ||
- Linting as well as security: prettier, xo, codacy | ||
- And their badges | ||
### Changed | ||
Made a lot of code changes in relation to what the previously mentioned linters reported. (e.g. #GH-23) | ||
### Security | ||
Made some code changes in relation to what the previously mentioned linters reported. (e.g. #GH-21, #GH-22) | ||
## [1.15.1] - 2018-06-22 | ||
@@ -11,0 +22,0 @@ ### Fixed |
/* | ||
* datalist-polyfill.js - https://github.com/mfranzke/datalist-polyfill | ||
* Datalist polyfill - https://github.com/mfranzke/datalist-polyfill | ||
* @license Copyright(c) 2017 by Maximilian Franzke | ||
@@ -13,441 +13,486 @@ * Supported by Christian, Johannes, @mitchhentges, @mertenhanisch, @ailintom, @Kravimir, @mischah, @hryamzik, @ottoville, @IceCreamYou, @wlekin, @eddr and @beebee1987 - many thanks for that ! | ||
(function() { | ||
'use strict'; | ||
'use strict'; | ||
// feature detection - let's break here, if it's even already supported | ||
if ('list' in document.createElement('input') && !!( document.createElement('datalist') && window.HTMLDataListElement)) { | ||
return false; | ||
} | ||
// Feature detection - let's break here, if it's even already supported | ||
if ( | ||
'list' in document.createElement('input') && | ||
Boolean(document.createElement('datalist') && window.HTMLDataListElement) | ||
) { | ||
return false; | ||
} | ||
// emulate the two properties regarding the datalist and input elements | ||
// list property / https://developer.mozilla.org/en/docs/Web/API/HTMLInputElement | ||
(function(constructor) { | ||
if (constructor && constructor.prototype && constructor.prototype.list === undefined) { | ||
Object.defineProperty(constructor.prototype, 'list', { | ||
get: function() { | ||
if (typeof( this ) === 'object' && this instanceof constructor) { | ||
var list = this.getAttribute('list'); | ||
// Emulate the two properties regarding the datalist and input elements | ||
// list property / https://developer.mozilla.org/en/docs/Web/API/HTMLInputElement | ||
(function(constructor) { | ||
if ( | ||
constructor && | ||
constructor.prototype && | ||
constructor.prototype.list === null | ||
) { | ||
Object.defineProperty(constructor.prototype, 'list', { | ||
get: function() { | ||
return typeof this === 'object' && this instanceof constructor | ||
? document.getElementById(this.getAttribute('list')) | ||
: null; | ||
} | ||
}); | ||
} | ||
})(window.HTMLInputElement); | ||
// Options property / https://developer.mozilla.org/en/docs/Web/API/HTMLDataListElement | ||
(function(constructor) { | ||
if ( | ||
constructor && | ||
constructor.prototype && | ||
constructor.prototype.options === null | ||
) { | ||
Object.defineProperty(constructor.prototype, 'options', { | ||
get: function() { | ||
return typeof this === 'object' && this instanceof constructor | ||
? this.getElementsByTagName('option') | ||
: null; | ||
} | ||
}); | ||
} | ||
})(window.HTMLElement); | ||
return document.getElementById(list); | ||
} | ||
return null; | ||
} | ||
}); | ||
} | ||
})(window.HTMLInputElement); | ||
// options property / https://developer.mozilla.org/en/docs/Web/API/HTMLDataListElement | ||
(function(constructor) { | ||
if (constructor && constructor.prototype && constructor.prototype.options === undefined) { | ||
Object.defineProperty(constructor.prototype, 'options', { | ||
get: function() { | ||
if (typeof( this ) === 'object' && this instanceof constructor) { | ||
return this.getElementsByTagName('option'); | ||
} | ||
} | ||
}); | ||
} | ||
})(window.HTMLElement); | ||
// Identify whether a select multiple is feasible | ||
var touched = false, | ||
// Speaking variables for the different keycodes | ||
keyENTER = 13, | ||
keyESC = 27, | ||
keyUP = 38, | ||
keyDOWN = 40, | ||
// Defining the text / value seperator for displaying the value and text values ... | ||
textValueSeperator = ' / ', | ||
// ... and defining the different input types that are supported by this polyfill | ||
supportedTypes = ['text', 'email', 'number', 'search', 'tel', 'url'], | ||
// Classes for elements | ||
classNameInput = 'polyfilled', | ||
classNamePolyfillingSelect = 'polyfilling'; | ||
// identify whether a select multiple is feasible | ||
var touched = false, | ||
// Differentiate for touch interactions, adapted by https://medium.com/@david.gilbertson/the-only-way-to-detect-touch-with-javascript-7791a3346685 | ||
window.addEventListener('touchstart', function onFirstTouch() { | ||
touched = true; | ||
// speaking variables for the different keycodes | ||
keyENTER = 13, | ||
keyESC = 27, | ||
keyUP = 38, | ||
keyDOWN = 40, | ||
window.removeEventListener('touchstart', onFirstTouch); | ||
}); | ||
// defining the text / value seperator for displaying the value and text values | ||
textValueSeperator = ' / ', | ||
// For observing any changes to the option elements within the datalist elements, define MutationObserver initially | ||
var MutationObserver = | ||
window.MutationObserver || window.WebKitMutationObserver, | ||
obs; | ||
// and defining the different input types that are supported by this polyfill | ||
supportedTypes = ['text', 'email', 'number', 'search', 'tel', 'url'], | ||
// Define a new observer | ||
if (typeof MutationObserver !== 'undefined') { | ||
obs = new MutationObserver(function(mutations) { | ||
var datalistNeedsAnUpdate = false; | ||
// classes for elements | ||
classNameInput = 'polyfilled', | ||
classNamePolyfillingSelect = 'polyfilling'; | ||
// Look through all mutations that just occured | ||
mutations.forEach(function(mutation) { | ||
// Look through all added nodes of this mutation | ||
for (var j = 0; j < mutation.addedNodes.length; ++j) { | ||
if (mutation.target.tagName.toLowerCase() === 'datalist') { | ||
datalistNeedsAnUpdate = mutation.target; | ||
} | ||
} | ||
}); | ||
// differentiate for touch interactions, adapted by https://medium.com/@david.gilbertson/the-only-way-to-detect-touch-with-javascript-7791a3346685 | ||
window.addEventListener('touchstart', function onFirstTouch() { | ||
touched = true; | ||
if (datalistNeedsAnUpdate) { | ||
var input = document.querySelector( | ||
'[list="' + datalistNeedsAnUpdate.id + '"]' | ||
); | ||
window.removeEventListener('touchstart', onFirstTouch); | ||
}); | ||
if (input.value !== '') { | ||
var dataList = datalistNeedsAnUpdate; | ||
// Prepare the options and toggle the visiblity afterwards | ||
toggleVisibility( | ||
prepOptions(dataList, input), | ||
dataList.getElementsByClassName(classNamePolyfillingSelect)[0] | ||
); | ||
} | ||
} | ||
}); | ||
} | ||
// for observing any changes to the option elements within the datalist elements, define MutationObserver initially | ||
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; | ||
// Function regarding the inputs interactions | ||
var inputInputList = function(event) { | ||
var eventTarget = event.target, | ||
eventTargetTagName = eventTarget.tagName.toLowerCase(), | ||
dataList = eventTarget.list; | ||
// define a new observer | ||
if (typeof(MutationObserver) !== 'undefined') { | ||
var obs = new MutationObserver(function(mutations) { | ||
var datalistNeedsAnUpdate = false; | ||
// Check for whether the events target was an input datalist and still check for an existing instance | ||
if ( | ||
eventTargetTagName && | ||
eventTargetTagName === 'input' && | ||
dataList !== null | ||
) { | ||
var dataListSelect = | ||
dataList.getElementsByClassName(classNamePolyfillingSelect)[0] || | ||
setUpPolyfillingSelect(eventTarget, dataList); | ||
// look through all mutations that just occured | ||
for(var i=0; i<mutations.length; ++i) { | ||
// Still check for an existing instance | ||
if (dataListSelect !== null) { | ||
var visible = false, | ||
inputValue = eventTarget.value, | ||
keyOpen = event.keyCode === keyUP || event.keyCode === keyDOWN; | ||
// look through all added nodes of this mutation | ||
for(var j=0; j<mutations[i].addedNodes.length; ++j) { | ||
if (mutations[i].target.tagName.toLowerCase() === 'datalist') { | ||
datalistNeedsAnUpdate = mutations[i].target; | ||
} | ||
} | ||
} | ||
// On an ESC or ENTER key press within the input, let's break here and afterwards hide the datalist select, and if the input contains a value | ||
if ( | ||
event.keyCode !== keyESC && | ||
event.keyCode !== keyENTER && | ||
(inputValue !== '' || keyOpen) | ||
) { | ||
// Prepare the options | ||
if (prepOptions(dataList, eventTarget)) { | ||
visible = true; | ||
} | ||
if (datalistNeedsAnUpdate) { | ||
var input = document.querySelector('[list="' + datalistNeedsAnUpdate.id + '"]'); | ||
var dataListSelectOptionsLength = dataListSelect.options.length, | ||
firstEntry = 0, | ||
lastEntry = dataListSelectOptionsLength - 1; | ||
if (input.value !== '') { | ||
var dataList = datalistNeedsAnUpdate; | ||
if (touched) { | ||
// Preselect best fitting index | ||
dataListSelect.selectedIndex = firstEntry; | ||
} else if (dataListSelect.selectedIndex === -1 && keyOpen) { | ||
dataListSelect.selectedIndex = | ||
event.keyCode === keyUP ? lastEntry : firstEntry; | ||
} | ||
// prepare the options and toggle the visiblity afterwards | ||
toggleVisibility(prepOptions(dataList, input), dataList.getElementsByClassName(classNamePolyfillingSelect)[0]); | ||
} | ||
} | ||
}); | ||
} | ||
// On arrow up or down keys, focus the select | ||
if (keyOpen) { | ||
dataListSelect.focus(); | ||
} | ||
} | ||
// function regarding the inputs interactions | ||
var inputInputList = function(event) { | ||
// Toggle the visibility of the datalist select according to previous checks | ||
toggleVisibility(visible, dataListSelect); | ||
} | ||
} | ||
}; | ||
var eventTarget = event.target, | ||
eventTargetTagName = eventTarget.tagName.toLowerCase(), | ||
dataList = eventTarget.list; | ||
// Function for preparing and sorting the options/suggestions | ||
var prepOptions = function(dataList, input) { | ||
if (typeof obs !== 'undefined') { | ||
obs.disconnect(); | ||
} | ||
// check for whether the events target was an input datalist and still check for an existing instance | ||
if (eventTargetTagName && eventTargetTagName === 'input' && dataList !== null) { | ||
var dataListSelect = | ||
dataList.getElementsByClassName(classNamePolyfillingSelect)[0] || | ||
setUpPolyfillingSelect(input, dataList), | ||
dataListOptions = dataList.querySelectorAll('option:not(:disabled)'), | ||
inputValue = input.value, | ||
newSelectValues = document.createDocumentFragment(), | ||
disabledValues = document.createDocumentFragment(); | ||
var dataListSelect = dataList.getElementsByClassName(classNamePolyfillingSelect)[0] || setUpPolyfillingSelect(eventTarget, dataList); | ||
// In case of type=email and multiple attribute, we would need grab the last piece | ||
if (input.type === 'email' && input.multiple) { | ||
inputValue = inputValue.substring(inputValue.lastIndexOf(',') + 1); | ||
} | ||
// still check for an existing instance | ||
if (dataListSelect !== undefined) { | ||
// ... create an array out of the options list | ||
var nodeArray = Array.prototype.slice.call(dataListOptions); | ||
var visible = false; | ||
// ... sort all entries and | ||
nodeArray | ||
.sort(function(a, b) { | ||
return a.value.localeCompare(b.value); | ||
}) | ||
.forEach(function(opt) { | ||
var optionValue = opt.value; | ||
// on an ESC or ENTER key press within the input, let's break here and afterwards hide the datalist select | ||
if (event.keyCode !== keyESC && event.keyCode !== keyENTER) { | ||
// ... put this option into the fragment that is meant to get inserted into the select | ||
// "Each option element that is a descendant of the datalist element, that is not disabled, and whose value is a string that isn't the empty string, represents a suggestion. Each suggestion has a value and a label." (W3C) | ||
if ( | ||
optionValue !== '' && | ||
optionValue.toLowerCase().indexOf(inputValue.toLowerCase()) !== -1 && | ||
opt.disabled === false | ||
) { | ||
var label = opt.getAttribute('label'), | ||
text = opt.text, | ||
textOptionPart = text.substr( | ||
0, | ||
optionValue.length + textValueSeperator.length | ||
), | ||
optionPart = optionValue + textValueSeperator; | ||
var inputValue = eventTarget.value, | ||
keyOpen = (event.keyCode === keyUP || event.keyCode === keyDOWN); | ||
// The innertext should be value / text in case they are different | ||
if ( | ||
text && | ||
!label && | ||
text !== optionValue && | ||
textOptionPart !== optionPart | ||
) { | ||
opt.innerText = optionValue + textValueSeperator + text; | ||
} else if (!opt.text) { | ||
// Manipulating the option inner text, that would get displayed | ||
opt.innerText = label || optionValue; | ||
} | ||
// if the input contains a value, than ... | ||
if (inputValue !== '' || keyOpen) { | ||
newSelectValues.appendChild(opt); | ||
} else { | ||
// ... or put this option that isn't relevant to the users into the fragment that is supposed to get inserted outside of the select | ||
disabledValues.appendChild(opt); | ||
} | ||
}); | ||
// prepare the options | ||
if (prepOptions(dataList, eventTarget)) { | ||
visible = true; | ||
} | ||
// Input the options fragment into the datalists select | ||
dataListSelect.appendChild(newSelectValues); | ||
var dataListSelectOptionsLength = dataListSelect.options.length, | ||
firstEntry = 0, | ||
lastEntry = dataListSelectOptionsLength - 1; | ||
var dataListSelectOptionsLength = dataListSelect.options.length; | ||
if (touched) { | ||
// preselect best fitting index | ||
dataListSelect.selectedIndex = firstEntry; | ||
dataListSelect.size = | ||
dataListSelectOptionsLength > 10 ? 10 : dataListSelectOptionsLength; | ||
dataListSelect.multiple = !touched && dataListSelectOptionsLength < 2; | ||
} else if (dataListSelect.selectedIndex === -1) { | ||
// Input the unused options as siblings next to the select - and differentiate in between the regular, and the IE9 fix syntax upfront | ||
var dataListAppend = | ||
dataList.getElementsByClassName('ie9_fix')[0] || dataList; | ||
switch (event.keyCode) { | ||
case keyUP: | ||
dataListSelect.selectedIndex = lastEntry; | ||
break; | ||
case keyDOWN: | ||
dataListSelect.selectedIndex = firstEntry; | ||
break; | ||
} | ||
} | ||
dataListAppend.appendChild(disabledValues); | ||
// on arrow up or down keys, focus the select | ||
if (keyOpen) { | ||
if (typeof obs !== 'undefined') { | ||
obs.observe(dataList, { | ||
childList: true | ||
}); | ||
} | ||
dataListSelect.focus(); | ||
} | ||
return dataListSelectOptionsLength; | ||
}; | ||
} | ||
} | ||
// Focus or blur events | ||
var changesInputList = function(event) { | ||
var eventTarget = event.target, | ||
eventTargetTagName = eventTarget.tagName.toLowerCase(), | ||
dataList = eventTarget.list; | ||
// toggle the visibility of the datalist select according to previous checks | ||
toggleVisibility(visible, dataListSelect); | ||
} | ||
} | ||
}; | ||
// Check for whether the events target was an input datalist and still check for an existing instance | ||
if ( | ||
eventTargetTagName && | ||
eventTargetTagName === 'input' && | ||
dataList !== null | ||
) { | ||
var eventType = event.type, | ||
// Creating the select if there's no instance so far (e.g. because of that it hasn't been handled or it has been dynamically inserted) | ||
dataListSelect = | ||
dataList.getElementsByClassName(classNamePolyfillingSelect)[0] || | ||
setUpPolyfillingSelect(eventTarget, dataList), | ||
// Either have the select set to the state to get displayed in case of that it would have been focused or because it's the target on the inputs blur - and check for general existance of any option as suggestions | ||
visible = | ||
dataListSelect && | ||
dataListSelect.querySelector('option:not(:disabled)') && | ||
((eventType === 'focusin' && eventTarget.value !== '') || | ||
(event.relatedTarget && event.relatedTarget === dataListSelect)); | ||
// function for preparing and sorting the options/suggestions | ||
var prepOptions = function(dataList, input) { | ||
// Test for whether this input has already been enhanced by the polyfill | ||
if ( | ||
' ' + eventTarget.className + ' '.indexOf(' ' + classNameInput + ' ') < | ||
0 | ||
) { | ||
// We'd like to prevent autocomplete on the input datalist field | ||
eventTarget.setAttribute('autocomplete', 'off'); | ||
if (typeof(obs) !== 'undefined') { | ||
obs.disconnect(); | ||
} | ||
// WAI ARIA attributes | ||
eventTarget.setAttribute('role', 'textbox'); | ||
eventTarget.setAttribute('aria-haspopup', 'true'); | ||
eventTarget.setAttribute('aria-autocomplete', 'list'); | ||
eventTarget.setAttribute('aria-owns', eventTarget.getAttribute('list')); | ||
var dataListSelect = dataList.getElementsByClassName(classNamePolyfillingSelect)[0] || setUpPolyfillingSelect(input, dataList), | ||
dataListOptions = dataList.querySelectorAll('option:not(:disabled)'), | ||
inputValue = input.value, | ||
newSelectValues = document.createDocumentFragment(), | ||
disabledValues = document.createDocumentFragment(); | ||
// Bind the keyup event on the related datalists input | ||
if (eventType === 'focusin') { | ||
eventTarget.addEventListener('keyup', inputInputList); | ||
// in case of type=email and multiple attribute, we would need to split the inputs value into pieces | ||
if (input.type === 'email' && input.multiple) { | ||
var multipleEntries = inputValue.split(','), | ||
relevantIndex = multipleEntries.length - 1; | ||
eventTarget.addEventListener('focusout', changesInputList, true); | ||
} else if (eventType === 'blur') { | ||
eventTarget.removeEventListener('keyup', inputInputList); | ||
inputValue = multipleEntries[relevantIndex].trim(); | ||
} | ||
eventTarget.removeEventListener('focusout', changesInputList, true); | ||
} | ||
// ... create an array out of the options list | ||
var nodeArray = Array.prototype.slice.call(dataListOptions); | ||
// Add class for identifying that this input is even already being polyfilled | ||
eventTarget.className += ' ' + classNameInput; | ||
} | ||
// ... sort all entries and | ||
nodeArray.sort(function(a, b) { | ||
return a.value.localeCompare(b.value); | ||
}) | ||
.forEach(function(opt) { | ||
var optionValue = opt.value; | ||
// Toggle the visibility of the datalist select according to previous checks | ||
toggleVisibility(visible, dataListSelect); | ||
} | ||
}; | ||
// ... put this option into the fragment that is meant to get inserted into the select | ||
// "Each option element that is a descendant of the datalist element, that is not disabled, and whose value is a string that isn't the empty string, represents a suggestion. Each suggestion has a value and a label." (W3C) | ||
if (optionValue !== '' && optionValue.toLowerCase() | ||
.indexOf(inputValue.toLowerCase()) !== -1 && opt.disabled === false) { | ||
// Define function for setting up the polyfilling select | ||
var setUpPolyfillingSelect = function(input, dataList) { | ||
// Check for whether it's of one of the supported input types defined at the beginning | ||
if (supportedTypes.indexOf(input.type) > -1) { | ||
// Still check for an existing instance | ||
if (dataList !== null) { | ||
var message = dataList.title, | ||
rects = input.getClientRects(), | ||
// Measurements | ||
inputStyles = window.getComputedStyle(input), | ||
inputStyleMarginRight = parseFloat( | ||
inputStyles.getPropertyValue('margin-right') | ||
), | ||
inputStyleMarginLeft = parseFloat( | ||
inputStyles.getPropertyValue('margin-left') | ||
), | ||
dataListSelect = document.createElement('select'); | ||
var label = opt.getAttribute('label'), | ||
text = opt.text, | ||
textOptionPart = text.substr(0, optionValue.length + textValueSeperator.length), | ||
optionPart = optionValue + textValueSeperator; | ||
// Setting a class for easier selecting that select afterwards | ||
dataListSelect.setAttribute('class', classNamePolyfillingSelect); | ||
// the innertext should be value / text in case they are different | ||
if (text && !label && text !== optionValue && textOptionPart !== optionPart) { | ||
opt.innerText = optionValue + textValueSeperator + text; | ||
// Set general styling related definitions | ||
dataListSelect.style.position = 'absolute'; | ||
} else if (!opt.text) { | ||
// manipulating the option inner text, that would get displayed | ||
opt.innerText = label || optionValue; | ||
} | ||
// Initially hiding the datalist select | ||
toggleVisibility(false, dataListSelect); | ||
newSelectValues.appendChild(opt); | ||
} else { | ||
// ... or put this option that isn't relevant to the users into the fragment that is supposed to get inserted outside of the select | ||
disabledValues.appendChild(opt); | ||
} | ||
}); | ||
// WAI ARIA attributes | ||
dataListSelect.setAttribute('aria-live', 'polite'); | ||
dataListSelect.setAttribute('role', 'listbox'); | ||
if (!touched) { | ||
dataListSelect.setAttribute('aria-multiselectable', 'false'); | ||
} | ||
// input the options fragment into the datalists select | ||
dataListSelect.appendChild(newSelectValues); | ||
// The select should get positioned underneath the input field ... | ||
if (inputStyles.getPropertyValue('display') === 'block') { | ||
dataListSelect.style.marginTop = | ||
'-' + inputStyles.getPropertyValue('margin-bottom'); | ||
} else { | ||
if (inputStyles.getPropertyValue('direction') === 'rtl') { | ||
dataListSelect.style.marginRight = | ||
'-' + (rects[0].width + inputStyleMarginLeft) + 'px'; | ||
} else { | ||
dataListSelect.style.marginLeft = | ||
'-' + (rects[0].width + inputStyleMarginRight) + 'px'; | ||
} | ||
var dataListSelectOptionsLength = dataListSelect.options.length; | ||
dataListSelect.style.marginTop = | ||
parseInt( | ||
rects[0].height + (input.offsetTop - dataList.offsetTop), | ||
10 | ||
) + 'px'; | ||
} | ||
dataListSelect.size = (dataListSelectOptionsLength > 10) ? 10 : dataListSelectOptionsLength; | ||
dataListSelect.multiple = (!touched && dataListSelectOptionsLength < 2); | ||
// Set the polyfilling selects border-radius equally as the one by the polyfilled input | ||
dataListSelect.style.borderRadius = inputStyles.getPropertyValue( | ||
'border-radius' | ||
); | ||
dataListSelect.style.minWidth = rects[0].width + 'px'; | ||
// input the unused options as siblings next to the select - and differentiate in between the regular, and the IE9 fix syntax upfront | ||
var dataListAppend = dataList.getElementsByClassName('ie9_fix')[0] || dataList; | ||
if (touched) { | ||
var messageElement = document.createElement('option'); | ||
dataListAppend.appendChild(disabledValues); | ||
// ... and it's first entry should contain the localized message to select an entry | ||
messageElement.innerText = message; | ||
// ... and disable this option, as it shouldn't get selected by the user | ||
messageElement.disabled = true; | ||
// ... and assign a dividable class to it | ||
messageElement.setAttribute('class', 'message'); | ||
// ... and finally insert it into the select | ||
dataListSelect.appendChild(messageElement); | ||
} | ||
if (typeof(obs) !== 'undefined') { | ||
obs.observe(dataList, { | ||
childList: true | ||
}); | ||
} | ||
// Add select to datalist element ... | ||
dataList.appendChild(dataListSelect); | ||
return dataListSelectOptionsLength; | ||
// ... and our upfollowing function to the change event | ||
}; | ||
if (touched) { | ||
dataListSelect.addEventListener('change', changeDataListSelect); | ||
} else { | ||
dataListSelect.addEventListener('click', changeDataListSelect); | ||
} | ||
dataListSelect.addEventListener('blur', changeDataListSelect); | ||
dataListSelect.addEventListener('keyup', changeDataListSelect); | ||
// focus or blur events | ||
var changesInputList = function(event) { | ||
return dataListSelect; | ||
} | ||
} | ||
}; | ||
var eventTarget = event.target, | ||
eventTargetTagName = eventTarget.tagName.toLowerCase(), | ||
dataList = eventTarget.list; | ||
// Function regarding changes to the datalist polyfilling created select | ||
var changeDataListSelect = function(event) { | ||
var eventTarget = event.target, | ||
eventTargetTagName = eventTarget.tagName.toLowerCase(); | ||
// check for whether the events target was an input datalist and whether it's of one of the supported input types defined above and still check for an existing instance | ||
if (eventTargetTagName && eventTargetTagName === 'input' && dataList !== null) { | ||
// Check for whether the events target was a select or an option | ||
if ( | ||
eventTargetTagName && | ||
(eventTargetTagName === 'select' || eventTargetTagName === 'option') | ||
) { | ||
var select = | ||
eventTargetTagName === 'select' | ||
? eventTarget | ||
: eventTarget.parentNode, | ||
datalist = select.parentNode, | ||
message = datalist.title, | ||
eventType = event.type, | ||
// ENTER and ESC | ||
visible = | ||
eventType === 'keyup' && | ||
(event.keyCode !== keyENTER && event.keyCode !== keyESC); | ||
var eventType = event.type, | ||
// creating the select if there's no instance so far (e.g. because of that it hasn't been handled or it has been dynamically inserted) | ||
dataListSelect = dataList.getElementsByClassName(classNamePolyfillingSelect)[0] || setUpPolyfillingSelect(eventTarget, dataList), | ||
// either have the select set to the state to get displayed in case of that it would have been focused or because it's the target on the inputs blur - and check for general existance of any option as suggestions | ||
visible = (dataListSelect && dataListSelect.querySelector('option:not(:disabled)') && ((eventType === 'focusin' && eventTarget.value !== '') || (event.relatedTarget && event.relatedTarget === dataListSelect))); | ||
// Change or mouseup event or ENTER key | ||
if ( | ||
eventType === 'change' || | ||
eventType === 'click' || | ||
(eventType === 'keyup' && event.keyCode === keyENTER) | ||
) { | ||
var list = datalist.id, | ||
inputList = document.querySelector('input[list="' + list + '"]'), | ||
selectValue = select.value; | ||
// test for whether this input has already been enhanced by the polyfill | ||
if (!new RegExp(' ' + classNameInput + ' ').test(' ' + eventTarget.className + ' ')) { | ||
// we'd like to prevent autocomplete on the input datalist field | ||
eventTarget.setAttribute('autocomplete', 'off'); | ||
// Input the selects value into the input on a change within the list | ||
if ( | ||
inputList !== null && | ||
typeof selectValue !== 'undefined' && | ||
selectValue.length > 0 && | ||
selectValue !== message | ||
) { | ||
var lastSeperator, evt; | ||
// WAI ARIA attributes | ||
eventTarget.setAttribute('role', 'textbox'); | ||
eventTarget.setAttribute('aria-haspopup', 'true'); | ||
eventTarget.setAttribute('aria-autocomplete', 'list'); | ||
eventTarget.setAttribute('aria-owns', eventTarget.getAttribute('list')); | ||
// In case of type=email and multiple attribute, we need to set up the resulting inputs value differently | ||
inputList.value = | ||
inputList.type === 'email' && | ||
inputList.multiple && | ||
(lastSeperator = inputList.value.lastIndexOf(',')) > -1 | ||
? inputList.value.slice(0, lastSeperator) + ',' + selectValue | ||
: (inputList.value = selectValue); | ||
// bind the keyup event on the related datalists input | ||
switch (eventType) { | ||
case 'focusin': | ||
eventTarget.addEventListener('keyup', inputInputList); | ||
// Create and dispatch the input event; divided for IE9 usage | ||
if (typeof Event === 'function') { | ||
evt = new Event('input', { | ||
bubbles: true | ||
}); | ||
} else { | ||
evt = document.createEvent('Event'); | ||
evt.initEvent('input', true, false); | ||
} | ||
inputList.dispatchEvent(evt); | ||
eventTarget.addEventListener('focusout', changesInputList, true); | ||
break; | ||
case 'blur': | ||
eventTarget.removeEventListener('keyup', inputInputList); | ||
// Finally focusing the input, as other browser do this as well | ||
inputList.focus(); | ||
eventTarget.removeEventListener('focusout', changesInputList, true); | ||
break; | ||
} | ||
// Set the visibility to false afterwards, as we're done here | ||
visible = false; | ||
} | ||
} | ||
// add class for identifying that this input is even already being polyfilled | ||
eventTarget.className += ' ' + classNameInput; | ||
} | ||
// Toggle the visibility of the datalist select according to previous checks | ||
toggleVisibility(visible, select); | ||
} | ||
}; | ||
// toggle the visibility of the datalist select according to previous checks | ||
toggleVisibility(visible, dataListSelect); | ||
} | ||
}; | ||
// Toggle the visibility of the datalist select | ||
var toggleVisibility = function(visible, dataListSelect) { | ||
if (visible) { | ||
dataListSelect.removeAttribute('hidden'); | ||
} else { | ||
dataListSelect.setAttributeNode(document.createAttribute('hidden')); | ||
} | ||
dataListSelect.setAttribute('aria-hidden', (!visible).toString()); | ||
}; | ||
// define function for setting up the polyfilling select | ||
var setUpPolyfillingSelect = function(input, dataList) { | ||
if (supportedTypes.indexOf(input.type) > -1) { | ||
// still check for an existing instance | ||
if (dataList !== null) { | ||
var message = dataList.title, | ||
rects = input.getClientRects(), | ||
// measurements | ||
inputStyles = window.getComputedStyle(input), | ||
inputStyleMarginRight = parseFloat(inputStyles.getPropertyValue('margin-right')), | ||
inputStyleMarginLeft = parseFloat(inputStyles.getPropertyValue('margin-left')), | ||
dataListSelect = document.createElement('select'); | ||
// setting a class for easier selecting that select afterwards | ||
dataListSelect.setAttribute('class', classNamePolyfillingSelect); | ||
// set general styling related definitions | ||
dataListSelect.style.position = 'absolute'; | ||
// initially hiding the datalist select | ||
toggleVisibility(false, dataListSelect); | ||
// WAI ARIA attributes | ||
dataListSelect.setAttribute('aria-live', 'polite'); | ||
dataListSelect.setAttribute('role', 'listbox'); | ||
if (!touched) { | ||
dataListSelect.setAttribute('aria-multiselectable', 'false'); | ||
} | ||
// the select should get positioned underneath the input field ... | ||
if (inputStyles.getPropertyValue('display') === 'block') { | ||
dataListSelect.style.marginTop = '-' + inputStyles.getPropertyValue('margin-bottom'); | ||
} else { | ||
if (inputStyles.getPropertyValue('direction') === 'rtl') { | ||
dataListSelect.style.marginRight = '-' + (rects[0].width + inputStyleMarginLeft) + 'px'; | ||
} else { | ||
dataListSelect.style.marginLeft = '-' + (rects[0].width + inputStyleMarginRight) + 'px'; | ||
} | ||
dataListSelect.style.marginTop = parseInt((rects[0].height + (input.offsetTop - dataList.offsetTop)), 10) + 'px'; | ||
} | ||
// set the polyfilling selects border-radius equally as the one by the polyfilled input | ||
dataListSelect.style.borderRadius = inputStyles.getPropertyValue('border-radius'); | ||
dataListSelect.style.minWidth = rects[0].width + 'px'; | ||
if (touched) { | ||
var messageElement = document.createElement('option'); | ||
// ... and it's first entry should contain the localized message to select an entry | ||
messageElement.innerText = message; | ||
// ... and disable this option, as it shouldn't get selected by the user | ||
messageElement.disabled = true; | ||
// ... and assign a dividable class to it | ||
messageElement.setAttribute('class', 'message'); | ||
// ... and finally insert it into the select | ||
dataListSelect.appendChild(messageElement); | ||
} | ||
// add select to datalist element ... | ||
dataList.appendChild(dataListSelect); | ||
// ... and our upfollowing function to the change event | ||
if (touched) { | ||
dataListSelect.addEventListener('change', changeDataListSelect); | ||
} else { | ||
dataListSelect.addEventListener('click', changeDataListSelect); | ||
} | ||
dataListSelect.addEventListener('blur', changeDataListSelect); | ||
dataListSelect.addEventListener('keyup', changeDataListSelect); | ||
return dataListSelect; | ||
} | ||
} | ||
}; | ||
// function regarding changes to the datalist polyfilling created select | ||
var changeDataListSelect = function(event) { | ||
var eventTarget = event.target, | ||
eventTargetTagName = eventTarget.tagName.toLowerCase(); | ||
// check for whether the events target was a select or an option | ||
if (eventTargetTagName && (eventTargetTagName === 'select' || eventTargetTagName === 'option')) { | ||
var select = (eventTargetTagName === 'select') ? eventTarget : eventTarget.parentNode, | ||
datalist = select.parentNode, | ||
message = datalist.title, | ||
eventType = event.type, | ||
// ENTER and ESC | ||
visible = (eventType === 'keyup' && (event.keyCode !== keyENTER && event.keyCode !== keyESC)); | ||
// change or mouseup event or ENTER key | ||
if (eventType === 'change' || eventType === 'click' || (eventType === 'keyup' && event.keyCode === keyENTER)) { | ||
var list = datalist.id, | ||
inputList = document.querySelector('input[list="' + list + '"]'), | ||
selectValue = select.value; | ||
// input the selects value into the input on a change within the list | ||
if (inputList !== null && typeof( selectValue ) !== 'undefined' && selectValue.length > 0 && selectValue !== message) { | ||
var lastSeperator, | ||
evt; | ||
// in case of type=email and multiple attribute, we need to set up the resulting inputs value differently | ||
inputList.value = (inputList.type === 'email' && inputList.multiple && (lastSeperator = inputList.value.lastIndexOf(',') ) > -1) ? | ||
inputList.value.slice(0, lastSeperator) + ',' + selectValue : | ||
inputList.value = selectValue; | ||
// create and dispatch the input event; divided for IE9 usage | ||
if (typeof(Event) === 'function') { | ||
evt = new Event('input', { | ||
bubbles:true | ||
}); | ||
} else { | ||
evt = document.createEvent('Event'); | ||
evt.initEvent('input', true, false); | ||
} | ||
inputList.dispatchEvent(evt); | ||
// finally focusing the input, as other browser do this as well | ||
inputList.focus(); | ||
// set the visibility to false afterwards, as we're done here | ||
visible = false; | ||
} | ||
} | ||
// toggle the visibility of the datalist select according to previous checks | ||
toggleVisibility(visible, select); | ||
} | ||
}; | ||
// toggle the visibility of the datalist select | ||
var toggleVisibility = function(visible, dataListSelect) { | ||
if (visible) { | ||
dataListSelect.removeAttribute('hidden'); | ||
} else { | ||
dataListSelect.setAttributeNode(document.createAttribute('hidden')); | ||
} | ||
dataListSelect.setAttribute('aria-hidden', (!visible).toString()); | ||
}; | ||
// binding the focus event - matching the input[list]s happens in the function afterwards | ||
document.addEventListener('focusin', changesInputList, true); | ||
// Binding the focus event - matching the input[list]s happens in the function afterwards | ||
document.addEventListener('focusin', changesInputList, true); | ||
})(); |
/* | ||
* datalist-polyfill.js - https://github.com/mfranzke/datalist-polyfill | ||
* Datalist polyfill - https://github.com/mfranzke/datalist-polyfill | ||
* @license Copyright(c) 2017 by Maximilian Franzke | ||
* Supported by Christian, Johannes, @mitchhentges, @mertenhanisch, @ailintom, @Kravimir, @mischah, @hryamzik, @ottoville, @IceCreamYou, @wlekin, @eddr and @beebee1987 - many thanks for that ! | ||
*/ | ||
!function(){"use strict";if("list"in document.createElement("input")&&document.createElement("datalist")&&window.HTMLDataListElement)return!1;!function(e){e&&e.prototype&&void 0===e.prototype.list&&Object.defineProperty(e.prototype,"list",{get:function(){if("object"==typeof this&&this instanceof e){var t=this.getAttribute("list");return document.getElementById(t)}return null}})}(window.HTMLInputElement),function(e){e&&e.prototype&&void 0===e.prototype.options&&Object.defineProperty(e.prototype,"options",{get:function(){if("object"==typeof this&&this instanceof e)return this.getElementsByTagName("option")}})}(window.HTMLElement);var e=!1,t=13,i=27,n=38,r=40,o=" / ",a=["text","email","number","search","tel","url"],l="polyfilled",s="polyfilling";window.addEventListener("touchstart",function t(){e=!0,window.removeEventListener("touchstart",t)});var u=window.MutationObserver||window.WebKitMutationObserver;if(void 0!==u)var d=new u(function(e){for(var t=!1,i=0;i<e.length;++i)for(var n=0;n<e[i].addedNodes.length;++n)"datalist"===e[i].target.tagName.toLowerCase()&&(t=e[i].target);if(t){var r=document.querySelector('[list="'+t.id+'"]');if(""!==r.value){var o=t;y(c(o,r),o.getElementsByClassName("polyfilling")[0])}}});var p=function(t){var i=t.target,n=i.tagName.toLowerCase(),r=i.list;if(n&&"input"===n&&null!==r){var o=r.getElementsByClassName("polyfilling")[0]||v(i,r);if(void 0!==o){var a=!1;if(27!==t.keyCode&&13!==t.keyCode){var l=i.value,s=38===t.keyCode||40===t.keyCode;if(""!==l||s){c(r,i)&&(a=!0);var u=o.options.length,d=0,p=u-1;if(e)o.selectedIndex=0;else if(-1===o.selectedIndex)switch(t.keyCode){case 38:o.selectedIndex=p;break;case 40:o.selectedIndex=0;break}s&&o.focus()}}y(a,o)}}},c=function(t,i){void 0!==d&&d.disconnect();var n=t.getElementsByClassName("polyfilling")[0]||v(i,t),r=t.querySelectorAll("option:not(:disabled)"),o=i.value,a=document.createDocumentFragment(),l=document.createDocumentFragment();if("email"===i.type&&i.multiple){var s=o.split(","),u=s.length-1;o=s[u].trim()}Array.prototype.slice.call(r).sort(function(e,t){return e.value.localeCompare(t.value)}).forEach(function(e){var t=e.value;if(""!==t&&-1!==t.toLowerCase().indexOf(o.toLowerCase())&&!1===e.disabled){var i=e.getAttribute("label"),n=e.text,r=n.substr(0,t.length+" / ".length),s=t+" / ";n&&!i&&n!==t&&r!==s?e.innerText=t+" / "+n:e.text||(e.innerText=i||t),a.appendChild(e)}else l.appendChild(e)}),n.appendChild(a);var p=n.options.length;return n.size=p>10?10:p,n.multiple=!e&&p<2,(t.getElementsByClassName("ie9_fix")[0]||t).appendChild(l),void 0!==d&&d.observe(t,{childList:!0}),p},f=function(e){var t=e.target,i=t.tagName.toLowerCase(),n=t.list;if(i&&"input"===i&&null!==n){var r=e.type,o=n.getElementsByClassName("polyfilling")[0]||v(t,n),a=o&&o.querySelector("option:not(:disabled)")&&("focusin"===r&&""!==t.value||e.relatedTarget&&e.relatedTarget===o);if(!new RegExp(" polyfilled ").test(" "+t.className+" ")){switch(t.setAttribute("autocomplete","off"),t.setAttribute("role","textbox"),t.setAttribute("aria-haspopup","true"),t.setAttribute("aria-autocomplete","list"),t.setAttribute("aria-owns",t.getAttribute("list")),r){case"focusin":t.addEventListener("keyup",p),t.addEventListener("focusout",f,!0);break;case"blur":t.removeEventListener("keyup",p),t.removeEventListener("focusout",f,!0);break}t.className+=" polyfilled"}y(a,o)}},v=function(t,i){if(a.indexOf(t.type)>-1&&null!==i){var n=i.title,r=t.getClientRects(),o=window.getComputedStyle(t),l=parseFloat(o.getPropertyValue("margin-right")),s=parseFloat(o.getPropertyValue("margin-left")),u=document.createElement("select");if(u.setAttribute("class","polyfilling"),u.style.position="absolute",y(!1,u),u.setAttribute("aria-live","polite"),u.setAttribute("role","listbox"),e||u.setAttribute("aria-multiselectable","false"),"block"===o.getPropertyValue("display")?u.style.marginTop="-"+o.getPropertyValue("margin-bottom"):("rtl"===o.getPropertyValue("direction")?u.style.marginRight="-"+(r[0].width+s)+"px":u.style.marginLeft="-"+(r[0].width+l)+"px",u.style.marginTop=parseInt(r[0].height+(t.offsetTop-i.offsetTop),10)+"px"),u.style.borderRadius=o.getPropertyValue("border-radius"),u.style.minWidth=r[0].width+"px",e){var d=document.createElement("option");d.innerText=n,d.disabled=!0,d.setAttribute("class","message"),u.appendChild(d)}return i.appendChild(u),e?u.addEventListener("change",m):u.addEventListener("click",m),u.addEventListener("blur",m),u.addEventListener("keyup",m),u}},m=function(e){var t=e.target,i=t.tagName.toLowerCase();if(i&&("select"===i||"option"===i)){var n="select"===i?t:t.parentNode,r=n.parentNode,o=r.title,a=e.type,l="keyup"===a&&13!==e.keyCode&&27!==e.keyCode;if("change"===a||"click"===a||"keyup"===a&&13===e.keyCode){var s=r.id,u=document.querySelector('input[list="'+s+'"]'),d=n.value;if(null!==u&&void 0!==d&&d.length>0&&d!==o){var p,c;u.value="email"===u.type&&u.multiple&&(p=u.value.lastIndexOf(","))>-1?u.value.slice(0,p)+","+d:u.value=d,"function"==typeof Event?c=new Event("input",{bubbles:!0}):(c=document.createEvent("Event"),c.initEvent("input",!0,!1)),u.dispatchEvent(c),u.focus(),l=!1}}y(l,n)}},y=function(e,t){e?t.removeAttribute("hidden"):t.setAttributeNode(document.createAttribute("hidden")),t.setAttribute("aria-hidden",(!e).toString())};document.addEventListener("focusin",f,!0)}(); | ||
!function(){"use strict";if("list"in document.createElement("input")&&Boolean(document.createElement("datalist")&&window.HTMLDataListElement))return!1;!function(e){e&&e.prototype&&null===e.prototype.list&&Object.defineProperty(e.prototype,"list",{get:function(){return"object"==typeof this&&this instanceof e?document.getElementById(this.getAttribute("list")):null}})}(window.HTMLInputElement),function(e){e&&e.prototype&&null===e.prototype.options&&Object.defineProperty(e.prototype,"options",{get:function(){return"object"==typeof this&&this instanceof e?this.getElementsByTagName("option"):null}})}(window.HTMLElement);var e=!1,t=13,i=27,n=38,o=40,l=" / ",r=["text","email","number","search","tel","url"],a="polyfilled",s="polyfilling";window.addEventListener("touchstart",function t(){e=!0,window.removeEventListener("touchstart",t)});var u=window.MutationObserver||window.WebKitMutationObserver,d;void 0!==u&&(d=new u(function(e){var t=!1;if(e.forEach(function(e){for(var i=0;i<e.addedNodes.length;++i)"datalist"===e.target.tagName.toLowerCase()&&(t=e.target)}),t){var i=document.querySelector('[list="'+t.id+'"]');if(""!==i.value){var n=t;v(c(n,i),n.getElementsByClassName("polyfilling")[0])}}}));var p=function(t){var i=t.target,n=i.tagName.toLowerCase(),o=i.list;if(n&&"input"===n&&null!==o){var l=o.getElementsByClassName("polyfilling")[0]||m(i,o);if(null!==l){var r=!1,a=i.value,s=38===t.keyCode||40===t.keyCode;if(27!==t.keyCode&&13!==t.keyCode&&(""!==a||s)){c(o,i)&&(r=!0);var u=l.options.length,d=0,p=u-1;e?l.selectedIndex=0:-1===l.selectedIndex&&s&&(l.selectedIndex=38===t.keyCode?p:0),s&&l.focus()}v(r,l)}}},c=function(t,i){void 0!==d&&d.disconnect();var n=t.getElementsByClassName("polyfilling")[0]||m(i,t),o=t.querySelectorAll("option:not(:disabled)"),l=i.value,r=document.createDocumentFragment(),a=document.createDocumentFragment();"email"===i.type&&i.multiple&&(l=l.substring(l.lastIndexOf(",")+1)),Array.prototype.slice.call(o).sort(function(e,t){return e.value.localeCompare(t.value)}).forEach(function(e){var t=e.value;if(""!==t&&-1!==t.toLowerCase().indexOf(l.toLowerCase())&&!1===e.disabled){var i=e.getAttribute("label"),n=e.text,o=n.substr(0,t.length+" / ".length),s=t+" / ";n&&!i&&n!==t&&o!==s?e.innerText=t+" / "+n:e.text||(e.innerText=i||t),r.appendChild(e)}else a.appendChild(e)}),n.appendChild(r);var s=n.options.length;return n.size=s>10?10:s,n.multiple=!e&&s<2,(t.getElementsByClassName("ie9_fix")[0]||t).appendChild(a),void 0!==d&&d.observe(t,{childList:!0}),s},f=function(e){var t=e.target,i=t.tagName.toLowerCase(),n=t.list;if(i&&"input"===i&&null!==n){var o=e.type,l=n.getElementsByClassName("polyfilling")[0]||m(t,n),r=l&&l.querySelector("option:not(:disabled)")&&("focusin"===o&&""!==t.value||e.relatedTarget&&e.relatedTarget===l);" "+t.className+" ".indexOf(" polyfilled ")<0&&(t.setAttribute("autocomplete","off"),t.setAttribute("role","textbox"),t.setAttribute("aria-haspopup","true"),t.setAttribute("aria-autocomplete","list"),t.setAttribute("aria-owns",t.getAttribute("list")),"focusin"===o?(t.addEventListener("keyup",p),t.addEventListener("focusout",f,!0)):"blur"===o&&(t.removeEventListener("keyup",p),t.removeEventListener("focusout",f,!0)),t.className+=" polyfilled"),v(r,l)}},m=function(t,i){if(r.indexOf(t.type)>-1&&null!==i){var n=i.title,o=t.getClientRects(),l=window.getComputedStyle(t),a=parseFloat(l.getPropertyValue("margin-right")),s=parseFloat(l.getPropertyValue("margin-left")),u=document.createElement("select");if(u.setAttribute("class","polyfilling"),u.style.position="absolute",v(!1,u),u.setAttribute("aria-live","polite"),u.setAttribute("role","listbox"),e||u.setAttribute("aria-multiselectable","false"),"block"===l.getPropertyValue("display")?u.style.marginTop="-"+l.getPropertyValue("margin-bottom"):("rtl"===l.getPropertyValue("direction")?u.style.marginRight="-"+(o[0].width+s)+"px":u.style.marginLeft="-"+(o[0].width+a)+"px",u.style.marginTop=parseInt(o[0].height+(t.offsetTop-i.offsetTop),10)+"px"),u.style.borderRadius=l.getPropertyValue("border-radius"),u.style.minWidth=o[0].width+"px",e){var d=document.createElement("option");d.innerText=n,d.disabled=!0,d.setAttribute("class","message"),u.appendChild(d)}return i.appendChild(u),e?u.addEventListener("change",y):u.addEventListener("click",y),u.addEventListener("blur",y),u.addEventListener("keyup",y),u}},y=function(e){var t=e.target,i=t.tagName.toLowerCase();if(i&&("select"===i||"option"===i)){var n="select"===i?t:t.parentNode,o=n.parentNode,l=o.title,r=e.type,a="keyup"===r&&13!==e.keyCode&&27!==e.keyCode;if("change"===r||"click"===r||"keyup"===r&&13===e.keyCode){var s=o.id,u=document.querySelector('input[list="'+s+'"]'),d=n.value;if(null!==u&&void 0!==d&&d.length>0&&d!==l){var p,c;u.value="email"===u.type&&u.multiple&&(p=u.value.lastIndexOf(","))>-1?u.value.slice(0,p)+","+d:u.value=d,"function"==typeof Event?c=new Event("input",{bubbles:!0}):(c=document.createEvent("Event"),c.initEvent("input",!0,!1)),u.dispatchEvent(c),u.focus(),a=!1}}v(a,n)}},v=function(e,t){e?t.removeAttribute("hidden"):t.setAttributeNode(document.createAttribute("hidden")),t.setAttribute("aria-hidden",(!e).toString())};document.addEventListener("focusin",f,!0)}(); |
{ | ||
"name": "datalist-polyfill", | ||
"version": "1.15.1", | ||
"version": "1.16.0", | ||
"description": "A lightweight and dependency-free vanilla JavaScript datalist polyfill.", | ||
"main": "datalist-polyfill.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"test": "xo" | ||
}, | ||
@@ -17,17 +17,32 @@ "repository": { | ||
"keywords": [ | ||
"datalist", | ||
"datalist", | ||
"polyfill", | ||
"javascript", | ||
"html", | ||
"autosuggest", | ||
"autosuggester", | ||
"suggest", | ||
"suggester", | ||
"select" | ||
"autosuggest", | ||
"autosuggester", | ||
"suggest", | ||
"suggester", | ||
"select" | ||
], | ||
"bugs": { | ||
"url": "https://github.com/mfranzke/datalist-polyfill/issues" | ||
"url": "https://github.com/mfranzke/datalist-polyfill/issues" | ||
}, | ||
"homepage": "https://github.com/mfranzke/datalist-polyfill", | ||
"dependencies" : {} | ||
"dependencies": {}, | ||
"devDependencies": { | ||
"prettier": "^1.13.6", | ||
"xo": "^0.21.1" | ||
}, | ||
"xo": { | ||
"envs": [ | ||
"browser" | ||
], | ||
"prettier": true, | ||
"esnext": false, | ||
"rules": { | ||
"one-var": 0, | ||
"no-use-before-define": 0 | ||
} | ||
} | ||
} |
[npm]: https://npmjs.com/package/datalist-polyfill "datalist polyfill – on NPM" | ||
[jsdelivr]: https://www.jsdelivr.com/package/npm/datalist-polyfill "datalist polyfill – on jsDelivr" | ||
[david-dm]: https://david-dm.org/mfranzke/datalist-polyfill "datalist polyfill – on david-dm" | ||
[mit]: https://opensource.org/licenses/mit-license.php | ||
[npm-badge]: https://img.shields.io/npm/v/datalist-polyfill.svg "npm version" | ||
[downl-badge]: https://img.shields.io/npm/dt/datalist-polyfill.svg "Count of total downloads – NPM" | ||
[jsdelivr-badge]: https://data.jsdelivr.com/v1/package/npm/datalist-polyfill/badge "Count of total downloads – jsDelivr" | ||
[dependency-badge]: https://david-dm.org/mfranzke/datalist-polyfill/status.svg "Count of dependencies" | ||
[license-badge]: https://img.shields.io/npm/l/datalist-polyfill.svg "license badge" | ||
@@ -14,6 +6,9 @@ | ||
[![datalist-polyfill on Npmjs][npm-badge]][npm] | ||
[![Total downloads ~ Npmjs][downl-badge]][npm] | ||
[![jsDelivr CDN downloads][jsdelivr-badge]][jsdelivr] | ||
[![dependencies Status][dependency-badge]][david-dm] | ||
[![datalist-polyfill on Npmjs](https://img.shields.io/npm/v/datalist-polyfill.svg "npm version")][npm] | ||
[![Total downloads ~ Npmjs](https://img.shields.io/npm/dt/datalist-polyfill.svg "Count of total downloads – NPM")][npm] | ||
[![jsDelivr CDN downloads](https://data.jsdelivr.com/v1/package/npm/datalist-polyfill/badge "Count of total downloads – jsDelivr")](https://www.jsdelivr.com/package/npm/datalist-polyfill "datalist polyfill – on jsDelivr") | ||
[![dependencies Status](https://david-dm.org/mfranzke/datalist-polyfill/status.svg "Count of dependencies")](https://david-dm.org/mfranzke/datalist-polyfill "datalist polyfill – on david-dm") | ||
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) | ||
[![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/xojs/xo) | ||
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/d1f98a2d1fd44c41b7ad5c7670d8cdcd)](https://app.codacy.com/app/mfranzke/datalist-polyfill?utm_source=github.com&utm_medium=referral&utm_content=mfranzke/datalist-polyfill&utm_campaign=badger) | ||
@@ -23,7 +18,7 @@ This is a minimal and dependency-free vanilla JavaScript polyfill for the awesome datalist-functionality, that will bring joy and happiness into our lives :-) | ||
* Mainly built for Safari (but supporting IE9 as well), as nearly all of the other browsers [support it quite nicely](https://caniuse.com/#feat=datalist) | ||
* Released under the [![MIT license][license-badge]][mit] | ||
* Released under the [![MIT license](https://img.shields.io/npm/l/datalist-polyfill.svg "license badge")](https://opensource.org/licenses/mit-license.php) | ||
* Made in Germany. And supported by so many great people from all over this planet - see "Credits" accordingly. | ||
## Features | ||
* Lightweight: 5.76 kB of minified JavaScript, around 2.54 kB gzipped | ||
* Lightweight: 5.64 kB of minified JavaScript, around 2.49 kB gzipped | ||
* Fully flexible to change the datalist entries / `<option>`s | ||
@@ -30,0 +25,0 @@ * Supporting: |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
461
0
52527
2
121