Differentiate between mouse and keyboard triggering onclick
Asked Answered
Q

9

39

I need to find a way to determine if a link has been activated via a mouse click or a keypress.

<a href="" onclick="submitData(event, '2011-07-04')">Save</a>

The idea is that if they are using a mouse to hit the link then they can keep using the mouse to choose what they do next. But if they tabbing around the page and they tab to the Save link, then I'll open then next line for editing (the page is like a spreadsheet with each line becoming editable using ajax).

I thought the event parameter could be queried for which mouse button is pressed, but when no button is pressed the answer is 0 and that's the same as the left mouse button. They I thought I could get the keyCode from the event but that is coming back as undefined so I'm assuming a mouse event doesn't include that info.

function submitData(event, id)
{
    alert("key = "+event.keyCode + "  mouse button = "+event.button);
}

always returns "key = undefined mouse button = 0"

Can you help?

Quicklime answered 18/9, 2011 at 22:52 Comment(2)
This strategy makes no sense at all. Just because a mouse click is used for one interaction doesn't mean the user wants to use a mouse for all interaction, the same for keyboard events. And why use a link that behaves like a button?Fiorin
Both those choices are above my paygrade, though I do understand the first. The idea is that certain power users, of which there are very few, will want to edit every row on the page, much like a spreadsheet, and they won't like having to switch from keyboard to mouse and back. Other users will only want to edit one row and then leave. If the following row became editable fields after they had finished editing the one they chose, they'd get distracted and maybe edit that one too.Quicklime
S
13

You can create a condition with event.type

function submitData(event, id)
{
    if(event.type == 'mousedown')
    {
        // do something
        return;
    }
    if(event.type == 'keypress')
    {
        // do something else
        return;
    }
}

Note: You'll need to attach an event which supports both event types. With JQuery it would look something like $('a.save').bind('mousedown keypress', submitData(event, this));

The inline onClick="" will not help you as it will always pass that click event since that's how it's trapped.

EDIT: Here's a working demo to prove my case with native JavaScript: http://jsfiddle.net/AlienWebguy/HPEjt/

I used a button so it'd be easier to see the node highlighted during a tab focus, but it will work the same with any node.

Sherrylsherurd answered 18/9, 2011 at 22:59 Comment(5)
I'm seeing an event.type of "click" in both cases.Nesselrode
This worked for me. Since the save button only gets rendered if the row has become editable I'll look into jQuery Live for hooking up the mousedown and keypress events. Cheers.Quicklime
Cool, but this doesn't really answer the question. A mousedown is not a click. This is a workaround that perverts the meaning of a click, which is a mousedown followed by a mouseup on the same element. Gonzalo's solution preserves the meaning of the concept.Toothed
It doesn't take "click" behavior into account. A click is mouse down and mouse up on same item, handling also long touch or drag touch on touch-sensitive devices, handling also various keyboard "clicks".Ilise
mousedown is not the same as click (although it may be close enough in some situations)Aguayo
I
24

Could check if event.screenX and event.screenY are zero.

$('a#foo').click(function(evt) {
  if (evt.screenX == 0 && evt.screenY == 0) {
    window.alert('Keyboard click.');
  } else {
    window.alert('Mouse click.');
  }
});

Demo on CodePen

I couldn't find a guarantee that it works in all browsers and all cases, but it has the benefit of not trying to detect a "click" done via the keyboard. So this solution detects "click" more reliably at the cost of detecting if it's from keyboard or mouse somewhat less reliably. If you prefer the reverse, look as the answer from @Gonzalo.

Note: One place I found using this method is Chromium

Ilise answered 27/1, 2016 at 10:6 Comment(4)
Clever. This is the only working solution on the page as of Sep 2018Anastaciaanastas
Doesn't work in Safari, though. Safari defaults the screen coordinates to the center of the target element. :(Posner
I had used this method too, but after @CraigKovatch's comment about Safari I noticed that that's indeed the case: it doesn't work on Safari (v13.1.1).Cupbearer
As of 2022, this is still the only working solution. At least in this thread. All ENTER, ARROW DOWN, and MOUSE CLICK events trigger the same evt.type value of click.Koppel
G
15

You can use event.detail

