I've implemented a simple drag and drop system using directives in Angular. It works fine in Chrome, but Firefox doesn't expose event.clientX, event.clientY properties on drag event (They just refuse to fix it).
So I'm looking for a good alternative to expose these properties on drag event: the x,y coordinates are needed for visual feedback on drag event.
Code is here - check out in Chrome and Firefox to see the problem.
In Chrome, drag an item in the folders, you'll have the same item displayed as visual feedback following the mouse, not in Firefox (because Firefox doesn't support e.clientX and e.clientY in the drag event).
the problem is here (beginning line 45):
.on('drag', function(e) {
if (e.originalEvent.clientX) {
el.css({
'top': e.originalEvent.clientY + 10,
'left': e.originalEvent.clientX + 10
});
} else {
el.css('display', 'none');
}
});
So how can I get the mouse position on screen during a drag event, in Firefox (the angular way, I mean with directives, no global variable, or whatever)?
You can hook up to dragover on document -- clientX and clientY are exposed there.
Use functional closure to not populating global scope. Here is updated PLNKR (tested in Chrome and FF).
Changes to js:
.directive('mpDrag', function($timeout, $window, $document) {
// keeping coordinates private and
// shared among all instances of the directive
var mouseX, mouseY;
$document.on("dragover", function(event){
mouseX = event.originalEvent.clientX;
mouseY = event.originalEvent.clientY;
})
return {
...
link: function($scope, element, attrs) {
...
$timeout(function() {
...
.on('drag', function(e) {
// just use mouseX, mouseY directely here
// (btw. you should detect differently when to hide the element)
console.log(mouseX, mouseY);
if (e.originalEvent.clientX) {
el.css({
'top': mouseY,
'left': mouseX
});
} else {
el.css('display', 'none');
}
});
});
}
};
})
You must borrow drag coordinates from the document itself:
var dragX = 0,
dragY = 0;
element.on('dragstart', function(e) {
document.ondragover = function(event) {
event = event || window.event;
dragX = event.pageX,
dragY = event.pageY;
};
});
element.on('drag', function(e) {
el.css({
'top': dragY + 10,
'left': dragX + 10
});
});
Updated plunker
Related
I create a div and then:
$("#div").resizable({
disabled:true
});
Then I enable the resize behaviour and at the same time i want to handle a click on the div:
$("#div").on("click", myHandler);
$("#div").resizable( "option", "disabled", false);
function myHandler() {
console.log("div clicked");
}
If now I drag the bottom-right corner of the div I can resize it, but when I release the drag if the mouse pointer is inside the div (this happens when shrinking it) myHandler() is also called. Since myHandler() is supposed to handle a different behavior - and not the resizing - how can I solve this?
What I've tried so far with no success:
$("#div").resizable({
disabled:true,
stop: function(event) { event.stopPropagation(); }
});
How about only doing the click handler when the mouse position is at the same place as it started?
var left = 0;
var top = 0;
("#div").on({
mousedown: function(e) {
left = e.pageX;
top = e.pageY;
},
mouseup: function(e) {
if (left === e.pageX && top === e.pageY) {
console.log('click');
}
}
});
in the snippet below, only the 'mousemove' event is working. 'mousedown' has no effect and I'm not able to decipher why this is happening. If I replace 'mousedown' with 'click', it does work, however I want to use mousedown so the fadeOut event happens immediately (at the start of a click, rather than using click which relies on the users mouse going down and then back up.
$('.issue-carousel').on({
// On mousemove, controls follow cursor
mousemove: function(e) {
var parentOffset = $(this).offset();
var relX = e.pageX - parentOffset.left;
var relY = e.pageY - parentOffset.top;
$('.drag-indicator').css({
left: relX,
top: relY
});
},
mousedown: function() {
$('.drag-indicator').fadeOut(300);
}
});
As per my comment, your code works fine - with one exception. The mousedown fadeout doesn't work because you technically aren't clicking the carousel but instead the drag indicator. I've updated the code as per below where I have changed the mousedown target accordingly;
$('.issue-carousel').on({
// On mousemove, controls follow cursor
mousemove: function(e) {
var parentOffset = $(this).offset();
var relX = e.pageX - parentOffset.left;
var relY = e.pageY - parentOffset.top;
$('.drag-indicator').css({
left: relX,
top: relY
});
}
});
$('.drag-indicator').on({
mousedown: function(e){
$(this).fadeOut(300);
}
})
A working fiddle can be found here: https://jsfiddle.net/o3cdLjp7/2/
I'm try to embed event handling inside of marionette but I got kind of stuck as when I embed events I loose the marionette view object which i require to update the width of a div to create an interactive slider.
SliderBehavior = Marionette.Behavior.extend({
ui: {
slider: '.slider-pointer',
foregroundScale: '.slider-default',
},
events: {
'mousedown #ui.slider': 'onSliderDrag'
},
onSliderDrag: function(e) {
console.log(this);
var handlers = {
mousemove : function(e){
this.ui.foregroundScale.css({
width : utility.round((this.view.options.max - (e.pageX - this.view.options.offset)), this.view.options.min, this.view.options.max)+ 'px'
});
},
mouseup : function(e){
$(e.target).off(handlers);
}
};
this.$el.on(handlers);
}
});
So the problem is insde handlers >> mousemove >> the this object is representing the event how can I access my ui hash from inside this function.
Regards
Quick answer use $.proxy to inject your own scope
var handlers = {
mousemove : $.proxy(function(e){
this.ui.foregroundScale.css({
width : utility.round((this.options.max - (e.pageX - this.options.offset)), this.options.min, this.options.max)+ 'px'
});
}, this),
mouseup : $.proxy(function(e){
$(e.target).off(handlers);
}, this)
};
Try storing a reference to this in a variable outside of the handlers definition.
onSliderDrag: function(e) {
console.log(this);
var viewRef = this;
var handlers = {
mousemove : function(e){
viewRef.ui.foregroundScale.css({
width : utility.round((this.view.options.max - (e.pageX - this.view.options.offset)), this.view.options.min, this.view.options.max)+ 'px'
});
},
mouseup : function(e){
$(e.target).off(handlers);
}
};
this.$el.on(handlers);
}
I have to move a div by clicking on it, clicking again I must stop the div in that position. Now the problem is: when I want to move again the div, does not activate the mousemove event ... how can I fix it?
$('.move_div').live('click', function() {
$('html').on('mousemove', function(e) {
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
$('div').css({'top': y, 'left': x});
});
$("html").live('click', function() {
$('html').off('mousemove');
});
});
var ele = '.move_adv';
var moveBool = false;
$(function () {
$('html').on('mousemove', function (e) {
console.log($(this).width());
if (moveBool == true) {
var x = e.pageX - $(ele).width()/2;
var y = e.pageY - $(ele).height()/2;
$(ele).css({
'top': y,
'left': x
});
}
});
});
$(ele).live('click', function () {
moveBool = !moveBool;
});
http://jsfiddle.net/6y24s/2/
The main logic is storing the 'moveability' state of the div in a boolean.
You would also like to refine the code more.
Fiddle here , if you want to keep your code then only thing you need to add is
event.stopPropagation();
When you click on the div the mousemove handler is added to the div. Then an event handler is added to the document that removes any mousemove event handlers.
You then move the mouse and the div follows, you click and the mousemove handler is deleted.
You click on the div again, a mousemove event handler is added, though then the click event handler from the document takes away the mousemove handler.
So whenever you click after the first two clicks the mousemove handler is simultaneously created and destroyed.
Also use .on() instead of .live()
.live() was deprecated in JQuery 1.7
I am using JQuery UI to make an element draggable and in the code, one can specify what to do on start, drag, and end.
But how can I run one function on drag left and another on drag right?
I have already limited the draggable axis to the x-axis only. So the element can only move left or right.
Check this demo: http://jsfiddle.net/3NtS9/
You can do it by checking against the previous event coordinate on each atomic drag operation.
var prevX = -1;
$('div').draggable({
drag: function(e) {
//console.log(e.pageX);
if(prevX == -1) {
prevX = e.pageX;
return false;
}
// dragged left
if(prevX > e.pageX) {
console.log('dragged left');
}
else if(prevX < e.pageX) { // dragged right
console.log('dragged right');
}
prevX = e.pageX;
}
});
Here's an easier way to do it:
$( "#draggable" ).draggable({
drag: function( event, ui ) {
$(this).text(ui.originalPosition.left > ui.position.left ? 'left' : 'right');
}
});
Demo: http://jsfiddle.net/GNHTW/
I would recommend you handle the start and stop events and determine the position delta in stop event to get whether it was moved left or right:
http://jqueryui.com/draggable/#events