I don't know whether it is only Chrome problem (can't check now), however let's try the following piece of code, where we bind two events to some element:
$("div").on({
mousemove: function(e) {
console.log("move");
},
click: function(e) {
console.log("click");
}
});
If we try to click the element, we'll find that for some reason mousemove event fires immediately after click, so in console we have:
>> ...
>> click
>> move
DEMO: http://jsfiddle.net/gKqVt/
Note, that mousedown and mouseup events work by the same scenario.
I saw many questions on SO about the same problem, but none (in my search) gave the straightforward idea what to do in order to fire the click event only.
Mousemove appears to be binded to every mouse action there is in Chrome, so store the mouse position every time the mouse "moves" and check it against the previous mouse position to validate that it has indeed "moved"..
var currentPos=[];
$("div").on({
mousemove: function(e) {
if (e.pageX!==currentPos[0] && e.pageY !==currentPos[1]){
currentPos=[e.pageX,e.pageY];
this.innerHTML = "Event: " + e.type;
console.log("move");
}
},
click: function(e) {
this.innerHTML = "Event: " + e.type;
console.log("click");
}
});
Demo | Source
This appears to be a bug in Chrome that was first reported back in November, and remains open.
Chromium Issue 161464
If you are targeting Chrome specifically then it may be worth comparing the event timestamps to get around it (using some minimum delta time as #ExplosionPills suggested. But if you're looking for general behavior it seems that you're better off treating them as separate events, because in every browser but chrome (and maybe Safari? the bug is labeled as webkit-core) they will in fact be separate events.
This behavior is odd, and it doesn't seem to occur universally (happens in Chrome/IE for me, but not FFX). I think you haven't gotten a straight answer because there isn't one really.
It's possible that the mouse is moved very slightly by the click action, but that's probably not it. Could just be a browser quirk. These don't even seem to be the same event since stopImmediatePropagation in click doesn't stop mousemove from firing. If you focus the element and hit a keyboard button, it will actually trigger click and only click.
Since this is so quirky, it seems like the only way to deal with it is times. As much of a kludge as this is, I do notice that click happens one millisecond before mousemove, so you could get close by comparing the click timestamp + 2 (or 10):
mousemove: function(e) {
if ($(this).data('lastClick') + 10 < e.timeStamp) {
http://jsfiddle.net/gKqVt/3/
This is very specific, though. You should consider not having behavior that occurs immediately on mousemove since it's so frequent.
Why don't just check that did the mouse really move or not like below:
function onMouseDown (e) {
mouseDown = { x: e.clientX, y: e.clientY };
console.log("click");
}
function onMouseMove (e) {
//To check that did mouse really move or not
if ( e.clientX !== mouseDown.x || e.clientY !== mouseDown.y) {
console.log("move");
}
}
FIDDLE DEMO
(I think it's will still correct in all browsers)
var a,b,c,d;
$(".prd img").on({
mousedown: function(e){
a= e.clientX, b= e.clientY;
},
mouseup: function(e){
c= e.clientX, d= e.clientY;
if(a==c&&b==d){
console.log('clicked');
}
}
});
Try this. This one work correct.
I noticed this behavior when I needed to differenciate between mousedown and mouseup without dragging between the two and mousedown and mouseup with dragging between them, the solution that I used is as follows:
var div = $('#clickablediv');
var mouseDown = false;
var isDragging = 0;
div.mousedown(function () {
isDragging = false;
mouseDown = true;
}).mousemove(function () {
if (mouseDown) isDragging++;
}).mouseup(function () {
mouseDown = false;
var wasDragging = isDragging;
isDragging = 0;
if (!wasDragging || wasDragging<=1) {
console.log('there was no dragging');
}
});
when I tried it, I noticed that periodacaly a simple click makes "isDragging" equal to 3 but not very frequently
I added the following to my mouseMove(event) function:
function mouseMove(event)
{
if ((event.movementX == 0) && (event.movementY == 0)) return;
Not clear why it triggers when there is no movement, but this worked for me. Had the issue in Chrome 102.0.5005.61 at least. It did not happen a few years ago.
Related
I need to make a webpage scrollable only by scrolling bar. I have tried to find how to catch scroll bar event, but as i see it is impossible. Currently i use this functions:
function preventDefault(e) {
e = e || window.event;
if (e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false;
}
}
function wheel(e) {
preventDefault(e);
}
function disable_scroll() {
if (window.addEventListener) {
window.addEventListener('DOMMouseScroll', wheel, false);
}
window.onmousewheel = document.onmousewheel = wheel;
}
But they are not very useful in my situation, because they block all scroll events. Do you have any ideas? I am thinking about it 3 days already and i didn't find any answer (and questions also). Thanks!
Prevent the window from scrolling with mouse wheel:
As document level wheel event listeners are treated as Passive, we need to mark this event listener to be treated as Active:
window.addEventListener("wheel", e => e.preventDefault(), { passive:false })
If the content of a <div> (or other element) is scrollable, you can prevent it like this:
document.getElementById('{element-id}').onwheel = function(){ return false; }
More info about scrolling intervention and using passive listeners to improve scrolling performance.
Outdated Method:
window.onwheel = function(){ return false; } // Old Method
more info (thanks #MatthewMorrone)
jQuery solution to prevent window scrolling with mouse wheel:
$(window).bind('mousewheel DOMMouseScroll', function(event){ return false});
If you want to prevent scrolling with mouse wheel in a single DOM element, try this:
$('#{element-id}').bind('mousewheel DOMMouseScroll', function (e) { return false; });
The DOMMouseScroll event is used in Firefox, so you have to listen on both.
I'm currently using this and it works fine. Scrolling using the bar works fine, but mouse wheel won't work.
The reason i'm doing it this way is that I have custom code to scroll the way I want, but if don't add any code it will just don't scroll on wheel.
window.addEventListener('wheel', function(e) {
e.preventDefault();
// add custom scroll code if you want
}
I have a very specific problem. I'm writing a web-page for mobile phones which has a button on it. I'm detecting touchevent on every browser including IE, but on IE it's quite specific. After a few seconds it automatically ends. Can you somehow help me? Here is my code (modified one, but still not working properly):
if (window.navigator.pointerEnabled) {
tapButton.addEventListener("pointerup", function(e) {
e.preventDefault();
addClass(this, 'clicked');
buttonTouched = true;
}, false);
tapButton.addEventListener("pointerdown", function(e) {
e.preventDefault();
removeClass(this, 'clicked');
buttonTouched = false;
}, false);
alert("pointerEnabled");
}
else if (window.navigator.msPointerEnabled) {
tapButton.addEventListener("MSPointerDown", function(e) {
e.preventDefault();
addClass(this, 'clicked');
buttonTouched = true;
}, false);
tapButton.addEventListener("MSPointerUp", function(e) {
e.preventDefault();
removeClass(this, 'clicked');
buttonTouched = false;
}, false);
alert("mspointerEnabled");
}
else {
alert("ordinary touch");
tapButton.addEventListener('touchstart', function(e) {
e.preventDefault();
addClass(this, 'clicked');
buttonTouched = true;
}, false);
tapButton.addEventListener('touchend', function(e) {
e.preventDefault();
removeClass(this, 'clicked');
buttonTouched = false;
}, false);
}
And the html tag has in it:
-ms-touch-action: none !important;
touch-action: none !important;
but that does not help either.
I suspect you are running into a multi-touch issue...
Remember, touch events are not the same as mouse events. You can touch with more than one finger. What happens if you touch with one finger than add a second finger? You get two consecutive touchstart events. The same is probably true for touchend. I suspect user light is right that it's probably triggering a finger release incorrectly...
Please have a look at what is happening to the touches, changedTouches and targetTouches properties of the TouchEvent you get in your listener. I strongly suspect you'll see that there still is a 'finger' left touching... So it went from 2 touches to 1...
Making sure that the (no longer) touching finger is actually the one that's on the button etc is all a lot less simple than the good old mouseup and mousedown events were.
EDIT: I realize your problem is with IE and it's pointer events... However they work mostly the same in that they too support multi-touch (and could thus suffer the same issues). I'm not seeing a property akin to touches, but I do see a pointerId, which can give you the same info (at the cost of some bookkeeping on your end).
This MSDN page has some good info. Especially this code snippet is enlightening I think:
function pointerdownHandler(evt) {
evt.target.setPointerCapture(evt.pointerId);
}
This seems to confirm that, when a finger hits the surface, the contact point gets an ID, which is used to inform you which finger left the surface when you receive the pointerup event.
I'd add some logging that just prints the pointerId on pointerdown and pointerup and I'll bet you will quickly find your solution.
I'm trying to prevent a mousewheel event captured by an element of the page to cause scrolling.
I expected false as last parameter to have the expected result, but using the mouse wheel over this "canvas" element still causes scrolling:
this.canvas.addEventListener('mousewheel', function(event) {
mouseController.wheel(event);
}, false);
Outside of this "canvas" element, the scroll needs to happen. Inside, it must only trigger the .wheel() method.
What am I doing wrong?
You can do so by returning false at the end of your handler (OG).
this.canvas.addEventListener('wheel',function(event){
mouseController.wheel(event);
return false;
}, false);
Or using event.preventDefault()
this.canvas.addEventListener('wheel',function(event){
mouseController.wheel(event);
event.preventDefault();
}, false);
Updated to use the wheel event as mousewheel deprecated for modern browser as pointed out in comments.
The question was about preventing scrolling not providing the right event so please check your browser support requirements to select the right event for your needs.
Updated a second time with a more modern approach option.
Have you tried event.preventDefault() to prevent the event's default behaviour?
this.canvas.addEventListener('mousewheel',function(event){
mouseController.wheel(event);
event.preventDefault();
}, false);
Keep in mind that nowadays mouswheel is deprecated in favor of wheel, so you should use
this.canvas.addEventListener('wheel',function(event){
mouseController.wheel(event);
event.preventDefault();
}, false);
Just adding, I know that canvas is only HTML5 so this is not needed, but just in case someone wants crossbrowser/oldbrowser compatibility, use this:
/* To attach the event: */
addEvent(el, ev, func) {
if (el.addEventListener) {
el.addEventListener(ev, func, false);
} else if (el.attachEvent) {
el.attachEvent("on" + ev, func);
} else {
el["on"+ev] = func; // Note that this line does not stack events. You must write you own stacker if you don't want overwrite the last event added of the same type. Btw, if you are going to have only one function for each event this is perfectly fine.
}
}
/* To prevent the event: */
addEvent(this.canvas, "mousewheel", function(event) {
if (!event) event = window.event;
event.returnValue = false;
if (event.preventDefault)event.preventDefault();
return false;
});
This kind of cancellation seems to be ignored in newer Chrome >18 Browsers (and perhaps other WebKit based Browsers). To exclusively capture the event you must directly change the onmousewheel method of the element.
this.canvas.onmousewheel = function(ev){
//perform your own Event dispatching here
return false;
};
Finally, after trying everything else, this worked:
canvas.addEventListener('wheel', (event) => {
// event.preventDefault(); Not Working
// event.stopPropagation(); Not Working
event.stopImmediatePropagation(); // WORKED!!
console.log('Was default prevented? : ',event.defaultPrevented); // Says true
}, false)
To prevent the wheel event, this worked for me in chrome -
this.canvas.addEventListener('wheel', function(event) {
event.stopPropagation()
}, true);
I am working with JavaScript and jQuery in an UIWevView on iOS.
I'v added some javascript event handler that allow me to capture a touch-and-hold event to show a message when someone taps an img for some time:
$(document).ready(function() {
var timeoutId = 0;
var messageAppeared = false;
$('img').on('touchstart', function(event) {
event.preventDefault();
timeoutId = setTimeout(function() {
/* Show message ... */
messageAppeared = true;
}, 1000);
}).on('touchend touchcancel', function(event) {
if (messageAppeared) {
event.preventDefault();
} else {
clearTimeout(timeoutId);
}
messageAppeared = false;
});
});
This works well to show the message. I added the two "event.preventDefault();" lines to stop imgs inside links to trigger the link.
The problem is: This also seems to prevent drag events to scroll the page from happen normally, so that the user wouldn't be able to scroll when his swipe happens to begin on an img.
How could I disable the default link action without interfering with scrolling?
You put me on the right track Stefan, having me think the other way around. For anyone still scratching their head over this, here's my solution.
I was trying to allow visitors to scroll through images horizontally, without breaking vertical scrolling. But I was executing custom functionality and waiting for a vertical scroll to happen. Instead, we should allow regular behavior first and wait for a specific gesture to happen like Stefan did.
For example:
$("img").on("touchstart", function(e) {
var touchStart = touchEnd = e.originalEvent.touches[0].pageX;
var touchExceeded = false;
$(this).on("touchmove", function(e) {
touchEnd = e.originalEvent.touches[0].pageX;
if(touchExceeded || touchStart - touchEnd > 50 || touchEnd - touchStart > 50) {
e.preventDefault();
touchExceeded = true;
// Execute your custom function.
}
});
$(this).on("touchend", function(e) {
$(this).off("touchmove touchend");
});
});
So basically we allow default behavior until the horizontal movement exceeds 50 pixels.
The touchExceeded variable makes sure our function still runs if we re-enter the initial < 50 pixel area.
(Note this is example code, e.originalEvent.touches[0].pageX is NOT cross browser compatible.)
Sometimes you have to ask a question on stack overflow to find the answer yourself. There is indeed a solution to my problem, and it's as follows:
$(document).ready(function() {
var timeoutId = 0;
$('img').on('touchstart', function(event) {
var imgElement = this;
timeoutId = setTimeout(function() {
$(imgElement).one('click', function(event) {
event.preventDefault();
});
/* Show message ... */
}, 1000);
}).on('touchend touchcancel', function(event) {
clearTimeout(timeoutId);
});
});
Explanation
No preventDefault() in the touch event handlers. This brings back scrolling behavior (of course).
Handle a normal click event once if the message appeared, and prevent it's default action.
You could look at a gesture library like hammer.js which covers all of the main gesture events across devices.
I'm trying to create a scrolling button that reacts differently to a quick click event than it does to a prolonged MouseDown (click and hold). The quick click event will scroll a specific number of pixels while click and hold will slowly scroll the pane until mouse up where it will stop.
This is what I have currently:
var mdown;
$('.next').bind('mousedown', function(event) {
mdown = event.timeStamp;
moving = setInterval(function(){
$('#main').scrollLeft($('#main').scrollLeft() + 5);
}, 1);
});
$('.next').bind('mouseup', function(event) {
clearInterval(moving);
if ((event.timeStamp - mdown) < 100)
$('#main').animate({ scrollLeft : '+=800'}, 500);
});
Is there another way of doing this without comparing event timestamps? Is a click event treated any differently than mousedown/mouseup? Thanks!
Check this plugin(It defines an event to handle long clicks):
https://github.com/pisi/Longclick