Onhashchange with browser buttons only - javascript

I've got this issue (I'm using jQuery but I'm not restricted to it):
I'm using a combo of Anchor navigation (#id) and Ajax requests. To get the pages to move into place (using anchor navigation) or to fetch information (using Ajax), I use the onhashchange event.
EDIT: I had a little typo. I forgot to check if the mouseDown flag was true and the hashchange event was triggered so I added that if statement.
with jQuery it looks like this: (of course this code is wrapped in a function and initialized on DOM load but it doesn't matter for the question)
$(window).bind('hashchange', function(e) { }
To ensure only browsers supporting the onhashchange reads the code I encapsulate it like this:
if ('onhashchange' in window) {
$(window).bind('hashchange', function(e) { }
}
My web app is made in such way that I only want the onhashchange event to trigger when I hit the back/forward buttons in the browser. To do that I do like this:
if ('onhashchange' in window) {
$(window).bind('mousedown hashchange', function(e) { }
}
Now if I click within the viewport I will trigger the mousedown event. If the mousedown event is triggered I know that I didn't click the browser back/forward buttons and I can stop the onhashchange event using a flag like this:
var mouseDown = false;
if ('onhashchange' in window) {
$(window).bind('mousedown hashchange', function(e) {
if (e.type === 'mousedown') {
mouseDown = true;
}
if (mouseDown && e.type === 'hashchange') {
// if the mousedown event was triggered and when the haschange event triggers,
// we need to stop the hashchange event and restore the mouseDown flag
mouseDown = false;
e.stopPropagation();
}
if (!mouseDown && e.type === 'hashchange') {
// Do the onhashchange stuff here
}
}
}
This causes a problem for IE since it seams you cannot bind mouse events to the window object (?). IE will never "see" the mousedown event.
To solve this IE issue I can take the "clientY" property. This property is passed in all event calls in IE and tells you the coordinates of the mouse. If e.clientY is less then 0, the mouse is outside the viewport and I will know that I triggered the onhashchange by clicking the browser back/forward buttons. It now looks like this:
var mouseDown = false;
if ('onhashchange' in window) {
$(window).bind('mousedown hashchange', function(e) {
// IE: Use e.clientY to check if the mouse position was within the viewport (i.e. not a nagative value for Y)
// !IE: Use e.type
if (e.type === 'mousedown' || e.clientY > 0 ) {
mouseDown = true;
}
if (mouseDown && e.type === 'hashchange') {
// if the mousedown event was triggered and when the haschange event triggers,
// we need to stop the hashchange event and restore the mouseDown flag
mouseDown = false;
e.stopPropagation();
}
if (!mouseDown && e.type === 'hashchange') {
// Do the onhashchange stuff here
}
}
}
This solution was working like a charm until I had to add support for navigating with the arrows on the keyboard. Now it doesn't matter where on the screen the mouse is. As long as the IE window is "active", the keydown event listening for keyboard input triggers when hitting the keyboard. This means that the clientY check does not work anymore as intended.
The Problem:
As far as I know, the onhashchange must be bound to the window object. All events must be processed within the same callback function if I want to be able to control one event by listening for another.
How can I get this to work?

So, simply put-
"how do I distinguish between a back/forward button press vs. navigation coming from interacting with the DOM".
You may want to have a flag such that when you are changing the hash part of the URL from code, you set this flag, ignore the hashchange event, then unset the flag. In which case the event will be ignored ( a kind of reverse solution as to what you're trying to do). You'd obviously want to wrap this in a function.
In general however, applications that use the hashchange event for navigation will often use the hashchange event as a means for changing the state of the application. Therefore, there is only one entry point and you do not need to distinguish between whether the event is generated by browser navigation vs. dom interaction. I'd probably recommend changing your approach.
I'd also point you to the fact that history can be supported across all browsers (even IE6 and IE7 using an iFrame hack). Take a look at the jQuery history plugin

The reference library to achieve this:
http://benalman.com/projects/jquery-bbq-plugin/
I used it and it works great
Think about putting "!" after "#" in the url, so that google can discover the ajax pages
http://code.google.com/web/ajaxcrawling/docs/getting-started.html

Related

Tab cycling back to first tabbable DOM element instead of address bar

I have a tree component at the end of the DOM that handles keydown events so that the tree is navigable via arrow keys. I don't have any cases that respond to a "Tab" press, but the default behavior seems to send the focus to the first tabbable element in the DOM, rather than shifting focus to the browser.
I am unsure if this would qualify as a keyboard trap, as the focus is able to pass through all available DOM elements, and users can still access the browser via Ctrl+L/Cmd+L.
If I add a tabbable item after this tree component, such as a , shifting focus from the tree moves it to the button, and another Tab press will bring the focus into the browser.
Currently, I am not able to add a tabbable element to the end of the DOM that would make sense with the UI design. My main question: Does this looping back to the first element in the DOM from the last element introduce a Keyboard Trap concern? If so, is there any way I could get a Tab keypress to move the focus into the browser via Javascript?
Edit: This behavior was observed on Chrome on a Windows VM.
We're using jQuery to handle the keydown events, but removing the Tab case still cycles the focus back to the top:
$.on({
mousedown: {
//...handle mousedown event
},
keydown: function (e) {
var newFocus;
if (e.keyCode === keyCodes.Tab) {
// NOTE: tried removing this check entirely before, still cycles back to the first element rather than exiting focus to the browser
// Perhaps cycling to the first tabbable element is default jQuery keydown behavior?
e.preventDefault(); // Prevent tab nav from bubbling-up and triggering focusin
newFocus = e.shiftKey ?
getPreviousTabTarget() :
getNextTabTarget();
} else if (arrowKeyCodes.indexOf(e.keyCode) !== -1) {
//...handle arrow key keydown event
} else if (e.keyCode === keyCodes.Enter) {
//...handle Enter keydown event
} else if (e.keyCode === keyCodes.Space) {
//...handle Space keydown event
}
if (newFocus) {
newFocus.focus();
}
},
focusin: function (e) {
if (!currentItem) {
currentItem = jqElement.children()[0];
}
if (currentItem) {
currentItem.focus();
}
}
});
Edit 2: This example page from W3 showing a jQuery keydown/keyup event handler does pass the tab into the browser properly. There is definitely something I missed here. Thanks #QuentinC for the sanity check! And thanks to everyone for your responses.

JavaScript click event does not work on mobile touchscreen [duplicate]

I'm working on a mobile web site that has to work on a variety of devices. The ones giving me a headache at the moment are BlackBerry.
We need to support both keyboard clicks as well as touch events.
Ideally I'd just use:
$thing.click(function(){...})
but the issue we're running into is that some of these blackberry devices have a very annoying delay from the time of the touch to it triggering a click.
The remedy is to instead use touchstart:
$thing.bind('touchstart', function(event){...})
But how do I go about binding both events, but only firing one? I still need the click event for keyboard devices, but of course, don't want the click event firing if I'm using a touch device.
A bonus question: Is there anyway to do this and additionally accommodate browsers that don't even have a touchstart event? In researching this, it looks like BlackBerry OS5 doesn't support touchstart so will also need to rely on click events for that browser.
ADDENDUM:
Perhaps a more comprehensive question is:
With jQuery, is it possible/recommended to handle both touch interactions and mouse interactions with the same bindings?
Ideally, the answer is yes. If not, I do have some options:
We use WURFL to get device info so could create our own matrix of devices. Depending on the device, we'll use touchstart OR click.
Detect for touch support in the browser via JS (I need to do some more research on that, but it seems like that is doable).
However, that still leaves one issue: what about devices that support BOTH. Some of the phones we support (namely the Nokias and BlackBerries) have both touch screens and keyboards. So that kind of takes me full circle back to the original question...is there a way to allow for both at once somehow?
Update: Check out the jQuery Pointer Events Polyfill project which allows you to bind to "pointer" events instead of choosing between mouse & touch.
Bind to both, but make a flag so the function only fires once per 100ms or so.
var flag = false;
$thing.bind('touchstart click', function(){
if (!flag) {
flag = true;
setTimeout(function(){ flag = false; }, 100);
// do something
}
return false
});
This is the fix that I "create" and it take out the GhostClick and implements the FastClick. Try on your own and let us know if it worked for you.
$(document).on('touchstart click', '.myBtn', function(event){
if(event.handled === false) return
event.stopPropagation();
event.preventDefault();
event.handled = true;
// Do your magic here
});
You could try something like this:
var clickEventType=((document.ontouchstart!==null)?'click':'touchstart');
$("#mylink").bind(clickEventType, myClickHandler);
Usually this works as well:
$('#buttonId').on('touchstart click', function(e){
e.stopPropagation(); e.preventDefault();
//your code here
});
Just adding return false; at the end of the on("click touchstart") event function can solve this problem.
$(this).on("click touchstart", function() {
// Do things
return false;
});
From the jQuery documentation on .on()
Returning false from an event handler will automatically call event.stopPropagation() and event.preventDefault(). A false value can also be passed for the handler as a shorthand for function(){ return false; }.
I had to do something similar. Here is a simplified version of what worked for me. If a touch event is detected, remove the click binding.
$thing.on('touchstart click', function(event){
if (event.type == "touchstart")
$(this).off('click');
//your code here
});
In my case the click event was bound to an <a> element so I had to remove the click binding and rebind a click event which prevented the default action for the <a> element.
$thing.on('touchstart click', function(event){
if (event.type == "touchstart")
$(this).off('click').on('click', function(e){ e.preventDefault(); });
//your code here
});
I succeeded by the following way.
Easy Peasy...
$(this).on('touchstart click', function(e){
e.preventDefault();
//do your stuff here
});
I believe the best practice is now to use:
$('#object').on('touchend mouseup', function () { });
touchend
The touchend event is fired when a touch point is removed from the touch surface.
The touchend event will not trigger any mouse events.
mouseup
The mouseup event is sent to an element when the mouse pointer is over the element, and the mouse button is released. Any HTML element can receive this event.
The mouseup event will not trigger any touch events.
EXAMPLE
$('#click').on('mouseup', function () { alert('Event detected'); });
$('#touch').on('touchend', function () { alert('Event detected'); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1 id="click">Click me</h1>
<h1 id="touch">Touch me</h1>
EDIT (2017)
As of 2017, browsers starting with Chrome are making steps towards making the click event .on("click") more compatible for both mouse and touch by eliminating the delay generated by tap events on click requests.
This leads to the conclusion that reverting back to using just the click event would be the simplest solution moving forward.
I have not yet done any cross browser testing to see if this is practical.
check fast buttons and chost clicks from google https://developers.google.com/mobile/articles/fast_buttons
Well... All of these are super complicated.
If you have modernizr, it's a no-brainer.
ev = Modernizr.touch ? 'touchstart' : 'click';
$('#menu').on(ev, '[href="#open-menu"]', function(){
//winning
});
Generally you don't want to mix the default touch and non-touch (click) api. Once you move into the world of touch it easier to deal only with the touch related functions. Below is some pseudo code that would do what you want it to.
If you connect in the touchmove event and track the locations you can add more items in the doTouchLogic function to detect gestures and whatnot.
var touchStartTime;
var touchStartLocation;
var touchEndTime;
var touchEndLocation;
$thing.bind('touchstart'), function() {
var d = new Date();
touchStartTime = d.getTime();
touchStartLocation = mouse.location(x,y);
});
$thing.bind('touchend'), function() {
var d = new Date();
touchEndTime= d.getTime();
touchEndLocation= mouse.location(x,y);
doTouchLogic();
});
function doTouchLogic() {
var distance = touchEndLocation - touchStartLocation;
var duration = touchEndTime - touchStartTime;
if (duration <= 100ms && distance <= 10px) {
// Person tapped their finger (do click/tap stuff here)
}
if (duration > 100ms && distance <= 10px) {
// Person pressed their finger (not a quick tap)
}
if (duration <= 100ms && distance > 10px) {
// Person flicked their finger
}
if (duration > 100ms && distance > 10px) {
// Person dragged their finger
}
}
Another implementation for better maintenance. However, this technique will also do event.stopPropagation (). The click is not caught on any other element that clicked for 100ms.
var clickObject = {
flag: false,
isAlreadyClicked: function () {
var wasClicked = clickObject.flag;
clickObject.flag = true;
setTimeout(function () { clickObject.flag = false; }, 100);
return wasClicked;
}
};
$("#myButton").bind("click touchstart", function (event) {
if (!clickObject.isAlreadyClicked()) {
...
}
}
Just for documentation purposes, here's what I've done for the fastest/most responsive click on desktop/tap on mobile solution that I could think of:
I replaced jQuery's on function with a modified one that, whenever the browser supports touch events, replaced all my click events with touchstart.
$.fn.extend({ _on: (function(){ return $.fn.on; })() });
$.fn.extend({
on: (function(){
var isTouchSupported = 'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
return function( types, selector, data, fn, one ) {
if (typeof types == 'string' && isTouchSupported && !(types.match(/touch/gi))) types = types.replace(/click/gi, 'touchstart');
return this._on( types, selector, data, fn);
};
}()),
});
Usage than would be the exact same as before, like:
$('#my-button').on('click', function(){ /* ... */ });
But it would use touchstart when available, click when not. No delays of any kind needed :D
I just came up with the idea to memorize if ontouchstart was ever triggered. In this case we are on a device which supports it and want to ignore the onclick event. Since ontouchstart should always be triggered before onclick, I'm using this:
<script> touchAvailable = false; </script>
<button ontouchstart="touchAvailable=true; myFunction();" onclick="if(!touchAvailable) myFunction();">Button</button>
You could try like this:
var clickEvent = (('ontouchstart' in document.documentElement)?'touchstart':'click');
$("#mylink").on(clickEvent, myClickHandler);
In my case this worked perfectly:
jQuery(document).on('mouseup keydown touchend', function (event) {
var eventType = event.type;
if (eventType == 'touchend') {
jQuery(this).off('mouseup');
}
});
The main problem was when instead mouseup I tried with click, on touch devices triggered click and touchend at the same time, if i use the click off, some functionality didn't worked at all on mobile devices. The problem with click is that is a global event that fire the rest of the event including touchend.
This worked for me, mobile listens to both, so prevent the one, which is the touch event. desktop only listen to mouse.
$btnUp.bind('touchstart mousedown',function(e){
e.preventDefault();
if (e.type === 'touchstart') {
return;
}
var val = _step( _options.arrowStep );
_evt('Button', [val, true]);
});
This hasn't been mentioned here, but you may want to check out this link: https://joshtronic.com/2015/04/19/handling-click-and-touch-events-on-the-same-element/
To recap for posterity, instead of trying to assign to both handlers and then sort out the result, you can simply check if the device is a touchscreen or not and only assign to the relevant event. Observe:
var clickEvent = (function() {
if ('ontouchstart' in document.documentElement === true)
return 'touchstart';
else
return 'click';
})();
// and assign thusly:
el.addEventListener( clickEvent, function( e ){
// things and stuff
});
I am using this to bind my events so that I can test on touchscreens that handle both touchstart and click events which would fire twice, and on my development PC which only hears the click
One problem the author of that link mentions though, is touchscreen laptops designed to handle both events:
I learned about a third device I was not considering, the touchscreen laptop. It’s a hybrid device that supports both touch and click events. Binding one event means only that event be supported. Does that mean someone with a touchscreen and mouse would have to explicitly touch because that’s the only event I am handling?
Binding touchstart and click seemed ideal to handle these hybrid devices. To keep the event from firing twice, I added e.stopPropagation() and e.preventDefault() to the callback functions. e.stopPropagation() stops events from “bubbling up” to their parents but also keeps a second event from firing. I included e.preventDefault() as a “just in case” but seems like it could be omitted.
Being for me the best answer the one given by Mottie, I'm just trying to do his code more reusable, so this is my contribution:
bindBtn ("#loginbutton",loginAction);
function bindBtn(element,action){
var flag = false;
$(element).bind('touchstart click', function(e) {
e.preventDefault();
if (!flag) {
flag = true;
setTimeout(function() {
flag = false;
}, 100);
// do something
action();
}
return false;
});
I am also working on an Android/iPad web app, and it seems that if only using "touchmove" is enough to "move components" ( no need touchstart ).
By disabling touchstart, you can use .click(); from jQuery. It's actually working because it hasn't be overloaded by touchstart.
Finally, you can binb .live("touchstart", function(e) { e.stopPropagation(); }); to ask the touchstart event to stop propagating, living room to click() to get triggered.
It worked for me.
There are many things to consider when trying to solve this issue. Most solutions either break scrolling or don't handle ghost click events properly.
For a full solution see https://developers.google.com/mobile/articles/fast_buttons
NB: You cannot handle ghost click events on a per-element basis. A delayed click is fired by screen location, so if your touch events modify the page in some way, the click event will be sent to the new version of the page.
It may be effective to assign to the events 'touchstart mousedown' or 'touchend mouseup' to avoid undesired side-effects of using click.
Taking advantage of the fact that a click will always follow a touch event, here is what I did to get rid of the "ghost click" without having to use timeouts or global flags.
$('#buttonId').on('touchstart click', function(event){
if ($(this).data("already")) {
$(this).data("already", false);
return false;
} else if (event.type == "touchstart") {
$(this).data("already", true);
}
//your code here
});
Basically whenever an ontouchstart event fires on the element, a flag a set and then subsequently removed (and ignored), when the click comes.
Why not use the jQuery Event API?
http://learn.jquery.com/events/event-extensions/
I've used this simple event with success. It's clean, namespaceable and flexible enough to improve upon.
var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent);
var eventType = isMobile ? "touchstart" : "click";
jQuery.event.special.touchclick = {
bindType: eventType,
delegateType: eventType
};
If you are using jQuery the following worked pretty well for me:
var callback; // Initialize this to the function which needs to be called
$(target).on("click touchstart", selector, (function (func){
var timer = 0;
return function(e){
if ($.now() - timer < 500) return false;
timer = $.now();
func(e);
}
})(callback));
Other solutions are also good but I was binding multiple events in a loop and needed the self calling function to create an appropriate closure. Also, I did not want to disable the binding since I wanted it to be invoke-able on next click/touchstart.
Might help someone in similar situation!
For simple features, just recognize touch or click I use the following code:
var element = $("#element");
element.click(function(e)
{
if(e.target.ontouchstart !== undefined)
{
console.log( "touch" );
return;
}
console.log( "no touch" );
});
This will return "touch" if the touchstart event is defined and "no touch" if not. Like I said this is a simple approach for click/tap events just that.
I am trying this and so far it works (but I am only on Android/Phonegap so caveat emptor)
function filterEvent( ob, ev ) {
if (ev.type == "touchstart") {
ob.off('click').on('click', function(e){ e.preventDefault(); });
}
}
$('#keypad').on('touchstart click', '.number, .dot', function(event) {
filterEvent( $('#keypad'), event );
console.log( event.type ); // debugging only
... finish handling touch events...
}
I don't like the fact that I am re-binding handlers on every touch, but all things considered touches don't happen very often (in computer time!)
I have a TON of handlers like the one for '#keypad' so having a simple function that lets me deal with the problem without too much code is why I went this way.
Try to use Virtual Mouse (vmouse) Bindings from jQuery Mobile.
It's virtual event especially for your case:
$thing.on('vclick', function(event){ ... });
http://api.jquerymobile.com/vclick/
Browser support list: http://jquerymobile.com/browser-support/1.4/
EDIT: My former answer (based on answers in this thread) was not the way to go for me. I wanted a sub-menu to expand on mouse enter or touch click and to collapse on mouse leave or another touch click. Since mouse events normally are being fired after touch events, it was kind of tricky to write event listeners that support both touchscreen and mouse input at the same time.
jQuery plugin: Touch Or Mouse
I ended up writing a jQuery plugin called "Touch Or Mouse" (897 bytes minified) that can detect whether an event was invoked by a touchscreen or mouse (without testing for touch support!). This enables the support of both touchscreen and mouse at the same time and completely separate their events.
This way the OP can use touchstart or touchend for quickly responding to touch clicks and click for clicks invoked only by a mouse.
Demonstration
First one has to make ie. the body element track touch events:
$(document.body).touchOrMouse('init');
Mouse events our bound to elements in the default way and by calling $body.touchOrMouse('get', e) we can find out whether the event was invoked by a touchscreen or mouse.
$('.link').click(function(e) {
var touchOrMouse = $(document.body).touchOrMouse('get', e);
if (touchOrMouse === 'touch') {
// Handle touch click.
}
else if (touchOrMouse === 'mouse') {
// Handle mouse click.
}
}
See the plugin at work at http://jsfiddle.net/lmeurs/uo4069nh.
Explanation
This plugin needs to be called on ie. the body element to track touchstart and touchend events, this way the touchend event does not have to be fired on the trigger element (ie. a link or button). Between these two touch events this plugin considers any mouse event to be invoked by touch.
Mouse events are fired only after touchend, when a mouse event is being fired within the ghostEventDelay (option, 1000ms by default) after touchend, this plugin considers the mouse event to be invoked by touch.
When clicking on an element using a touchscreen, the element gains the :active state. The mouseleave event is only fired after the element loses this state by ie. clicking on another element. Since this could be seconds (or minutes!) after the mouseenter event has been fired, this plugin keeps track of an element's last mouseenter event: if the last mouseenter event was invoked by touch, the following mouseleave event is also considered to be invoked by touch.
Here's a simple way to do it:
// A very simple fast click implementation
$thing.on('click touchstart', function(e) {
if (!$(document).data('trigger')) $(document).data('trigger', e.type);
if (e.type===$(document).data('trigger')) {
// Do your stuff here
}
});
You basically save the first event type that is triggered to the 'trigger' property in jQuery's data object that is attached to the root document, and only execute when the event type is equal to the value in 'trigger'. On touch devices, the event chain would likely be 'touchstart' followed by 'click'; however, the 'click' handler won't be executed because "click" doesn't match the initial event type saved in 'trigger' ("touchstart").
The assumption, and I do believe it's a safe one, is that your smartphone won't spontaneously change from a touch device to a mouse device or else the tap won't ever register because the 'trigger' event type is only saved once per page load and "click" would never match "touchstart".
Here's a codepen you can play around with (try tapping on the button on a touch device -- there should be no click delay): http://codepen.io/thdoan/pen/xVVrOZ
I also implemented this as a simple jQuery plugin that also supports jQuery's descendants filtering by passing a selector string:
// A very simple fast click plugin
// Syntax: .fastClick([selector,] handler)
$.fn.fastClick = function(arg1, arg2) {
var selector, handler;
switch (typeof arg1) {
case 'function':
selector = null;
handler = arg1;
break;
case 'string':
selector = arg1;
if (typeof arg2==='function') handler = arg2;
else return;
break;
default:
return;
}
this.on('click touchstart', selector, function(e) {
if (!$(document).data('trigger')) $(document).data('trigger', e.type);
if (e.type===$(document).data('trigger')) handler.apply(this, arguments);
});
};
Codepen: http://codepen.io/thdoan/pen/GZrBdo/

Is there in JQuery or JS event only for mouse click but not for touch [duplicate]

I'm working on a mobile web site that has to work on a variety of devices. The ones giving me a headache at the moment are BlackBerry.
We need to support both keyboard clicks as well as touch events.
Ideally I'd just use:
$thing.click(function(){...})
but the issue we're running into is that some of these blackberry devices have a very annoying delay from the time of the touch to it triggering a click.
The remedy is to instead use touchstart:
$thing.bind('touchstart', function(event){...})
But how do I go about binding both events, but only firing one? I still need the click event for keyboard devices, but of course, don't want the click event firing if I'm using a touch device.
A bonus question: Is there anyway to do this and additionally accommodate browsers that don't even have a touchstart event? In researching this, it looks like BlackBerry OS5 doesn't support touchstart so will also need to rely on click events for that browser.
ADDENDUM:
Perhaps a more comprehensive question is:
With jQuery, is it possible/recommended to handle both touch interactions and mouse interactions with the same bindings?
Ideally, the answer is yes. If not, I do have some options:
We use WURFL to get device info so could create our own matrix of devices. Depending on the device, we'll use touchstart OR click.
Detect for touch support in the browser via JS (I need to do some more research on that, but it seems like that is doable).
However, that still leaves one issue: what about devices that support BOTH. Some of the phones we support (namely the Nokias and BlackBerries) have both touch screens and keyboards. So that kind of takes me full circle back to the original question...is there a way to allow for both at once somehow?
Update: Check out the jQuery Pointer Events Polyfill project which allows you to bind to "pointer" events instead of choosing between mouse & touch.
Bind to both, but make a flag so the function only fires once per 100ms or so.
var flag = false;
$thing.bind('touchstart click', function(){
if (!flag) {
flag = true;
setTimeout(function(){ flag = false; }, 100);
// do something
}
return false
});
This is the fix that I "create" and it take out the GhostClick and implements the FastClick. Try on your own and let us know if it worked for you.
$(document).on('touchstart click', '.myBtn', function(event){
if(event.handled === false) return
event.stopPropagation();
event.preventDefault();
event.handled = true;
// Do your magic here
});
You could try something like this:
var clickEventType=((document.ontouchstart!==null)?'click':'touchstart');
$("#mylink").bind(clickEventType, myClickHandler);
Usually this works as well:
$('#buttonId').on('touchstart click', function(e){
e.stopPropagation(); e.preventDefault();
//your code here
});
Just adding return false; at the end of the on("click touchstart") event function can solve this problem.
$(this).on("click touchstart", function() {
// Do things
return false;
});
From the jQuery documentation on .on()
Returning false from an event handler will automatically call event.stopPropagation() and event.preventDefault(). A false value can also be passed for the handler as a shorthand for function(){ return false; }.
I had to do something similar. Here is a simplified version of what worked for me. If a touch event is detected, remove the click binding.
$thing.on('touchstart click', function(event){
if (event.type == "touchstart")
$(this).off('click');
//your code here
});
In my case the click event was bound to an <a> element so I had to remove the click binding and rebind a click event which prevented the default action for the <a> element.
$thing.on('touchstart click', function(event){
if (event.type == "touchstart")
$(this).off('click').on('click', function(e){ e.preventDefault(); });
//your code here
});
I succeeded by the following way.
Easy Peasy...
$(this).on('touchstart click', function(e){
e.preventDefault();
//do your stuff here
});
I believe the best practice is now to use:
$('#object').on('touchend mouseup', function () { });
touchend
The touchend event is fired when a touch point is removed from the touch surface.
The touchend event will not trigger any mouse events.
mouseup
The mouseup event is sent to an element when the mouse pointer is over the element, and the mouse button is released. Any HTML element can receive this event.
The mouseup event will not trigger any touch events.
EXAMPLE
$('#click').on('mouseup', function () { alert('Event detected'); });
$('#touch').on('touchend', function () { alert('Event detected'); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1 id="click">Click me</h1>
<h1 id="touch">Touch me</h1>
EDIT (2017)
As of 2017, browsers starting with Chrome are making steps towards making the click event .on("click") more compatible for both mouse and touch by eliminating the delay generated by tap events on click requests.
This leads to the conclusion that reverting back to using just the click event would be the simplest solution moving forward.
I have not yet done any cross browser testing to see if this is practical.
check fast buttons and chost clicks from google https://developers.google.com/mobile/articles/fast_buttons
Well... All of these are super complicated.
If you have modernizr, it's a no-brainer.
ev = Modernizr.touch ? 'touchstart' : 'click';
$('#menu').on(ev, '[href="#open-menu"]', function(){
//winning
});
Generally you don't want to mix the default touch and non-touch (click) api. Once you move into the world of touch it easier to deal only with the touch related functions. Below is some pseudo code that would do what you want it to.
If you connect in the touchmove event and track the locations you can add more items in the doTouchLogic function to detect gestures and whatnot.
var touchStartTime;
var touchStartLocation;
var touchEndTime;
var touchEndLocation;
$thing.bind('touchstart'), function() {
var d = new Date();
touchStartTime = d.getTime();
touchStartLocation = mouse.location(x,y);
});
$thing.bind('touchend'), function() {
var d = new Date();
touchEndTime= d.getTime();
touchEndLocation= mouse.location(x,y);
doTouchLogic();
});
function doTouchLogic() {
var distance = touchEndLocation - touchStartLocation;
var duration = touchEndTime - touchStartTime;
if (duration <= 100ms && distance <= 10px) {
// Person tapped their finger (do click/tap stuff here)
}
if (duration > 100ms && distance <= 10px) {
// Person pressed their finger (not a quick tap)
}
if (duration <= 100ms && distance > 10px) {
// Person flicked their finger
}
if (duration > 100ms && distance > 10px) {
// Person dragged their finger
}
}
Another implementation for better maintenance. However, this technique will also do event.stopPropagation (). The click is not caught on any other element that clicked for 100ms.
var clickObject = {
flag: false,
isAlreadyClicked: function () {
var wasClicked = clickObject.flag;
clickObject.flag = true;
setTimeout(function () { clickObject.flag = false; }, 100);
return wasClicked;
}
};
$("#myButton").bind("click touchstart", function (event) {
if (!clickObject.isAlreadyClicked()) {
...
}
}
Just for documentation purposes, here's what I've done for the fastest/most responsive click on desktop/tap on mobile solution that I could think of:
I replaced jQuery's on function with a modified one that, whenever the browser supports touch events, replaced all my click events with touchstart.
$.fn.extend({ _on: (function(){ return $.fn.on; })() });
$.fn.extend({
on: (function(){
var isTouchSupported = 'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
return function( types, selector, data, fn, one ) {
if (typeof types == 'string' && isTouchSupported && !(types.match(/touch/gi))) types = types.replace(/click/gi, 'touchstart');
return this._on( types, selector, data, fn);
};
}()),
});
Usage than would be the exact same as before, like:
$('#my-button').on('click', function(){ /* ... */ });
But it would use touchstart when available, click when not. No delays of any kind needed :D
I just came up with the idea to memorize if ontouchstart was ever triggered. In this case we are on a device which supports it and want to ignore the onclick event. Since ontouchstart should always be triggered before onclick, I'm using this:
<script> touchAvailable = false; </script>
<button ontouchstart="touchAvailable=true; myFunction();" onclick="if(!touchAvailable) myFunction();">Button</button>
You could try like this:
var clickEvent = (('ontouchstart' in document.documentElement)?'touchstart':'click');
$("#mylink").on(clickEvent, myClickHandler);
In my case this worked perfectly:
jQuery(document).on('mouseup keydown touchend', function (event) {
var eventType = event.type;
if (eventType == 'touchend') {
jQuery(this).off('mouseup');
}
});
The main problem was when instead mouseup I tried with click, on touch devices triggered click and touchend at the same time, if i use the click off, some functionality didn't worked at all on mobile devices. The problem with click is that is a global event that fire the rest of the event including touchend.
This worked for me, mobile listens to both, so prevent the one, which is the touch event. desktop only listen to mouse.
$btnUp.bind('touchstart mousedown',function(e){
e.preventDefault();
if (e.type === 'touchstart') {
return;
}
var val = _step( _options.arrowStep );
_evt('Button', [val, true]);
});
This hasn't been mentioned here, but you may want to check out this link: https://joshtronic.com/2015/04/19/handling-click-and-touch-events-on-the-same-element/
To recap for posterity, instead of trying to assign to both handlers and then sort out the result, you can simply check if the device is a touchscreen or not and only assign to the relevant event. Observe:
var clickEvent = (function() {
if ('ontouchstart' in document.documentElement === true)
return 'touchstart';
else
return 'click';
})();
// and assign thusly:
el.addEventListener( clickEvent, function( e ){
// things and stuff
});
I am using this to bind my events so that I can test on touchscreens that handle both touchstart and click events which would fire twice, and on my development PC which only hears the click
One problem the author of that link mentions though, is touchscreen laptops designed to handle both events:
I learned about a third device I was not considering, the touchscreen laptop. It’s a hybrid device that supports both touch and click events. Binding one event means only that event be supported. Does that mean someone with a touchscreen and mouse would have to explicitly touch because that’s the only event I am handling?
Binding touchstart and click seemed ideal to handle these hybrid devices. To keep the event from firing twice, I added e.stopPropagation() and e.preventDefault() to the callback functions. e.stopPropagation() stops events from “bubbling up” to their parents but also keeps a second event from firing. I included e.preventDefault() as a “just in case” but seems like it could be omitted.
Being for me the best answer the one given by Mottie, I'm just trying to do his code more reusable, so this is my contribution:
bindBtn ("#loginbutton",loginAction);
function bindBtn(element,action){
var flag = false;
$(element).bind('touchstart click', function(e) {
e.preventDefault();
if (!flag) {
flag = true;
setTimeout(function() {
flag = false;
}, 100);
// do something
action();
}
return false;
});
I am also working on an Android/iPad web app, and it seems that if only using "touchmove" is enough to "move components" ( no need touchstart ).
By disabling touchstart, you can use .click(); from jQuery. It's actually working because it hasn't be overloaded by touchstart.
Finally, you can binb .live("touchstart", function(e) { e.stopPropagation(); }); to ask the touchstart event to stop propagating, living room to click() to get triggered.
It worked for me.
There are many things to consider when trying to solve this issue. Most solutions either break scrolling or don't handle ghost click events properly.
For a full solution see https://developers.google.com/mobile/articles/fast_buttons
NB: You cannot handle ghost click events on a per-element basis. A delayed click is fired by screen location, so if your touch events modify the page in some way, the click event will be sent to the new version of the page.
It may be effective to assign to the events 'touchstart mousedown' or 'touchend mouseup' to avoid undesired side-effects of using click.
Taking advantage of the fact that a click will always follow a touch event, here is what I did to get rid of the "ghost click" without having to use timeouts or global flags.
$('#buttonId').on('touchstart click', function(event){
if ($(this).data("already")) {
$(this).data("already", false);
return false;
} else if (event.type == "touchstart") {
$(this).data("already", true);
}
//your code here
});
Basically whenever an ontouchstart event fires on the element, a flag a set and then subsequently removed (and ignored), when the click comes.
Why not use the jQuery Event API?
http://learn.jquery.com/events/event-extensions/
I've used this simple event with success. It's clean, namespaceable and flexible enough to improve upon.
var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent);
var eventType = isMobile ? "touchstart" : "click";
jQuery.event.special.touchclick = {
bindType: eventType,
delegateType: eventType
};
If you are using jQuery the following worked pretty well for me:
var callback; // Initialize this to the function which needs to be called
$(target).on("click touchstart", selector, (function (func){
var timer = 0;
return function(e){
if ($.now() - timer < 500) return false;
timer = $.now();
func(e);
}
})(callback));
Other solutions are also good but I was binding multiple events in a loop and needed the self calling function to create an appropriate closure. Also, I did not want to disable the binding since I wanted it to be invoke-able on next click/touchstart.
Might help someone in similar situation!
For simple features, just recognize touch or click I use the following code:
var element = $("#element");
element.click(function(e)
{
if(e.target.ontouchstart !== undefined)
{
console.log( "touch" );
return;
}
console.log( "no touch" );
});
This will return "touch" if the touchstart event is defined and "no touch" if not. Like I said this is a simple approach for click/tap events just that.
I am trying this and so far it works (but I am only on Android/Phonegap so caveat emptor)
function filterEvent( ob, ev ) {
if (ev.type == "touchstart") {
ob.off('click').on('click', function(e){ e.preventDefault(); });
}
}
$('#keypad').on('touchstart click', '.number, .dot', function(event) {
filterEvent( $('#keypad'), event );
console.log( event.type ); // debugging only
... finish handling touch events...
}
I don't like the fact that I am re-binding handlers on every touch, but all things considered touches don't happen very often (in computer time!)
I have a TON of handlers like the one for '#keypad' so having a simple function that lets me deal with the problem without too much code is why I went this way.
Try to use Virtual Mouse (vmouse) Bindings from jQuery Mobile.
It's virtual event especially for your case:
$thing.on('vclick', function(event){ ... });
http://api.jquerymobile.com/vclick/
Browser support list: http://jquerymobile.com/browser-support/1.4/
EDIT: My former answer (based on answers in this thread) was not the way to go for me. I wanted a sub-menu to expand on mouse enter or touch click and to collapse on mouse leave or another touch click. Since mouse events normally are being fired after touch events, it was kind of tricky to write event listeners that support both touchscreen and mouse input at the same time.
jQuery plugin: Touch Or Mouse
I ended up writing a jQuery plugin called "Touch Or Mouse" (897 bytes minified) that can detect whether an event was invoked by a touchscreen or mouse (without testing for touch support!). This enables the support of both touchscreen and mouse at the same time and completely separate their events.
This way the OP can use touchstart or touchend for quickly responding to touch clicks and click for clicks invoked only by a mouse.
Demonstration
First one has to make ie. the body element track touch events:
$(document.body).touchOrMouse('init');
Mouse events our bound to elements in the default way and by calling $body.touchOrMouse('get', e) we can find out whether the event was invoked by a touchscreen or mouse.
$('.link').click(function(e) {
var touchOrMouse = $(document.body).touchOrMouse('get', e);
if (touchOrMouse === 'touch') {
// Handle touch click.
}
else if (touchOrMouse === 'mouse') {
// Handle mouse click.
}
}
See the plugin at work at http://jsfiddle.net/lmeurs/uo4069nh.
Explanation
This plugin needs to be called on ie. the body element to track touchstart and touchend events, this way the touchend event does not have to be fired on the trigger element (ie. a link or button). Between these two touch events this plugin considers any mouse event to be invoked by touch.
Mouse events are fired only after touchend, when a mouse event is being fired within the ghostEventDelay (option, 1000ms by default) after touchend, this plugin considers the mouse event to be invoked by touch.
When clicking on an element using a touchscreen, the element gains the :active state. The mouseleave event is only fired after the element loses this state by ie. clicking on another element. Since this could be seconds (or minutes!) after the mouseenter event has been fired, this plugin keeps track of an element's last mouseenter event: if the last mouseenter event was invoked by touch, the following mouseleave event is also considered to be invoked by touch.
Here's a simple way to do it:
// A very simple fast click implementation
$thing.on('click touchstart', function(e) {
if (!$(document).data('trigger')) $(document).data('trigger', e.type);
if (e.type===$(document).data('trigger')) {
// Do your stuff here
}
});
You basically save the first event type that is triggered to the 'trigger' property in jQuery's data object that is attached to the root document, and only execute when the event type is equal to the value in 'trigger'. On touch devices, the event chain would likely be 'touchstart' followed by 'click'; however, the 'click' handler won't be executed because "click" doesn't match the initial event type saved in 'trigger' ("touchstart").
The assumption, and I do believe it's a safe one, is that your smartphone won't spontaneously change from a touch device to a mouse device or else the tap won't ever register because the 'trigger' event type is only saved once per page load and "click" would never match "touchstart".
Here's a codepen you can play around with (try tapping on the button on a touch device -- there should be no click delay): http://codepen.io/thdoan/pen/xVVrOZ
I also implemented this as a simple jQuery plugin that also supports jQuery's descendants filtering by passing a selector string:
// A very simple fast click plugin
// Syntax: .fastClick([selector,] handler)
$.fn.fastClick = function(arg1, arg2) {
var selector, handler;
switch (typeof arg1) {
case 'function':
selector = null;
handler = arg1;
break;
case 'string':
selector = arg1;
if (typeof arg2==='function') handler = arg2;
else return;
break;
default:
return;
}
this.on('click touchstart', selector, function(e) {
if (!$(document).data('trigger')) $(document).data('trigger', e.type);
if (e.type===$(document).data('trigger')) handler.apply(this, arguments);
});
};
Codepen: http://codepen.io/thdoan/pen/GZrBdo/

Why does middle-click not trigger 'click' in several cases?

Here's a JSFiddle of the behavior I'm seeing, relating to middle-click and the click event in Chrome and FF.
'click' kinda sorta works
Approach 1: Bind a click handler directly to an a element and a middle-click will trigger the handler in Chrome but not in FF.
$('div a').on('click', function(ev) {
// middle click triggers this handler
});
Approach 2: Bind a delegated click handler to a div which contains one or more a. Middle click will not trigger this handler in Chrome or FF.
$('div').on('click', 'a', function(ev) {
// middle click doesn't trigger this handler
});
This approach is extremely valuable if the div starts out empty and the a elements are filled in later by an AJAX call, or as a result of some user input.
'mouseup' works
Using mouseup instead of click causes both approach 1 and 2 to work in both browsers.
// Approach 1 w/ mouseup
$('div a').on('mouseup', function(ev) {
// middle click **does** trigger this handler in Chrome and FF
});
// Approach 2 w/ mouseup
$('div').on('mouseup', 'a', function(ev) {
// middle click **does** trigger this handler in Chrome and FF
});
Here's the JSFiddle with mouseup.
This is interesting and might be useful in some cases, because mouseup is almost click. But mouseup isn't click, and I'm after the behavior of click. I do not want to create a hacky mousedown; setTimeout; mouseup simulation of click.
I'm pretty sure the answer is "nope", but is there a cross-browser way to cause middle-click to trigger click handlers? If not, what are the reasons why?
The click event is generally fired for the left mouse button, however, depending on the browser, the click event may or may not occur for the right and/or middle button.
In Internet Explorer and Firefox the click event is not fired for the right or middle buttons.
Therefore, we cannot reliably use the click event for event handlers on the middle or right button.
Instead, to distinguish between the mouse buttons we have to use the mousedown and mouseup events as most browsers do fire mousedown and mouseup events for any mouse button.
in Firefox and Chrome event.which should contain a number indicating what mouse button was pressed (1 is left, 2 is middle, 3 is right).
In Internet Explorer on the other hand, event.button indicates what mouse button was clicked (1 is left, 4 is middle, 2 is right);
event.button should also work in Firefox and other browsers, but the numbers can be slightly different (0 is left, 1 is middle, 2 is right).
So to put that together we usually do something like this :
document.onmousedown = function(e) {
var evt = e==null ? event : e;
if (evt.which) { // if e.which, use 2 for middle button
if (evt.which === 2) {
// middle button clicked
}
} else if (evt.button) { // and if e.button, use 4
if (evt.button === 4) {
// middle button clicked
}
}
}
As jQuery normalizes event.which, you should only have to use that in jQuery event handlers, and as such be doing:
$('div a').on('mousedown', function(e) {
if (e.which === 2) {
// middle button clicked
}
});
In other words you can't use the onclick event, so to simulate it you can use both mousedown and mouseup.
You can add a timer to limit the time allowed between the mousedown and mouseup event, or even throw in a mousemove handler to limit the movement between a mousedown and mouseup event, and make the event handler not fire if the mouse pointer moved more than ten pixels etc. the possibilites are almost endless, so that shouldn't really be an issue.
$('#test').on({
mousedown: function(e) {
if (e.which === 2) {
$(this).data('down', true);
}
},
mouseup: function(e) {
if (e.which === 2 && $(this).data('down')) {
alert('middle button clicked');
$(this).data('down', false);
}
}
});
Short answer: Nope.
The question is, what do you want to capture the middle clicks for? A middle click isn't meant to interact with the current page but rather to open a link in a new tab.
Chrome is also currently working on droping this behavior: https://code.google.com/p/chromium/issues/detail?id=255
And there is currently a general discussion on the w3c mailing list about this topic: http://lists.w3.org/Archives/Public/www-dom/2013JulSep/0203.html
Yet for now, you can catch middleclicks in Firefox on a document-level:
$(document).on('click', function(e){
console.log(e);
});
I've build a factory for creating Middle mouse click handlers using vanilla JS and working in latest Firefox and Chrome:
const MiddleClickHandlerFactory = (node, handlerFn) => {
node.addEventListener('mousedown', e => {
if (e.button !== 1) return;
e.preventDefault(); // stop default scrolling crap! Instead install ScrollAnywhere!
const originalTarget = e.target;
document.addEventListener('mouseup', e => { // register on DOCUMENT to be sure it will fire even if we release it somewhere else
if (e.target.isSameNode(originalTarget)) handlerFn(e);
}, {capture: true, once: true, passive: true});
}, true)
};

Preventing mouse emulation events (i.e. click) from touch events in Mobile Safari / iPhone using Javascript

In doing a single page Javascript app with interactive DOM elements I've found that the "mouseover-mousemove-mousedown-mouseup-click" sequence happens all in a bunch after the "touchstart-touchmove-touchend" sequence of events.
I've also found that it is possible to prevent the "mouse*-click" events from happening by doing an "event.preventDefault()" during the touchstart event, but only then, and not during the touchmove and touchend. This is a strange design, because because it is not possible to know during the touchstart yet whether the user intents to drag or swipe or just tap/click on the item.
I ended up setting up a "ignore_next_click" flag somewhere tied to a timestamp, but this is obviously not very clean.
Does anybody know of a better way of doing this, or are we missing something?
Note that while a "click" can be recognized as a "touchstart-touchend" sequence (ie no "touchmove"), there are certain things, such as keyboard input focus, that can only happen during a proper click event.
Just prevent the touchend event. It will let the browser scroll the page when you touch the element but won't let it emit artificial mouse events.
element.addEventListener('touchend', event => {
event.preventDefault();
});
I've run into similar problems making cross-platform HTML5/JS apps. The only real answer for me was to preventDefault on the touch events, and actually manage the touch states and fire click, drags, etc. events on my own according to my logic. This sounds much much more daunting than it really is, but the mimicked click/mouse events work perfectly on most mobile browsers.
Click and the extra mouse sequence are all there for your convenience (and compatibility). My rule of thumb- if it's for your convenience but it's inconvenient, best kill it.
As far as the input boxes, they only need the touchend events. I've killed click/mouse events and was still able to let mobile browsers respond correctly to touches on inputs. If it's still giving you issues, you can modify the event handler to only supress events on non-inputs:
function touchHandler(event) {
var shouldIgnore = event.target != null
&& ( event.target.tagName.toLowerCase() == "input" || event.target.tagName.toLowerCase() == "textarea" );
if(!shouldIgnore) e.preventDefault();
}
I've made a solution myself, since I have not found a sufficient solution elsewhere:
var isTouch = ('ontouchstart' in window);
function kill(type){
window.document.body.addEventListener(type, function(e){
e.preventDefault();
e.stopPropagation();
return false;
}, true);
}
if( isTouch ){
kill('mousedown');
kill('mouseup');
kill('click');
kill('mousemove');
}
The check of isTouch lets things work as normal on mouse-input devices but kills the emulated events on Safari/iOS. The trick is to use useCapture = true in the call to addEventListener so we scoop up all the mouse events in the page without hacking the code all over the web app. See the docs for the function here: https://developer.mozilla.org/en-US/docs/DOM/EventTarget.addEventListener?redirectlocale=en-US&redirectslug=DOM%2Felement.addEventListener
Edit:
Now that libraries for handling this issue are better, you can just use something like Fastclick as an alternative (https://github.com/ftlabs/fastclick).
If you have to support devices which support both mouse and touch, another solution is to use a capture event listener which stops all mouse events which occur either
within a delay after the touch event
at the same position as the touch event
on the same target element as the touch event
The information (time, position or target element) of the touch event can be recorded in another capture event listener.
Wrapping your mouse-only code in a Window.matchesMedia function is the cleanest way I found.
if (window.matchMedia('(hover: hover), (any-hover: hover), (-moz-touch-enabled: 0)').matches) {
el.addEventListener('mouseover', ev => {
// mouse handler, no simulated hover
}
}
This works for preventing simulated hovers but will likely prevent simulated clicks, too.
Note: -moz-touch-enabled part required on Firefox as of version 58.
This solution allows you to listen for PointerEvents if they exist, followed by TouchEvents if they exist, followed by MouseEvents if neither of the other two exist. Mobile Safari will still raise both touchstart and mousedown, but you'll only be listening for touchstart.
if (window.PointerEvent) { /* decent browsers */
etouch.addEventListener('pointerdown', (e) => {
console.log('pointerdown');
});
}
else if (window.TouchEvent) { /* mobile Safari */
etouch.addEventListener('touchstart', (e) => {
console.log('touchstart');
});
}
else { /* desktop Safari */
etouch.addEventListener('mousedown', (e) => {
console.log('mousedown');
});
}
Using 'pointerwhatever' instead of 'mousewhatever' seems to work fine on current browsers (2019).
i.e. they invented a way of having the same code for all the entry devices.
Creating Fast Buttons for Mobile Web Applications has their solution to the problem.
Also beware that when using IE10 preventDefault() doesn't stop the ghost/synthetic/emulated mouse events after a MSPointerDown event, so a true cross-browser solution is harder.
You could try to quit the function on "click", "mousedown" or "mouseup" events when the device supports touch events.
use.addEventListener("click",function(e){
// EXIT FUNCTION IF DEVICE SUPPORTS TOUCH EVENTS
if ("ontouchstart" in document.documentElement) return false;
// YOURMOUSEONLY CODE HERE
});
Add an event listener to touchstart that adds attribute data-touched to the element. Add another event listener to click that checks for data-touched. If it's there, prevent default and remove it. Here's some JS from my implementation.
var menuLinks = document.querySelectorAll('#my-nav>li>a');
for (var i = 0; i < menuLinks.length; i++) {
var menuLink = menuLinks[i];
menuLink.addEventListener('touchstart', function () {
menuLink.setAttribute('data-touched', '');
});
menuLink.addEventListener('click', function (event) {
if (menuLink.hasAttribute('data-touched')) {
menuLink.removeAttribute('data-touched');
event.preventDefault();
}
});
pointer... events have a pointerType property that mouse... events lack. You can use this property to detect and ignore events that were generated by touch rather than by a mouse.
Before:
window.addEventListner('mousemove', (e) => {
/* No way to tell if this event came from a mouse or a finger */
console.log(':(');
});
After:
window.addEventListner('pointermove', (e) => {
if (e.pointerType !== 'mouse') return;
/* This event definitely came from a mouse */
console.log(':)');
});
You can take advantage of this property just by replacing your mouse... event listeners with pointer... listeners. pointer... events are well-supported in modern browsers (going back at least three years).

Categories

Resources