12
votes

When elements of a page have focus (such as a link or button), they show an outline. I would like this outline to only display when that element was given focus by the keyboard, not by the mouse.

Is it possible to determine how that element got its focus with JavaScript? If so, how do I then control the browser's own outlining feature?

7
If you dont know where to begin, you can look at CSS. Add a css only to input element and see how it works. Afterwards, you can do something more fancy to work in the way you want.wendelbsilva

7 Answers

11
votes

Browsers use the CSS outline property to show which element has the focus, as you might already know. So, in jQuery, you might use:

$(document).ready(function() {
    $("body").on("mousedown", "*", function(e) {
        if (($(this).is(":focus") || $(this).is(e.target)) && $(this).css("outline-style") == "none") {
            $(this).css("outline", "none").on("blur", function() {
                $(this).off("blur").css("outline", "");
            });
        }
    });
});

Explanation: This function looks for the mousedown event on any element. This event is delegated, meaning it will apply to elements currently on the page as well as any created dynamically in the future. When the mouse is clicked over the element, its CSS outline property is set to none; the outline is removed.

The targeted element gets a new handler for blur. When focus is taken from the element, the outline property is set to a blank string (this removes it from the element's style attribute), allowing the browser to control the outline again. Then, the element removes its own blur handler to free up memory. This way, an element is only outlined when focused from the keyboard.

Edit

Based on Rakesh's comments below, I made a slight change. The function can now detect if there's already an outline set, and will avoid overriding it. Demo here.

3
votes

http://jsfiddle.net/np3FE/2/

$(function(){
    var lastKey = new Date(),
        lastClick = new Date();

    $(document).on( "focusin", function(e){
        $(".non-keyboard-outline").removeClass("non-keyboard-outline");
        var wasByKeyboard = lastClick < lastKey
        if( wasByKeyboard ) {
            $( e.target ).addClass( "non-keyboard-outline");
        }

    });

    $(document).on( "click", function(){
        lastClick = new Date();
    });
    $(document).on( "keydown", function() {
        lastKey = new Date();
    });


});

CSS

*:active, *:focus {
    outline: none;
}

*:active.non-keyboard-outline, *:focus.non-keyboard-outline {
    outline: red auto 5px;
}
3
votes

Removing outline is terrible for accessibility! Ideally, the focus ring shows up only when the user intends to use the keyboard.

2018 Answer: Use :focus-visible. It's currently a W3C proposal for styling keyboard-only focus using CSS. Until major browsers support it, you can use this robust polyfill. It doesn't require adding extra elements or altering the tabindex.

/* Remove outline for non-keyboard :focus */
*:focus:not(.focus-visible) {
  outline: none;
}

/* Optional: Customize .focus-visible */
.focus-visible {
  outline-color: lightgreen;
}

I also wrote a more detailed post with some demo just in case you need more info.

1
votes

One easy way I can see is to use the mouse event to prevent the focus from firing.

$('#element').click(function(){
   $(this).blur();
});

This brings a potencial problem that you won't be able to use the mouse to select the element at all. So you can also just add a class and adjust the focus style.

$('#element').click(function(){
   $(this).addClass('fromMouse');
});
$('#element').blur(function(){
  if($(this).hasClass('fromMouse'){
     $(this).removeClass('fromMouse');
  }
});

CSS

.fromMouse{
  outline: none;
}

http://api.jquery.com/blur/

0
votes

CSS

:focus{
  outline: none;
}

.outline{
  outline: 2px solid rgba(200,120,120, 0.8);
}

jQuery code

$(function(){
  $('*').on('keydown.tab', function(e){
    /*
    TAB or Shift Tab, Aw.
    Add some more key code if you really want
    */
    if ( 9== e.which && this == e.target ){
      window.setTimeout( function(){
        $('.outline').removeClass('outline');
         $(document.activeElement).addClass('outline');
      }, 100 );
    }

  });

});

This works fine. You will get outline only when the element is focused using Keyboard ( I am aware of Tab and Shift Tab only, you can add more though )

See it working: http://jsbin.com/okarez/1

0
votes

Based on @theftprevention answer, a more customisable solution can be :

$(function(){
    $('body')
    .on('focus', '*', function() {
        var e = $(this);
        if (!e.is('.focus-mouse')) {
            e.addClass('focus-keyboard');
        }
    })
    .on('mousedown', '*', function() {
        $(this).removeClass('focus-keyboard').addClass('focus-mouse');
    })
    .on('blur', '*', function() {
        $(this).removeClass('focus-keyboard').removeClass('focus-mouse');
    });
});

Now, you just have to cutomize using .focus-keyboard and .focus-mouse classes in CSS.

.focus-keyboard{
    background:#eeeeee;
}
.focus-mouse{
    outline: 0;
}
0
votes

you can add class to body to know css if user is currently using mouse or keyboard

document.body.addEventListener('mousedown', function() {
  document.body.classList.add('using-mouse');
});
document.body.addEventListener('keydown', function() {
  document.body.classList.remove('using-mouse');
});

and in css

:focus {
  outline: #08f auto 2px;
}

body.using-mouse :focus {
  outline: none;
}