datalist-polyfill
Advanced tools
Comparing version 1.20.1 to 1.21.0
{ | ||
"name": "datalist-polyfill", | ||
"description": "A minimal and dependency-free vanilla JavaScript datalist polyfill. Supports all standard's functionality as well as mimics other browsers behavior.", | ||
"version": "1.20.1", | ||
"homepage": "https://github.com/mfranzke/datalist-polyfill", | ||
"authors": [{ | ||
"name": "Maximilian Franzke", | ||
"url": "https://www.mfranzke.net/", | ||
"email": "contact_mfr@nzke.net" | ||
}], | ||
"keywords": [ | ||
"datalist", | ||
"autosuggest", | ||
"autosuggester", | ||
"suggest", | ||
"suggester", | ||
"select" | ||
], | ||
"licenses": [{ | ||
"type": "MIT", | ||
"url": "https://opensource.org/licenses/mit-license.php" | ||
}], | ||
"ignore": [ | ||
"bower.json", | ||
"demos/index.html", | ||
"demos/ie9/index.html", | ||
"readme.md" | ||
] | ||
"name": "datalist-polyfill", | ||
"description": | ||
"A minimal and dependency-free vanilla JavaScript datalist polyfill. Supports all standard's functionality as well as mimics other browsers behavior.", | ||
"version": "1.21.0", | ||
"homepage": "https://github.com/mfranzke/datalist-polyfill", | ||
"authors": [ | ||
{ | ||
"name": "Maximilian Franzke", | ||
"url": "https://www.mfranzke.net/", | ||
"email": "contact_mfr@nzke.net" | ||
} | ||
], | ||
"keywords": [ | ||
"datalist", | ||
"autosuggest", | ||
"autosuggester", | ||
"suggest", | ||
"suggester", | ||
"select" | ||
], | ||
"licenses": [ | ||
{ | ||
"type": "MIT", | ||
"url": "https://opensource.org/licenses/mit-license.php" | ||
} | ||
], | ||
"ignore": [ | ||
"bower.json", | ||
"demos/index.html", | ||
"demos/ie9/index.html", | ||
"readme.md" | ||
] | ||
} |
140
CHANGELOG.md
# Changelog | ||
All notable changes to this project will be documented in this file. | ||
@@ -9,11 +10,34 @@ | ||
## [1.21.0] - 2018-08-07 | ||
### Added | ||
- Added testcases and added [crossbrowsertesting](https://crossbrowsertesting.com/) logo to README | ||
### Changed | ||
- increased specificity on one of the selectors according the usage in other parts of the code | ||
- Check for correct element on the `input`s event delegation | ||
- Simplified some code parts | ||
- Further aligned the naming conventions | ||
- code and complexity simplifications | ||
### Fixed | ||
- Arrow keys (top&down) disturb the general browser behaviour on number fields (#GH-32) | ||
## [1.20.1] - 2018-07-20 | ||
### Changed | ||
- Code simplifications | ||
### Fixed | ||
- Suggestions aren‘t working onclick any more #GH-31 | ||
- Suggestions aren‘t working onclick any more #GH-31 | ||
## [1.20.0] - 2018-07-18 | ||
### Changed | ||
- on `input[type=url]` omitting the scheme part and performing intelligent matching on the domain name (#GH-28) | ||
@@ -24,2 +48,3 @@ - README: Updated the tested browsers list as well as updated the Features section due to the updates by this release | ||
### Fixed | ||
- IE9: Use .getAttribute for retrieving .type and .multiple values/existance (#GH-29) | ||
@@ -29,6 +54,9 @@ - list IDL attribute must return the current suggestions source element (#GH-30) | ||
## [1.19.0] - 2018-07-13 | ||
### Added | ||
- In case of the ESC key being pressed while focusing the polyfilling select, we still want to focus the `input[list]` | ||
### Changed | ||
- Performance: Set a local variable | ||
@@ -39,7 +67,11 @@ - Preparation for some automated testing | ||
## [1.18.1] - 2018-07-10 | ||
### Added | ||
- Dispatch the input event as well on the related `input[list]` on using the Backspace key within the polyfilling select | ||
## [1.18.0] - 2018-07-10 | ||
### Changed | ||
- Defined the system-font for the demo-page | ||
@@ -50,6 +82,9 @@ - Renamed some variables to some more meaningful names | ||
### Removed | ||
- Removed an old separation in between eventTarget-Tagnames of select and option, that was integrated due to the mouse-event, which has been replaced again a while ago | ||
## [1.17.0] - 2018-07-07 | ||
### Added | ||
- Include behavior on pressing Tab or other printable keys (#GH-#27) | ||
@@ -59,3 +94,5 @@ - Added Greenkeeper badge. I'm using this service to keep being updated on the dev dependencies. | ||
## [1.16.2] - 2018-07-04 | ||
### Fixed | ||
- Fixed a bug that lead to an incorrectly selected suggestion (first instead of last) while using the up key on the input element | ||
@@ -65,7 +102,11 @@ - Fix for `input[list]` elements with class attribute - thanks to @mricherzhagen for mentioning this and even also providing a solution by pull request #GH-25 | ||
## [1.16.1] - 2018-06-28 | ||
### Fixed | ||
- Introduced a new bug by the fix for #GH-23. Reverted that one and corrected the ESLint rules settings. (#GH-24) | ||
## [1.16.0] - 2018-06-27 | ||
### Added | ||
- Linting as well as security: prettier, xo, codacy | ||
@@ -75,32 +116,47 @@ - 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 | ||
### Fixed | ||
- A previous checkin has broken the solution provided for #GH-16, so I've fixed this again. | ||
## [1.15.0] - 2018-06-22 | ||
### Changed | ||
- Mainly simplified the code. | ||
## [1.14.4] - 2018-06-21 | ||
### Fixed | ||
- IE9: After choosing a suggestion out of the polyfilling select, the select itself wouldn't get hidden. (#GH-19) | ||
## [1.14.3] - 2018-06-20 | ||
### Changed | ||
- Changed the order in a comparsion as this simplifies the response. | ||
### Fixed | ||
- Sadly another small bug slipped through today, it's about an incorrect variable being used. | ||
## [1.14.2] - 2018-06-20 | ||
### Fixed | ||
- A small bug sadly slipped through that doesn't hide the polyfilling select on non-matching option elements regarding the value. | ||
## [1.14.1] - 2018-06-20 | ||
### Added | ||
- Hiding the polyfilling select on pressing ENTER, which is equal to the behavior on typing ESC. | ||
@@ -110,15 +166,21 @@ - Added more badges to the readme. I'm loving badges. | ||
### Changed | ||
- Made some necessary changes to enhance the current demo page by the minimum amount of relevant HTML tags that should be included within every page (and even also added the IE related meta tag due to backwards compatibility). | ||
### Removed | ||
- Removed some sample code out of the readme page due to the new NPM websites layout. | ||
### Fixed | ||
- The determination of option elements within the polyfilling select has incorrectly even also included `:disabled` elements. | ||
## [1.14.0] - 2018-06-12 | ||
### Added | ||
- 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 ! ;-) | ||
@@ -128,15 +190,22 @@ - Additionally did some necessary updates to the documentation. | ||
## [1.13.2] - 2018-06-11 | ||
### Changed | ||
- Focusing the input[list] after selecting a suggestion, as in #GH-18 | ||
## [1.13.1] - 2018-06-04 | ||
### Changed | ||
- Some code refactoring, nothing really serious | ||
By the way, it was polyfills 1st birthday one month ago. Yeah !!! | ||
By the way, it was polyfills 1st birthday one month ago. Yeah !!! | ||
## [1.13.0] - 2018-05-28 | ||
### Added | ||
- Thanks to @eddr and @Kravimir for inspiring me via #GH-5 that there should be another possibility on defining value and label for the suggestions. As the browser vendors (GC vs. the others) don't seem to be aligned on this topic, I've decided to enable the label-attribute to serve as the definitive label being displayed, even if a value is being defined differing from the label. Check out the „Different ways of defining an option“ section on the demo page regarding this topic. | ||
### Changed | ||
- The docs. And changed (dependencies) and added (jsdelivr) badges. I like badges. | ||
@@ -146,29 +215,43 @@ - As well as extracted the CHANGELOG to an external file. | ||
## [1.12.3] - 2018-05-04 | ||
### Fixed | ||
- @wlekin thankfully mentioned (extracted to #GH-15) that the polyfilling `select` gets positioned incorrectly underneath the `input[list]` element on iOS. | ||
## [1.12.2] - 2018-05-01 | ||
### Fixed | ||
- Thank you @IceCreamYou for fixing the case sensitive focusOut -> focusout event name | ||
## [1.12.1] - 2018-04-07 | ||
### Changed | ||
- simple (code) style changes (plus added editorconfig to keep it that way) and typo | ||
- simple (code) style changes (plus added editorconfig to keep it that way) and typo | ||
## [1.12.0] - 2018-03-18 | ||
### Added | ||
- @ottoville thankfully contributed by mentioning and implementing the feature of emitting an event when item in datalist is selected | ||
## [1.11.2] - 2018-03-17 | ||
### Changed | ||
- @mertenhanisch has styled the code according to more „standard“ formatting and also improved the wording of the documentation, which is awesome. | ||
- And @mitchhentges thankfully supports on reviewing your great community support and ensures to the keep the wheels turning on the development of this projects. | ||
Many kudos to the both of you !!! | ||
Many kudos to the both of you !!! | ||
## [1.11.1] - 2017-11-24 | ||
### Fixed | ||
- @hryamzik thankfully mentioned by #GH-7 that the polyfilling `select` gets positioned incorrectly in case of the `input[list]` element being styled as a block-level element. | ||
## [1.11.0] - 2017-10-08 | ||
### Changed | ||
- I'm very thankful for @ailintom mentioning the missing IE9 support with #GH-2, which is still relevant (at least and maybe foremost) for the Windows Vista users. | ||
@@ -178,40 +261,61 @@ - Additionally @Kravimir thankfully brought to my attention, that IE9 handles the `option` subelements quite restricted - so I've added a section regarding IE9 support to the demo page with the additional two lines of HTML, that you'll need to add in case you also need / want to still support IE9 in your projects, as well as changed the JavaScript code to even also support IE9. | ||
## [1.10.3] - 2017-10-07 | ||
### Changed | ||
- Added a comment regarding IE9 - and some simple code styling. | ||
## [1.10.2] - 2017-09-26 | ||
### Fixed | ||
- Simple corrections. | ||
## [1.10.1] - 2017-09-25 | ||
### Fixed | ||
- Simple bugfix, that came up through the latest implementation on the up and down arrow keys. | ||
## [1.10.0] - 2017-08-16 | ||
### Changed | ||
- Added the ability to open the datalist on the up and down keys even in case that no value has been provided - this seems to be intentionally and even also adapts the behavior by supporting browsers. | ||
## [1.9.0] - 2017-07-20 | ||
### Changed | ||
Regarding the changes out of release version 1.6.0 to emulate the expected UI quite nicely, I was still struggling with using that hacky solution (`multiple` attribute) and even also of how to prevent multiple selections on the polyfilling select. | ||
- Actually the attribute `size` came to my attention, which much better fits the requirements and behaves as designed quite perfectly. Chapeau! | ||
## [1.8.1] - 2017-07-18 | ||
### Fixed | ||
- Bugfix regarding the handling of the label values. | ||
## [1.8.0] - 2017-07-24 | ||
### Changed | ||
- Restricted the polyfill to only work with relevant input types; we’d like to exclude the ones that even already need another polyfill to „work“ correctly or have a meaningful UI, like e.g. color or date-related ones, as those polyfills should handle the support of the datalist themselves depending on their own functionality. | ||
## [1.7.0] - 2017-06-29 | ||
### Added | ||
- As mentioned by @aFarkas [within his review](https://github.com/h5bp/html5please/issues/18), `option` elements could be of some different formats. This release especially follows [the spec](https://www.w3.org/TR/html5/forms.html#the-datalist-element) regarding the aspect that „Each suggestion has a value and a label.“. | ||
## [1.6.2] - 2017-06-28 | ||
### Changed | ||
- Optimized the behavior to select the entries within the polyfilling `select[multiple]` on using the up and down arrow keys from the polyfilled `input[list]`. | ||
## [1.6.1] - 2017-06-16 | ||
### Changed | ||
- Introduced speaking variables for the different keycodes. | ||
@@ -222,4 +326,7 @@ - And implemented some feedback by flow. | ||
## [1.6.0] - 2017-06-16 | ||
### Changed | ||
This is so far the biggest and greatest update ! | ||
- Depending of the feedback by Michael the visual appearance has changed and will better emulate the expected layout as in other browsers (on non-touch interactions). That for the script is creating the polyfilling select as a multiple-selection type, which emulates the expected „form“ better. | ||
@@ -229,26 +336,39 @@ - And better positioning as well as styling the polyfilling select according to the input field, like e.g. even also set the polyfilling selects border-radius equally as the one by the polyfilled input. | ||
## [1.5.0] - 2017-06-10 | ||
### Changed | ||
- Simplified the styling and got rid of the external CSS files / dependency. You could remove that one now. Yeah! | ||
## [1.4.0] - 2017-06-09 | ||
### Added | ||
- Added RTL text-direction support | ||
## [1.3.0] - 2017-05-30 | ||
### Added | ||
- Added support for multiple email addresses, separated by comma. | ||
### Changed | ||
- And again, updated documentation slightly. And demo accordingly. | ||
## [1.2.1] - 2017-05-29 | ||
### Changed | ||
- Simple code style modifications. Because style matters. | ||
## [1.2.0] - 2017-05-29 | ||
### Added | ||
- Added .options (for `datalist` elements) and .list (for `input` elements) properties according to the specs. | ||
## [1.1.2] - 2017-05-22 | ||
### Changed | ||
- Further simplified the code, so that we could even skip the `.matches()` polyfill. Yeah. | ||
@@ -258,23 +378,35 @@ - And documentation updates. | ||
## [1.1.1] - 2017-05-10 | ||
### Fixed | ||
- fixed another simple bug that lead to an incorrect index being selected - let's skip this, as it's not even the standard behavior | ||
## [1.1.0] - 2017-05-09 | ||
### Fixed | ||
- some small corrections | ||
## [1.0.3] - 2017-05-09 | ||
### Changed | ||
- better preselection on entries within the dropdown depending on the inputs value | ||
## [1.0.2] - 2017-05-08 | ||
### Added | ||
- added a `package.json` file | ||
## [1.0.1] - 2017-05-08 | ||
### Fixed | ||
- Small, but important typo. :-) Thanks @Fyrd for mentioning this. | ||
## [1.0.0] - 2017-05-04 | ||
### Added | ||
- First release. |
@@ -62,2 +62,8 @@ /* | ||
// .matches polyfill | ||
// TODO: probably needs enhancement on the to supported browsers | ||
if (!Element.prototype.matches) { | ||
Element.prototype.matches = Element.prototype.msMatchesSelector; | ||
} | ||
// Define some global settings and configurations | ||
@@ -107,3 +113,3 @@ var touched = false, | ||
var input = dcmnt.querySelector( | ||
'[list="' + datalistNeedsAnUpdate.id + '"]' | ||
'input[list="' + datalistNeedsAnUpdate.id + '"]' | ||
); | ||
@@ -127,50 +133,47 @@ | ||
var input = event.target, | ||
datalist = input.list; | ||
// Check for whether the events target was an input and still check for an existing instance of the datalist | ||
if ( | ||
input.tagName && | ||
input.tagName.toLowerCase() === 'input' && | ||
datalist !== null | ||
) { | ||
datalist = input.list, | ||
// 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 = | ||
datalistSelect = | ||
datalist.getElementsByClassName(classNamePolyfillingSelect)[0] || | ||
setUpPolyfillingSelect(input, datalist); | ||
// Still check for an existing instance | ||
if (datalistSelect !== undefined) { | ||
var visible = false, | ||
keyOpen = event.keyCode === keyUP || event.keyCode === keyDOWN; | ||
// Check for whether the events target was an input and still check for an existing instance of the datalist and polyfilling select | ||
if ( | ||
input.tagName.toLowerCase() !== 'input' || | ||
datalist === null || | ||
datalistSelect === undefined | ||
) { | ||
return; | ||
} | ||
var visible = false, | ||
keyOpen = event.keyCode === keyUP || event.keyCode === keyDOWN; | ||
// 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 ( | ||
event.keyCode !== keyESC && | ||
event.keyCode !== keyENTER && | ||
(input.value !== '' || keyOpen) | ||
) { | ||
// ... prepare the options | ||
if (prepOptions(datalist, input).length > 0) { | ||
visible = true; | ||
} | ||
// 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 ( | ||
event.keyCode !== keyESC && | ||
event.keyCode !== keyENTER && | ||
(input.value !== '' || keyOpen) | ||
) { | ||
// ... prepare the options | ||
if (prepOptions(datalist, input).length > 0) { | ||
visible = true; | ||
} | ||
var firstEntry = 0, | ||
lastEntry = datalistSelect.options.length - 1; | ||
var firstEntry = 0, | ||
lastEntry = datalistSelect.options.length - 1; | ||
// ... preselect best fitting index | ||
if (touched) { | ||
datalistSelect.selectedIndex = firstEntry; | ||
} else if (keyOpen) { | ||
datalistSelect.selectedIndex = | ||
event.keyCode === keyUP ? lastEntry : firstEntry; | ||
// ... preselect best fitting index | ||
if (touched) { | ||
datalistSelect.selectedIndex = firstEntry; | ||
} else if (keyOpen && input.getAttribute('type') !== 'number') { | ||
datalistSelect.selectedIndex = | ||
event.keyCode === keyUP ? lastEntry : firstEntry; | ||
// ... and on arrow up or down keys, focus the select | ||
datalistSelect.focus(); | ||
} | ||
} | ||
// Toggle the visibility of the datalist select according to previous checks | ||
toggleVisibility(visible, datalistSelect); | ||
// ... and on arrow up or down keys, focus the select | ||
datalistSelect.focus(); | ||
} | ||
} | ||
// Toggle the visibility of the datalist select according to previous checks | ||
toggleVisibility(visible, datalistSelect); | ||
}; | ||
@@ -192,3 +195,3 @@ | ||
// In case of type=email and multiple attribute, we would need grab the last piece | ||
// 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 | ||
@@ -281,2 +284,7 @@ if ( | ||
var changesInputList = function(event) { | ||
// Check for correct element on this event delegation | ||
if (!event.target.matches('input[list]')) { | ||
return; | ||
} | ||
var input = event.target, | ||
@@ -286,50 +294,46 @@ datalist = input.list; | ||
// Check for whether the events target was an input and still check for an existing instance of the datalist | ||
if ( | ||
input.tagName && | ||
input.tagName.toLowerCase() === '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(input, 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' && input.value !== '') || | ||
(event.relatedTarget && event.relatedTarget === datalistSelect)); | ||
if (input.tagName.toLowerCase() !== 'input' || datalist === null) { | ||
return; | ||
} | ||
// Test for whether this input has already been enhanced by the polyfill | ||
if ( | ||
(' ' + input.className + ' ').indexOf(' ' + classNameInput + ' ') < 0 | ||
) { | ||
// We'd like to prevent autocomplete on the input datalist field | ||
input.setAttribute('autocomplete', 'off'); | ||
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(input, 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' && input.value !== '') || | ||
(event.relatedTarget && event.relatedTarget === datalistSelect)); | ||
// WAI ARIA attributes | ||
input.setAttribute('role', 'textbox'); | ||
input.setAttribute('aria-haspopup', 'true'); | ||
input.setAttribute('aria-autocomplete', 'list'); | ||
input.setAttribute('aria-owns', input.getAttribute('list')); | ||
// Test for whether this input has already been enhanced by the polyfill | ||
if (!input.matches('.' + classNameInput)) { | ||
// We'd like to prevent autocomplete on the input datalist field | ||
input.setAttribute('autocomplete', 'off'); | ||
// Bind the keyup event on the related datalists input | ||
if (eventType === 'focusin') { | ||
input.addEventListener('keyup', inputInputList); | ||
// WAI ARIA attributes | ||
input.setAttribute('role', 'textbox'); | ||
input.setAttribute('aria-haspopup', 'true'); | ||
input.setAttribute('aria-autocomplete', 'list'); | ||
input.setAttribute('aria-owns', input.getAttribute('list')); | ||
input.addEventListener('focusout', changesInputList, true); | ||
} else if (eventType === 'blur') { | ||
input.removeEventListener('keyup', inputInputList); | ||
// Bind the keyup event on the related datalists input | ||
if (eventType === 'focusin') { | ||
input.addEventListener('keyup', inputInputList); | ||
input.removeEventListener('focusout', changesInputList, true); | ||
} | ||
input.addEventListener('focusout', changesInputList, true); | ||
} else if (eventType === 'blur') { | ||
input.removeEventListener('keyup', inputInputList); | ||
// Add class for identifying that this input is even already being polyfilled | ||
input.className += ' ' + classNameInput; | ||
input.removeEventListener('focusout', changesInputList, true); | ||
} | ||
// Toggle the visibility of the datalist select according to previous checks | ||
toggleVisibility(visible, datalistSelect); | ||
// Add class for identifying that this input is even already being polyfilled | ||
input.className += ' ' + classNameInput; | ||
} | ||
// Toggle the visibility of the datalist select according to previous checks | ||
toggleVisibility(visible, datalistSelect); | ||
}; | ||
@@ -341,90 +345,87 @@ | ||
// Using .getAttribute here for IE9 purpose - elsewhere it wouldn't return the newer HTML5 values correctly | ||
if (supportedTypes.indexOf(input.getAttribute('type')) > -1) { | ||
// Still check for an existing instance | ||
if (datalist !== null) { | ||
var rects = input.getClientRects(), | ||
// Measurements | ||
inputStyles = window.getComputedStyle(input), | ||
datalistSelect = dcmnt.createElement('select'); | ||
// and still check for an existing instance | ||
if ( | ||
supportedTypes.indexOf(input.getAttribute('type')) === -1 || | ||
datalist === null | ||
) { | ||
return; | ||
} | ||
// Setting a class for easier identifying that select afterwards | ||
datalistSelect.setAttribute('class', classNamePolyfillingSelect); | ||
var rects = input.getClientRects(), | ||
// Measurements | ||
inputStyles = window.getComputedStyle(input), | ||
datalistSelect = dcmnt.createElement('select'); | ||
// Set general styling related definitions | ||
datalistSelect.style.position = 'absolute'; | ||
// Setting a class for easier identifying that select afterwards | ||
datalistSelect.setAttribute('class', classNamePolyfillingSelect); | ||
// Initially hiding the datalist select | ||
toggleVisibility(false, datalistSelect); | ||
// Set general styling related definitions | ||
datalistSelect.style.position = 'absolute'; | ||
// The select itself shouldn't be a possible target for tabbing | ||
datalistSelect.setAttribute('tabindex', '-1'); | ||
// 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 itself shouldn't be a possible target for tabbing | ||
datalistSelect.setAttribute('tabindex', '-1'); | ||
// The select should get positioned underneath the input field ... | ||
if (inputStyles.getPropertyValue('display') === 'block') { | ||
datalistSelect.style.marginTop = | ||
'-' + inputStyles.getPropertyValue('margin-bottom'); | ||
} else { | ||
var direction = | ||
inputStyles.getPropertyValue('direction') === 'rtl' | ||
? 'right' | ||
: 'left'; | ||
// WAI ARIA attributes | ||
datalistSelect.setAttribute('aria-live', 'polite'); | ||
datalistSelect.setAttribute('role', 'listbox'); | ||
if (!touched) { | ||
datalistSelect.setAttribute('aria-multiselectable', 'false'); | ||
} | ||
datalistSelect.style.setProperty( | ||
'margin-' + direction, | ||
'-' + | ||
(rects[0].width + | ||
parseFloat( | ||
inputStyles.getPropertyValue('margin-' + direction) | ||
)) + | ||
'px' | ||
); | ||
datalistSelect.style.marginTop = | ||
parseInt( | ||
rects[0].height + (input.offsetTop - datalist.offsetTop), | ||
10 | ||
) + 'px'; | ||
} | ||
// The select should get positioned underneath the input field ... | ||
if (inputStyles.getPropertyValue('display') === 'block') { | ||
datalistSelect.style.marginTop = | ||
'-' + inputStyles.getPropertyValue('margin-bottom'); | ||
} else { | ||
var direction = | ||
inputStyles.getPropertyValue('direction') === 'rtl' ? 'right' : 'left'; | ||
// Set the polyfilling selects border-radius equally to the one by the polyfilled input | ||
datalistSelect.style.borderRadius = inputStyles.getPropertyValue( | ||
'border-radius' | ||
); | ||
datalistSelect.style.minWidth = rects[0].width + 'px'; | ||
datalistSelect.style.setProperty( | ||
'margin-' + direction, | ||
'-' + | ||
(rects[0].width + | ||
parseFloat(inputStyles.getPropertyValue('margin-' + direction))) + | ||
'px' | ||
); | ||
datalistSelect.style.marginTop = | ||
parseInt(rects[0].height + (input.offsetTop - datalist.offsetTop), 10) + | ||
'px'; | ||
} | ||
if (touched) { | ||
var messageElement = dcmnt.createElement('option'); | ||
// Set the polyfilling selects border-radius equally to the one by the polyfilled input | ||
datalistSelect.style.borderRadius = inputStyles.getPropertyValue( | ||
'border-radius' | ||
); | ||
datalistSelect.style.minWidth = rects[0].width + 'px'; | ||
// ... and it's first entry should contain the localized message to select an entry | ||
messageElement.innerText = datalist.title; | ||
// ... 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 (touched) { | ||
var messageElement = dcmnt.createElement('option'); | ||
// Add select to datalist element ... | ||
datalist.appendChild(datalistSelect); | ||
// ... and it's first entry should contain the localized message to select an entry | ||
messageElement.innerText = datalist.title; | ||
// ... 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); | ||
} | ||
// ... and our upfollowing functions to the related event | ||
if (touched) { | ||
datalistSelect.addEventListener('change', changeDataListSelect); | ||
} else { | ||
datalistSelect.addEventListener('click', changeDataListSelect); | ||
} | ||
datalistSelect.addEventListener('blur', changeDataListSelect); | ||
datalistSelect.addEventListener('keydown', changeDataListSelect); | ||
datalistSelect.addEventListener('keypress', datalistSelectKeyPress); | ||
// Add select to datalist element ... | ||
datalist.appendChild(datalistSelect); | ||
return datalistSelect; | ||
} | ||
// ... and our upfollowing functions to the related event | ||
if (touched) { | ||
datalistSelect.addEventListener('change', changeDataListSelect); | ||
} else { | ||
datalistSelect.addEventListener('click', changeDataListSelect); | ||
} | ||
datalistSelect.addEventListener('blur', changeDataListSelect); | ||
datalistSelect.addEventListener('keydown', changeDataListSelect); | ||
datalistSelect.addEventListener('keypress', datalistSelectKeyPress); | ||
return datalistSelect; | ||
}; | ||
@@ -434,34 +435,25 @@ | ||
var datalistSelectKeyPress = function(event) { | ||
var datalistSelect = event.target; | ||
var datalistSelect = event.target, | ||
datalist = datalistSelect.parentNode, | ||
input = dcmnt.querySelector('input[list="' + datalist.id + '"]'); | ||
// Check for whether the events target was a select | ||
if ( | ||
datalistSelect.tagName && | ||
datalistSelect.tagName.toLowerCase() === 'select' | ||
) { | ||
var datalist = datalistSelect.parentNode, | ||
inputList = dcmnt.querySelector('input[list="' + datalist.id + '"]'); | ||
// Check for whether the events target was a select or whether the input doesn't exist | ||
if (datalistSelect.tagName.toLowerCase() !== 'select' || input === null) { | ||
return; | ||
} | ||
if ( | ||
inputList !== null && | ||
// Determine a relevant key - either printable characters (that would have a length of 1) or controlling like Backspace | ||
event.key && | ||
(event.key === 'Backspace' || event.key.length === 1) | ||
) { | ||
inputList.focus(); | ||
// Determine a relevant key - either printable characters (that would have a length of 1) or controlling like Backspace | ||
if (event.key && (event.key === 'Backspace' || event.key.length === 1)) { | ||
input.focus(); | ||
if (event.key === 'Backspace') { | ||
inputList.value = inputList.value.substr( | ||
0, | ||
inputList.value.length - 1 | ||
); | ||
if (event.key === 'Backspace') { | ||
input.value = input.value.substr(0, input.value.length - 1); | ||
// Dispatch the input event on the related input[list] | ||
dispatchInputEvent(inputList); | ||
} else { | ||
inputList.value += event.key; | ||
} | ||
// Dispatch the input event on the related input[list] | ||
dispatchInputEvent(input); | ||
} else { | ||
input.value += event.key; | ||
} | ||
prepOptions(datalist, inputList); | ||
} | ||
prepOptions(datalist, input); | ||
} | ||
@@ -472,64 +464,57 @@ }; | ||
var changeDataListSelect = function(event) { | ||
var datalistSelect = event.currentTarget; | ||
var datalistSelect = event.currentTarget, | ||
datalist = datalistSelect.parentNode, | ||
input = dcmnt.querySelector('input[list="' + datalist.id + '"]'); | ||
// Check for whether the events target was a select | ||
// Check for whether the events target was a select or whether the input doesn't exist | ||
if (datalistSelect.tagName.toLowerCase() !== 'select' || input === null) { | ||
return; | ||
} | ||
var eventType = event.type, | ||
// ENTER and ESC | ||
visible = | ||
eventType === 'keydown' && | ||
(event.keyCode !== keyENTER && event.keyCode !== keyESC); | ||
// On change, click or after pressing ENTER or TAB key, input the selects value into the input on a change within the list | ||
if ( | ||
datalistSelect.tagName && | ||
datalistSelect.tagName.toLowerCase() === 'select' | ||
eventType !== 'blur' && | ||
(eventType === 'keydown' && | ||
(event.keyCode === keyENTER || event.key === 'Tab')) && | ||
datalistSelect.value.length > 0 && | ||
datalistSelect.value !== datalist.title | ||
) { | ||
var datalist = datalistSelect.parentNode, | ||
inputList = dcmnt.querySelector('input[list="' + datalist.id + '"]'), | ||
datalistSelectValue = datalistSelect.value, | ||
eventType = event.type, | ||
// ENTER and ESC | ||
visible = | ||
eventType === 'keydown' && | ||
(event.keyCode !== keyENTER && event.keyCode !== keyESC); | ||
if (inputList !== null) { | ||
// On change, click or after pressing ENTER or TAB key, input the selects value into the input on a change within the list | ||
if ( | ||
(eventType === 'change' || | ||
eventType === 'click' || | ||
(eventType === 'keydown' && | ||
(event.keyCode === keyENTER || event.key === 'Tab'))) && | ||
typeof datalistSelectValue !== 'undefined' && | ||
datalistSelectValue.length > 0 && | ||
datalistSelectValue !== datalist.title | ||
) { | ||
var lastSeperator; | ||
var lastSeperator; | ||
// In case of type=email and multiple attribute, we need to set up the resulting inputs value differently | ||
inputList.value = | ||
// Using .getAttribute here for IE9 purpose - elsewhere it wouldn't return the newer HTML5 values correctly | ||
inputList.getAttribute('type') === 'email' && | ||
inputList.getAttribute('multiple') !== null && | ||
(lastSeperator = inputList.value.lastIndexOf(',')) > -1 | ||
? inputList.value.slice(0, lastSeperator) + | ||
',' + | ||
datalistSelectValue | ||
: (inputList.value = datalistSelectValue); | ||
// In case of type=email and multiple attribute, we need to set up the resulting inputs value differently | ||
input.value = | ||
// Using .getAttribute here for IE9 purpose - elsewhere it wouldn't return the newer HTML5 values correctly | ||
input.getAttribute('type') === 'email' && | ||
input.getAttribute('multiple') !== null && | ||
(lastSeperator = input.value.lastIndexOf(',')) > -1 | ||
? input.value.slice(0, lastSeperator) + ',' + datalistSelect.value | ||
: (input.value = datalistSelect.value); | ||
// Dispatch the input event on the related input[list] | ||
dispatchInputEvent(inputList); | ||
// Dispatch the input event on the related input[list] | ||
dispatchInputEvent(input); | ||
// Finally focusing the input, as other browser do this as well | ||
if (event.key !== 'Tab') { | ||
inputList.focus(); | ||
} | ||
// Finally focusing the input, as other browser do this as well | ||
if (event.key !== 'Tab') { | ||
input.focus(); | ||
} | ||
// Set the visibility to false afterwards, as we're done here | ||
visible = false; | ||
} else if (eventType === 'keydown' && event.keyCode === keyESC) { | ||
// In case of the ESC key being pressed, we still want to focus the input[list] | ||
inputList.focus(); | ||
} | ||
// Set the visibility to false afterwards, as we're done here | ||
visible = false; | ||
} else if (eventType === 'keydown' && event.keyCode === keyESC) { | ||
// In case of the ESC key being pressed, we still want to focus the input[list] | ||
input.focus(); | ||
} | ||
// Toggle the visibility of the datalist select according to previous checks | ||
toggleVisibility(visible, datalistSelect); | ||
} | ||
} | ||
// Toggle the visibility of the datalist select according to previous checks | ||
toggleVisibility(visible, datalistSelect); | ||
}; | ||
// Create and dispatch the input event; divided for IE9 usage | ||
var dispatchInputEvent = function(inputList) { | ||
var dispatchInputEvent = function(input) { | ||
var evt; | ||
@@ -545,3 +530,3 @@ | ||
} | ||
inputList.dispatchEvent(evt); | ||
input.dispatchEvent(evt); | ||
}; | ||
@@ -548,0 +533,0 @@ |
@@ -6,2 +6,2 @@ /* | ||
*/ | ||
!function(){"use strict";var e=window.document;if("list"in e.createElement("input")&&Boolean(e.createElement("datalist")&&window.HTMLDataListElement))return!1;!function(t){t&&t.prototype&&void 0===t.prototype.list&&Object.defineProperty(t.prototype,"list",{get:function(){return"object"==typeof this&&this instanceof t?e.querySelector("datalist#"+this.getAttribute("list")):null}})}(window.HTMLInputElement),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);var t=!1,i=13,n=27,a=38,r=40,l=" / ",o=["text","email","number","search","tel","url"],s="polyfilled",u="polyfilling";window.addEventListener("touchstart",function e(){t=!0,window.removeEventListener("touchstart",e)});var d=window.MutationObserver||window.WebKitMutationObserver,p;void 0!==d&&(p=new d(function(t){var i=!1;if(t.forEach(function(e){for(var t=0;t<e.addedNodes.length;++t)"datalist"===e.target.tagName.toLowerCase()&&(i=e.target)}),i){var n=e.querySelector('[list="'+i.id+'"]');""!==n.value&&h(y(i,n).length,i.getElementsByClassName("polyfilling")[0])}}));var c=function(e){var i=e.target,n=i.list;if(i.tagName&&"input"===i.tagName.toLowerCase()&&null!==n){var a=n.getElementsByClassName("polyfilling")[0]||f(i,n);if(void 0!==a){var r=!1,l=38===e.keyCode||40===e.keyCode;if(27!==e.keyCode&&13!==e.keyCode&&(""!==i.value||l)){y(n,i).length>0&&(r=!0);var o=0,s=a.options.length-1;t?a.selectedIndex=0:l&&(a.selectedIndex=38===e.keyCode?s:0,a.focus())}h(r,a)}}},y=function(i,n){void 0!==p&&p.disconnect();var a=i.getElementsByClassName("polyfilling")[0]||f(n,i),r=n.value,l=e.createDocumentFragment(),o=e.createDocumentFragment();"email"===n.getAttribute("type")&&null!==n.getAttribute("multiple")&&(r=r.substring(r.lastIndexOf(",")+1)),Array.prototype.slice.call(i.querySelectorAll("option:not(:disabled)")).sort(function(e,t){var i=e.value,a=t.value;return"url"===n.getAttribute("type")&&(i=i.replace(/(^\w+:|^)\/\//,""),a=a.replace(/(^\w+:|^)\/\//,"")),i.localeCompare(a)}).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,a=n.substr(0,t.length+" / ".length),s=t+" / ";n&&!i&&n!==t&&a!==s?e.innerText=t+" / "+n:e.text||(e.innerText=i||t),l.appendChild(e)}else o.appendChild(e)}),a.appendChild(l);var s=a.options.length;return a.size=s>10?10:s,a.multiple=!t&&s<2,(i.getElementsByClassName("ie9_fix")[0]||i).appendChild(o),void 0!==p&&p.observe(i,{childList:!0}),a.options},v=function(e){var t=e.target,i=t.list;if(t.tagName&&"input"===t.tagName.toLowerCase()&&null!==i){var n=e.type,a=i.getElementsByClassName("polyfilling")[0]||f(t,i),r=a&&a.querySelector("option:not(:disabled)")&&("focusin"===n&&""!==t.value||e.relatedTarget&&e.relatedTarget===a);(" "+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"===n?(t.addEventListener("keyup",c),t.addEventListener("focusout",v,!0)):"blur"===n&&(t.removeEventListener("keyup",c),t.removeEventListener("focusout",v,!0)),t.className+=" polyfilled"),h(r,a)}},f=function(i,n){if(o.indexOf(i.getAttribute("type"))>-1&&null!==n){var a=i.getClientRects(),r=window.getComputedStyle(i),l=e.createElement("select");if(l.setAttribute("class","polyfilling"),l.style.position="absolute",h(!1,l),l.setAttribute("tabindex","-1"),l.setAttribute("aria-live","polite"),l.setAttribute("role","listbox"),t||l.setAttribute("aria-multiselectable","false"),"block"===r.getPropertyValue("display"))l.style.marginTop="-"+r.getPropertyValue("margin-bottom");else{var s="rtl"===r.getPropertyValue("direction")?"right":"left";l.style.setProperty("margin-"+s,"-"+(a[0].width+parseFloat(r.getPropertyValue("margin-"+s)))+"px"),l.style.marginTop=parseInt(a[0].height+(i.offsetTop-n.offsetTop),10)+"px"}if(l.style.borderRadius=r.getPropertyValue("border-radius"),l.style.minWidth=a[0].width+"px",t){var u=e.createElement("option");u.innerText=n.title,u.disabled=!0,u.setAttribute("class","message"),l.appendChild(u)}return n.appendChild(l),t?l.addEventListener("change",m):l.addEventListener("click",m),l.addEventListener("blur",m),l.addEventListener("keydown",m),l.addEventListener("keypress",g),l}},g=function(t){var i=t.target;if(i.tagName&&"select"===i.tagName.toLowerCase()){var n=i.parentNode,a=e.querySelector('input[list="'+n.id+'"]');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),b(a)):a.value+=t.key,y(n,a))}},m=function(t){var i=t.currentTarget;if(i.tagName&&"select"===i.tagName.toLowerCase()){var n=i.parentNode,a=e.querySelector('input[list="'+n.id+'"]'),r=i.value,l=t.type,o="keydown"===l&&13!==t.keyCode&&27!==t.keyCode;if(null!==a){if(("change"===l||"click"===l||"keydown"===l&&(13===t.keyCode||"Tab"===t.key))&&void 0!==r&&r.length>0&&r!==n.title){var s;a.value="email"===a.getAttribute("type")&&null!==a.getAttribute("multiple")&&(s=a.value.lastIndexOf(","))>-1?a.value.slice(0,s)+","+r:a.value=r,b(a),"Tab"!==t.key&&a.focus(),o=!1}else"keydown"===l&&27===t.keyCode&&a.focus();h(o,i)}}},b=function(t){var i;"function"==typeof Event?i=new Event("input",{bubbles:!0}):(i=e.createEvent("Event"),i.initEvent("input",!0,!1)),t.dispatchEvent(i)},h=function(t,i){t?i.removeAttribute("hidden"):i.setAttributeNode(e.createAttribute("hidden")),i.setAttribute("aria-hidden",(!t).toString())};e.addEventListener("focusin",v,!0)}(); | ||
!function(){"use strict";var e=window.document;if("list"in e.createElement("input")&&Boolean(e.createElement("datalist")&&window.HTMLDataListElement))return!1;!function(t){t&&t.prototype&&void 0===t.prototype.list&&Object.defineProperty(t.prototype,"list",{get:function(){return"object"==typeof this&&this instanceof t?e.querySelector("datalist#"+this.getAttribute("list")):null}})}(window.HTMLInputElement),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),Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector);var t=!1,i=13,n=27,r=38,l=40,a=" / ",o=["text","email","number","search","tel","url"],s="polyfilled",u="polyfilling";window.addEventListener("touchstart",function e(){t=!0,window.removeEventListener("touchstart",e)});var d=window.MutationObserver||window.WebKitMutationObserver,p;void 0!==d&&(p=new d(function(t){var i=!1;if(t.forEach(function(e){for(var t=0;t<e.addedNodes.length;++t)"datalist"===e.target.tagName.toLowerCase()&&(i=e.target)}),i){var n=e.querySelector('input[list="'+i.id+'"]');""!==n.value&&h(y(i,n).length,i.getElementsByClassName("polyfilling")[0])}}));var c=function(e){var i=e.target,n=i.list,r=n.getElementsByClassName("polyfilling")[0]||f(i,n);if("input"===i.tagName.toLowerCase()&&null!==n&&void 0!==r){var l=!1,a=38===e.keyCode||40===e.keyCode;if(27!==e.keyCode&&13!==e.keyCode&&(""!==i.value||a)){y(n,i).length>0&&(l=!0);var o=0,s=r.options.length-1;t?r.selectedIndex=0:a&&"number"!==i.getAttribute("type")&&(r.selectedIndex=38===e.keyCode?s:0,r.focus())}h(l,r)}},y=function(i,n){void 0!==p&&p.disconnect();var r=i.getElementsByClassName("polyfilling")[0]||f(n,i),l=n.value,a=e.createDocumentFragment(),o=e.createDocumentFragment();"email"===n.getAttribute("type")&&null!==n.getAttribute("multiple")&&(l=l.substring(l.lastIndexOf(",")+1)),Array.prototype.slice.call(i.querySelectorAll("option:not(:disabled)")).sort(function(e,t){var i=e.value,r=t.value;return"url"===n.getAttribute("type")&&(i=i.replace(/(^\w+:|^)\/\//,""),r=r.replace(/(^\w+:|^)\/\//,"")),i.localeCompare(r)}).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,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 o.appendChild(e)}),r.appendChild(a);var s=r.options.length;return r.size=s>10?10:s,r.multiple=!t&&s<2,(i.getElementsByClassName("ie9_fix")[0]||i).appendChild(o),void 0!==p&&p.observe(i,{childList:!0}),r.options},v=function(e){if(e.target.matches("input[list]")){var t=e.target,i=t.list;if("input"===t.tagName.toLowerCase()&&null!==i){var n=e.type,r=i.getElementsByClassName("polyfilling")[0]||f(t,i),l=r&&r.querySelector("option:not(:disabled)")&&("focusin"===n&&""!==t.value||e.relatedTarget&&e.relatedTarget===r);t.matches(".polyfilled")||(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"===n?(t.addEventListener("keyup",c),t.addEventListener("focusout",v,!0)):"blur"===n&&(t.removeEventListener("keyup",c),t.removeEventListener("focusout",v,!0)),t.className+=" polyfilled"),h(l,r)}}},f=function(i,n){if(-1!==o.indexOf(i.getAttribute("type"))&&null!==n){var r=i.getClientRects(),l=window.getComputedStyle(i),a=e.createElement("select");if(a.setAttribute("class","polyfilling"),a.style.position="absolute",h(!1,a),a.setAttribute("tabindex","-1"),a.setAttribute("aria-live","polite"),a.setAttribute("role","listbox"),t||a.setAttribute("aria-multiselectable","false"),"block"===l.getPropertyValue("display"))a.style.marginTop="-"+l.getPropertyValue("margin-bottom");else{var s="rtl"===l.getPropertyValue("direction")?"right":"left";a.style.setProperty("margin-"+s,"-"+(r[0].width+parseFloat(l.getPropertyValue("margin-"+s)))+"px"),a.style.marginTop=parseInt(r[0].height+(i.offsetTop-n.offsetTop),10)+"px"}if(a.style.borderRadius=l.getPropertyValue("border-radius"),a.style.minWidth=r[0].width+"px",t){var u=e.createElement("option");u.innerText=n.title,u.disabled=!0,u.setAttribute("class","message"),a.appendChild(u)}return n.appendChild(a),t?a.addEventListener("change",m):a.addEventListener("click",m),a.addEventListener("blur",m),a.addEventListener("keydown",m),a.addEventListener("keypress",g),a}},g=function(t){var i=t.target,n=i.parentNode,r=e.querySelector('input[list="'+n.id+'"]');"select"===i.tagName.toLowerCase()&&null!==r&&(!t.key||"Backspace"!==t.key&&1!==t.key.length||(r.focus(),"Backspace"===t.key?(r.value=r.value.substr(0,r.value.length-1),b(r)):r.value+=t.key,y(n,r)))},m=function(t){var i=t.currentTarget,n=i.parentNode,r=e.querySelector('input[list="'+n.id+'"]');if("select"===i.tagName.toLowerCase()&&null!==r){var l=t.type,a="keydown"===l&&13!==t.keyCode&&27!==t.keyCode;if("blur"!==l&&"keydown"===l&&(13===t.keyCode||"Tab"===t.key)&&i.value.length>0&&i.value!==n.title){var o;r.value="email"===r.getAttribute("type")&&null!==r.getAttribute("multiple")&&(o=r.value.lastIndexOf(","))>-1?r.value.slice(0,o)+","+i.value:r.value=i.value,b(r),"Tab"!==t.key&&r.focus(),a=!1}else"keydown"===l&&27===t.keyCode&&r.focus();h(a,i)}},b=function(t){var i;"function"==typeof Event?i=new Event("input",{bubbles:!0}):(i=e.createEvent("Event"),i.initEvent("input",!0,!1)),t.dispatchEvent(i)},h=function(t,i){t?i.removeAttribute("hidden"):i.setAttributeNode(e.createAttribute("hidden")),i.setAttribute("aria-hidden",(!t).toString())};e.addEventListener("focusin",v,!0)}(); |
{ | ||
"name": "datalist-polyfill", | ||
"version": "1.20.1", | ||
"description": "A minimal and dependency-free vanilla JavaScript datalist polyfill. Supports all standard's functionality as well as mimics other browsers behavior.", | ||
"version": "1.21.0", | ||
"description": | ||
"A minimal and dependency-free vanilla JavaScript datalist polyfill. Supports all standard's functionality as well as mimics other browsers behavior.", | ||
"main": "datalist-polyfill.js", | ||
@@ -33,9 +34,11 @@ "scripts": { | ||
"devDependencies": { | ||
"chai": "^4.1.2", | ||
"mocha": "^5.2.0", | ||
"prettier": "^1.13.6", | ||
"xo": "^0.21.1" | ||
"wdio-mocha-framework": "^0.6.2", | ||
"webdriverio": "^4.13.1", | ||
"xo": "^0.22.0" | ||
}, | ||
"xo": { | ||
"envs": [ | ||
"browser" | ||
], | ||
"envs": ["browser"], | ||
"prettier": true, | ||
@@ -42,0 +45,0 @@ "esnext": false, |
123
README.md
@@ -1,11 +0,10 @@ | ||
[npm]: https://npmjs.com/package/datalist-polyfill "datalist polyfill – on NPM" | ||
[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] | ||
[![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") | ||
[![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] | ||
[![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) | ||
@@ -18,40 +17,42 @@ [![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/xojs/xo) | ||
* Supports all standard's functionality as well as mimics other browsers behavior. | ||
* 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 | ||
* 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. | ||
- 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 | ||
- Made in Germany. And supported by so many great people from all over this planet - see "Credits" accordingly. | ||
## Features | ||
* Lightweight: 6.2 kB of minified JavaScript, around 2.63 kB gzipped | ||
* Fully flexible to change the datalist entries / `<option>`s | ||
* Supporting: | ||
* the relevant input field types: `text`, `email`, `number`, `search`, `tel` and `url` ... | ||
* ... while leaving the others like color or date related, as those would most likely need another polyfill to work correctly or have a meaningful UI | ||
* `input[type=email]` elements `multiple` attribute | ||
* properties `.options` for `datalist` elements and `.list` for `input` elements | ||
* right to left text-direction | ||
* non-touch and touch interactions | ||
* different types of `option` declarations | ||
* both Safari and Internet Explorer (IE9+) browsers | ||
* controlling the display of differing `value` and `label` values | ||
* on `input[type=url]` omitting the scheme part and performing intelligent matching on the domain name | ||
* Emits "input" event when item in the `datalist` is selected | ||
* Enables core keyboard controls such as the | ||
* on the `input` | ||
* up and down arrow keys | ||
* on the `select` | ||
* `ESC` | ||
* `ENTER` | ||
* `BACKSPACE` | ||
* pressing printable characters | ||
* Implements the [WAI-ARIA design pattern](https://www.w3.org/TR/wai-aria-practices/) | ||
- Lightweight: 6.23 kB of minified JavaScript, around 2.66 kB gzipped | ||
- Fully flexible to change the datalist entries / `<option>`s | ||
- Supporting: | ||
_ the relevant input field types: `text`, `email`, `number`, `search`, `tel` and `url` ... | ||
_ ... while leaving the others like color or date related, as those would most likely need another polyfill to work correctly or have a meaningful UI | ||
_ `input[type=email]` elements `multiple` attribute | ||
_ properties `.options` for `datalist` elements and `.list` for `input` elements | ||
_ right to left text-direction | ||
_ non-touch and touch interactions | ||
_ different types of `option` declarations | ||
_ both Safari and Internet Explorer (IE9+) browsers | ||
_ controlling the display of differing `value` and `label` values | ||
_ on `input[type=url]` omitting the scheme part and performing intelligent matching on the domain name | ||
- Emits "input" event when item in the `datalist` is selected | ||
- Enables core keyboard controls such as the | ||
_ on the `input` | ||
_ up and down arrow keys | ||
_ on the `select` | ||
_ `ESC` | ||
_ `ENTER` | ||
_ `BACKSPACE` \* pressing printable characters | ||
- Implements the [WAI-ARIA design pattern](https://www.w3.org/TR/wai-aria-practices/) | ||
## Core concepts | ||
The polyfill was designed with the following concepts kept in mind: | ||
* dependency-free | ||
* Supporting DOM changes by event delegation and MutationObserver | ||
* code accessibility / readability | ||
- dependency-free | ||
- Supporting DOM changes by event delegation and MutationObserver | ||
- code accessibility / readability | ||
## Installation | ||
Just integrate the JavaScript file into your code - et voilà. | ||
@@ -65,2 +66,3 @@ | ||
## API | ||
Nothing really, just plug it in, it will work out of the box. | ||
@@ -73,5 +75,7 @@ | ||
### 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 and additionally using MutationObserver (IE11+) that makes your UI work easily - no refresh nor reinit function to call after DOM manipulation or something similar. | ||
### Changes to the available `option` elements | ||
If you'd like to make a change to the integrated list of `<option>` elements, feel free to either remove or add them right away - the list would get generated on the fly after the user typed in something into the `<input>` field, so you're covered on this. | ||
@@ -82,7 +86,11 @@ | ||
### Differing `value` and `label` values | ||
As the browser vendors (Google Chrome vs. the others) don't seem to be aligned on this topic, I've decided to enable the `label`-attribute to serve as the definitive label being displayed, even if a value is being defined differing from the label. On different `value` and `text` values, both of them would get displayed within the suggestions, as Google Chrome does it. But if you define a differing `label`-attribute, its value would get displayed exclusively (as all the other browsers do it) to give you some flexibility on how to define those suggestions. Check out the „Different ways of defining an option“ section on the demo page regarding this topic. | ||
### Microsoft Internet Explorer | ||
#### Internet Explorer 9 | ||
You'll need the declaration for the standard `hidden` attribute, that you might already have included in case you're using [`normalize.css`](https://github.com/necolas/normalize.css/). Otherwise just adapt it from there: | ||
```css | ||
@@ -95,3 +103,3 @@ /** | ||
[hidden] { | ||
display: none; | ||
display: none; | ||
} | ||
@@ -104,12 +112,15 @@ ``` | ||
## Demo | ||
See the polyfill in action either by downloading / forking this repo and have a look at `demos/index.html` and `demos/ie9/index.html`, or at the hosted demo: <https://mfranzke.github.io/datalist-polyfill/demos/> and <https://mfranzke.github.io/datalist-polyfill/demos/ie9/> | ||
## things to keep in mind | ||
* 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. | ||
* 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. | ||
* 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. | ||
* As I wanted to mainly focus on native elements in the most low level / simple way instead of visually emulating a list and than afterwards regain all of the functionality via a lot of JavaScript logic, I've ended up with this element, that knows how to play nicely with nested `<option>` elements. | ||
* I tried its `multiple` attribute, as this is most likely already what you're up to regarding appearance, but it does violate the form-follows-function concept and results in - surprise - the possibility for multiple selections, which isn't always `<datalist>` elements kind of thing... Then the `size` attribute came to my attention, which much better fits the requirements and behaves as designed quite perfectly. | ||
- 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. | ||
- 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. | ||
- 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. | ||
_ As I wanted to mainly focus on native elements in the most low level / simple way instead of visually emulating a list and than afterwards regain all of the functionality via a lot of JavaScript logic, I've ended up with this element, that knows how to play nicely with nested `<option>` elements. | ||
_ I tried its `multiple` attribute, as this is most likely already what you're up to regarding appearance, but it does violate the form-follows-function concept and results in - surprise - the possibility for multiple selections, which isn't always `<datalist>` elements kind of thing... Then the `size` attribute came to my attention, which much better fits the requirements and behaves as designed quite perfectly. | ||
## Credits | ||
Supported by Christian, Johannes, @mitchhentges, @mertenhanisch, @ailintom, @Kravimir, @mischah, @hryamzik, @ottoville, @IceCreamYou, @wlekin, @eddr, @beebee1987 and @mricherzhagen. Thank you very much for that, highly appreciated ! | ||
@@ -119,14 +130,20 @@ | ||
* Mac | ||
* Mac OSX 10.13, Safari 11 | ||
* Mac OSX 10.12, Safari 10 | ||
* Mac OSX 10.11, Safari 9 | ||
* iOS | ||
* iPhone 8 Simulator, Mobile Safari 11.0 | ||
* iPhone 7 Plus Simulator, Mobile Safari 10.0 | ||
* iPad Pro Simulator, Mobile Safari 9.3 | ||
* Windows | ||
* Windows 7 SP1, Internet Explorer 9.0.8112.16421 | ||
- Mac | ||
_ Mac OSX 10.13, Safari 11 | ||
_ Mac OSX 10.12, Safari 10 \* Mac OSX 10.11, Safari 9 | ||
- iOS | ||
_ iPhone 8 Simulator, Mobile Safari 11.0 | ||
_ iPhone 7 Plus Simulator, Mobile Safari 10.0 \* iPad Pro Simulator, Mobile Safari 9.3 | ||
- Windows \* Windows 7 SP1, Internet Explorer 9.0.8112.16421 | ||
### Big Thanks | ||
Cross-browser testing platform provided by [CrossBrowserTesting][crossbrowsertestinghomepage] | ||
[![CrossBrowserTesting](https://crossbrowsertesting.com/blog/wp-content/uploads/2017/09/cbt-wp-logo.png 'CrossBrowserTesting')][crossbrowsertestinghomepage] | ||
[crossbrowsertestinghomepage]: https://crossbrowsertesting.com | ||
## Outro | ||
Personally I even also do like the "keep it simple" approach provided within the [W3C specs](https://www.w3.org/TR/html5/forms.html#the-datalist-element) even already. | ||
@@ -133,0 +150,0 @@ |
Sorry, the diff of this file is not supported yet
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
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
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
128415
31
1952
147
6
1