43

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

  • 1
    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. – Wesley Murch Apr 13 '11 at 17:48
  • 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. –  Apr 13 '11 at 17:59

6 Answers6

13

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.

Richard Neil Ilagan
  • 14,627
  • 5
  • 48
  • 66
8

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).

Nick F
  • 9,781
  • 7
  • 75
  • 90
7

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
  }
}
jfd
  • 1,151
  • 10
  • 10
  • 2
    `event.originalEvent.originalEvent.type` gives me `Cannot read property 'type' of undefined` in Chrome v34 – poshest Apr 23 '14 at 23:57
  • 1
    The problem is that ```originalEvent``` is not normalized across browsers AFAIK – Andrey Feb 20 '16 at 15:08
  • 1
    Yeah, that would be a perfect solution, but it seems to be supported only in Firefox: https://developer.mozilla.org/en-US/docs/Web/API/Event/originalTarget#Browser_compatibility and they discourage using it in production. +1 for the try though. – jayarjo Dec 15 '18 at 05:52
  • 2
    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. – ron Nov 17 '20 at 20:33
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';
}
Tyler2P
  • 2,324
  • 26
  • 22
  • 31
Shivam
  • 11
  • 1
0

The first thing that comes to mind is that you can find the position of the mouse and check to see if its within the position of the element

Use this to store the position of the element:

var input = $('#your_autocompleted_element_id'),
    offset = input.offset(),
    input_x = offset.top,
    input_y = offset.left,
    input_w = input.outerWidth(),
    input_h = input.outerHeight();

Then use this to find absolute position of the mouse within the window:

var cur_mx, cur_my;
$(document).mousemove(function(e){
   cur_mx = e.pageX;
   cur_my = e.pageY;
});

Then in your autcomplete setup:

focus: function(event, ui) {
   // mouse is doing the focus when...
   // mouse x is greater than input x and less than input x + input width
   // and y is greater than input y and less than input y + input height
   if (cur_mx >= input_x && cur_mx <= input_x + input_w && cur_my >= input_y && cur_my <= input_y + input_h) {
      // do your silly mouse focus witchcraft here
   } else {
      // keyboard time!
   }
}
DiegoSalazar
  • 13,361
  • 2
  • 38
  • 55
  • 3
    ... but then, there's always the off-chance that the mouse cursor will be over the just-focused element during a tab keypress. :p – Richard Neil Ilagan Apr 13 '11 at 19:07
  • Yeah this is a great idea. But as RIchard points out it wouldn't work if the mouse happened to be positioned over the autocomplete area. +1 for effort though! –  Apr 13 '11 at 21:06
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 "/>
Nathan5x
  • 159
  • 1
  • 5