From c7c44648c070bef57222e9806d943995252aa962 Mon Sep 17 00:00:00 2001 From: Daniel Tonon Date: Thu, 16 May 2019 22:43:08 +1000 Subject: [PATCH 1/9] Added missing label error and support for more labelling techniques Adding a clear missing label error message to make it easy to understand what is going wrong if you have not provided a label. I've also added support for more valid input labelling options. This fixes this issue: https://github.com/Dan503/time-input-polyfill/issues/2 --- core/getters/get_label.js | 48 ++++++++++++++++++++++++++++++++++++--- index.js | 2 +- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/core/getters/get_label.js b/core/getters/get_label.js index 955c805..b0ae14b 100644 --- a/core/getters/get_label.js +++ b/core/getters/get_label.js @@ -2,12 +2,54 @@ var get_ancestors = require('./get_ancestors'); module.exports = function get_label ($input) { - var $forLabel = document.querySelector('label[for="'+$input.id+'"]'); - if ($forLabel) return $forLabel; + var labelText = + aria_labelledby($input) || + aria_label($input) || + for_attribute($input) || + label_wrapper_element($input) || + title_attribute($input); + + if (labelText) return labelText; + + console.error('Label text for input not found.', $input); + throw new Error('Cannot polyfill time input due to a missing label.'); +} + +function aria_labelledby($input){ + var ariaLabelByID = $input.getAttribute('aria-labelledby'); + if (ariaLabelByID) { + var $ariaLabelBy = document.getElementById(ariaLabelByID); + if ($ariaLabelBy) return $ariaLabelBy.textContent; + } + return false; +} + +function aria_label($input){ + var ariaLabel = $input.getAttribute('aria-label'); + if (ariaLabel) return ariaLabel; + return false; +} + +function for_attribute($input){ + if ($input.id) { + var $forLabel = document.querySelector('label[for="'+$input.id+'"]'); + if ($forLabel) return $forLabel.textContent; + } + return false; +} + +function label_wrapper_element($input){ var ancestors = get_ancestors($input); var $parentLabel = ancestors.filter(function($elem){ return $elem.nodeName === 'LABEL'; })[0]; - return $parentLabel; + if ($parentLabel) return $parentLabel.textContent; + return false +} + +function title_attribute($input){ + var titleLabel = $input.getAttribute('title'); + if (titleLabel) return titleLabel; + return false } diff --git a/index.js b/index.js index ef2c20e..a79c242 100644 --- a/index.js +++ b/index.js @@ -26,7 +26,7 @@ function TimePolyfill($input) { accessibility_block_created = true; } - var label = get_label($input).textContent; + var label = get_label($input); $input.polyfill = { $a11y: $a11y, From 099f4d8e81e2180d5ca69c6307768ebedc116ba5 Mon Sep 17 00:00:00 2001 From: Daniel Tonon Date: Thu, 16 May 2019 23:36:11 +1000 Subject: [PATCH 2/9] Made the get_ancestor function more efficient It can take a second parameter now that can limit how far up the DOM tree it searches for ancestors. It can stop after a single element now rather than needlessly going up the entire DOM tree for every input[type=time] element. --- core/getters/get_ancestors.js | 15 +++++++++------ core/getters/get_label.js | 8 +++----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/core/getters/get_ancestors.js b/core/getters/get_ancestors.js index ee19c0c..b995030 100644 --- a/core/getters/get_ancestors.js +++ b/core/getters/get_ancestors.js @@ -1,12 +1,15 @@ - -module.exports = function ($input) { - var a = $input; +// selector is optional, it allows for an early exit +module.exports = function ($input, selectorString) { + var $elem = $input; // https://stackoverflow.com/a/8729274/1611058 var ancestors = []; - while (a) { - ancestors.push(a); - a = a.parentNode; + while ($elem) { + ancestors.push($elem); + if ($elem.matches(selectorString)) { + return ancestors; + } + $elem = $elem.parentElement; } return ancestors; diff --git a/core/getters/get_label.js b/core/getters/get_label.js index b0ae14b..77fd0f7 100644 --- a/core/getters/get_label.js +++ b/core/getters/get_label.js @@ -40,11 +40,9 @@ function for_attribute($input){ } function label_wrapper_element($input){ - var ancestors = get_ancestors($input); - var $parentLabel = ancestors.filter(function($elem){ - return $elem.nodeName === 'LABEL'; - })[0]; - if ($parentLabel) return $parentLabel.textContent; + var ancestors = get_ancestors($input, 'label'); + var $parentLabel = ancestors[ancestors.length - 1]; + if ($parentLabel.nodeName == 'LABEL') return $parentLabel.textContent; return false } From 43e6dc3193b03543451058ed5c419b0c461a4143 Mon Sep 17 00:00:00 2001 From: Daniel Tonon Date: Thu, 16 May 2019 23:44:33 +1000 Subject: [PATCH 3/9] Added documentation about needing a label for it to work --- README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/README.md b/README.md index 98b1d7f..d733836 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,38 @@ if ($input.polyfill) $input.polyfill.update(); The `update()` method will return the input element that it was called on so it can be chained if you want. +### All `$input` elements must have a label + +The polyfill will fail if the `$input` is missing a label. + +The following is a list of ways you can add a label to the `$input`. The list is in order from the **best** method to the **worst** method: + +1. Nesting the `$input` inside a `