jQuery UI : Before start draggable - javascript

How to implement a before start event to have a change to change the position and place in the DOM of the draggable element before jQueryUI start to drag?

You could extent prototype method:
SEE DEMO
var oldMouseStart = $.ui.draggable.prototype._mouseStart;
$.ui.draggable.prototype._mouseStart = function (event, overrideHandle, noActivation) {
this._trigger("beforeStart", event, this._uiHash());
oldMouseStart.apply(this, [event, overrideHandle, noActivation]);
};
$("#draggable").draggable({
beforeStart: function () {
console.log('beforeStart::');
}
});

I found that a method passed as the "helper" option to the sortable will get called before "start" and is passed (as the second argument) the item that has been clicked. You could do what you need to do in this method and then just return the element itself (the default "original" helper behavior). I'm using this to set the height of my container so that it doesn't collapse and trigger scrolling of the browser window. Here's what that looks like:
$(list).sortable({
helper: function(event, element) {
// it's too late if we wait until start to do this
$(this).css('height', this.$().height());
return element;
}
})

I didn't dare to access jQuery UI private variables, so I implemented it like this:
// The mouse interaction sequence for dragging starts with a mousedown action.
element.on('mousedown', function() {
// Mouseup cancels dragging. This is a boring click.
element.one('mouseup', function() {
element.off('mousemove.mynamespace');
});
// Moving the mouse while holding mousedown is dragging.
// This is also what sets off jQuery UI draggable,
// but we registered our event listeners first.
element.one('mousemove.mynamespace', function() {
// !! Your beforeStart code here.
});
});
// Initialize jQuery UI draggable AFTER our own event listeners.
element.draggable();

For that I used mouseup and mousedown:
var timeout;
$('.draggable').mousedown(function() {
$('#dragContainer').append($(this));
$(this).css({
top: 0,
left: 0
});
});
$('.draggable').draggable();
I also used mouseup to reset the old parent and position if the mousedown was actually a click and not a drag.
It would be nice to have a beforeStart event which work with the distance option but I didn't find it...

Related

Drag event not firing with angular 2

I have a canvas and I want users to be able to drag graphic elements around it. Thus, I don't want the canvas itself to drag, but I want to handle dragstart, drag, and drop events when the mouse does those things.
I'm using Angular 2, so I have:
<!-- editor.component.html -->
<div #rendererContainer
draggable="true"
(dragstart)="onDragStart($event)"
(drag)="onDrag($event)"
(dragover)="onDrag($event)"
(drop)="onDragEnd($event)"
(dragend)="onDragEnd($event)">
</div>
Then in editor.component.ts:
onDragStart(event) {
console.log(`starting`);
event.preventDefault();
}
onDrag(event) {
console.log('dragging');
event.preventDefault();
}
onDragEnd(event) {
console.log('drag end');
event.preventDefault();
}
When I try dragging something, I get starting printed in the console, but that's it. How do I get the other drag events to fire? Do I have to roll my own dragging from mousedown/move/up events?
Stand-alone example on stackblitz. I want "dragging" the div around to fire dragstart/drag/drop events, but it only fires the starting one.
You do not have to use event.preventDefault(). This is only necessary if you want to use pure JS.
Try this Stackblitz: https://stackblitz.com/edit/angular-x7umar
Also refer to the MDN implementation guide to choose the right events for your purposes: https://developer.mozilla.org/en-US/docs/Web/Events/drag
Further steps
If you want to modify the dragged element, simply implement some CSS adjustments inside your dragstart and dragend handler of the event.target:
onDragStart(event: DragEvent) {
console.log(`starting`, event);
// Hide dragged element
event.target.style.opacity = 0;
}
onDragEnd(event: DragEvent) {
console.log('drag end', event);
// Show dragged element again
event.target.style.opacity = 1;
}
With event.target you have the complete manipulable DOM element of the dragged element.
try with
(dragover)="onDragOver($event)"
(dragleave)="onDragLeave($event)"
Component
onDragOver(event) {
// do something
event.preventDefault();
}
onDragLeave(event) {
// do something
event.preventDefault();
}

