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

python - Why doesn't list have safe "get" method like dictionary? -

razor - Is this a bug in WebMatrix PageData? -

sql server - Stored procedure -