jQuery - How to destroy an event which is attached to an element? - javascript

I have some line of codes which will move an element to mouse position after it is mousedown-ed.
I want to remove the event attached to it, so it won't following the mouse position anymore after it is mouseup-ed!
The Problem
The element still follows the mouse after mouseup!
I want it to follow the mouse on mousedown and stop following the mouse after mouseup! How do I remove the mousemove listener from the element?
Here is the JS
jQuery(document).ready(function ($) {
$(".crossY").on("mousedown", function (e) {
var j = $(this);
$(document).on("mousemove", function (e) {
j.css({
"top": e.pageY,
"left": e.pageX
});
});
})
$(".crossY").on("mouseup", function (e) {
var j = $(this);
$(document).on("mousemove", function (e) {
j.css({
"top": j.css("top"),
"left": j.css("left")
});
});
});
});
and the FIDDLE DEMO

In order to remove a mouse listener, you need to use the jQuery .off method. In order to get this to work easily, you should namespace the mousemove event. This will allow you to easily detach the necessary mousemove listener.
Inside the mousedown we want to attach the listener
$(document).on('mousemove.following', function (e) { /* my event handler */ })
Inside the mouseup we want to detach the listener
$(document).off('mousemove.following')
The following namespace makes sure that no other event listeners are detached.
Here is an example of this working (your jsfiddle except updated).
Another thing you might want to do is make the moving part centered underneath the mouse.
$(".crossY").on("mousedown", function (e) {
var j = $(this);
var height = j.height(), width = j.width();
$(document).on("mousemove", function (e) {
j.css({
"top": e.pageY - height/2,
"left": e.pageX - width/2,
});
});
})
Subtracting half of the element height and width keeps the element centered underneath the mouse, which will also ensure that the mouseup even is fired.

try using bind() and unbind() like this: DEMO
jQuery(document).ready(function ($) {
$(".crossY").on("mousedown", function (e) {
var j = $(this);
$(document).bind("mousemove", function (e) {
j.css({
"top": e.pageY-10,
"left": e.pageX-10
});
});
})
$(".crossY").on("mouseup", function (e) {
var j = $(this);
$(document).unbind("mousemove");
});
});

Try
jQuery(document).ready(function ($) {
$(".crossY").on("mousedown", function (e) {
var j = $(this);
$(document).on("mousemove", function (e) {
j.css({
"top": e.pageY,
"left": e.pageX
});
});
})
$(".crossY").on("mouseup", function (e) {
var j = $(this);
$(document).off("mousemove");
});
});

Related

Cannot bind event to a dom element

I'm trying to make a draggable div. I wrote some code to intercept the events with this html:
<div class="draggable">
<iframe id="frame" src="http://www.wikipedia.org"></iframe>
</div>
Here's the js:
$(".draggable").bind("mousedown", function(e){
e.preventDefault();
console.log(e);
$(".draggable").bind("mousemove",function(e){
var x = e.pageX;
var y = e.pageY;
$("#draggable").css({top: y, left: x});
});
});
$(".draggable").bind("mouseup", function(e){
e.preventDefault();
$(".draggable").unbind("mousemove");
});
$(".draggable").bind("touchstart touchmove", function(e) {
e.preventDefault();
var orig = e.originalEvent;
var x = orig.changedTouches[0].pageX;
var y = orig.changedTouches[0].pageY;
// Move a div with id "rect"
$(".draggable").css({top: y, left: x});
});
Here's a jsfiddle: http://jsfiddle.net/xbv3opd7/34/
The issue is that the element does not seem to intercept the events. The code works if I bind the event to the document: http://jsfiddle.net/xbv3opd7/35/
Where am I wrong?
Yea, because you'll first need to add jQuery-UI to your page.
You can download it here: http://jqueryui.com/download/
Then you need to make it .draggable(), by doing this:
$(".draggable").draggable();
Let me know how that works for you.

Expand on click, collapse on click outside

I have an element which I want to expand on click and then collapse on click outside and thus came up with the following code. However when I run this it will start to expand and then immediately collapse since both functions are called sequentially. I don't understand why and how to solve this.
jQuery(document).ready(function() {
var element = jQuery("#search-main");
var defaultWidth = jQuery("#search-main").css('width');
var expandWidth = "200px";
var fnSearch = {
expand : function() {
jQuery(element).animate({
width : expandWidth
});
jQuery(document).bind('click', fnSearch.collapse);
},
collapse : function() {
jQuery(element).animate({
width : defaultWidth
});
event.stopPropagation();
jQuery(document).unbind("click", fnSearch.collapse);
}
}
jQuery("#search-main").bind("click", fnSearch.expand);
});
You are having the problem because the #search-main click event is propagating to the document; i.e. first the #search-main click event triggers, then the document click event triggers. Click events do this by default. To stop this event propagation, you want to use http://api.jquery.com/event.stoppropagation/ in your expand function:
jQuery(document).ready(function() {
var element = jQuery("#search-main");
var defaultWidth = jQuery("#search-main").css('width');
var expandWidth = "200px";
var fnSearch = {
expand : function(event) { // add event parameter to function
// add this call:
event.stopPropagation();
jQuery(element).animate({
width : expandWidth
});
jQuery(document).bind('click', fnSearch.collapse);
},
collapse : function() {
jQuery(element).animate({
width : defaultWidth
});
jQuery(document).unbind("click", fnSearch.collapse);
}
}
jQuery("#search-main").bind("click", fnSearch.expand);
});
That said, Jason P's solution is better for what you want. It's more reliable and less messy, since you don't have to bind stuff to the document, which can easily become hard to track and cause conflicts with other code if you use that strategy habitually.
You could unbind the click event from the #search-main element after clicking, or stop the propagation of the event, but I would recommend binding to the blur and focus events instead:
http://jsfiddle.net/6Mxt9/
(function ($) {
$(document).ready(function () {
var element = jQuery("#search-main");
var defaultWidth = jQuery("#search-main").css('width');
var expandWidth = "200px";
$('#search-main').on('focus', function () {
$(element).animate({
width: expandWidth
});
}).on('blur', function () {
$(element).animate({
width: defaultWidth
});
});
});
})(jQuery);
That way, it will work even if the user tabs in or out of the field.