Is there an event called while a jQuery UI draggable element is being reverted to its original position?

I have a draggable element using jQuery UI, and have a function attached the drag event that gets continuously called with the element's position while the user is dragging it.
I also have revert: true set on this element, so when the user stops dragging the element springs back to its original position. Is there a way I can attach a listener to perform the same functionality as when it's being manually moved?
I can't see anything specifically related to the revert property in the docs, so if that's not possible is there a more general event called while an element is moving?
Thanks!
It appears there's nothing in the API for this, so I've had to hack it using setInterval.
var revertInterval;
var revertEvent = function(el) {
revertInterval = setInterval(function() {
// Do things
// e.g. console.log(el.position().top);
}, 5);
}
$('#draggable').draggable({
revert: function() {
revertEvent($(this));
return true;
},
stop: function() {
clearInterval(revertInterval);
}
});
the revert is accept function as follow
revert: function(param){}
DEMO

Scroll event doesn't fire unless page moves

I'm looking to get an event to fire when one scrolls "up" from $(window).scrollTop == 0.
If you have the following code:
$(window).scroll(function(){
console.log("scrolling")
});
On a page where the document < window height then that event never fires because $(window).scrollTop isn't changing, but this doesn't mean that there's no mouse scroll input. I want an event to fire on mouse scroll regardless if the page is moving or not.
Seems like what you are looking for:
http://jsfiddle.net/n8eVQ/
$(document).on('mousewheel DOMMouseScroll MozMousePixelScroll', function(event, delta) {
console.log('mousewheel');
//you could trigger window scroll handler
$(window).triggerHandler('scroll');
});
Other way is to capture scroll event on modern browsers which support event capturing phase (IE>8). This can be used for any dynamic element. As jQuery doesn't implement capturing phase, you have to use javascript addEventListener() method. Here an example implementing logic to get scrolling direction for a textarea:
document.addEventListener('scroll', function (event) {
var $elm = $(event.target);
if ($elm.is('textarea')) { // or any other filtering condition
// do some stuff
var direction = $elm.scrollTop() > ($elm.data('scrollTop') || 0) ? "down" : "up";
$elm.data('scrollTop', $elm.scrollTop());
console.log('scrolling', direction);
}
}, true);
-DEMO-
document.addEventListener('DOMMouseScroll', callbackFunction, false);
Solution for firefox; for other browsers see #roasted solution

jQuery draggable problem with 'scroll' on object inside overflow:hidden container when using appendTo: 'body'

I have a draggable object, with that can be accepted in several droppables.. I have put all the droppables in a container, and simply want to be able to detect when the draggable is hovering over the container of droppables...
At first, I tried making use of the 'over' and 'out' callbacks for droppables, but it was not working because hovering from one droppable to another (inside the same container) was causing it to think the mouse had left the container...
So my next approach was to in the drag start callback, do an event listener for mouseenter and mouseleave on the container-- and then stop listening on the drag stop callback...
However, this results in total crazy behavior... If you look at my example page:
http://collinatorstudios.com/www/jquery_draggable_test.html
When dragging the box to the red dropzone, you should see "enter" when the mouseenter event fires, and "leave" when mouseleave happens.. However, just dragging the box over the inside of the container causes "leave" to appear a zillion times..... I cannot figure out why this is happening, nor what solution there is to my problem so I can do what I need to. I've been working on this for almost 4 hours now and am losing my mind over what seems like it should be so simple to achieve.
Any help would be greatly appreciated... Thanks.
Try adding a droppable for the container:
$('#drop_zone_container').droppable({
over: function(){ feedback.text('enter')},
out: function(){feedback.text('leave')}
});
You only need to bind to the events once! There is no need to bind and unbind them each time... I separated them out in the code below to make it more clear about binding once.
And as ZDYN said (+1 to him), you need to include a droppable code, but instead of using the container, use the zones inside... here is a demo and the full code below.
var feedback = $('#feedback');
$('.item').draggable({
revert: true,
zIndex: 999,
cursor: 'move'
});
$('.drop_zone').droppable({
drop: function(event, ui) {
ui.draggable.appendTo($(this));
}
}).bind('dropover dropout', function(e) {
var id = this.id;
feedback.text(e.type === 'dropover' ? 'Over: ' + id : 'Out: ' + id);
});

