Cancel incoming jQuery click event if mouse is moved after mousedown - javascript

I'm trying to prevent a click event from firing if the mouse is moved after the 'mousedown' event. Currently I'm doing everything manually via conditionals and booleans. I still don't have it working how I want, and I feel it's just a poor approach to accomplishing this.
var mousemove = false;
var mousedown = false;
var cancelClick = false;
$('.example').click( function() {
if (!cancelClick) {
if ( $(this).attr('id') === 'example-green') {
$(this).attr('id', 'example-blue');
} else {
$(this).attr('id', 'example-green');
}
}
cancelClick = false;
});
$('.example').mousedown( function() {
mousedown = true;
});
$('.example').mouseup( function() {
if (mousemove) {
cancelClick = true;
}
mousedown = false;
mousemove = false;
});
$('.example').mousemove( function() {
if (mousedown) {
mousemove = true;
}
});
http://jsfiddle.net/aGf6G/4/
Is there is a simpler way to achieve this? Preferably one that prevents the click events from being processed, or removes them from the pending event queue (I'm not sure if they are queued until after you release the mouse). That way the callbacks themselves aren't coupled with the implementation of this.

I would just store the x/y coordinates of the mouse on mousedown and compare it to the current coordinates in click.
$('.example')
.on('mousedown', function() {
$(this).data("initcoords", { x: event.clientX, y: event.clientY });
})
.on('click', function() {
var initCoords = $(this).data("initcoords") || { x: 0, y: 0 };
if (event.clientX === initCoords.x && event.clientY === initCoords.y) {
if ( $(this).attr('id') === 'example-green') {
$(this).attr('id', 'example-blue');
} else {
$(this).attr('id', 'example-green');
}
$(this).data('initcoords', {x:-1, y:-1});
}
});
http://jsfiddle.net/zp2y2/8/
You could also toggle the click event on and off. It is a little more concise but I wonder about the overhead of setting up event handlers compared to the method above.
$('.example')
.on('mousedown', function() { $(this).one("click", handleClick); })
.on('mousemove mouseout', function() { $(this).off('click'); });
function handleClick(){
var $el = $('.example');
if ( $el.attr('id') === 'example-green') {
$el.attr('id', 'example-blue');
} else {
$el.attr('id', 'example-green');
}
}
http://jsfiddle.net/du7ZX/

EDIT: http://api.jquery.com/event.stopimmediatepropagation/ Here is one that stops all events on one element from executing except the one you want.
If the differnt events are not all on the same element but rather spread among child/parent you could:
Event.stopPropagation() will stop all other events except the one you actually want.
I believe this here is your solution: http://api.jquery.com/event.stoppropagation/
Here is a jsfiddle to actually test with and without stopPropagation:
In this example I show how a div within a div inherits the event from his parent. Notice in the second example if you mouse over the inner div first, you will get two alerts. If you mouseover the inner div in the first example you will only get one alert.
http://jsfiddle.net/Grimbode/vsKM9/3/
/** test with stopprogation **/
$('#test').on('mouseover', function(event){
event.stopPropagation();
alert('mouseover 1');
});
$('#test2').on('mouseover', function(event){
event.stopPropagation();
alert('mouseover 2');
});
/*** test with no stoppropagation ***/
$('#test3').on('mouseover', function(event){
alert('mouseover 3');
});
$('#test4').on('mouseover', function(event){
alert('mouseover 4');
});
You could also use .off() method that removes events on a specific element.

Here's another option, I tested it and it works well:
$('.example')
.on('mousedown', function() {
$(this).data("couldBeClick", true );
})
.on('mousemove', function() {
$(this).data("couldBeClick", false );
})
.on('click', function() {
if($(this).data("couldBeClick")) {
alert('this is really a click !');
}
});

Related

How to make the clearTimeout fire

I have an SVG with <uses> not and when you touch one of them a touchstart events fires. If they move a touchmove event fires that has a timer. On touchend the timer is cleared before the value is supposed to change. However, it changes anyway.
The results of this are that the IsDraggingUnit is set to true even after the touchend is fired. I tested with an alert and it is firing successfully when ending your touch on the element.
var IsDraggingUnit = false;
var timeOutClear;
$('#Selected_Items use').on("touchstart", function(event) {
IsDraggingUnit = false;
$('#test > p').text(IsDraggingUnit);
$(this).on("touchmove", function(event) {
timeOutClear = setTimeout(function() {
IsDraggingUnit = true;
$('#test > p').text(IsDraggingUnit);
}, 500);
});
$(this).on("touchend", function(event) {
clearTimeout(timeOutClear);
if (IsDraggingUnit == false) {
fnPlotShow($(this), bookingRental, bookingExists)
}
});
});
The issue is because you are nesting the touchmove and touchend events within the touchstart event. Therefore on subsequent touchstart occurrences each event fires multiple times and the reference to the original timeout gets lost and clearTimeout() does not clear them all.
To fix this, do not make the event handlers nested, and don't forget to set isDraggingUnit back to false on touchend
var isDraggingUnit = false, timeOutReference;
$('#Selected_Items use').on({
touchstart: function(event) {
isDraggingUnit = false;
$('#test > p').text(isDraggingUnit);
},
touchmove: function(event) {
timeOutReference = setTimeout(function() {
isDraggingUnit = true;
$('#test > p').text(isDraggingUnit);
}, 500);
},
touchend: function(event) {
clearTimeout(timeOutReference);
if (!isDraggingUnit) {
fnPlotShow($(this), bookingRental, bookingExists)
}
isDraggingUnit = false;
}
});
Also note that your question states the SVG holds <uses> nodes, yet you select use. I assume this is just a typo in the question, though, as it wouldn't work at all if there was a mismatch there.

how to implement mousemove while mouseDown pressed js

I have to implement mouse move event only when mouse down is pressed.
I need to execute "OK Moved" only when mouse down and mouse move.
I used this code
$(".floor").mousedown(function() {
$(".floor").bind('mouseover',function(){
alert("OK Moved!");
});
})
.mouseup(function() {
$(".floor").unbind('mouseover');
});
Use the mousemove event.
From mousemove and mouseover jquery docs:
The mousemove event is sent to an element when the mouse pointer moves inside the element.
The mouseover event is sent to an element when the mouse pointer enters the element.
Example: (check console output)
$(".floor").mousedown(function () {
$(this).mousemove(function () {
console.log("OK Moved!");
});
}).mouseup(function () {
$(this).unbind('mousemove');
}).mouseout(function () {
$(this).unbind('mousemove');
});
https://jsfiddle.net/n4820hsh/
In pure javascript, you can achieve this with
function mouseMoveWhilstDown(target, whileMove) {
var endMove = function () {
window.removeEventListener('mousemove', whileMove);
window.removeEventListener('mouseup', endMove);
};
target.addEventListener('mousedown', function (event) {
event.stopPropagation(); // remove if you do want it to propagate ..
window.addEventListener('mousemove', whileMove);
window.addEventListener('mouseup', endMove);
});
}
Then using the function along the lines of
mouseMoveWhilstDown(
document.getElementById('move'),
function (event) { console.log(event); }
);
(nb: in the above example, you don't need the function - you could call it as mouseMoveWhilstDown(document.getElementById('move'), console.log), but you might want to do something with it other than output it to the console!)
I know that this issue was submitted and resolved approximately seven years ago, but there is a simpler solution now:
element.addEventListener('mousemove', function(event) {
if(event.buttons == 1) {
event.preventDefault();
// Your code here!
}
});
or for touch compatible devices:
element.addEventListener('touchmove', function(event) {
if(event.touches.length == 1) {
event.preventDefault();
// Your code here!
}
}
For more information on MouseEvent.buttons, click here to visit MDN Web Docs. Touch compatible devices, however, tend to listen to TouchEvents instead of MouseEvents. TouchEvent.touches.length achieves a similar effect to MouseEvent.buttons.
To provide an example, I used the following code to move an element I created. For moving an element, I used the 'mousemove' event's MouseEvent.movementX and MouseEvent.movementY to simplify the code. The 'touchmove' event does not have these so I stored the previous touch coordinates and cleared them on 'touchstart'. You can do something similar for the 'mousemove' event if desired, as the movementX and movementY values may vary across browsers.
document.addEventListener('DOMContentLoaded', () => {
var element = document.getElementById('box');
element.style.position = 'fixed';
// MouseEvent solution.
element.addEventListener('mousemove', function(event) {
if(event.buttons == 1) {
event.preventDefault();
this.style.left = (this.offsetLeft+event.movementX)+'px';
this.style.top = (this.offsetTop+event.movementY)+'px';
}
});
// TouchEvent solution.
element.addEventListener('touchstart', function(event) {
/* Elements do not have a 'previousTouch' property. I create
this property during the touchmove event to store and
access the previous touchmove event's touch coordinates. */
delete this.previousTouch;
});
element.addEventListener('touchmove', function(event) {
if(event.touches.length == 1) {
event.preventDefault();
if(typeof this.previousTouch == 'object') {
this.style.left = (this.offsetLeft+event.touches[0].pageX-this.previousTouch.x)+'px';
this.style.top = (this.offsetTop+event.touches[0].pageY-this.previousTouch.y)+'px';
}
this.previousTouch = {
x: event.touches[0].pageX,
y: event.touches[0].pageY
};
}
});
});
#box {
width: 100px;
height: 100px;
padding: 1ch;
box-sizing: border-box;
background-color: red;
border-radius: 5px;
color: white;
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="box">Drag Me!</div>
</body>
</html>
Hopefully this solution is helpful to you!
The default behaviour will stop mouseMove and mouseUp from running, you can solve this by basically adding event.preventDefault() to the mousedown function
please ensure that you use the same parameter name passed in the mousedown function to trigger the preventDefault() if not it will not work , in the example below i passed event as the parameter to the mousedown function and then triggered preventDefault() by typing event.preventDefault()
let sliderImages = Array.from(document.getElementsByClassName('slidess'));
const sliderPos = sliderImages.forEach( function (slide, index) {
let mousePosStart, isDown = false;
slide.addEventListener('mousedown', mousedown)
slide.addEventListener('mousemove', mousemove)
slide.addEventListener('mouseup', mouseup)
function mousedown(event) {
if (isDown == false) {
mousePosStart = event.pageX - this.offsetLeft;
isDown = true;
event.preventDefault();
}
}
function mousemove(event) {
if (isDown == true) {
let mousePosMove = event.pageX - this.offsetLeft;
}
}
function mouseup(event) {
if (isDown === true) {
isDown = false;
let mousePosEnd = event.pageX - this.offsetLeft;
}
}
});

Canceling a custom drag function when mouse slides off div

I have a draggable function in jquery to make it so I can drag and move elements on a div. Sometimes, when dragging the mouse comes off the div and I am not able to put back down the element.
I'm trying to add a keydown event for the escape button or something so that when pressed, the same thing happens on .on("mouseup", function(event) {
I've tried doing .on("mouseup keydown", function(event) { but it doesn't catch any keys that are being pressed.
Does anyone have any ideas on how I can cancel the drag? Either by a keydown or even on a mouseup regardless of if the mouse is on the div or not that is being dragged?
Just to be clear, the problem I am having is sometimes I will be dragging the element, I will mouseup but the mouse wasn't on the element when mouseup was called. Therefore, the element is still dragging and I no longer have my finger on the mouse and I have no way to stop the element from dragging to get it back on the document.
EDIT: Here is a jsfiddle, notice I am trying to get this to work on a scaled container. youtube video showing drag glitch
(function($) {
$.fn.drags = function(opt, callback) {
opt = $.extend({
handle: "",
cursor: "move"
}, opt);
if (opt.handle === "") {
var $el = this;
} else {
var $el = this.find(opt.handle);
}
return $el.css('cursor', opt.cursor).on("mousedown", function(e) {
if (opt.handle === "") {
var $drag = $(this).addClass('draggable');
} else {
var $drag = $(this).addClass('active-handle').parent().addClass('draggable');
}
var z_idx = $drag.css('z-index'),
drg_h = $drag.outerHeight(),
drg_w = $drag.outerWidth(),
pos_y = $drag.offset().top + drg_h - e.pageY,
pos_x = $drag.offset().left + drg_w - e.pageX;
$drag.css('z-index', 1000).parents().on("mousemove", function(e) {
$('.draggable').offset({
top: e.pageY + pos_y - drg_h,
left: e.pageX + pos_x - drg_w
}).on("mouseup", function() {
$(this).removeClass('draggable').css('z-index', z_idx);
});
});
e.preventDefault();
}).on("mouseup", function(event) {
if (opt.handle === "") {
$(this).removeClass('draggable');
} else {
$(this).removeClass('active-handle').parent().removeClass('draggable');
}
if (typeof callback == 'function') {
alert("this is a callback");
}
});
}
})(jQuery);
Here are a few things that might work:
Instead of listening for mouseup on the target element, listen for it on document.body. That way it will fire regardless of if the cursor is over the dragged element.
If you want to cancel the drag when the cursor wanders out of the page, add an event listener for mouseleave on document.body and use it to cancel the drag.
If you make a code-pen (or similar) test case, I will be happy to dig into the code.
Edit__
Handling mouseleave on the document prevents it from getting stuck in a draggable state. It also fixes the multiplied movement that you were seeing.
$(document.body).on('mouseleave', function(){
$el.removeClass('draggable').css('z-index', z_idx);
});
Edit2__
Previous JSFiddle was incorrect.
https://jsfiddle.net/spk4523t/6/

Mousemove and onclick events

I am making a slider and I detect the position of the mouse to define click event. I create a $(document).on('mousemove') event and when I am on the position I want a click, I add a on('click') event, but in this case, the event it's called so many time, and not just one time.
Let's see this code. Here is a quick example : jsFiddle
var widthScreen = $(window).width();
$(document).on('mousemove', function(e) {
console.log(e.pageX);
if (e.pageX > (widthScreen - 120) && e.pageX < widthScreen) {
$('ul').addClass('right-show');
$('li').eq(2).on('click', function() {
console.log('too much click');
});
} elseĀ {
$('ul').removeClass('right-show');
}
});
How can I solve this issue ?
When ever you do the mousemove, it is checking the condition and attaching the click event. So the click event is attached many times. To avoid this, first remove the click event using off('click') and attach it. Here is the updated fiddle.
var widthScreen = $(window).width();
$(document).on('mousemove', function(e) {
console.log(e.pageX);
if (e.pageX > (widthScreen - 120) && e.pageX < widthScreen) {
$('ul').addClass('right-show');
$('li').eq(2).off('click').on('click', function() {
console.log('too much click');
});
} elseĀ {
$('ul').removeClass('right-show');
}
});
Remove the event attachment from mousemove handler, and create a flag to check, if the cursor is on correct position:
var widthScreen = $(window).width(),
onArea = false;
$(document).on('mousemove', function(e) {
if (e.pageX > (widthScreen - 120) && e.pageX < widthScreen) {
$('ul').addClass('right-show');
onArea = true;
} else {
$('ul').removeClass('right-show');
onArea = false;
}
});
$('li').eq(2).on('click', function(e) {
if (onArea) {
console.log('Not too much clicks.');
}
});
A live demo at jsFiddle.
Have a look at underscore.js throttle function:
_.throttle(function, wait, [options])
Creates and returns a new, throttled version of the passed function, that, when invoked repeatedly, will only actually call the original function at most once per every wait milliseconds. Useful for rate-limiting events that occur faster than you can keep up with
Use .one() instead of .on() Here

Binding multiple events and run a function when all events are fired! JS, Jquery

I want to bind two mouse events to a function (mousedown and moucemove). But I want to run the function only if both events are fired.
This will bind each event to to the function: (It's not what i want)
$("#someid").bind("mousedown mousemove", function (event) { someFunction(); });
I can do this and it works:
$("#someid").bind("mousedown", function (event) {
someFunction();
$("#someid").bind("mousemove", function (event) {
someFunction();
});
});
$("#someid").bind("mouseup", function (event) {
$("#someid").unbind("mousemove");
});
Is there a better, quicker way to do this???
Bind only to the mousemove event. If the left mouse button is pressed while you move, event.which will be 1.
$(document).on('mousemove', function(e) {
if (e.which == 1) {
//do some stuff
}
});
I guess you want the mousemove handler work only when the mouse button is pressed down? If so, I'd suggest using some kind of boolean flag. You toggle its state on mousedown and mouseup events:
var flag = false;
$('#someid')
.on('mousedown', function(e) {
flag = true;
})
.on('mouseup', function(e) {
flag = false;
})
.on('mousemove', function(e) {
if (!flag) {
return false;
}
// else... do your stuff
});

Categories

Resources