datalist-polyfill
Advanced tools
Comparing version 1.13.2 to 1.14.0
{ | ||
"name": "datalist-polyfill", | ||
"description": "An extremely lightweight and library-dependency-free vanilla JavaScript datalist polyfill.", | ||
"version": "1.13.2", | ||
"version": "1.14.0", | ||
"homepage": "https://github.com/mfranzke/datalist-polyfill", | ||
@@ -21,3 +21,3 @@ "authors": [{ | ||
"type": "MIT", | ||
"url": "http://www.opensource.org/licenses/mit-license.php" | ||
"url": "https://opensource.org/licenses/mit-license.php" | ||
}], | ||
@@ -24,0 +24,0 @@ "ignore": [ |
# Changelog | ||
All notable changes to this project will be documented in this file. | ||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) | ||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). | ||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) | ||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||
## [Unreleased] | ||
## [1.14.0] - 2018-06-12 | ||
### Added | ||
- #GH-16 correction as an enhancement to the current functionality | ||
- Added MutationObserver to even also capture changes to the option elements as a correction to enhance the current functionality (#GH-16). | ||
### Changed | ||
- Changed some URLs from HTTP to the new kid on the block: HTTPS. Nice ! ;-) | ||
- Additionally did some necessary updates to the documentation. | ||
## [1.13.2] - 2018-06-11 | ||
@@ -12,0 +18,0 @@ ### Changed |
/* | ||
* datalist-polyfill.js - https://github.com/mfranzke/datalist-polyfill | ||
* @license Copyright(c) 2017 by Maximilian Franzke | ||
* Supported by Christian, Johannes, @mitchhentges, @mertenhanisch, @ailintom, @Kravimir, Michael, @hryamzik, @ottoville, @IceCreamYou, @wlekin and @eddr - many thanks for that ! | ||
* Supported by Christian, Johannes, @mitchhentges, @mertenhanisch, @ailintom, @Kravimir, @mischah, @hryamzik, @ottoville, @IceCreamYou, @wlekin, @eddr and @beebee1987 - many thanks for that ! | ||
*/ | ||
@@ -83,2 +83,35 @@ /* | ||
// for observing any changes to the option elements within the datalist elements, define MutationObserver initially | ||
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; | ||
// define a new observer | ||
if (typeof(MutationObserver) !== 'undefined') { | ||
var obs = new MutationObserver(function(mutations) { | ||
var datalistNeedsAnUpdate = false; | ||
// look through all mutations that just occured | ||
for(var i=0; i<mutations.length; ++i) { | ||
// 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; | ||
} | ||
} | ||
} | ||
if (datalistNeedsAnUpdate) { | ||
var input = document.querySelector('[list="' + datalistNeedsAnUpdate.id + '"]'); | ||
if (input.value !== '') { | ||
var dataList = datalistNeedsAnUpdate; | ||
prepOptions(dataList, input); | ||
toggleVisibility(dataList.getElementsByClassName(classNamePolyfillingSelect)[0]); | ||
} | ||
} | ||
}); | ||
} | ||
// function regarding the inputs interactions | ||
@@ -99,3 +132,3 @@ var inputInputList = function(event) { | ||
var dataListSelect = dataList.getElementsByClassName(classNamePolyfillingSelect)[0]; | ||
var dataListSelect = dataList.getElementsByClassName(classNamePolyfillingSelect)[0] || setUpPolyfillingSelect(eventTarget, dataList); | ||
@@ -112,63 +145,10 @@ // still check for an existing instance | ||
var dataListOptions = dataList.querySelectorAll('option:not([disabled]):not(.message)'), | ||
inputValue = eventTarget.value, | ||
newSelectValues = document.createDocumentFragment(), | ||
disabledValues = document.createDocumentFragment(), | ||
visible = false, | ||
multipleEmails = (eventTarget.type === 'email' && eventTarget.multiple), | ||
var inputValue = eventTarget.value, | ||
keyOpen = (event.keyCode === keyUP || event.keyCode === keyDOWN); | ||
// in case of type=email and multiple attribute, we would need to split the inputs value into pieces | ||
if (multipleEmails) { | ||
var multipleEntries = inputValue.split(','), | ||
relevantIndex = multipleEntries.length - 1; | ||
inputValue = multipleEntries[relevantIndex].trim(); | ||
} | ||
// if the input contains a value, than ... | ||
if (inputValue !== '' || keyOpen) { | ||
// ... create an array out of the options list | ||
var nodeArray = Array.prototype.slice.call(dataListOptions); | ||
prepOptions(dataList, eventTarget); | ||
// ... sort all entries and | ||
nodeArray.sort(function(a, b) { | ||
return a.value.localeCompare(b.value); | ||
}) | ||
.forEach(function(opt) { | ||
var optionValue = opt.value; | ||
// ... 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; | ||
// 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; | ||
} | ||
newSelectValues.appendChild(opt); | ||
// ... and set the state of the select to get displayed in that case | ||
visible = true; | ||
} 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); | ||
} | ||
}); | ||
// input the options fragment into the datalists select | ||
dataListSelect.appendChild(newSelectValues); | ||
var dataListSelectOptionsLength = dataListSelect.options.length, | ||
@@ -178,5 +158,2 @@ firstEntry = 0, | ||
dataListSelect.size = (dataListSelectOptionsLength > 10) ? 10 : dataListSelectOptionsLength; | ||
dataListSelect.multiple = (!touched && dataListSelectOptionsLength < 2); | ||
if (touched) { | ||
@@ -198,10 +175,6 @@ // preselect best fitting index | ||
// 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; | ||
dataListAppend.appendChild(disabledValues); | ||
} | ||
// toggle the visibility of the datalist select according to previous checks | ||
toggleVisibility(dataListSelect, visible); | ||
toggleVisibility(dataListSelect); | ||
@@ -218,97 +191,104 @@ // on arrow up or down keys, focus the select | ||
// focus or blur events | ||
var changesInputList = function(event) { | ||
// function for preparing and sorting the options/suggestions | ||
var prepOptions = function(dataList, input) { | ||
var eventTarget = event.target, | ||
eventTargetTagName = eventTarget.tagName.toLowerCase(), | ||
inputType = eventTarget.type, | ||
inputStyles = window.getComputedStyle(eventTarget); | ||
if (typeof(obs) !== 'undefined') { | ||
obs.disconnect(); | ||
} | ||
// check for whether the events target was an input datalist and whether it's of one of the supported input types defined above | ||
if (eventTargetTagName && eventTargetTagName === 'input' && eventTarget.getAttribute('list') && supportedTypes.indexOf(inputType) > -1) { | ||
var dataListSelect = dataList.getElementsByClassName(classNamePolyfillingSelect)[0] || setUpPolyfillingSelect(input, dataList), | ||
dataListOptions = dataList.querySelectorAll('option:not([disabled]):not(.message)'), | ||
inputValue = input.value, | ||
newSelectValues = document.createDocumentFragment(), | ||
disabledValues = document.createDocumentFragment(), | ||
multipleEmails = (input.type === 'email' && input.multiple); | ||
var eventType = event.type, | ||
list = eventTarget.getAttribute('list'), | ||
dataList = document.getElementById(list); | ||
// in case of type=email and multiple attribute, we would need to split the inputs value into pieces | ||
if (multipleEmails) { | ||
var multipleEntries = inputValue.split(','), | ||
relevantIndex = multipleEntries.length - 1; | ||
// still check for an existing instance | ||
if (dataList !== null) { | ||
inputValue = multipleEntries[relevantIndex].trim(); | ||
} | ||
var dataListSelect = dataList.getElementsByClassName(classNamePolyfillingSelect)[0], | ||
// 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 | ||
visible = (((eventType === 'focus' && eventTarget.value !== '') || (event.relatedTarget && event.relatedTarget === dataListSelect)) && dataListSelect && dataListSelect.options && dataListSelect.options.length), | ||
message = dataList.title; | ||
// ... create an array out of the options list | ||
var nodeArray = Array.prototype.slice.call(dataListOptions); | ||
// 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) | ||
if (dataListSelect === undefined) { | ||
var rects = eventTarget.getClientRects(), | ||
// measurements | ||
inputStyleMarginRight = parseFloat(inputStyles.getPropertyValue('margin-right')), | ||
inputStyleMarginLeft = parseFloat(inputStyles.getPropertyValue('margin-left')); | ||
// ... sort all entries and | ||
nodeArray.sort(function(a, b) { | ||
return a.value.localeCompare(b.value); | ||
}) | ||
.forEach(function(opt) { | ||
var optionValue = opt.value; | ||
dataListSelect = document.createElement('select'); | ||
// ... 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) { | ||
// setting a class for easier selecting that select afterwards | ||
dataListSelect.setAttribute('class', classNamePolyfillingSelect); | ||
var label = opt.getAttribute('label'), | ||
text = opt.text, | ||
textOptionPart = text.substr(0, optionValue.length + textValueSeperator.length), | ||
optionPart = optionValue + textValueSeperator; | ||
// set general styling related definitions | ||
dataListSelect.style.position = 'absolute'; | ||
// the innertext should be value / text in case they are different | ||
if (text && !label && text !== optionValue && textOptionPart !== optionPart) { | ||
opt.innerText = optionValue + textValueSeperator + text; | ||
// initially hiding the datalist select | ||
toggleVisibility(dataListSelect, false); | ||
} else if (!opt.text) { | ||
// manipulating the option inner text, that would get displayed | ||
opt.innerText = label || optionValue; | ||
} | ||
// WAI ARIA attributes | ||
dataListSelect.setAttribute('aria-live', 'polite'); | ||
dataListSelect.setAttribute('role', 'listbox'); | ||
if (!touched) { | ||
dataListSelect.setAttribute('aria-multiselectable', 'false'); | ||
} | ||
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); | ||
} | ||
}); | ||
// 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'; | ||
} | ||
// input the options fragment into the datalists select | ||
dataListSelect.appendChild(newSelectValues); | ||
dataListSelect.style.marginTop = parseInt((rects[0].height + (eventTarget.offsetTop - dataList.offsetTop)), 10) + 'px'; | ||
} | ||
var dataListSelectOptionsLength = dataListSelect.options.length; | ||
// 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'; | ||
dataListSelect.size = (dataListSelectOptionsLength > 10) ? 10 : dataListSelectOptionsLength; | ||
dataListSelect.multiple = (!touched && dataListSelectOptionsLength < 2); | ||
if (touched) { | ||
var messageElement = document.createElement('option'); | ||
// 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; | ||
// ... 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); | ||
} | ||
dataListAppend.appendChild(disabledValues); | ||
// add select to datalist element ... | ||
dataList.appendChild(dataListSelect); | ||
if (typeof(obs) !== 'undefined') { | ||
obs.observe(dataList, { | ||
childList: true | ||
}); | ||
} | ||
// ... 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) { | ||
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 | ||
if (eventTargetTagName && eventTargetTagName === 'input' && eventTarget.getAttribute('list')) { | ||
var eventType = event.type, | ||
list = eventTarget.getAttribute('list'), | ||
dataList = document.getElementById(list); | ||
// still check for an existing instance | ||
if (dataList !== null) { | ||
// 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) | ||
var 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 = (((eventType === 'focus' && eventTarget.value !== '') || (event.relatedTarget && event.relatedTarget === dataListSelect)) && dataListSelect && dataListSelect.options && dataListSelect.options.length); | ||
// test for whether this input has already been enhanced by the polyfill | ||
if (!new RegExp(' ' + classNameInput + ' ').test(' ' + eventTarget.className + ' ')) { | ||
// plus we'd like to prevent autocomplete on the input datalist field | ||
// we'd like to prevent autocomplete on the input datalist field | ||
eventTarget.setAttribute('autocomplete', 'off'); | ||
@@ -346,2 +326,83 @@ | ||
// define function for setting up the polyfilling select | ||
var setUpPolyfillingSelect = function(input, dataList) { | ||
var inputType = input.type; | ||
if (supportedTypes.indexOf(inputType) > -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(dataListSelect, false); | ||
// 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 | ||
@@ -395,3 +456,3 @@ var changeDataListSelect = function(event) { | ||
// finally focusing the input, as other browser do it as well | ||
// finally focusing the input, as other browser do this as well | ||
inputList.focus(); | ||
@@ -411,2 +472,6 @@ | ||
var toggleVisibility = function(dataListSelect, visible) { | ||
if (visible === undefined) { | ||
visible = (dataListSelect && dataListSelect.options && dataListSelect.options.length); | ||
} | ||
if (visible) { | ||
@@ -413,0 +478,0 @@ dataListSelect.removeAttribute('hidden'); |
/* | ||
* datalist-polyfill.js - https://github.com/mfranzke/datalist-polyfill | ||
* @license Copyright(c) 2017 by Maximilian Franzke | ||
* Supported by Christian, Johannes, @mitchhentges, @mertenhanisch, @ailintom, @Kravimir, Michael, @hryamzik, @ottoville, @IceCreamYou, @wlekin and @eddr - many thanks for that ! | ||
* 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,o=40,r=" / ",a=["text","email","number","search","tel","url"],l="polyfilled",s="polyfilling";window.addEventListener("touchstart",function t(){e=!0,window.removeEventListener("touchstart",t)});var u=function(t){var i=t.target,n=i.tagName.toLowerCase();if(n&&"input"===n&&i.getAttribute("list")){var o=i.getAttribute("list"),r=document.getElementById(o);if(null!==r){var a=r.getElementsByClassName("polyfilling")[0];if(void 0!==a){if(27===t.keyCode)return void c(a,!1);var l=r.querySelectorAll("option:not([disabled]):not(.message)"),s=i.value,u=document.createDocumentFragment(),d=document.createDocumentFragment(),p=!1,f="email"===i.type&&i.multiple,m=38===t.keyCode||40===t.keyCode;if(f){var y=s.split(","),g=y.length-1;s=y[g].trim()}if(""!==s||m){Array.prototype.slice.call(l).sort(function(e,t){return e.value.localeCompare(t.value)}).forEach(function(e){var t=e.value;if(""!==t&&-1!==t.toLowerCase().indexOf(s.toLowerCase())&&!1===e.disabled){var i=e.getAttribute("label"),n=e.text,o=n.substr(0,t.length+" / ".length),r=t+" / ";n&&!i&&n!==t&&o!==r?e.innerText=t+" / "+n:e.text||(e.innerText=i||t),u.appendChild(e),p=!0}else d.appendChild(e)}),a.appendChild(u);var v=a.options.length,b=0,h=v-1;if(a.size=v>10?10:v,a.multiple=!e&&v<2,e)a.selectedIndex=0;else if(-1===a.selectedIndex)switch(t.keyCode){case 38:a.selectedIndex=h;break;case 40:a.selectedIndex=0;break}(r.getElementsByClassName("ie9_fix")[0]||r).appendChild(d)}c(a,p),m&&a.focus()}}}},d=function(t){var i=t.target,n=i.tagName.toLowerCase(),o=i.type,r=window.getComputedStyle(i);if(n&&"input"===n&&i.getAttribute("list")&&a.indexOf(o)>-1){var l=t.type,s=i.getAttribute("list"),f=document.getElementById(s);if(null!==f){var m=f.getElementsByClassName("polyfilling")[0],y=("focus"===l&&""!==i.value||t.relatedTarget&&t.relatedTarget===m)&&m&&m.options&&m.options.length,g=f.title;if(void 0===m){var v=i.getClientRects(),b=parseFloat(r.getPropertyValue("margin-right")),h=parseFloat(r.getPropertyValue("margin-left"));if(m=document.createElement("select"),m.setAttribute("class","polyfilling"),m.style.position="absolute",c(m,!1),m.setAttribute("aria-live","polite"),m.setAttribute("role","listbox"),e||m.setAttribute("aria-multiselectable","false"),"block"===r.getPropertyValue("display")?m.style.marginTop="-"+r.getPropertyValue("margin-bottom"):("rtl"===r.getPropertyValue("direction")?m.style.marginRight="-"+(v[0].width+h)+"px":m.style.marginLeft="-"+(v[0].width+b)+"px",m.style.marginTop=parseInt(v[0].height+(i.offsetTop-f.offsetTop),10)+"px"),m.style.borderRadius=r.getPropertyValue("border-radius"),m.style.minWidth=v[0].width+"px",e){var E=document.createElement("option");E.innerText=g,E.disabled=!0,E.setAttribute("class","message"),m.appendChild(E)}f.appendChild(m),e?m.addEventListener("change",p):m.addEventListener("click",p),m.addEventListener("blur",p),m.addEventListener("keyup",p)}if(!new RegExp(" polyfilled ").test(" "+i.className+" ")){switch(i.setAttribute("autocomplete","off"),i.setAttribute("role","textbox"),i.setAttribute("aria-haspopup","true"),i.setAttribute("aria-autocomplete","list"),i.setAttribute("aria-owns",s),l){case"focus":i.addEventListener("keyup",u),i.addEventListener("focusout",d,!0);break;case"blur":i.removeEventListener("keyup",u),i.removeEventListener("focusout",d,!0);break}i.className+=" polyfilled"}c(m,y)}}},p=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,r=o.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=o.id,u=document.querySelector('input[list="'+s+'"]'),d=n.value;if(null!==u&&void 0!==d&&d.length>0&&d!==r){var p=u.value,f,m="email"===u.type&&u.multiple,y;m&&(f=p.lastIndexOf(","))>-1?u.value=p.slice(0,f)+","+d:u.value=d,"function"==typeof Event?y=new Event("input",{bubbles:!0}):(y=document.createEvent("Event"),y.initEvent("input",!0,!1)),u.dispatchEvent(y),u.focus(),l=!1}}c(n,l)}},c=function(e,t){t?e.removeAttribute("hidden"):e.setAttributeNode(document.createAttribute("hidden")),e.setAttribute("aria-hidden",(!t).toString())};document.addEventListener("focus",d,!0)}(); | ||
!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,o=40,r=" / ",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 o=document.querySelector('[list="'+t.id+'"]');if(""!==o.value){var r=t;c(r,o),g(r.getElementsByClassName("polyfilling")[0])}}});var p=function(t){var i=t.target,n=i.tagName.toLowerCase();if(n&&"input"===n&&i.getAttribute("list")){var o=i.getAttribute("list"),r=document.getElementById(o);if(null!==r){var a=r.getElementsByClassName("polyfilling")[0]||m(i,r);if(void 0!==a){if(27===t.keyCode)return void g(a,!1);var l=i.value,s=38===t.keyCode||40===t.keyCode;if(""!==l||s){c(r,i);var u=a.options.length,d=0,p=u-1;if(e)a.selectedIndex=0;else if(-1===a.selectedIndex)switch(t.keyCode){case 38:a.selectedIndex=p;break;case 40:a.selectedIndex=0;break}}g(a),s&&a.focus()}}}},c=function(t,i){void 0!==d&&d.disconnect();var n=t.getElementsByClassName("polyfilling")[0]||m(i,t),o=t.querySelectorAll("option:not([disabled]):not(.message)"),r=i.value,a=document.createDocumentFragment(),l=document.createDocumentFragment();if("email"===i.type&&i.multiple){var s=r.split(","),u=s.length-1;r=s[u].trim()}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(r.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),a.appendChild(e)}else l.appendChild(e)}),n.appendChild(a);var p=n.options.length;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})},f=function(e){var t=e.target,i=t.tagName.toLowerCase();if(i&&"input"===i&&t.getAttribute("list")){var n=e.type,o=t.getAttribute("list"),r=document.getElementById(o);if(null!==r){var a=r.getElementsByClassName("polyfilling")[0]||m(t,r),l=("focus"===n&&""!==t.value||e.relatedTarget&&e.relatedTarget===a)&&a&&a.options&&a.options.length;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",o),n){case"focus":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"}g(a,l)}}},m=function(t,i){var n=t.type;if(a.indexOf(n)>-1&&null!==i){var o=i.title,r=t.getClientRects(),l=window.getComputedStyle(t),s=parseFloat(l.getPropertyValue("margin-right")),u=parseFloat(l.getPropertyValue("margin-left")),d=document.createElement("select");if(d.setAttribute("class","polyfilling"),d.style.position="absolute",g(d,!1),d.setAttribute("aria-live","polite"),d.setAttribute("role","listbox"),e||d.setAttribute("aria-multiselectable","false"),"block"===l.getPropertyValue("display")?d.style.marginTop="-"+l.getPropertyValue("margin-bottom"):("rtl"===l.getPropertyValue("direction")?d.style.marginRight="-"+(r[0].width+u)+"px":d.style.marginLeft="-"+(r[0].width+s)+"px",d.style.marginTop=parseInt(r[0].height+(t.offsetTop-i.offsetTop),10)+"px"),d.style.borderRadius=l.getPropertyValue("border-radius"),d.style.minWidth=r[0].width+"px",e){var p=document.createElement("option");p.innerText=o,p.disabled=!0,p.setAttribute("class","message"),d.appendChild(p)}return i.appendChild(d),e?d.addEventListener("change",v):d.addEventListener("click",v),d.addEventListener("blur",v),d.addEventListener("keyup",v),d}},v=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,r=o.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=o.id,u=document.querySelector('input[list="'+s+'"]'),d=n.value;if(null!==u&&void 0!==d&&d.length>0&&d!==r){var p=u.value,c,f="email"===u.type&&u.multiple,m;f&&(c=p.lastIndexOf(","))>-1?u.value=p.slice(0,c)+","+d:u.value=d,"function"==typeof Event?m=new Event("input",{bubbles:!0}):(m=document.createEvent("Event"),m.initEvent("input",!0,!1)),u.dispatchEvent(m),u.focus(),l=!1}}g(n,l)}},g=function(e,t){void 0===t&&(t=e&&e.options&&e.options.length),t?e.removeAttribute("hidden"):e.setAttributeNode(document.createAttribute("hidden")),e.setAttribute("aria-hidden",(!t).toString())};document.addEventListener("focus",f,!0)}(); |
{ | ||
"name": "datalist-polyfill", | ||
"version": "1.13.2", | ||
"version": "1.14.0", | ||
"description": "A lightweight and dependency-free vanilla JavaScript datalist polyfill.", | ||
@@ -5,0 +5,0 @@ "main": "datalist-polyfill.js", |
@@ -10,7 +10,8 @@ # datalist-polyfill | ||
Tested in Safari, which it's mainly meant for, as nearly all of the other browsers support it quite well: <http://caniuse.com/#feat=datalist> | ||
Released under the MIT License: <http://www.opensource.org/licenses/mit-license.php> | ||
* Mainly built for Safari, as nearly all of the other browsers support it quite well: <https://caniuse.com/#feat=datalist> | ||
* Released under the MIT License: <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.24 kB of JavaScript, around 2.35 kB gzipped | ||
* Lightweight: 5.94 kB of minified JavaScript, around 2.59 kB gzipped | ||
* Fully flexible to change the datalist entries / `<option>`s | ||
@@ -35,3 +36,3 @@ * Supporting: | ||
* dependency-free | ||
* Supporting DOM changes by event delegation | ||
* Supporting DOM changes by event delegation and MutationObserver | ||
* code accessibility / readability | ||
@@ -55,3 +56,3 @@ | ||
### dynamic HTML (or DHTML, if you like to be a little bit nostalgic) | ||
In case that you'd like to dynamically add or modify / create your HTML code, you're good to go with this polyfill, as it's based on event delegation that makes your UI work easily - no refresh nor reinit function to call after DOM manipulation or something similar. | ||
In case that you'd like to dynamically add or modify / create your HTML code, you're good to go with this polyfill, as it's based on event delegation and additionally using MutationObserver (IE11+) that makes your UI work easily - no refresh nor reinit function to call after DOM manipulation or something similar. | ||
@@ -106,3 +107,3 @@ ### Changes to the available `option` elements | ||
## Credits | ||
Supported by Christian, Johannes, @mitchhentges, @mertenhanisch, @ailintom, @Kravimir, @mischah, @hryamzik, @ottoville, @IceCreamYou, @wlekin and @eddr. Thank you very much for that, highly appreciated ! | ||
Supported by Christian, Johannes, @mitchhentges, @mertenhanisch, @ailintom, @Kravimir, @mischah, @hryamzik, @ottoville, @IceCreamYou, @wlekin, @eddr and @beebee1987. Thank you very much for that, highly appreciated ! | ||
@@ -121,2 +122,4 @@ ## Tested with | ||
* iPhone 6 Plus / 10.3, Mobile Safari 10.0 | ||
* Windows | ||
* Windows 7 SP1, Internet Explorer 9.0.8112.16421 | ||
@@ -123,0 +126,0 @@ ## Outro |
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
51655
408
128