javascript - Restrict tabindex focusing to a section of the page -
situation:
i have webpage opens modal windows (light boxes) contain forms user can input data. users navigate using keyboard, tabbing 1 field next.
problem:
when modal window opens, window active, rest of page not accessible using mouse, elements can reached tabbing out of modal window.
question:
how can restrict movement using tab button elements within form window?
the thing can think of using javascript set tabindex=-1
on form elements (and other focusable elements) when modal window opened , set tabindex
values previous values when modal window closed. there simpler/better way?
no, it's way.
- find elements have
tabindex
greater-1
† , don't belong modal. - create array‡ , fill references each element along original
tabindex
. - set each element's
tabindex
-1
can no longer receive focus keyboard. - when modal dialog closed, iterate on array , restore original
tabindex
.
here's quick demo:
function isdescendant(ancestor, descendant) { { if (descendant === ancestor) return true; } while (descendant = descendant.parentnode); return false; } var tabindexrestorefunctions; var lastfocused; document.getelementbyid("btn-show-modal").addeventlistener("click", function(e) { lastfocused = document.activeelement; var modal = document.queryselector(".modal"); tabindexrestorefunctions = array.prototype // tabable items aren't children of our modal .filter.call(document.all, o => o.tabindex > -1 && !isdescendant(modal, o)) // iterate on items, set tabindex -1, , // return function restore tabindex .map(o => { var oldtabindex = o.tabindex; o.tabindex = -1; return () => o.tabindex = oldtabindex; }); // show modal modal.classlist.add("shown"); // focus modal autofocus modal.queryselector("[autofocus]").focus(); }); document.getelementbyid("btn-close-modal").addeventlistener("click", function(e) { // restore tabs tabindexrestorefunctions && tabindexrestorefunctions.foreach(f => f()); tabindexrestorefunctions = null; // hide modal document.queryselector(".modal").classlist.remove("shown"); // restore focus lastfocused && lastfocused.focus(); });
.modal { display: none; position: fixed; top: 0; right: 0; bottom: 0; left: 0; background-color: rgba(128, 128, 128, .75); } .modal.shown { display: flex; } .modal-content { margin: auto; width: 500px; padding: 30px; border: 1px solid #333; background-color: #fdfdfd; }
<label>test <input autofocus /> </label> <button>dummy button</button> <hr/> <button id="btn-show-modal">open modal</button> <div class="modal"> <div class="modal-content"> <label>test <input autofocus /> </label> <button id="btn-close-modal">close modal</button> </div> </div>
† tabindex > -1
can focus on tabable elements. further restrict filter ignore hidden elements, i'll leave you. in either case, list shouldn't big.
‡ alternatively, in demo, fill array series of functions sole purpose reset tabindex
. forego array entirely , add data-original-tab-index
attribute affected elements... using document.queryselectorall("[data-original-tab-index]")
retrieve them after fact.
here's demo uses data attributes store original tabindex
don't have maintain own array:
function isdescendant(ancestor, descendant) { { if (descendant === ancestor) return true; } while (descendant = descendant.parentnode); return false; } var lastfocused; document.getelementbyid("btn-show-modal").addeventlistener("click", function(e) { lastfocused = document.activeelement; var modal = document.queryselector(".modal"); array.prototype.foreach.call(document.all, o => { if (o.tabindex > -1 && !isdescendant(modal, o)) { o.dataset.originaltabindex = o.tabindex; o.tabindex = -1; } }); // show modal modal.classlist.add("shown"); // focus modal autofocus modal.queryselector("[autofocus]").focus(); }); document.getelementbyid("btn-close-modal").addeventlistener("click", function(e) { // restore tabs array.prototype.foreach.call(document.queryselectorall("[data-original-tab-index]"), o => { o.tabindex = o.dataset.originaltabindex; delete o.dataset.originaltabindex; }); // hide modal document.queryselector(".modal").classlist.remove("shown"); // restore focus lastfocused && lastfocused.focus(); });
.modal { display: none; position: fixed; top: 0; right: 0; bottom: 0; left: 0; background-color: rgba(128, 128, 128, .75); } .modal.shown { display: flex; } .modal-content { margin: auto; width: 500px; padding: 30px; border: 1px solid #333; background-color: #fdfdfd; }
<label>test <input autofocus /> </label> <button>dummy button</button> <hr/> <button id="btn-show-modal">open modal</button> <div class="modal"> <div class="modal-content"> <label>test <input autofocus /> </label> <button id="btn-close-modal">close modal</button> </div> </div>
see htmlelement.dataset
Comments
Post a Comment