javascript - Enable :focus only on keyboard use (or tab press) -


i want disable :focus when it's not needed because don't how navigation looks when focus on it. uses same style .active , it's confusing. don't want rid of people use keyboard.

i thinking add class enabled-focus on body on tab press , have body.enabled-focus a:focus{...} add lot of css every element has focus. remove class body on first mouse down.

how go it? there better solution?

this excellent article roman komarov poses viable solution achieving keyboard-only focus styles buttons, links , other container elements such spans or divs (which artificially made focusable tabindex attribute)

the solution:

button {    -moz-appearance: none;    -webkit-appearance: none;    background: none;    border: none;    outline: none;    font-size: inherit;  }    .btn {    all: initial;    margin: 1em;    display: inline-block;   }    .btn__content {    background: orange;    padding: 1em;    cursor: pointer;    display: inline-block;  }      /* fixing safari bug `<button>`s overflow */  .btn__content {      position: relative;  }    /* states on inner element */  .btn:hover > .btn__content  {      background: salmon;  }    .btn:active > .btn__content  {      background: darkorange;  }    .btn:focus > .btn__content  {      box-shadow: 0 0 2px 2px #51a7e8;      color: lime;  }    /* removing default outline after we've added our custom 1 */  .btn:focus,  .btn__content:focus {      outline: none;  }
<h2>keyboard-only focus styles</h2>    <button id="btn" class="btn" type="button">      <span class="btn__content" tabindex="-1">          i'm button!      </span>  </button>    <a class="btn" href="#x">      <span class="btn__content" tabindex="-1">          i'm link!      </span>  </a>    <span class="btn" tabindex="0">      <span class="btn__content" tabindex="-1">          i'm span!      </span>  </span>    <p>try clicking of the 3 focusable elements above - no focus styles show</p>  <p>now try tabbing - behold - focus styles</p>

codepen

1) wrap content of original interactive element inside additional inner element tabindex="-1" (see explanation below)

so instead of say:

<button id="btn" class="btn" type="button">i'm button!</button> 

do this:

<button id="btn" class="btn" type="button">     <span class="btn__content" tabindex="-1">         i'm button!     </span> </button> 

2) move css styling inner element (layout css should remain on original outer element) - width / height of outer element come inner 1 etc.

3) remove default focus styling both outer , inner elements:

.btn:focus, .btn__content:focus {     outline: none; } 

4) add focus styling inner element only when outer element has focus:

.btn:focus > .btn__content  {     box-shadow: 0 0 2px 2px #51a7e8; /* keyboard-only focus styles */     color: lime; /* keyboard-only focus styles */ }  

why work?

the trick here setting inner element tabindex="-1" - see mdn:

a negative value (usually tabindex="-1" means element should focusable, should not reachable via sequential keyboard navigation...

so element focusable via mouse clicks or programatically, on other hand - can't reached via keyboard 'tabs'.

so when interactive element clicked - inner element gets focus. no focus styles show because have removed them.

.btn:focus, .btn__content:focus {     outline: none; } 

note only 1 dom element can focused @ given time (and document.activeelement returns element) - only inner element focused.

on other hand: when tab using keyboard - only outer element focus (remember: inner element has tabindex="-1" , isn't reachable via sequential keyboard navigation) [note inherently non-focusable outer elements clickable <div> - have artificially make them focusable adding tabindex="0"]

now our css kicks in , adds keyboard-only focus styles the inner element.

.btn:focus > .btn__content  {     box-shadow: 0 0 2px 2px #51a7e8; /* keyboard-only focus styles */     color: lime; /* keyboard-only focus styles */ }  

of course, want make sure when tab , press enter - haven't broken our interactive element , javascript run.

here demo show indeed case, note though free (ie pressing enter cause click event) inherently interactive elements buttons , links... other elements such spans - need code manually :)

//var elem = array.prototype.slice.call(document.queryselectorall('.btn'));  var btns = document.queryselectorall('.btn');  var fakebtns = document.queryselectorall('.btn[tabindex="0"]');      var animate = function() {    console.log('clicked!');  }    var kbanimate = function(e) {    console.log('clicking fake btn keyboard tab + enter...');    var code = e.which;    // 13 = return, 32 = space    if (code === 13) {      this.click();    }    }    array.from(btns).foreach(function(element) {    element.addeventlistener('click', animate);  });    array.from(fakebtns).foreach(function(element) {    element.addeventlistener('keydown', kbanimate);  });
button {    -moz-appearance: none;    -webkit-appearance: none;    background: none;    border: none;    outline: none;    font-size: inherit;  }    .btn {    all: initial;    margin: 1em;    display: inline-block;   }    .btn__content {    background: orange;    padding: 1em;    cursor: pointer;    display: inline-block;  }      /* fixing safari bug `<button>`s overflow */  .btn__content {      position: relative;  }    /* states on inner element */  .btn:hover > .btn__content  {      background: salmon;  }    .btn:active > .btn__content  {      background: darkorange;  }    .btn:focus > .btn__content  {      box-shadow: 0 0 2px 2px #51a7e8;      color: lime;  }    /* removing default outline after we've added our custom 1 */  .btn:focus,  .btn__content:focus {      outline: none;  }
<h2>keyboard-only focus styles</h2>    <button id="btn" class="btn" type="button">      <span class="btn__content" tabindex="-1">          i'm button!      </span>  </button>    <a class="btn" href="#x">      <span class="btn__content" tabindex="-1">          i'm link!      </span>  </a>    <span class="btn" tabindex="0">      <span class="btn__content" tabindex="-1">          i'm span!      </span>  </span>    <p>try clicking of the 3 focusable elements above - no focus styles show</p>  <p>now try tabbing + enter - behold - our interactive elements work</p>

codepen


nb:

1) although seems overly-complicated solution, non-javascript solution it's quite impressive. simpler css-only 'solutions' involving :hover , :active pseudo class styling don't work. (unless of course assume interactive element disappears on click button within modal say)

button {    -moz-appearance: none;    -webkit-appearance: none;    background: none;    border: none;    font-size: inherit;  }    .btn {    margin: 1em;    display: inline-block;     background: orange;    padding: 1em;    cursor: pointer;  }    .btn:hover, .btn:active {    outline: none;  }
<h2>remove css :focus outline on :hover , :active states</h2>    <button class="btn" type="button">i'm button!</button>    <a class="btn" href="#x">i'm link!</a>    <span class="btn" tabindex="0">i'm span!</span>    <h3>problem: click on interactive element.as hover out - focus styling - because still focused (at least regarding button , focusable span) </h3>

codepen

2) solution isn't perfect: firefox on windows still focus styles buttons on click - seems firefox bug (see the article)

3) when browsers implement :fo­cus-ring pseudo class - there may simpler solution problem - (see the article) it's worth, there a polyfill :focus-ring - see this article chris demars


a pragmatic alternative keyboard-only focus styles

so achieving keyboard-only focus styles surprisingly difficult. 1 alternative / workaround much simpler , may both fulfil designer's expectations , accessible - style focus style hover.

codepen

so although technically not implementing keyboard-only styles, removes need keyboard-only styles.


Comments

Popular posts from this blog

javascript - Using jquery append to add option values into a select element not working -

Android soft keyboard reverts to default keyboard on orientation change -

jquery - javascript onscroll fade same class but with different div -