How to disable/stop drag action when using dojo/dnd/Moveable? - javascript

I am using a dojo/dnd/Moveable in order to drag a div on a page, similarly to the example below.
During event 'move' which run when user is dragging the div I need to apply some logic (in my real example I need to check for some mouse coordinate), if logic is not valid I need to STOP dragging the div.
I have tried to use dojo.dnd.manager.stopDrag() with no success.
I would like to know:
How to STOP/DISABLE the drag action?
A sample of working code would be appreciated. Thanks.
https://jsfiddle.net/mbrhb7nn/
require(["dojo/dnd/Moveable", "dojo/dom", "dojo/on", "dojo/domReady!"],
function(Moveable, dom, on){
on(dom.byId("doIt"), "click", function(){
var dnd = new Moveable(dom.byId("dndOne"));
dnd.on('Move', function (event) {
// add some logic here and stop drag
}.bind(this));
});
});

I was able to solve my problem using constrainedMoveable from dojo/dnd/move.
Basically it is possible to setup an "area" where the dragging is only allowed, I used constrainedMoveable as I need to dynamically calculated the "area" .
Actually dojo/dnd/move offers severals specialized Movables:
constrainedMoveable
used to constrain a move to a dynamically calculated box.
Notes: the function constraints (must be overwritten) and has to return an object with the the following properties for l,t,w,h (left/top/width/height).
boxConstrainedMoveable
can be used to constrain a move to a predefined box.
parentConstrainedMoveable
can be used to constrain the move by the boundaries of the node’s parent.
More information can be found here.
Working example here:
https://jsfiddle.net/mbrhb7nn/5/
require(["dojo/dnd/move", "dojo/dom", "dojo/on", "dojo/domReady!"],
function (move, dom, on) {
var dnd = new move.constrainedMoveable(dom.byId("dndOne"));
dnd.constraints = function (event) {
// defines the area where dragging is allowed
return {
l: 0,
t: 0,
w: 300,
h: 300
};
}
});

Related

Change the dragged object when draggable begins

I am trying to create some thing like a UI designer. So I have a 'toolbox' and a 'canvas'. When I mark the tool box items as draggable, I am able to drag them. But what I need is to drag a copy, so that I can drag multiple instances of a tool item to the canvas.
$('.tool-box-item').draggable({
helper : 'clone',
drag : function (event, ui){
// jQuery does not allow object modification here
},
});
This does not work as the cloning is applied only while dragging and as soon as the drag is complete, the original element moves. I tried overriding the drag property, but I am unable to modify the dragged element. Same was the case with start.
Note : I can not use the stop event as I want to create the copy when the drag starts and not after it. I was also able to create a copy by defining the canvas as droppable and making a copy there, But then again I want to create a copy when the drag starts.
I think I will need to create a custom drag function but was hoping if jQuery has any other alternative way.
Here is a jsfiddle, but I want to clone the element at the start and not after the drag.
EDIT : The destination is a 3rd party control. It has its own implementation for drop event (stacking and aligning). In case I clone it at the end I will have to modify their implementation.
To keep "dragend" event, try to use original element for dragging, and clone it for place to your "toolbox".
var graggableConfig = {
helper:"clone",
stop: function(event, ui){
var clon = $(this).clone();
clon.attr("style","")
.appendTo(".src")
.draggable(graggableConfig);
$(this).detach()
.attr("style","")
.appendTo(".dst")
.draggable( "destroy" );
}
};
$(".item").draggable(graggableConfig);
codepen here
You can use jquery's clone (https://api.jquery.com/clone/) method to clone your draggable element once it is dropped. Would something like this meet your needs?
$( function() {
makeDraggable($( "#draggable" ));
});
function makeDraggable(element) {
$(element).attr('style', 'position:absolute').draggable({
start: function() {
makeDraggable($('#draggable').clone().attr('style', '').appendTo('body'));
}
});
}
Its a little rough but here is the jsbin: https://jsbin.com/tasodixoka/edit?html,output
Edit: I updated your fiddle: https://jsfiddle.net/z9rrL6po/6/

Change cursor on hovering over a Raphael element