js/jQuery Drag'n'Drop, recalculate the drop targets

I have the following issue, I have a large tree which has subnodes which can be folded and unfolded on demand (the data within nodes gets fetched with AJAX). However, I use jquery.event.drop/drag to create my drag/drop targets.
However, when I fold/unfold the drop targets change position and I need to recalculate. This is how I wanted to do that:
function create_drop_targets() {
$('li a')
.bind('dropstart', function(event) {
})
.bind('drop', function(event) {
})
.bind('dropend', function(event) {
});
}
create_drop_targets() is called upon fold/unfold.
However, this doesn't work. I have located the following within jquery.event.drop:
var drop = $.event.special.drop = {
setup: function(){
drop.$elements = drop.$elements.add( this );
drop.data[ drop.data.length ] = drop.locate( this );
},
locate: function( elem ){ // return { L:left, R:right, T:top, B:bottom, H:height, W:width }
var $el = $(elem), pos = $el.offset(), h = $el.outerHeight(), w = $el.outerWidth();
return { elem: elem, L: pos.left, R: pos.left+w, T: pos.top, B: pos.top+h, W: w, H: h };
}
Now I need to know how I can call the setup() method again so it repopulates $elements with the new positions for the droppables.
Just had the same issue. I wandered around within the source-code of jQuery and found this (in ui.droppable.js):
drag: function(draggable, event) {
//If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event);
...
So, you'd just have to use
$(".cocktails").draggable({
refreshPositions: true,
});
Seems not to be documented very much... but it fixed my problem. Makes everything a bit slower of course, I would advise some usage-dependent tweaking (enable it before the changes occur, and disable it once the user has moved his mouse and the changes have occured).
Maybe it will be better to add live events introduced in jQuery 1.3?
$("li a").live("dropstart", function(){...});
I ran into the same issue when I tried to combine scrolling with draggable rows in liteGrid, but I found a work-around. Your mileage may vary, but what I did was add logic to my drag event handler that would check to see if the grid was being scrolled (which is when I needed to force the droppable positions to be refreshed), and if so, I set refreshPositions to true on the draggable. This doesn't immediately refresh the positions, but it will cause them to refresh the next time the drag handle moves. Since refreshPositions slows things down, I then re-disable it the next time my drag event handler fires. The net result is that refreshPositions is enabled only when the grid is scrolling in liteGrid, and its disabled the rest of the time. Here's some code to illustrate:
//This will be called every time the user moves the draggable helper.
function onDrag(event, ui) {
//We need to re-aquire the drag handle; we don't
//hardcode it to a selector, so this event can be
//used by multiple draggables.
var dragHandle = $(event.target);
//If refreshOptions *was* true, jQueryUI has already refreshed the droppables,
//so we can now switch this option back off.
if (dragHandle.draggable('option', 'refreshPositions')) {
dragHandle.draggable('option', 'refreshPositions', false)
}
//Your other drag handling code
if (/* logic to determine if your droppables need to be refreshed */) {
dragHandle.draggable('option', 'refreshPositions', true);
}
}
$("#mydraggable").draggable({
//Your options here, note that refreshPositions is off.
drag: onDrag
});
I hope that saves you from ramming your head into the keyboard as many times as I did...
I realize the original question is quite old now, but one little trick I came up with to refresh the position of draggable elements without much overhead (AFAICT) is to disable and immediately re-enable them wherever appropriate.
For instance, I noticed that resizing my browser window would not refresh the position of my draggable table rows, so I did this:
$(window).resize(function () {
$(".draggable").draggable("option", "disabled", true);
$(".draggable").draggable("option", "disabled", false);
});
I hope this helps someone out there!

Categories

Resources