datalist-polyfill
Advanced tools
Comparing version 1.23.3 to 1.24.0
@@ -14,2 +14,27 @@ # Changelog | ||
## [1.24.0] - 2020-02-26 | ||
### Added | ||
- Linking Apples changelog on the datalist feature to our readme | ||
- gitignore file | ||
- Docs: further mentioned IE10+ and EDGE specifics | ||
- .npmignore file | ||
- Commitlint & husky for CI | ||
### Changed | ||
- Updated webdriverio and xo dependencies | ||
- Reformatted the JS code | ||
- Removed pointless loop in the code PR #GH-61 | ||
- Updated docs regarding browser support #GH-62 | ||
### Fixed | ||
- Docs: filesize in the readme | ||
- fixed to have a "correct" minification #GH-58 | ||
- Fix exception in IE9 emulator mode PR #GH-65 | ||
- Badges displaying problems in some readers / compilers | ||
- Docs: optimizing on the typos and clearity | ||
## [1.23.3] - 2019-01-28 | ||
@@ -16,0 +41,0 @@ |
/* | ||
* 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, @beebee1987, @mricherzhagen, @acespace90, @damien-git and @nexces - many thanks for that ! | ||
* Supported by Christian, Johannes, @mitchhentges, @mertenhanisch, @ailintom, @Kravimir, @mischah, @hryamzik, @ottoville, @IceCreamYou, @wlekin, @eddr, @beebee1987, @mricherzhagen, @acespace90, @damien-git, @nexces, @Sora2455, @jscho13 and @alexirion - many thanks for that ! | ||
*/ | ||
@@ -23,6 +23,8 @@ /* | ||
Boolean(dcmnt.createElement('datalist') && window.HTMLDataListElement), | ||
// IE & EDGE browser detection via UserAgent | ||
// TODO: obviously ugly. But sadly necessary until Microsoft enhances the UX within EDGE (compare to https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/9573654/) | ||
// adapted out of https://gist.github.com/gaboratorium/25f08b76eb82b1e7b91b01a0448f8b1d : | ||
isGteIE10 = Boolean(ua.match(/Trident\/[6-7]\./)), | ||
// Tested against the following UA strings: http://useragentstring.com/pages/useragentstring.php?name=Internet+Explorer | ||
// And adapted out of https://gist.github.com/gaboratorium/25f08b76eb82b1e7b91b01a0448f8b1d : | ||
isGteIE10 = Boolean(ua.match(/MSIE\s1[0-1]./) || ua.match(/rv:11./)), | ||
isEDGE = Boolean(ua.indexOf('Edge/') !== -1); | ||
@@ -77,7 +79,7 @@ | ||
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; | ||
} | ||
// Check if any of the mutated nodes was a datalist | ||
if (mutation.target instanceof HTMLElement && | ||
mutation.target.tagName.toLowerCase() === "datalist" && | ||
mutation.addedNodes.length > 1) { | ||
datalistNeedsAnUpdate = mutation.target; | ||
} | ||
@@ -131,2 +133,3 @@ }); | ||
} | ||
return; | ||
@@ -193,5 +196,5 @@ } | ||
/* | ||
/* as mentioned in the discussion within #GH-63: | ||
Check for whether the current option is a valid suggestion and replace its value by | ||
- the current input string, as IE10+ and EDGE don't do substring, but only prefix matching | ||
- the current input string, as IE10+ and EDGE don't do substring, but only prefix matching (#GH-36, #GH-39) | ||
- followed by a unique string that should prevent any interferance | ||
@@ -222,3 +225,5 @@ - and the original string, that is still necessary e.g. for sorting within the suggestions list | ||
var option = datalist.querySelector( | ||
'option[value="' + getInputValue(input).replace(/\\([\s\S])|(")/g, '\\$1$2') + '"]' | ||
'option[value="' + | ||
getInputValue(input).replace(/\\([\s\S])|(")/g, '\\$1$2') + | ||
'"]' | ||
); | ||
@@ -339,6 +344,6 @@ | ||
// Using .getAttribute here for IE9 purpose - elsewhere it wouldn't return the newer HTML5 values correctly | ||
return ( | ||
input.getAttribute('type') === 'email' && | ||
return input.getAttribute('type') === 'email' && | ||
input.getAttribute('multiple') !== null | ||
) ? input.value.substring(input.value.lastIndexOf(',') + 1) : input.value; | ||
? input.value.substring(input.value.lastIndexOf(',') + 1) | ||
: input.value; | ||
}; | ||
@@ -540,2 +545,3 @@ | ||
} | ||
datalistSelect.addEventListener('blur', changeDataListSelect); | ||
@@ -640,2 +646,3 @@ datalistSelect.addEventListener('keydown', changeDataListSelect); | ||
} | ||
input.dispatchEvent(evt); | ||
@@ -651,2 +658,3 @@ }; | ||
} | ||
datalistSelect.setAttribute('aria-hidden', (!visible).toString()); | ||
@@ -682,2 +690,3 @@ }; | ||
})(window.HTMLInputElement); | ||
// Options property / https://developer.mozilla.org/en/docs/Web/API/HTMLDataListElement | ||
@@ -684,0 +693,0 @@ (function(constructor) { |
/* | ||
* 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, @beebee1987, @mricherzhagen, @acespace90, @damien-git and @nexces - many thanks for that ! | ||
* Supported by Christian, Johannes, @mitchhentges, @mertenhanisch, @ailintom, @Kravimir, @mischah, @hryamzik, @ottoville, @IceCreamYou, @wlekin, @eddr, @beebee1987, @mricherzhagen, @acespace90, @damien-git, @nexces, @jscho13 and @alexirion - many thanks for that ! | ||
*/ | ||
/* | ||
* A minimal and dependency-free vanilla JavaScript datalist polyfill. | ||
* Supports all standard's functionality as well as mimics other browsers behavior. | ||
* Tests for native support of an inputs elements datalist functionality. | ||
* Elsewhere the functionality gets emulated by a select element. | ||
*/ | ||
!function(){"use strict"; | ||
// Performance: Set local variables | ||
var u=window.document,e=window.navigator.userAgent, | ||
// Feature detection | ||
t="list"in u.createElement("input")&&Boolean(u.createElement("datalist")&&window.HTMLDataListElement), | ||
// IE & EDGE browser detection via UserAgent | ||
// TODO: obviously ugly. But sadly necessary until Microsoft enhances the UX within EDGE (compare to https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/9573654/) | ||
// adapted out of https://gist.github.com/gaboratorium/25f08b76eb82b1e7b91b01a0448f8b1d : | ||
s=Boolean(e.match(/Trident\/[6-7]\./)),d=Boolean(-1!==e.indexOf("Edge/")); | ||
// Let's break here, if it's even already supported ... and not IE10+ or EDGE | ||
if(t&&!s&&!d)return; | ||
// .matches polyfill | ||
// TODO: probably needs enhancement on the expected to be supported browsers | ||
Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector); | ||
// Define some global settings and configurations | ||
var p=!1, | ||
// Speaking variables for the different keycodes | ||
c=13,v=27,y=38,f=40, | ||
// Defining the text / value seperator for displaying the value and text values ... | ||
g=" / ", | ||
// ... and defining the different input types that are supported by this polyfill | ||
l=["text","email","number","search","tel","url"], | ||
// Classes for elements | ||
o="polyfilled",m="polyfilling", | ||
// Defining a most likely unique polyfill string | ||
n="###[P0LYFlLLed]###"; | ||
// Differentiate for touch interactions, adapted by https://medium.com/@david.gilbertson/the-only-way-to-detect-touch-with-javascript-7791a3346685 | ||
window.addEventListener("touchstart",function e(){p=!0,window.removeEventListener("touchstart",e)}); | ||
// For observing any changes to the option elements within the datalist elements, define MutationObserver initially | ||
var i=window.MutationObserver||window.WebKitMutationObserver,a; | ||
// Define a new observer | ||
void 0!==i&&(a=new i(function(e){var i=!1; | ||
// Look through all mutations that just occured | ||
if(e.forEach(function(e){ | ||
// Look through all added nodes of this mutation | ||
for(var t=0;t<e.addedNodes.length;++t)"datalist"===e.target.tagName.toLowerCase()&&(i=e.target)}),i){var t=u.querySelector('input[list="'+i.id+'"]');""!==A(t)&& | ||
// Prepare the options and toggle the visiblity afterwards | ||
O(k(i,t).length,i.getElementsByClassName(m)[0])}})); | ||
// Function regarding the inputs interactions on keyup event | ||
var r=function(e){var t=e.target,i=t.list,a=e.keyCode===y||e.keyCode===f; | ||
// Check for whether the events target was an input and still check for an existing instance of the datalist and polyfilling select | ||
if("input"===t.tagName.toLowerCase()&&null!==i) | ||
// Handling IE10+ & EDGE | ||
if(s||d) | ||
// On keypress check for value | ||
""===A(t)||a||e.keyCode===c||e.keyCode===v|| | ||
// As only EDGE doesn't trigger the input event after selecting an item via mouse, we need to differentiate here | ||
!s&&"text"!==t.type||(b(t,i), | ||
// TODO: Check whether this update is necessary depending on the options values | ||
t.focus());else{var n=!1, | ||
// 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) | ||
r=i.getElementsByClassName(m)[0]||x(t,i); | ||
// On an ESC or ENTER key press within the input, let's break here and afterwards hide the datalist select, but if the input contains a value or one of the opening keys have been pressed ... | ||
if(e.keyCode!==v&&e.keyCode!==c&&(""!==A(t)||a)&&void 0!==r){ | ||
// ... prepare the options | ||
0<k(i,t).length&&(n=!0);var o=0,l=r.options.length-1; | ||
// ... preselect best fitting index | ||
p?r.selectedIndex=0:a&&"number"!==t.getAttribute("type")&&(r.selectedIndex=e.keyCode===y?l:0, | ||
// ... and on arrow up or down keys, focus the select | ||
r.focus())} | ||
// Toggle the visibility of the datalist select according to previous checks | ||
O(n,r)}},b=function(e,t){var a=A(e); | ||
// Loop through the options | ||
Array.prototype.slice.call(t.options,0).forEach(function(e){ | ||
// We're using .getAttribute instead of .dataset here for IE10 | ||
var t=e.getAttribute("data-originalvalue"),i=t||e.value; | ||
// In case of that the original value hasn't been saved as data so far, do that now | ||
t|| | ||
// We're using .setAttribute instead of .dataset here for IE10 | ||
e.setAttribute("data-originalvalue",i), | ||
// As we'd manipulate the value in the next step, we'd like to put in that value as either a label or text if none of those exist | ||
e.label||e.text||(e.label=i) | ||
/* | ||
Check for whether the current option is a valid suggestion and replace its value by | ||
- the current input string, as IE10+ and EDGE don't do substring, but only prefix matching | ||
- followed by a unique string that should prevent any interferance | ||
- and the original string, that is still necessary e.g. for sorting within the suggestions list | ||
As the value is being inserted on users selection, we'll replace that one within the upfollowing inputInputListIE function | ||
*/,e.value=w(e,a)?a+n+i.toLowerCase():i})},h=function(e){var t=e.target,i=t.list;if(t.matches("input[list]")&&t.matches("."+o)&&i){ | ||
// Query for related option - and escaping the value as doublequotes wouldn't work | ||
var a=i.querySelector('option[value="'+A(t).replace(/\\([\s\S])|(")/g,"\\$1$2")+'"]'); | ||
// We're using .getAttribute instead of .dataset here for IE10 | ||
a&&a.getAttribute("data-originalvalue")&&L(t,a.getAttribute("data-originalvalue"))}},w=function(e,t){var i=e.value.toLowerCase(),a=t.toLowerCase(),n=e.getAttribute("label"),r=e.text.toLowerCase(); | ||
/* | ||
"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." | ||
"If appropriate, the user agent should use the suggestion's label and value to identify the suggestion to the user." | ||
*/return Boolean(!1===e.disabled&&(""!==i&&-1!==i.indexOf(a)||n&&-1!==n.toLowerCase().indexOf(a)||""!==r&&-1!==r.indexOf(a)))},E=function(e){ | ||
// Check for correct element on this event delegation | ||
if(e.target.matches("input[list]")){var t=e.target,i=t.list; | ||
// Check for whether the events target was an input and still check for an existing instance of the datalist | ||
if("input"===t.tagName.toLowerCase()&&null!==i){ | ||
// #GH-49: Microsoft EDGE / datalist popups get "emptied" when receiving focus via tabbing | ||
if( | ||
// Test for whether this input has already been enhanced by the polyfill | ||
t.matches("."+o)||C(t,e.type),d&&"focusin"===e.type){ | ||
// Set the value of the first option to it's value - this actually triggers a redraw of the complete list | ||
var a=t.list.options[0];a.value=a.value} | ||
// Break here for IE10+ & EDGE | ||
if(!s&&!d){var// 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) | ||
n=i.getElementsByClassName(m)[0]||x(t,i), | ||
// 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 | ||
r=n&&n.querySelector("option:not(:disabled)")&&("focusin"===e.type&&""!==A(t)||e.relatedTarget&&e.relatedTarget===n); | ||
// Toggle the visibility of the datalist select according to previous checks | ||
O(r,n)}}}},C=function(e,t){ | ||
// We'd like to prevent autocomplete on the input datalist field | ||
e.setAttribute("autocomplete","off"), | ||
// WAI ARIA attributes | ||
e.setAttribute("role","textbox"),e.setAttribute("aria-haspopup","true"),e.setAttribute("aria-autocomplete","list"),e.setAttribute("aria-owns",e.getAttribute("list")), | ||
// Bind the keyup event on the related datalists input | ||
"focusin"===t?(e.addEventListener("keyup",r),e.addEventListener("focusout",E,!0), | ||
// As only EDGE doesn't trigger the input event after selecting an item via mouse, we need to differentiate here | ||
(s||d&&"text"===e.type)&&e.addEventListener("input",h)):"blur"===t&&(e.removeEventListener("keyup",r),e.removeEventListener("focusout",E,!0), | ||
// As only EDGE doesn't trigger the input event after selecting an item via mouse, we need to differentiate here | ||
(s||d&&"text"===e.type)&&e.removeEventListener("input",h)), | ||
// Add class for identifying that this input is even already being polyfilled | ||
e.className+=" "+o},A=function(e){ | ||
// In case of type=email and multiple attribute, we would need to grab the last piece | ||
// Using .getAttribute here for IE9 purpose - elsewhere it wouldn't return the newer HTML5 values correctly | ||
return"email"===e.getAttribute("type")&&null!==e.getAttribute("multiple")?e.value.substring(e.value.lastIndexOf(",")+1):e.value},L=function(e,t){var i; | ||
// In case of type=email and multiple attribute, we need to set up the resulting inputs value differently | ||
e.value= | ||
// Using .getAttribute here for IE9 purpose - elsewhere it wouldn't return the newer HTML5 values correctly | ||
"email"===e.getAttribute("type")&&null!==e.getAttribute("multiple")&&-1<(i=e.value.lastIndexOf(","))?e.value.slice(0,i)+","+t:t}; | ||
// On keypress check all options for that as a substring, save the original value as a data-attribute and preset that inputs value (for sorting) for all option values (probably as well enhanced by a token) | ||
// Break here for IE10+ & EDGE | ||
if( | ||
// Binding the focus event - matching the input[list]s happens in the function afterwards | ||
u.addEventListener("focusin",E,!0),!s&&!d){ | ||
// Function for preparing and sorting the options/suggestions | ||
var k=function(e,n){void 0!==a&&a.disconnect();var// 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) | ||
t=e.getElementsByClassName(m)[0]||x(n,e),o=A(n),l=u.createDocumentFragment(),s=u.createDocumentFragment(); | ||
// Create an array out of the options list | ||
Array.prototype.slice.call(e.querySelectorAll("option:not(:disabled)")).sort(function(e,t){var i=e.value,a=t.value; | ||
// Using the knowledge that the values are URLs to allow the user to omit the scheme part and perform intelligent matching on the domain name | ||
return"url"===n.getAttribute("type")&&(i=i.replace(/(^\w+:|^)\/\//,""),a=a.replace(/(^\w+:|^)\/\//,"")),i.localeCompare(a)}).forEach(function(e){var t=e.value,i=e.getAttribute("label"),a=e.text; | ||
// Put this option into the fragment that is meant to get inserted into the select. Additionally according to the specs ... | ||
// TODO: This might get slightly changed/optimized in a future release | ||
if(w(e,o)){var n=a.substr(0,t.length+g.length),r; | ||
// The innertext should be 'value seperator text' in case they are different | ||
a&&!i&&a!==t&&n!==t+g?e.innerText=t+g+a:e.text||( | ||
// Manipulating the option inner text, that would get displayed | ||
e.innerText=i||t),l.appendChild(e)}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 | ||
s.appendChild(e)}), | ||
// Input the options fragment into the datalists select | ||
t.appendChild(l);var i=t.options.length;return t.size=10<i?10:i,t.multiple=!p&&i<2, | ||
// Input the unused options as siblings next to the select - and differentiate in between the regular, and the IE9 fix syntax upfront | ||
(e.getElementsByClassName("ie9_fix")[0]||e).appendChild(s),void 0!==a&&a.observe(e,{childList:!0}),t.options},x=function(e,t){ | ||
// Check for whether it's of one of the supported input types defined at the beginning | ||
// Using .getAttribute here for IE9 purpose - elsewhere it wouldn't return the newer HTML5 values correctly | ||
// and still check for an existing instance | ||
if(!(e.getAttribute("type")&&-1===l.indexOf(e.getAttribute("type"))||null===t)){var i=e.getClientRects(), | ||
// Measurements | ||
a=window.getComputedStyle(e),n=u.createElement("select"); | ||
// Setting a class for easier identifying that select afterwards | ||
// The select should get positioned underneath the input field ... | ||
if(n.setAttribute("class",m), | ||
// Set general styling related definitions | ||
n.style.position="absolute", | ||
// Initially hiding the datalist select | ||
O(!1,n), | ||
// The select itself shouldn't be a possible target for tabbing | ||
n.setAttribute("tabindex","-1"), | ||
// WAI ARIA attributes | ||
n.setAttribute("aria-live","polite"),n.setAttribute("role","listbox"),p||n.setAttribute("aria-multiselectable","false"),"block"===a.getPropertyValue("display"))n.style.marginTop="-"+a.getPropertyValue("margin-bottom");else{var r="rtl"===a.getPropertyValue("direction")?"right":"left";n.style.setProperty("margin-"+r,"-"+(i[0].width+parseFloat(a.getPropertyValue("margin-"+r)))+"px"),n.style.marginTop=parseInt(i[0].height+(e.offsetTop-t.offsetTop),10)+"px"} | ||
// Set the polyfilling selects border-radius equally to the one by the polyfilled input | ||
if(n.style.borderRadius=a.getPropertyValue("border-radius"),n.style.minWidth=i[0].width+"px",p){var o=u.createElement("option"); | ||
// ... and it's first entry should contain the localized message to select an entry | ||
o.innerText=t.title, | ||
// ... and disable this option, as it shouldn't get selected by the user | ||
o.disabled=!0, | ||
// ... and assign a dividable class to it | ||
o.setAttribute("class","message"), | ||
// ... and finally insert it into the select | ||
n.appendChild(o)} | ||
// Add select to datalist element ... | ||
return t.appendChild(n), | ||
// ... and our upfollowing functions to the related event | ||
p?n.addEventListener("change",N):n.addEventListener("click",N),n.addEventListener("blur",N),n.addEventListener("keydown",N),n.addEventListener("keypress",T),n}},T=function(e){var t=e.target,i=t.parentNode,a=u.querySelector('input[list="'+i.id+'"]'); | ||
// Check for whether the events target was a select or whether the input doesn't exist | ||
"select"===t.tagName.toLowerCase()&&null!==a&&( | ||
// Determine a relevant key - either printable characters (that would have a length of 1) or controlling like Backspace | ||
!e.key||"Backspace"!==e.key&&1!==e.key.length||(a.focus(),"Backspace"===e.key?(a.value=a.value.substr(0,a.value.length-1), | ||
// Dispatch the input event on the related input[list] | ||
B(a)):a.value+=e.key,k(i,a)))},N=function(e){var t=e.currentTarget,i=t.parentNode,a=u.querySelector('input[list="'+i.id+'"]'); | ||
// Check for whether the events target was a select or whether the input doesn't exist | ||
if("select"===t.tagName.toLowerCase()&&null!==a){var n=e.type, | ||
// ENTER and ESC | ||
r="keydown"===n&&e.keyCode!==c&&e.keyCode!==v; | ||
// On change, click or after pressing ENTER or TAB key, input the selects value into the input on a change within the list | ||
("change"===n||"click"===n||"keydown"===n&&(e.keyCode===c||"Tab"===e.key))&&0<t.value.length&&t.value!==i.title?(L(a,t.value), | ||
// Dispatch the input event on the related input[list] | ||
B(a), | ||
// Finally focusing the input, as other browser do this as well | ||
"Tab"!==e.key&&a.focus(), | ||
// #GH-51 / Prevent the form to be submitted on selecting a value via ENTER key within the select | ||
e.keyCode===c&&e.preventDefault(), | ||
// Set the visibility to false afterwards, as we're done here | ||
r=!1):"keydown"===n&&e.keyCode===v&& | ||
// In case of the ESC key being pressed, we still want to focus the input[list] | ||
a.focus(), | ||
// Toggle the visibility of the datalist select according to previous checks | ||
O(r,t)}},B=function(e){var t;"function"==typeof Event?t=new Event("input",{bubbles:!0}):(t=u.createEvent("Event")).initEvent("input",!0,!1),e.dispatchEvent(t)},O=function(e,t){e?t.removeAttribute("hidden"):t.setAttributeNode(u.createAttribute("hidden")),t.setAttribute("aria-hidden",(!e).toString())},S,P; | ||
// Define function for setting up the polyfilling select | ||
( | ||
// Emulate the two properties regarding the datalist and input elements | ||
// list property / https://developer.mozilla.org/en/docs/Web/API/HTMLInputElement | ||
S=window.HTMLInputElement)&&S.prototype&&void 0===S.prototype.list&&Object.defineProperty(S.prototype,"list",{get:function(){ | ||
/* | ||
According to the specs ... | ||
"The list IDL attribute must return the current suggestions source element, if any, or null otherwise." | ||
"If there is no list attribute, or if there is no element with that ID, or if the first element with that ID is not a datalist element, then there is no suggestions source element." | ||
*/ | ||
var e=u.getElementById(this.getAttribute("list"));return"object"==typeof this&&this instanceof S&&e&&e.matches("datalist")?e:null}}),( | ||
// Options property / https://developer.mozilla.org/en/docs/Web/API/HTMLDataListElement | ||
P=window.HTMLElement)&&P.prototype&&void 0===P.prototype.options&&Object.defineProperty(P.prototype,"options",{get:function(){return"object"==typeof this&&this instanceof P?this.getElementsByTagName("option"):null}})}}(); | ||
!function(){"use strict";var e=window.document,t=window.navigator.userAgent,i="list"in e.createElement("input")&&Boolean(e.createElement("datalist")&&window.HTMLDataListElement),n=Boolean(t.match(/MSIE\s1[0-1]./)||t.match(/rv:11./)),a=Boolean(-1!==t.indexOf("Edge/"));if(i&&!n&&!a)return!1;Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector);var r=!1,o=["text","email","number","search","tel","url"];window.addEventListener("touchstart",function e(){r=!0,window.removeEventListener("touchstart",e)});var l,s=window.MutationObserver||window.WebKitMutationObserver;void 0!==s&&(l=new s(function(t){var i=!1;if(t.forEach(function(e){e.target instanceof HTMLElement&&"datalist"===e.target.tagName.toLowerCase()&&e.addedNodes.length>1&&(i=e.target)}),i){var n=e.querySelector('input[list="'+i.id+'"]');""!==f(n)&&A(b(i,n).length,i.getElementsByClassName("polyfilling")[0])}}));var u=function(e){var t=e.target,i=t.list,o=38===e.keyCode||40===e.keyCode;if("input"===t.tagName.toLowerCase()&&null!==i)if(n||a)""===f(t)||o||13===e.keyCode||27===e.keyCode||!n&&"text"!==t.type||(d(t,i),t.focus());else{var l=!1,s=i.getElementsByClassName("polyfilling")[0]||h(t,i);if(27!==e.keyCode&&13!==e.keyCode&&(""!==f(t)||o)&&void 0!==s){b(i,t).length>0&&(l=!0);var u=s.options.length-1;r?s.selectedIndex=0:o&&"number"!==t.getAttribute("type")&&(s.selectedIndex=38===e.keyCode?u:0,s.focus())}A(l,s)}},d=function(e,t){var i=f(e);Array.prototype.slice.call(t.options,0).forEach(function(e){var t=e.getAttribute("data-originalvalue"),n=t||e.value;t||e.setAttribute("data-originalvalue",n),e.label||e.text||(e.label=n),e.value=c(e,i)?i+"###[P0LYFlLLed]###"+n.toLowerCase():n})},p=function(e){var t=e.target,i=t.list;if(t.matches("input[list]")&&t.matches(".polyfilled")&&i){var n=i.querySelector('option[value="'+f(t).replace(/\\([\s\S])|(")/g,"\\$1$2")+'"]');n&&n.getAttribute("data-originalvalue")&&g(t,n.getAttribute("data-originalvalue"))}},c=function(e,t){var i=e.value.toLowerCase(),n=t.toLowerCase(),a=e.getAttribute("label"),r=e.text.toLowerCase();return Boolean(!1===e.disabled&&(""!==i&&-1!==i.indexOf(n)||a&&-1!==a.toLowerCase().indexOf(n)||""!==r&&-1!==r.indexOf(n)))},v=function(e){if(e.target.matches("input[list]")){var t=e.target,i=t.list;if("input"===t.tagName.toLowerCase()&&null!==i){if(t.matches(".polyfilled")||y(t,e.type),a&&"focusin"===e.type){var r=t.list.options[0];r.value=r.value}if(!n&&!a){var o=i.getElementsByClassName("polyfilling")[0]||h(t,i),l=o&&o.querySelector("option:not(:disabled)")&&("focusin"===e.type&&""!==f(t)||e.relatedTarget&&e.relatedTarget===o);A(l,o)}}}},y=function(e,t){e.setAttribute("autocomplete","off"),e.setAttribute("role","textbox"),e.setAttribute("aria-haspopup","true"),e.setAttribute("aria-autocomplete","list"),e.setAttribute("aria-owns",e.getAttribute("list")),"focusin"===t?(e.addEventListener("keyup",u),e.addEventListener("focusout",v,!0),(n||a&&"text"===e.type)&&e.addEventListener("input",p)):"blur"===t&&(e.removeEventListener("keyup",u),e.removeEventListener("focusout",v,!0),(n||a&&"text"===e.type)&&e.removeEventListener("input",p)),e.className+=" polyfilled"},f=function(e){return"email"===e.getAttribute("type")&&null!==e.getAttribute("multiple")?e.value.substring(e.value.lastIndexOf(",")+1):e.value},g=function(e,t){var i;e.value="email"===e.getAttribute("type")&&null!==e.getAttribute("multiple")&&(i=e.value.lastIndexOf(","))>-1?e.value.slice(0,i)+","+t:t};if(e.addEventListener("focusin",v,!0),!n&&!a){var m,b=function(t,i){void 0!==l&&l.disconnect();var n=t.getElementsByClassName("polyfilling")[0]||h(i,t),a=f(i),o=e.createDocumentFragment(),s=e.createDocumentFragment();Array.prototype.slice.call(t.querySelectorAll("option:not(:disabled)")).sort(function(e,t){var n=e.value,a=t.value;return"url"===i.getAttribute("type")&&(n=n.replace(/(^\w+:|^)\/\//,""),a=a.replace(/(^\w+:|^)\/\//,"")),n.localeCompare(a)}).forEach(function(e){var t=e.value,i=e.getAttribute("label"),n=e.text;if(c(e,a)){var r=n.substr(0,t.length+" / ".length);n&&!i&&n!==t&&r!==t+" / "?e.innerText=t+" / "+n:e.text||(e.innerText=i||t),o.appendChild(e)}else s.appendChild(e)}),n.appendChild(o);var u=n.options.length;return n.size=u>10?10:u,n.multiple=!r&&u<2,(t.getElementsByClassName("ie9_fix")[0]||t).appendChild(s),void 0!==l&&l.observe(t,{childList:!0}),n.options},h=function(t,i){if(!(t.getAttribute("type")&&-1===o.indexOf(t.getAttribute("type"))||null===i)){var n=t.getClientRects(),a=window.getComputedStyle(t),l=e.createElement("select");if(l.setAttribute("class","polyfilling"),l.style.position="absolute",A(!1,l),l.setAttribute("tabindex","-1"),l.setAttribute("aria-live","polite"),l.setAttribute("role","listbox"),r||l.setAttribute("aria-multiselectable","false"),"block"===a.getPropertyValue("display"))l.style.marginTop="-"+a.getPropertyValue("margin-bottom");else{var s="rtl"===a.getPropertyValue("direction")?"right":"left";l.style.setProperty("margin-"+s,"-"+(n[0].width+parseFloat(a.getPropertyValue("margin-"+s)))+"px"),l.style.marginTop=parseInt(n[0].height+(t.offsetTop-i.offsetTop),10)+"px"}if(l.style.borderRadius=a.getPropertyValue("border-radius"),l.style.minWidth=n[0].width+"px",r){var u=e.createElement("option");u.innerText=i.title,u.disabled=!0,u.setAttribute("class","message"),l.appendChild(u)}return i.appendChild(l),r?l.addEventListener("change",w):l.addEventListener("click",w),l.addEventListener("blur",w),l.addEventListener("keydown",w),l.addEventListener("keypress",E),l}},E=function(t){var i=t.target,n=i.parentNode,a=e.querySelector('input[list="'+n.id+'"]');"select"===i.tagName.toLowerCase()&&null!==a&&(!t.key||"Backspace"!==t.key&&1!==t.key.length||(a.focus(),"Backspace"===t.key?(a.value=a.value.substr(0,a.value.length-1),C(a)):a.value+=t.key,b(n,a)))},w=function(t){var i=t.currentTarget,n=i.parentNode,a=e.querySelector('input[list="'+n.id+'"]');if("select"===i.tagName.toLowerCase()&&null!==a){var r=t.type,o="keydown"===r&&13!==t.keyCode&&27!==t.keyCode;("change"===r||"click"===r||"keydown"===r&&(13===t.keyCode||"Tab"===t.key))&&i.value.length>0&&i.value!==n.title?(g(a,i.value),C(a),"Tab"!==t.key&&a.focus(),13===t.keyCode&&t.preventDefault(),o=!1):"keydown"===r&&27===t.keyCode&&a.focus(),A(o,i)}},C=function(t){var i;"function"==typeof Event?i=new Event("input",{bubbles:!0}):(i=e.createEvent("Event")).initEvent("input",!0,!1),t.dispatchEvent(i)},A=function(t,i){t?i.removeAttribute("hidden"):i.setAttributeNode(e.createAttribute("hidden")),i.setAttribute("aria-hidden",(!t).toString())};(m=window.HTMLInputElement)&&m.prototype&&void 0===m.prototype.list&&Object.defineProperty(m.prototype,"list",{get:function(){var t=e.getElementById(this.getAttribute("list"));return"object"==typeof this&&this instanceof m&&t&&t.matches("datalist")?t:null}}),function(e){e&&e.prototype&&void 0===e.prototype.options&&Object.defineProperty(e.prototype,"options",{get:function(){return"object"==typeof this&&this instanceof e?this.getElementsByTagName("option"):null}})}(window.HTMLElement)}}(); |
{ | ||
"name": "datalist-polyfill", | ||
"version": "1.23.3", | ||
"version": "1.24.0", | ||
"description": "A minimal and dependency-free vanilla JavaScript datalist polyfill. Supports all standard's functionality as well as mimics other browsers behavior.", | ||
@@ -33,8 +33,13 @@ "main": "datalist-polyfill.js", | ||
"devDependencies": { | ||
"chai": "^4.1.2", | ||
"mocha": "^5.2.0", | ||
"@commitlint/cli": "^8.3.4", | ||
"@commitlint/config-conventional": "^8.3.4", | ||
"@wdio/cli": "^5.8.5", | ||
"@wdio/dot-reporter": "^5.7.8", | ||
"@wdio/local-runner": "^5.8.6", | ||
"@wdio/mocha-framework": "^5.8.1", | ||
"@wdio/sync": "^5.8.6", | ||
"husky": "^4.0.2", | ||
"prettier": "^1.13.6", | ||
"wdio-mocha-framework": "^0.6.2", | ||
"webdriverio": "^5.0.0", | ||
"xo": "^0.24.0" | ||
"webdriverio": "^5.8.5", | ||
"xo": "^0.27.1" | ||
}, | ||
@@ -41,0 +46,0 @@ "xo": { |
@@ -1,8 +0,6 @@ | ||
[npm]: https://npmjs.com/package/datalist-polyfill 'datalist polyfill – on NPM' | ||
# datalist-polyfill | ||
[![MIT license](https://img.shields.io/npm/l/datalist-polyfill.svg 'license badge')](https://opensource.org/licenses/mit-license.php) | ||
[![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] | ||
[![datalist-polyfill on Npmjs](https://img.shields.io/npm/v/datalist-polyfill.svg 'npm version')](https://npmjs.com/package/datalist-polyfill 'datalist polyfill – on NPM') | ||
[![Total downloads ~ Npmjs](https://img.shields.io/npm/dt/datalist-polyfill.svg 'Count of total downloads – NPM')](https://npmjs.com/package/datalist-polyfill 'datalist polyfill – on 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') | ||
@@ -15,3 +13,3 @@ [![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') | ||
**Update:** Safari TP seems to support the `datalist` element at least basically. Yeah !!! Exciting news! | ||
**Update:** Safari now supports the `datalist` element at least basically, as [announced earlier this year with the latest release of Safari both for iOS and MacOS X](https://developer.apple.com/documentation/safari_release_notes/safari_12_1_release_notes#3130314). Yeah !!! Exciting news! | ||
I'm planning to release a new major version soon to both cheer as well as accommodate their implementation. | ||
@@ -21,10 +19,10 @@ | ||
- Supports all standard's functionality as well as mimics other browsers behavior. | ||
- Mitigating the [different levels of support](https://caniuse.com/#feat=datalist) both by Safari and IE9+ as well as EDGE | ||
- Released under the MIT license | ||
- Made in Germany. And supported by so many great people from all over this planet - see "Credits" accordingly. | ||
- supports all standard's functionality as well as mimics other browsers behavior. | ||
- mitigating the [different levels of support](https://caniuse.com/#feat=datalist) both by Safari and IE9+ as well as Edge | ||
- released under the MIT license | ||
- made in Germany. And supported by so many great people from all over this planet - see "Credits" accordingly. | ||
## Features | ||
- Lightweight: 6.7 kB of minified JavaScript, around 2.5 kB gzipped | ||
- Lightweight: 6.9 kB of minified JavaScript, around 2.6 kB gzipped | ||
- Fully flexible to change the datalist entries / `<option>`s | ||
@@ -93,12 +91,24 @@ - Supporting: | ||
### `value` property on the `option` elements for Microsoft IE 10 & IE 11 and Edge | ||
As explained in detail below in the section ["Microsoft Internet Explorer 10 & 11 and Microsoft Edge"](#microsoft-internet-explorer-10--11-and-microsoft-edge), for fixing missing behaviour in IE 10+ and Edge we're manipulating the `value` for the `option` elements in those browser so you can't access them securely as a getter, but would need to take the original values out of `data-originalvalue`. | ||
### Microsoft Internet Explorer | ||
#### Microsoft EDGE | ||
#### Microsoft Edge | ||
Microsoft EDGE doesn't trigger the `input` event any more after selecting an item via mouseclick (on `input` elements other than type of `text`), even though that IE11 still did, nevermind ... | ||
Microsoft Edge doesn't trigger the `input` event any more after selecting an item via mouseclick (on `input` elements other than type of `text`), even though that IE11 still did, nevermind ... | ||
That for the optimizations on substring matching for Microsoft EDGE specifically by #GH-39 need to get restricted to `input[type="text"]` elements even only. | ||
That for the optimizations on substring matching for Microsoft Edge specifically by #GH-39 (as explained further in the following ["Microsoft Internet Explorer 10 & 11 and Microsoft Edge"](#microsoft-internet-explorer-10--11-and-microsoft-edge) section) need to get restricted to `input[type="text"]` elements even only. | ||
There might be possible solutions to even also achieve the expected behaviour on non-text-input elements - even though that I only could think about ugly solutions that I don't want to have within the polyfill and that might even also break existing CSS & JS architecture / selectors. | ||
#### Microsoft Internet Explorer 10 & 11 and Microsoft Edge | ||
As mentioned with #GH-63, related to aspects reported via #GH-36 and #GH-39 (and in [Microsoft Edges platform issues](https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/9573654/)), it doesn't work in IE 10 & 11 as well as in Edge to "Search both the value and label, using substring matching; currently it searches both the value and label, but uses prefix matching". | ||
As requested with #GH-36 we wanted to even also enrich the experience within the "newest" IE versions (10 & 11) and Edge browsers that provided basic support, but not the substring matching for users input. In this case the technical solution has been to manipulate the values in a way that the browser could actually handle that functionality as well, by including the users input at the beginning of the value after a substring matching to the original value, followed by a unique string for preventing any inconsistencies, followed by the original value itself, in this case for the sorting of the entries (this is mainly done in the `updateIEOptions` function around line 191 to 200 of the code). | ||
This actually leads to a different behavior for the developers on the `value` property of each `option` elements within the `datalist` element for IE & Edge, but on the other hand provides a better UX for IE & Edge users by a consistent behavior for the user. | ||
#### Microsoft Internet Explorer 9 | ||
@@ -128,3 +138,3 @@ | ||
- The demo HTML code is meant to be simple - I do know that things like a surrounding `<form>` are missing, and I've left the latin letters and english expressions for the right to left text-direction example. But lets focus on the relevant tags that this polyfill is all about for the demo. | ||
- The HTML demo code is meant to be simple - I do know that things like a surrounding `<form>` are missing, and I've left the latin letters and english expressions for the right to left text-direction example. But lets focus on the relevant tags that this polyfill is all about for the demo. | ||
- iOS Safari handles the `label`-attribute different from Safari on Mac OS. This is being equalized during the handling of the `label`-attributes-value for differing `value` and `label` values. | ||
@@ -135,6 +145,7 @@ - After I thought it through and did some experiments, I've finally chosen the `<select>` element to polyfill the `<datalist>`, as it brought most of the functionality, whereas I accepted that it doesn't behave and doesn't look equally. | ||
- Let the `datalist` element be a direct follower of the `input` element - and don't nest it into the `label` in case that you're doing so with the `input` (which you nevertheless shouldn't do in general, but hey, gods great zoo is great). | ||
- If embedding a webview within an iOS app, you should be using `WKWebView` instead of `UIWebView`, as it supports `datalist` right natively and the latter even also leads to a JavaScript error (thanks to @jscho13 for mentioning this). | ||
## Credits | ||
Supported by Christian, Johannes, @mitchhentges, @mertenhanisch, @ailintom, @Kravimir, @mischah, @hryamzik, @ottoville, @IceCreamYou, @wlekin, @eddr, @beebee1987, @mricherzhagen, @acespace90, @damien-git and @nexces. Thank you very much for that, highly appreciated ! | ||
Supported by Christian, Johannes, @mitchhentges, @mertenhanisch, @ailintom, @Kravimir, @mischah, @hryamzik, @ottoville, @IceCreamYou, @wlekin, @eddr, @beebee1987, @mricherzhagen, @acespace90, @damien-git, @nexces, @Sora2455, @jscho13 and @alexirion. Thank you very much for that, highly appreciated ! | ||
@@ -144,5 +155,5 @@ ## Tested with | ||
- Mac | ||
- Mac OSX 10.13, Safari 11 | ||
- Mac OSX 10.12, Safari 10 | ||
- Mac OSX 10.11, Safari 9 | ||
- macOS 10.13, Safari 11 | ||
- macOS 10.12, Safari 10 | ||
- macOS 10.11, Safari 9 | ||
- iOS | ||
@@ -173,6 +184,7 @@ - iPhone 8 Simulator, Mobile Safari 11.0 | ||
<th>iOS</th> | ||
<th>Safari</th> | ||
<th>Safari TP</th> | ||
<th>Safari < 12.1</th> | ||
<th>iOS WebView</th> | ||
<th>Safari >= 12.1</th> | ||
<th>IE11+</th> | ||
<th>EDGE</th> | ||
<th>Edge</th> | ||
<th>Firefox</th> | ||
@@ -185,2 +197,3 @@ <th>Chrome</th> | ||
<td colspan="3" align="center">✔ <i>Polyfill</i></td> | ||
<td align="center">✔ via WKWebView</td> | ||
<td colspan="5" align="center">✔</td> | ||
@@ -191,3 +204,3 @@ <td align="center"><a href="https://github.com/mfranzke/datalist-polyfill/issues/33">#GH-33</a></td> | ||
<th align="left"><a href="https://bugs.chromium.org/p/chromium/issues/detail?id=773041">long lists of items are unscrollable resulting in unselectable options</a></th> | ||
<td colspan="7" align="center">✔</td> | ||
<td colspan="8" align="center">✔</td> | ||
<td align="center"><a href="https://bugs.chromium.org/p/chromium/issues/detail?id=773041" target="_blank">fixed with v.69</a></td> | ||
@@ -198,3 +211,3 @@ <td align="center">✔</td> | ||
<th align="left"><a href="https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/9573654/">No substring matching for the suggestions</a></th> | ||
<td colspan="4" align="center">✔</td> | ||
<td colspan="5" align="center">✔</td> | ||
<td colspan="2" align="center">✔ by <a href="https://github.com/mfranzke/datalist-polyfill/issues/39">#GH-39</a></td> | ||
@@ -205,3 +218,3 @@ <td colspan="3" align="center">✔</td> | ||
<th align="left"><a href="https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/20066595/">`datalist` popups gets "emptied" when receiving focus via tab</a></th> | ||
<td colspan="5" align="center">✔</td> | ||
<td colspan="6" align="center">✔</td> | ||
<td align="center">✔ by <a href="https://github.com/mfranzke/datalist-polyfill/issues/49">#GH-49</a></td> | ||
@@ -219,1 +232,3 @@ <td colspan="3" align="center">✔</td> | ||
If you're trying out and using my work, feel free to contact me and give me any feedback. I'm curious about how it's gonna be used. | ||
And if you do like this polyfill, please consider even also having a look at the other polyfill we've developed: <https://github.com/mfranzke/loading-attribute-polyfill> |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
223
67034
11
8
654
1