Differentiate between focus event triggered by keyboard/mouse
Asked Answered
C

5

45

I'm using jquery ui autocomplete and want to decipher between focus events triggered by keyboard interaction and mouse interaction. How would I go about this?

$('input').autocomplete({
    source: function(request, response) {
        ...
    },
    focus: function(event, ui) {
        // If focus triggered by keyboard interaction
            alert('do something');
        // If focus event triggered by mouse interaction
            alert('do something else');
    }
});

Thanks

Chiromancy answered 13/4, 2011 at 17:44 Comment(2)
Well, the only way I know of to focus with a mouse is the click event, but how to recognize the keyboard focus - good question. I'd assume that checking what key was pressed (TAB only?) would be the right idea, but I'm not too sure. Maybe checking if click was fired on focus()? Not sure how to do these off the top of my head, but maybe this will help some one who wants to take a stab at this.Buttock
The two things I am trying to segregate are actually using arrow keys to move down/up the autocomplete list, and hovering over the autocomplete list items with the mouse. UI autocomplete handles mouse clicks by selecting the item and closing autocomplete - taking a select parameter seperately.Chiromancy
P
14

The only way I can think of doing this is to have a handler listen in on the keypress and click events, and toggle a boolean flag on/off. Then on the focus handler of your input, you can just check what the value of your flag is, and go from there.

Probably something like

var isClick;
$(document).bind('click', function() { isClick = true; })
           .bind('keypress', function() { isClick = false; })
           ;

var focusHandler = function () {
    if (isClick) {
        // clicky!
    } else {
        // tabby!
    }
}

$('input').focus(function() {
    // we set a small timeout to let the click / keypress event to trigger
    // and update our boolean
    setTimeout(focusHandler,100);
});

Whipped up a small working prototype on jsFiddle (don't you just love this site?). Check it out if you want.

Of course, this is all running off a focus event on an <input>, but the focus handler on the autocomplete works in the same way.

The setTimeout will introduce a bit of lag, but at 100ms, it might be negligible, based on your needs.

Pamulapan answered 13/4, 2011 at 19:27 Comment(5)
Also a great idea - but I'm actually not looking for click, but for hover : )Chiromancy
In that case, it shouldn't be too hard to switch out the click handler for a hover (or better yet I think, a mousein) handler. Let me see if I can whip up something in a while. :DPamulapan
setTimeout for the focus handler. Great idea.Cleanser
the keydown event is more reliable, i thinkSisterhood
@Sisterhood yes, keydown also detects tab, alt, ctrl and suchChronaxie
B
10

The easiest and most elegant way I've found of achieving this is to use the "What Input?" library. It's tiny (~2K minified), and gives you access to the event type both in scripts:

if (whatInput.ask() === 'mouse') {
  // do something
}

...and also (via a single data attribute that it adds to the document body) styles:

[data-whatinput="mouse"] :focus,
[data-whatinput="touch"] :focus {
  // focus styles for mouse and touch only
}

I particularly like the fact that where you just want a different visual behaviour for mouse / keyboard it makes it possible to do that in the stylesheet (where it really belongs) rather than via some hacky bit of event-checking Javascript (though of course if you do need to do something that's not just purely visual, the former approach lets you handle it in Javascript instead).

Bekah answered 4/4, 2016 at 17:7 Comment(0)
T
6

You should actually be able to determine this from the event-Object that is passed into the focus-event. Depending on your code structure this might be different, but there is usually a property called originalEvent in there, which might be nested to some depth. Examine the event-object more closely to determine the correct syntax. Then test on mousenter or keydown via regular expression. Something like this:

focus: function(event, ui){
  if(/^key/.test(event.originalEvent.originalEvent.type)){
    //code for keydown
  }else{
    //code for mouseenter and any other event
  }
}
Tung answered 10/11, 2011 at 11:19 Comment(4)
event.originalEvent.originalEvent.type gives me Cannot read property 'type' of undefined in Chrome v34Unclean
The problem is that originalEvent is not normalized across browsers AFAIKJeopardous
Yeah, that would be a perfect solution, but it seems to be supported only in Firefox: developer.mozilla.org/en-US/docs/Web/API/Event/… and they discourage using it in production. +1 for the try though.Maros
As of Nov 2020, this approach appears to work flawlessly on Chrome, Firefox, Safari and Edge. It's supported even though the Firefox docs say it isn't.Glottochronology
P
1

This can be handled using mousedown event, see my example below.

this.focusFrom = 'keyboard' => 

onFocus = () => {
    if (this.focusFrom === 'keyboard') {
        // do something when focus from keyboard
    }
}

handleMouseDown = () => {
    this.focusFrom = 'mouse';
}

handleOnClick = () => {
    this.focusFrom = 'keyboard';
}
Puryear answered 5/8, 2021 at 8:52 Comment(3)
@Tyler2P what do you mean? Looks like ES6Overprint
This wont work since focus fires before mousedown and keydown.Prelude
focus does not usually fire before mousedown, but I've never been able to find anything in the spec that guarantees the order anyway.Approximal
M
0

I know this question was asked a long time ago. But in case if anyone wants a solution, here is a sample code in plain JS with text input.

Console will also tell you the order of events that is happened.

const testInput = document.getElementById('testInput');
let click = false;

testInput.addEventListener('mousedown', (event) => {
  console.log('mousedown --->');
  click = true;
});


testInput.addEventListener('keydown', (event) => {
  console.log('keydown --->');
  click = false;
});


testInput.addEventListener('focus', (event) => {
  console.log('focus --->');
  if (click) {
    testInput.placeholder = "Through Mouseclick";
    testInput.classList.remove('key');
    testInput.classList.add('expand');
  } else {
    testInput.placeholder = "Through keyboard";
    testInput.classList.remove('expand');
    testInput.classList.add('key');
  }
});

testInput.addEventListener('blur', (event) => {
   // Reset flags and remove classes
   testInput.placeholder = "Enter your text   ";
   testInput.classList.remove('key');
   testInput.classList.remove('expand');
   click = false;
});
input[type='text'] {
  transition: width 200ms;
  width: 150px;
  padding: 10px;
  border-radius: 5px;
}

input[type='text'].key {
  outline: 1px solid blue;
}

input[type='text'].expand {
  outline: 2px solid green;
  width: 250px;
}
<a href="#dummy-link"> Dummy Link </a>
<br/>
<input type="text" id="testInput" placeholder="Enter your text "/>
Mccollough answered 31/8, 2023 at 20:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.