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.

  1. find elements have tabindex greater -1 , don't belong modal.
  2. create array , fill references each element along original tabindex.
  3. set each element's tabindex -1 can no longer receive focus keyboard.
  4. 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

Popular posts from this blog

c# - How to set Z index when using WPF DrawingContext? -

razor - Is this a bug in WebMatrix PageData? -

visual c++ - Using relative values in array sorting ( asm ) -