From 531f2ee158e63334a38cdc281b5ed5cab1d9150c Mon Sep 17 00:00:00 2001 From: Tatiana Date: Fri, 13 Oct 2017 20:13:33 -0700 Subject: [PATCH] Typeahead behavior for listbox --- examples/listbox/js/listbox-rearrangeable.js | 3 - examples/listbox/js/listbox.js | 60 ++++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/examples/listbox/js/listbox-rearrangeable.js b/examples/listbox/js/listbox-rearrangeable.js index 3755948555..0d0f76d8ba 100644 --- a/examples/listbox/js/listbox-rearrangeable.js +++ b/examples/listbox/js/listbox-rearrangeable.js @@ -23,7 +23,4 @@ window.addEventListener('load', function () { ex2ImportantListbox.setupDelete(document.getElementById('ex2-add'), ex2UnimportantListbox); ex2UnimportantListbox.setupDelete(document.getElementById('ex2-delete'), ex2ImportantListbox); - - var ex3 = document.getElementById('ex3'); - var ex3Listbox = new aria.Listbox(document.getElementById('ss_elem_list')); }); diff --git a/examples/listbox/js/listbox.js b/examples/listbox/js/listbox.js index 0df0b5d81a..5c1e6944be 100644 --- a/examples/listbox/js/listbox.js +++ b/examples/listbox/js/listbox.js @@ -21,6 +21,7 @@ aria.Listbox = function (listboxNode) { this.upButton = null; this.downButton = null; this.deleteButton = null; + this.keysSoFar = ''; this.registerEvents(); }; @@ -149,7 +150,66 @@ aria.Listbox.prototype.checkKeyPress = function (evt) { this.focusItem(nextItem); } break; + default: + var itemToFocus = this.findItemToFocus(key); + if (itemToFocus) { + this.focusItem(itemToFocus); + } + break; + } +}; + +aria.Listbox.prototype.findItemToFocus = function (key) { + var itemList = this.listboxNode.querySelectorAll('[role="option"]'); + var character = String.fromCharCode(key); + + if (!this.keysSoFar) { + for (var i = 0; i < itemList.length; i++) { + if (itemList[i].getAttribute('id') == this.activeDescendant) { + this.searchIndex = i; + } + } + } + this.keysSoFar += character; + this.clearKeysSoFarAfterDelay(); + + var nextMatch = this.findMatchInRange( + itemList, + this.searchIndex + 1, + itemList.length + ); + if (!nextMatch) { + nextMatch = this.findMatchInRange( + itemList, + 0, + this.searchIndex + ); + } + + return nextMatch || itemList[this.searchIndex]; +}; + +aria.Listbox.prototype.clearKeysSoFarAfterDelay = function () { + if (this.keyClear) { + clearTimeout(this.keyClear); + this.keyClear = null; + } + this.keyClear = setTimeout((function () { + this.keysSoFar = ''; + this.keyClear = null; + }).bind(this), 500); +}; + +aria.Listbox.prototype.findMatchInRange = function (list, startIndex, endIndex) { + // Find the first item starting with the keysSoFar substring, searching in + // the specified range of items + for (var n = startIndex; n < endIndex; n++) { + var label = list[n].innerText; + if (label && label.toUpperCase().indexOf(this.keysSoFar) === 0) { + return list[n]; + } } + return null; }; /**