Update available drop targets after dragstart fires in jquery.event.drag

Overview:
I have a page which uses jquery.event.drag and jquery.event.drop.
I need to be able to drag and drop onto elements which are constantly being added to the dom, even after the drag has started.
Problem:
When the dragstart event fires it checks for available drop targets and adds them to the drag object.
The problem I have is I am adding drop targets dynamically, after the dragstart event has fired, and therefore the user cannot drop onto these dynamically added drop targets.
Example:
http://jsfiddle.net/blowsie/36AJq/
Question:
How can I update the drag to allow dropping on elements which have been added to the dom after drag has started?
You can use this snippet.
The important function is: $.event.special.drop.locate();
Tested on chrome/safari/firefox/ie9 and seems to work.
SEE DEMO
UPDATE
For overlapping events, see if following code works. I set it inside an anonymous function just to avoid any global variable.
Idea is to use currentTarget property of event to check if not the same element is triggering same event. I set an id on newdrop element just in purpose of test here.
SEE UPDATED DEMO
(function () {
var $body = $("body"),
newdrops = [],
currentTarget = {},
ondragstart = function () {
$(this).css('opacity', .75);
}, ondrag = function (ev, dd) {
$(this).css({
top: dd.offsetY,
left: dd.offsetX
});
}, ondragend = function () {
$(this).css('opacity', '');
for (var i = 0, z = newdrops.length; i < z; i++)
$(newdrops[i]).off('dropstart drop dropend').removeClass('tempdrop');
newdrops = [];
}, ondropstart = function (e) {
if (currentTarget.dropstart === e.currentTarget) return;
currentTarget.dropstart = e.currentTarget;
currentTarget.dropend = null;
console.log('start::' + e.currentTarget.id)
$(this).addClass("active");
}, ondrop = function () {
$(this).toggleClass("dropped");
}, ondropend = function (e) {
if (currentTarget.dropend === e.currentTarget) return;
currentTarget.dropend = e.currentTarget;
currentTarget.dropstart = null;
console.log('end::' + e.currentTarget.id)
$(this).removeClass("active");
};
$body.on("dragstart", ".drag", ondragstart)
.on("drag", ".drag", ondrag)
.on("dragend", ".drag", ondragend)
.on("dropstart", ".drop", ondropstart)
.on("drop", ".drop", ondrop)
.on("dropend", ".drop", ondropend);
var cnt = 0;
setInterval(function () {
var dataDroppables = $body.data('dragdata')['interactions'] ? $body.data('dragdata')['interactions'][0]['droppable'] : [];
var $newDrop = $('<div class="drop tempdrop" id="' + cnt + '">Drop</div>');
cnt++;
$("#dropWrap").append($newDrop);
var offset = $newDrop.offset();
var dropdata = {
active: [],
anyactive: 0,
elem: $newDrop[0],
index: $('.drop').length,
location: {
bottom: offset.top + $newDrop.height(),
elem: $newDrop[0],
height: $newDrop.height(),
left: offset.left,
right: offset.left + $newDrop.width,
top: offset.top,
width: $newDrop.width
},
related: 0,
winner: 0
};
$newDrop.data('dropdata', dropdata);
dataDroppables.push($newDrop[0]);
$newDrop.on("dropstart", ondropstart)
.on("drop", ondrop)
.on("dropend", ondropend);
$.event.special.drop.locate($newDrop[0], dropdata.index);
newdrops.push($newDrop[0]);
}, 1000);
})();
I wasn't able to get this working using jquery.event.drag and jquery.event.drop, but I did make it work with the native HTML5 events:
http://jsfiddle.net/R2B8V/1/
The solution was to bind the events on the drop targets within a function and call that to update the bindings. I suspect you could get this working with jquery.event.drag and jquery.event.drop using a similar principal. If I can get those working I will update my answer.
Here is the JS:
$(function() {
var bind_targets = function() {
$(".drop").on({
dragenter: function() {
$(this).addClass("active");
return true;
},
dragleave: function() {
$(this).removeClass("active");
},
drop: function() {
$(this).toggleClass("dropped");
}
});
};
$("div[draggable]").on({
dragstart: function(evt) {
evt.originalEvent.dataTransfer.setData('Text', 'data');
},
dragend: function(evt) {
$('.active.drop').removeClass('active');
}
});
setInterval(function () {
$("#dropWrap").append('<div class="drop">Drop</div>');
// Do something here to update the dd.available
bind_targets();
}, 1000)
});
You can't. On dragstart, possible drop zones are calculated from the DOM, and can't be edited until dragend. Even constantly rebinding the .on() (Demo: http://jsfiddle.net/36AJq/84/) will not provide the desired effect.
I solved the issue a little differently. (Demo: http://jsfiddle.net/36AJq/87/)
Start with every <div> in the HTML.
Apply opacity: 0 to make it invisible, and width: 0 to keep it from getting a dropend when hidden.
Use setInterval to show the next hidden div ($('.drop:not(.visible)').first()) each 1000ms.
JS:
$("body")
.on("dragstart", ".drag", function () {
$(this).css('opacity', .75);
})
.on("drag", ".drag", function (ev, dd) {
$(this).css({
top: dd.offsetY,
left: dd.offsetX
});
})
.on("dragend", ".drag", function () {
$(this).css('opacity', '');
})
.on("dropstart", ".drop", function () {
$(this).addClass("active");
})
.on("drop", ".drop", function () {
$(this).toggleClass("dropped");
})
.on("dropend", ".drop", function () {
$(this).removeClass("active");
});
setInterval(function () {
$('.drop:not(.visible)').first()
.addClass('visible').removeClass('hidden');
}, 1000)
Enable the refreshPositions option.
Why not place all the divs into the page and set their visibility to hidden? Then use setInterval() to change each one's visibility every second.

How to detect when mousemove has stopped

How is it possible to detect with an eventListener when mousemove has finished?
document.AddEventListener('mousemove', startInteractionTimer, false);
function startInteractionTimer(){
clearInterval(touchInterval);
touchInterval = setInterval(noAction, 6000);
}
I want to start the function startInteractionTimer immediately after the mousemove has ended and I would like to catch that. On the code example above, it is starting if the mouse is moved.
Thanks
Edit: Alright, I answered my own question and the script above --^ is just fine.
You could always make a custom event for it:
(function ($) {
var timeout;
$(document).on('mousemove', function (event) {
if (timeout !== undefined) {
window.clearTimeout(timeout);
}
timeout = window.setTimeout(function () {
// trigger the new event on event.target, so that it can bubble appropriately
$(event.target).trigger('mousemoveend');
}, 100);
});
}(jQuery));
Now you can just do this:
$('#my-el').on('mousemoveend', function () {
...
});
Edit:
Also, for consistency with other jQuery events:
(function ($) {
$.fn.mousemoveend = function (cb) {
return this.on('mousemoveend', cb);
});
}(jQuery));
Now you can:
$('#my-el').mousemoveend(fn);
You could try setting/clearing a timeout solely to detect the end of moving the mouse...
var x;
document.addEventListener('mousemove', function() {
if (x) clearTimeout(x);
x = setTimeout(startInteractionTimer, 200);
}, false);
How long you want to wait is up to you. I don't know how long you want to say is "the end of a mousemove"
Example: http://jsfiddle.net/jeffshaver/ZjHD6/
Here is another custom-event solution, but without jQuery. It creates an event called mousestop which will be triggered on the element that the mouse pointer is on. It will bubble up like other mouse events.
So once you have that piece of code included, you can add event listeners to any element with addEventListener('mousestop', fn):
(function (mouseStopDelay) {
var timeout;
document.addEventListener('mousemove', function (e) {
clearTimeout(timeout);
timeout = setTimeout(function () {
var event = new CustomEvent("mousestop", {
detail: {
clientX: e.clientX,
clientY: e.clientY
},
bubbles: true,
cancelable: true
});
e.target.dispatchEvent(event);
}, mouseStopDelay);
});
}(1000));
// Example use
document.getElementById('link').addEventListener('mousestop', function(e) {
console.log('You stopped your mouse while on the link');
console.log('Mouse coordinates are: ', e.detail.clientX, e.detail.clientY);
// The event will bubble up to parent elements.
});
<h1>Title</h1>
<div>
content content<br>
<a id="link" href="#">stop your mouse over this link for 1 second</a><br>
content content content
</div>

How attach html-element to mouse using jQuery?

How do I attach html-element to mouse cursor using jQuery. This should be something like 'draggable', but I want that element clung to the cursor after mouse double-click and to follow the cursor until the left mouse button is pressed.
You'll want to use .mousemove() and .offset().
$("#clickedElement").dblclick(function () {
var $someElement = $("#elementToCling");
$(document).mousemove(function (e) {
$someElement.offset({ top: e.pageY, left: e.pageX });
}).click(function () {
$(this).unbind("mousemove");
});
});
Working demo: http://jsfiddle.net/EbbxA/

Categories

Resources