From b41f8977fb3144fdb56a0f50571be463b4dea156 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 2 Nov 2021 12:00:31 -0500 Subject: [PATCH] Select-Only Combobox Example: Add scroll into view feature (#2074) * updated reference table * updated the example to scroll the element referenced by aria-activedescendat into view * updated documentation --- examples/combobox/combobox-select-only.html | 1 + examples/combobox/js/select-only.js | 24 +++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/examples/combobox/combobox-select-only.html b/examples/combobox/combobox-select-only.html index 83ca76e4ca..a17c97d229 100644 --- a/examples/combobox/combobox-select-only.html +++ b/examples/combobox/combobox-select-only.html @@ -83,6 +83,7 @@

Accessibility Features

The value is set when users press Space, Enter, or Tab, or when focus moves out of the combobox. The current value is retained if the listbox is closed with Escape or if the user collapses the list by clicking the input. +
  • Browsers do not manage visibility of elements referenced by aria-activedescendant like they do for elements with focus. When a keyboard event changes the active option in the listbox, the JavaScript scrolls the option referenced by aria-activedescendant into view. Managing aria-activedescendant visibility is essential to accessibility for people who use a browser's zoom feature to increase the size of content.
  • diff --git a/examples/combobox/js/select-only.js b/examples/combobox/js/select-only.js index f8441cfc51..fdf5874a81 100644 --- a/examples/combobox/js/select-only.js +++ b/examples/combobox/js/select-only.js @@ -128,6 +128,20 @@ function getUpdatedIndex(currentIndex, maxIndex, action) { } } +// check if element is visible in browser view port +function isElementInView(element) { + var bounding = element.getBoundingClientRect(); + + return ( + bounding.top >= 0 && + bounding.left >= 0 && + bounding.bottom <= + (window.innerHeight || document.documentElement.clientHeight) && + bounding.right <= + (window.innerWidth || document.documentElement.clientWidth) + ); +} + // check if an element is currently scrollable function isScrollable(element) { return element && element.clientHeight < element.scrollHeight; @@ -318,6 +332,12 @@ Select.prototype.onOptionChange = function (index) { if (isScrollable(this.listboxEl)) { maintainScrollVisibility(options[index], this.listboxEl); } + + // ensure the new option is visible on screen + // ensure the new option is in view + if (!isElementInView(options[index])) { + options[index].scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + } }; Select.prototype.onOptionClick = function (index) { @@ -364,6 +384,10 @@ Select.prototype.updateMenuState = function (open, callFocus = true) { const activeID = open ? `${this.idBase}-${this.activeIndex}` : ''; this.comboEl.setAttribute('aria-activedescendant', activeID); + if (activeID === '' && !isElementInView(this.comboEl)) { + this.comboEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + } + // move focus back to the combobox, if needed callFocus && this.comboEl.focus(); };