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;
}
}
});
Related
I asked a related question here
I'm trying to stop click events if dragged.
I think the simplest version of this is dragging yourself. Basically IF the user presses down, then moves, then releases I don't want a click event.
Note, the code below is not trying to allow click, it's trying only to prevent it. I thought calling preventDefault in mouseup would tell the browser, don't do the default thing, that being sending a click event because the user let up on the mouse.
let dragTarget;
let dragMouseStartX;
let dragMouseStartY;
let dragTargetStartX;
let dragTargetStartY;
const px = v => `${v}px`;
function dragStart(e) {
e.preventDefault();
e.stopPropagation();
dragTarget = this;
const rect = this.getBoundingClientRect();
dragMouseStartX = e.pageX;
dragMouseStartY = e.pageY;
dragTargetStartX = (window.scrollX + rect.left) | 0;
dragTargetStartY = (window.scrollY + rect.top) | 0;
window.addEventListener('mousemove', dragMove, {passive: false});
window.addEventListener('mouseup', dragStop, {passive: false});
}
function dragMove(e) {
if (dragTarget) {
e.preventDefault();
e.stopPropagation();
const x = dragTargetStartX + (e.pageX - dragMouseStartX);
const y = dragTargetStartY + (e.pageY - dragMouseStartY);
dragTarget.style.left = px(x);
dragTarget.style.top = px(y);
}
}
function dragStop(e) {
e.preventDefault();
e.stopPropagation();
dragTarget = undefined;
window.removeEventListener('mousemove', dragMove);
window.removeEventListener('mouseup', dragStop);
}
document.querySelector('.drag').addEventListener('mousedown', dragStart);
document.querySelector('.drag').addEventListener('click', () => {
console.log('clicked', new Date());
});
body { height: 100vh; }
.drag {
background: red;
color: white;
padding: 1em;
position: absolute;
user-select: none;
cursor: pointer;
}
<div class="drag">drag and release me</div>
One solution is to remove the click event and just do it myself in mouseup. If there was no movement call whatever I was going to call for click
But, in my actual use case dragging is on the parent like this (you can drag red or blue)
let dragTarget;
let dragMouseStartX;
let dragMouseStartY;
let dragTargetStartX;
let dragTargetStartY;
const px = v => `${v}px`;
function dragStart(e) {
e.preventDefault();
e.stopPropagation();
dragTarget = this;
const rect = this.getBoundingClientRect();
dragMouseStartX = e.pageX;
dragMouseStartY = e.pageY;
dragTargetStartX = (window.scrollX + rect.left) | 0;
dragTargetStartY = (window.scrollY + rect.top) | 0;
window.addEventListener('mousemove', dragMove, {passive: false});
window.addEventListener('mouseup', dragStop, {passive: false});
}
function dragMove(e) {
if (dragTarget) {
e.preventDefault();
e.stopPropagation();
const x = dragTargetStartX + (e.pageX - dragMouseStartX);
const y = dragTargetStartY + (e.pageY - dragMouseStartY);
dragTarget.style.left = px(x);
dragTarget.style.top = px(y);
}
}
function dragStop(e) {
e.preventDefault();
e.stopPropagation();
dragTarget = undefined;
window.removeEventListener('mousemove', dragMove);
window.removeEventListener('mouseup', dragStop);
}
document.querySelector('.drag').addEventListener('mousedown', dragStart);
document.querySelector('.click').addEventListener('click', () => {
console.log('clicked', new Date());
});
body { height: 100vh; }
.drag {
background: blue;
color: white;
padding: 2em;
position: absolute;
user-select: none;
cursor: pointer;
}
.click {
padding: 1em;
background: red;
}
<div class="drag"><div class="click">drag and release me</div></div>
So now the 2 elements are not related directly but, if the user drags on red I don't want the inner element to get a click event. Also note in my real code there are lots of child elements that I don't want to receieve click events in the same way (parent is the drag target). Note: again, in the example above I'm just trying to stop all click events (calling preventDefault) and failing.
I can think of lots of hacky solutions, for example 2 are.
In the first mousemove event, search all children for click event listeners, remove all of them, on mouseup restore them (possibly after a timeout)
In mouseup, set a flag to ignore clicks and set a timeout to clear the flag, have all click listeners no-op if the flag is set.
Both of those require a bunch of coordination.
In the first, I'd need to write some kind of system to keep track of click handlers and the elements they are on so I can save and restore them so instead of elem.addEventListener('click', someHandler) it would have to be more like registerClickListener(elem, someHandler). Not hard but if I forget then it fails.
In the second I'd have to remember to always check some global variable in every listener implemention.
elem.addEventListener('click', () => {
if (ignoreClicks) return;
...
})
Again if I forget then it fails. By forget I mean much much deeper in the DOM in unrelated code.
Both seem semi error prone so wondering if there is some other solution I'm overlooking that works like I thought preventDefault would work.
I could wrap addEventListener so for click handlers it adds a wrapper to filter out unwanted clicks. That's less error prone but it seems overkill.
Am I missing a simpler solution?
I think how it works is that click fires after a mouseup and mousedown occur on the same element and doesn't wait to see what they do (e.g. trying to stop the click from happening).
The easiest way I've seen to stop this from happening is by disabling pointer events for that element while dragging. It changes the cursor to default while dragging which isn't optimal but that might be avoidable and this is still my fav solution. For example:
let dragTarget;
let dragMouseStartX;
let dragMouseStartY;
let dragTargetStartX;
let dragTargetStartY;
const px = v => `${v}px`;
function dragStart(e) {
dragTarget = this;
dragTarget.classList.add("dragging");
const rect = this.getBoundingClientRect();
dragMouseStartX = e.pageX;
dragMouseStartY = e.pageY;
dragTargetStartX = (window.scrollX + rect.left) | 0;
dragTargetStartY = (window.scrollY + rect.top) | 0;
window.addEventListener('mousemove', dragMove, {passive: false});
window.addEventListener('mouseup', dragStop, {passive: false});
return false;
}
function dragMove(e) {
if (dragTarget) {
e.preventDefault();
e.stopPropagation();
const x = dragTargetStartX + (e.pageX - dragMouseStartX);
const y = dragTargetStartY + (e.pageY - dragMouseStartY);
dragTarget.style.left = px(x);
dragTarget.style.top = px(y);
}
}
function dragStop(e) {
dragTarget.classList.remove("dragging");
dragTarget = undefined;
window.removeEventListener('mousemove', dragMove);
window.removeEventListener('mouseup', dragStop);
}
document.querySelector('.drag').addEventListener('mousedown', dragStart);
document.querySelector('.click').addEventListener('click', () => {
console.log('clicked', new Date());
});
body { height: 100vh; }
.drag {
background: blue;
color: white;
padding: 2em;
position: absolute;
user-select: none;
cursor: pointer;
}
.click {
padding: 1em;
background: red;
}
.dragging {
pointer-events: none;
}
<div class="drag"><div class="click">drag and release me</div></div>
Some of the other options you were thinking of going down are certainly possible but this is the simplest in my opinion that should work for a lot of use cases.
I got the idea for this solution from this answer here:
https://stackoverflow.com/a/24273710/12259250
I believe some of the other answers to that question mimicked some of your alternatives so you can check those out too.
There is an optional third argument for addEventListener called capture which indicates that your function is called before the event function of any other element. By setting it to true and apply on your root element (window) you can simply shut off all click events by setting a flag.
let ignoreClicks = true; // switch me to turn on click events
document.getElementById ('d1').addEventListener ('click', function() { console.log ('d1'); });
document.getElementById ('d2').addEventListener ('click', function() { console.log ('d2'); });
window.addEventListener ('click', function (event) {
if (ignoreClicks) event.stopPropagation();
}, true );
<div id="d1" style="background-color:red;width:200px;height:100px;"></div>
<div id="d2" style="background-color:green;width:200px;height:100px;"></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/
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 !');
}
});
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
});
I'm trying to implement Google's Fast button for the mobile touch events, and I seem to be stuck. I'm trying to set it up so that I can make links into fastbuttons, but I can't seem to get my library structure right. What ends up happening is the fastbutton re-inits itself when I try to run a for loop on the links.
I'm sure it's just the way I'm setting up the library. Can someone please check it out?
http://code.google.com/mobile/articles/fast_buttons.html
;(function() {
/*Construct the FastButton with a reference to the element and click handler.*/
this.FastButton = function(element, handler) {
console.log('fastbutton init');
this.element = element;
this.handler = handler;
console.log(this);
element.addEventListener('touchstart', this, false);
element.addEventListener('click', this, false);
};
/*acts as an event dispatcher*/
this.FastButton.prototype.handleEvent = function(event) {
console.log(event);
switch (event.type) {
case 'touchstart': this.onTouchStart(event); break;
case 'touchmove': this.onTouchMove(event); break;
case 'touchend': this.onClick(event); break;
case 'click': this.onClick(event); break;
}
};
/*Save a reference to the touchstart coordinate and start listening to touchmove and
touchend events. Calling stopPropagation guarantees that other behaviors don’t get a
chance to handle the same click event. This is executed at the beginning of touch.*/
this.FastButton.prototype.onTouchStart = function(event) {
event.stopPropagation();
this.element.addEventListener('touchend', this, false);
document.body.addEventListener('touchmove', this, false);
this.startX = event.touches[0].clientX;
this.startY = event.touches[0].clientY;
};
/*When /if touchmove event is invoked, check if the user has dragged past the threshold of 10px.*/
this.FastButton.prototype.onTouchMove = function(event) {
if (Math.abs(event.touches[0].clientX - this.startX) > 10 ||
Math.abs(event.touches[0].clientY - this.startY) > 10) {
this.reset(); //if he did, then cancel the touch event
}
};
/*Invoke the actual click handler and prevent ghost clicks if this was a touchend event.*/
this.FastButton.prototype.onClick = function(event) {
event.stopPropagation();
this.reset();
this.handler(event);
if (event.type == 'touchend') {
console.log('touchend');
//clickbuster.preventGhostClick(this.startX, this.startY);
}
};
this.FastButton.prototype.reset = function() {
this.element.removeEventListener('touchend', this, false);
document.body.removeEventListener('touchmove', this, false);
};
this.clickbuster = function() {
console.log('init clickbuster');
}
/*Call preventGhostClick to bust all click events that happen within 25px of
the provided x, y coordinates in the next 2.5s.*/
this.clickbuster.preventGhostClick = function(x, y) {
clickbuster.coordinates.push(x, y);
window.setTimeout(this.clickbuster.pop, 2500);
};
this.clickbuster.pop = function() {
this.clickbuster.coordinates.splice(0, 2);
};
/*If we catch a click event inside the given radius and time threshold then we call
stopPropagation and preventDefault. Calling preventDefault will stop links
from being activated.*/
this.clickbuster.onClick = function(event) {
for (var i = 0; i < clickbuster.coordinates.length; i += 2) {
console.log(this);
var x = clickbuster.coordinates[i];
var y = clickbuster.coordinates[i + 1];
if (Math.abs(event.clientX - x) < 25 && Math.abs(event.clientY - y) < 25) {
event.stopPropagation();
event.preventDefault();
}
}
};
})(this);
document.addEventListener('click', clickbuster.onClick, true);
clickbuster.coordinates = [];
Try calling the constructor with new?
new FastButton(el, function() {});