A Raphael element is created dynamically (in response to user input, not upon page load). I want to use Raphael's .hover() method to change the cursor to "pointer" (the hand usually used for links) when the user hovers over the object. How can this be accomplished?
(I know that you can use CSS to achieve this effect, but because the DOM element is created via scripting, rather than being built into the page upon load, I don't know if CSS can be applied here, or how it would be if it can be.)
http://www.w3schools.com/cssref/playit.asp?filename=playcss_cursor&preval=pointer
https://web.archive.org/web/20160121064823/http://raphaeljs.com/reference.html
Looks like "cursor" is actually one of the attributes than can be modified with Raphael's .attr() function. So,
el.hover(function(){el.attr({'cursor':'pointer'})},
function(){el.attr({'cursor':'default'})}, el, el);
does the trick.
var paper = Raphael("rect", 400, 400);
var hoverIn = function() {
// if the context is not stated forcefully, this refers to the element in context.
// can be any valid cursor types.
this.attr({"cursor": "pointer"});
};
var hoverOut = function() {
this.attr({"cursor": "default"});
}
// simple click callback.
var clickFN = function (){
alert('Hey you just clicked me :p, Thanks!!!')
};
var cItem = paper.rect(40,40,50,30)
// attaching the hovering callbacks.
.hover(hoverIn, hoverOut)
// these are just additional cosmetics and fuctionalities.
.attr({
"fill": "#0ff000"
})
.click(clickFN);
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.2.1/raphael.min.js"></script>
<div id="rect"></div>
or play with this fiddle!
Hey while invoking the .hover function the third and fourth parameters remains optional and used only if you need to change the context in your code.
Set the cursor attribute value for a specific object directly.
Keep it simple! :)
var paper = Raphael("rect", 400, 400);
var myRectangle = paper.rect(40,40,50,30);
myRectangle.attr({
'cursor':'pointer',
"fill": "#0ff000"
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.2.1/raphael.min.js"></script>
<div id="rect"></div>

Jquery on mousedown not working on dynamically generated elements

So i'm trying to create a js/css "wave game" like tower defense ones.
When all the pre-generated enemys from first wave are dead, it spawns the second wave and so on.
So far so good.
The problem is that i just can't attack mobs dynamically spawned within second wave.
I used to try .live() in similar cases, but its deprecated, so i'm trying .on(), as instructed
$('.enemy').on('mousedown' , function(event) {
//attack code
}
Its working fine for initial mobs (1st wave) but it still just not working on dynamic mobs (>= 2nd wave)
Help, guys, please?
You need to specify an element that is already there when the DOM is created. In the parameters, you specify the elements you want to add the mousedown method. By simply assigning $('.enemy'), it will attach the method to those that are already present in the DOM.
$('body').on('mousedown', '.enemy', function(event) {
//attack code
}
As Wex mentioned in the comments, instead of writting $('body') you should use the container's name (the container which wraps the .enemy elements. This way, when a .enemy element is added, the event doesn't need to bubble all the way up to the body tag.
The binding '.on()' works only with the content that created earlier then the script ran.
So one solution could be you bind the event to the parent element.
$('.PARENT_ELEMENT').on('mousedown', '.enemy', function(event){
// your code here
}
That should do it.
I made this google like drop down suggestions search box and I faced a problem similar to yours where there was suggestions disappearing before the re-direct happened. I overcame it by using and modifing vyx.ca answer:
var mousedownHappened = false;
var clicked_link;
$("#search-box").blur(function(e) {
if (mousedownHappened)// cancel the blur event
{
mousedownHappened = false;
window.location.href = clicked_link;
} else {
// no link was clicked just remove the suggestions box if exists
if ($('#search-btn').next().hasClass('suggestions')) {
$(".suggestions").remove();
}
}
});
//attaching the event to the document is better
$(document).on('mousedown', '.suggestions a', function() {
clicked_link= $(this).attr('href');
mousedownHappened = true;
});

How to dynamically add an ondragstart event to a dynamically created div

There are a couple of questions that seem to ask this question, but no one accepted answers and nothing has worked for me.
I have a 'group' button that when it's clicked, will dynamically create a 'group' div (I then append some pre-selected 'child' divs within it)
However I need the new 'group' div to be draggable.
I can set the draggable attribute dynamically, no problem.
But being draggable is not much good without the ondragstart() event, and no matter what I've tried, I can't get this assigned.
I am using jQuery which may have a bearing.
The latest iteration of my code is (this appears in an init() function that is called from body.onload):
var group=$(document.createElement('div'));
group.attr({id: 'group'+grpcount});
group.attr({draggable: "true"});
group.addClass('group');
group.html("<span class='group'>Group"+grpcount+"</span>");
$('#boundary').append(group);
document.getElementById('group'+grpcount).addEventListener("startdrag",drag);
But I have also tried various combinations of jQuery .bind:
group.bind("dragstart", drag(ev));
group.bind("dragstart", function(ev){drag(ev);});
All to no avail.
I have a drag function already defined (and I've tried putting it before and after the code above):
function drag(ev) {
ev.dataTransfer.setData("Text",ev.target.id+":"+ev.pageX+":"+ev.pageY);
}
I hope there is something glaringly obvious that I just can't see.
Can somebody solve this?
You'll be better to take a look at Jquery UI draggable here
and if you dont know yet, you can call multiple jquery finctions in a chain, so your code looks more readable like below:
var group=$('div').attr('id', 'group'+grpcount)
.addClass('group')
.html("<span class='group'>Group"+grpcount+"</span>");
$('#boundary').append(group);
and instead of
document.getElementById('group'+grpcount).addEventListener("startdrag",drag);
use this
group.draggable({
start:function(event, ui){
//this is where dragging starts when you push mousedown and move mouse
},
drag:function(event, ui){
//this function will be called after drag started each time you move your mouse
}
stop:function(event, ui){
//this is where you release mouse button
}
})
this jquery draggable widget with droppable widget will ease your life if you really want to implement complex drag-and-drop functionality
I made this:
function drag(ev) { alert('Hi'); }
var grpcount = 21;
var group=$(document.createElement('div'));
group.attr({id: 'group'+grpcount});
group.attr({draggable: "true"});
group.addClass('group');
group.html("<span class='group'>Group"+grpcount+"</span>");
group.bind("dragstart", function(ev){drag(ev);});
$('#boundary').append(group);
See the working fiddle here: http://jsfiddle.net/nxcSz/

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