if(event.detail === 0) {
    // keypress
} else {
    // mouse event
}
Grade answered 27/3, 2019 at 10:59 Comment(3)
almost perfect. Doesn't work for Bootstrap 4 radio buttons, as when the active radio button is Mouse-clicked twice, two events are triggered (one for the aesthetic button label, and one for the input radio button behind). The first one (for <label>) returns event.detail === 1 as expected, but the second one (for <input radio>) return event.detail === 0, probably because Bootstrap is handling the change in background. +1 for promising answer, and it'll work in most cases :)Woolpack
@Woolpack maybe you can figure out a workaround using event.stopPropagation? I'm not sure whether the label always fires even when you click directly on the radio button.Lancashire
IE11 event.detail is 0 for both mouse and keyboard click events :(Posner
S
13

You can create a condition with event.type

function submitData(event, id)
{
    if(event.type == 'mousedown')
    {
        // do something
        return;
    }
    if(event.type == 'keypress')
    {
        // do something else
        return;
    }
}

Note: You'll need to attach an event which supports both event types. With JQuery it would look something like $('a.save').bind('mousedown keypress', submitData(event, this));

The inline onClick="" will not help you as it will always pass that click event since that's how it's trapped.

EDIT: Here's a working demo to prove my case with native JavaScript: http://jsfiddle.net/AlienWebguy/HPEjt/

I used a button so it'd be easier to see the node highlighted during a tab focus, but it will work the same with any node.

Sherrylsherurd answered 18/9, 2011 at 22:59 Comment(5)
I'm seeing an event.type of "click" in both cases.Nesselrode
This worked for me. Since the save button only gets rendered if the row has become editable I'll look into jQuery Live for hooking up the mousedown and keypress events. Cheers.Quicklime
Cool, but this doesn't really answer the question. A mousedown is not a click. This is a workaround that perverts the meaning of a click, which is a mousedown followed by a mouseup on the same element. Gonzalo's solution preserves the meaning of the concept.Toothed
It doesn't take "click" behavior into account. A click is mouse down and mouse up on same item, handling also long touch or drag touch on touch-sensitive devices, handling also various keyboard "clicks".Ilise
mousedown is not the same as click (although it may be close enough in some situations)Aguayo
P
5

You can differentiate between a click and a keyboard hit capturing and discarding the keydown event originated at the moment of the key press:

jQuery(function($) {
    $("a#foo").keydown(function() {
        alert("keyboard");
        return false;
    }).click(function() {
        alert("mouse");
        return false;
    })
})

http://jsfiddle.net/NuP2g/

Proficiency answered 18/9, 2011 at 23:1 Comment(3)
+1 Excellent! This is not just the most elegant answer, but really the only actual working answer to the question that preserves the meaning of the "click" concept. The other answers switch out click (the original question) for either mousedown or mouseup, which are only part of a click. Nobody wants a mouseup that began as a mousedown on a different element to be handled as a click. Nice!Toothed
There are various ways to "click" from keyboard. Usually its [ctrl+][alt+]enter but many of those open in a new tab so that should remain consistent, and sometimes other keyboard shortcuts.Ilise
This doesn't work with "Enter" key. It fires both events sequentially.Noellenoellyn
P
3

I use the following

const isKeyboardClick = nativeEvent.detail === 0 && !nativeEvent.pointerType;

Works in evergreen browsers via detail and IE11 via pointerType. Does not work for the case where e.g. radio button <input> is wrapped by a <label> element.

Posner answered 17/8, 2021 at 21:41 Comment(0)
B
3

Nowadays, you can make use of instanceof which even has full browser support.

function onMouseOrKeyboardSubmit(event, id) {
    if (event instanceof KeyboardEvent) {
        alert("Submitted via keyboard");
    } else if (event instanceof MouseEvent) {
        alert("Submitted via mouse");
    } else {
        alert("Unexpected submit event");
    }
}
Barbaric answered 5/1, 2022 at 16:17 Comment(0)
T
1

I know this is an old question but given how much time I lost looking for a working, no jquery and IE-compatible solution, I think it won't be a bad idea to put it here (where I came first).

I tested this and found it working fine :

let mouseDown = false;

element.addEventListener('mousedown', () => {
  mouseDown = true;
});

element.addEventListener('mouseup', () => {
  mouseDown = false;
});

element.addEventListener('focus', (event) => {
  if (mouseDown) {
    // keyboard
  } else {
    // mouse
  }
});

Source link : https://www.darrenlester.com/blog/focus-only-on-tab

Treaty answered 13/3, 2020 at 11:14 Comment(0)
I
1

Wasn't able to come up with solution relying entirely on the events but you can position an anchor tag over a button and give it a tabindex of -1. This gives you a button that can be focused and engaged with keyboard enter/spacebar, as well as giving you a clickable surface that gives you an option to differentiate the two codepaths.

.button {
  position: relative;
}

.anchor {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}
<button id="button" class="button">
  button
  <a class="anchor" href="#example" tabindex="-1"></a>
</button>
Ibidem answered 10/7, 2020 at 15:41 Comment(0)
S
-2

Handle the mouseup event.
If you get a click right afterwards, it was probably done with the mouse.

Sock answered 18/9, 2011 at 22:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.