I'm working on a small jQuery plugin that mimics the jQuery UI draggable/droppable behavior with native HTML5 drag and drop events.
A feature I'd want to add is the ability to specify the node which will serve as the drag proxy.
I did a bit of research, and according to MDN, to do this requires the use of setDragImage(), passing an image or an element.
What is the support for setDragImage in different browsers?
I've noticed there's a plugin named jquery.event.drag which takes a different than I expected to this problem.
Would this feature require me to make some kind of workaround like the above plugin, or should this be possible out-of-the-box in most or all browsers using setDragImage?
EDIT
After playing around a bit with this functionality, it would seem that this function is quite limited.
Besides having no support in quite a few browsers, using an arbitrary DOM element as the helper requires it to be in the DOM tree and visible, and so you have the element itself on the body, and a copy of it as the handler. This is mostly unwanted for this sort of plugin.
Further more, rendering is also problematic even when the right terms are met. When trying to create a helper from <span>TEST</span>, the helper itself only showed a white rectangle with the dimensions of the span.
Are these issues that were to be expected according to the specs? Could they be fixed in code or would they require a workaround?
setDragImage is IMO a vital feature for any non trivial drag and drop use case. e.g consider a multi select list where a drag needs to include all the selected items and not just the row that the drag gesture was made on. it's odd that the thing you want to set needs to be visible in the DOM but even worse is that this method is not implemented at all in IE as of version 11.
However, with a bit of effort I was able to get it working reasonably satisfactorily. The custom drag image node can be removed from the DOM in a timeout 0 function. so add it to the DOM in dragstart then use it in set drag image and then remove it. This works perfectly in FF but in chrome the drag image node will flicker before the timeout fires. One way to prevent this is to position it such that the actual browser generated drag image will appear in exactly the same place, this is not as bad as it sounds since you can control the position of the custom drag image relative to the cursor.
I was playing with this recently and was able to get it working on IE as well. the trick there is to get IE to drag the custom drag image node and not the node that dragstart fired on. you can do this with the IE specific dragDrop() method.
The final thing to be aware of is that on windows there is a 300px limit on the width of the custom drag image node this applies to all draggables not just the custom node actually. so the browser applies a heavy radial gradient if the drag image is too big.
http://jsfiddle.net/stevendwood/akScu/21/
$(function() {
(function($) {
var isIE = (typeof document.createElement("span").dragDrop === "function");
$.fn.customDragImage = function(options) {
var offsetX = options.offsetX || 0,
offsetY = options.offsetY || 0;
var createDragImage = function($node, x, y) {
var $img = $(options.createDragImage($node));
$img.css({
"top": Math.max(0, y-offsetY)+"px",
"left": Math.max(0, x-offsetX)+"px",
"position": "absolute",
"pointerEvents": "none"
}).appendTo(document.body);
setTimeout(function() {
$img.remove();
});
return $img[0];
};
if (isIE) {
$(this).on("mousedown", function(e) {
var originalEvent = e.originalEvent,
node = createDragImage($(this), originalEvent.pageX, originalEvent.pageY);
node.dragDrop();
});
}
$(this).on("dragstart", function(e) {
var originalEvent = e.originalEvent,
dt = originalEvent.dataTransfer;
if (typeof dt.setDragImage === "function") {
node = createDragImage($(this), originalEvent.pageX, originalEvent.pageY);
dt.setDragImage(node, offsetX, offsetY);
}
});
return this;
};
}) (jQuery);
$("[draggable='true']").customDragImage({
offsetX: 50,
offsetY: 50,
createDragImage: function($node) {
return $node.clone().html("I'm a custom DOM node/drag image").css("backgroundColor", "orange");
}
}).on("dragstart", function(e) {
e.originalEvent.dataTransfer.setData("Text", "Foo");
});
});
Related
TLDR: Here is a JS Fiddle https://jsfiddle.net/qn8jhsaf/10/
I am attempting to create a simple drag and drop UI using mouse events as I didn't like any of the libraries available for the framework I am using. While I have all the events wired up and am getting the application side behavior I want, animating the div moving around isn't working how I would expect.
I am trying to use vanilla JS to insert a cloned div into the dom, absolutlely position it, and then move it around with tranform: translate as the mouse moves around.
So in onMouseDown, I'm doing just that:
const onMouseDown = (e) => {
isDragging = true
const rect = element.getBoundingClientRect()
let node = element.cloneNode(true)
node.style.position = 'absolute'
node.style.zIndex = 1000
node.style.left = rect.x
node.style.top = rect.y
console.log(rect.x)
console.log(node.style.left)
draggableNode = node
document.body.append(node)
}
The curious part happens when I call node.style.left =. It does not throw an error or anything but the value remains stubbornly empty ("") and it does not get translated to the style attribute of the cloned div. As far as I can tell from documentation this is a supported thing. I'm not sure what else to try.
The left and top styles need to be strings.
node.style.left = `${rect.x}px`
node.style.top = `4{rect.y}px`
FWIW, I swear I tried this at some point but ¯\_(ツ)_/¯
Basically, I'd like to set the tolerance to fit when I drag the mouse from left to right, and touch when I go from right to left (akin to how CAD programs do it). I've looked around and, besides modifying the standard JQuery UI code to move at least 2 variables into a global scope (I'd prefer not to modify any of the standard files), there doesn't seem to be a method of doing this.
The current method I can see involves modifying the _mouseDrag function within selectable method, so as to move the x1 and x2 variables to a more global state to read them (they represent the start and end points of the selected area box horizontally).
To clarify:
JQuery = 1.10.2 (Same as in the JQuery UI demo's)
JQuery UI = 1.11.4 (Same as in the JQuery UI demo's) (Line 12059 is the start of _mouseDrag)
Browser = Firefox on Ubuntu 14.04, everything up to date
You can check on mousemove if the clientX is less or more than clientX on the start event and modify the tolerance option depending.
Like this:
start: function (e, ui) {
startX = e.clientX;
$('#selectable').mousemove(function (e) {
if (e.clientX < startX) {
$('#selectable').selectable('option', 'tolerance', 'touch');
} else {
$('#selectable').selectable('option', 'tolerance', 'fit');
}
});
}
http://jsfiddle.net/8fpr6c14/2/
I've implemented the drag and drop algorithm from this page: https://github.com/EightMedia/hammer.js/blob/master/examples/drag.html
It works fine if the mouse doesn't move too fast on the desktop, otherwise it loses track of the original picked up element and just moves the new one, which is below the current mouse position.
A Video of the problem can be seen here:
http://www.screenr.com/tIO8
I tried to change the code to this, then it works fine, but it is not able to use multiple touches for different objects at the same time.
for(var t=0,len=touches.length; t<len; t++) {
var target = $(this);
target.css({
zIndex: 1337,
left: touches[t].pageX-50,
top: touches[t].pageY-50
});
The code shouldn't lose track of the object and should be able to use multitouch.
it's my first post here, so be gentle with me ;)
I'm trying to make a simple editor using MooTools (still 1.2.5 though..). Every element has a drag corner to make it resizable.
The whole thing works really great, until the element is rotated. After giving it -moz-transform: rotate(Xdeg) property the drags are going crazy (simple example: http://jsfiddle.net/HTg57/1/)
I know why this happens - although element is rotated all events come as the element would be in normal position.
The question is - are there somewhere any libs which can handle this problem?
I think that I know how to solve the problem by myself, but it will take time, which I don't have.
So - are there any ready solutions to make dragging work properly when element is rotated?
not sure what you mean by 'work properly' but you really should look at this, which makes more sense (behaviour wise): http://jsfiddle.net/dimitar/HTg57/2/
var Box = new Class({
mEl: null,
mDragEl: null,
mDrag: null,
initialize: function(pEl) {
this.mEl = pEl;
this.createDrag();
},
createDrag: function() {
this.mDragEl = new Element('div', {
'class': 'drag'
}).inject(this.mEl);
this.mEl.makeResizable({
handle: this.mDragEl,
limit: {
x: [60,300],
y: [60,300]
}
});
}
});
new Box(document.getElement('div.box'));
new Box(document.getElement('div.rotate'));
it takes advantage of the element.makeResizable with a handle and a size limiter. it works fine when element is rotated as well. have fun...
I use the following javascript (with jQuery) to handle drag and drop on a script I am writing:
jsc.ui.dragging = false;
jsc.ui.drag_element = {};
$(".drag-target").mousedown(function() {
jsc.ui.drag_element = $(this);
jsc.ui.dragging = true;
});
$("html").mousemove(function(e) {
if(jsc.ui.dragging) {
jsc.ui.drag_element.css({"position": "absolute", "top": e.clientY - 1, "left": e.clientX - 1, "z-index": "100"}); // - 1s are due to IE not leaving go otherwise
$("#overlay").show(); // Overlay stops text beneath being selected. TODO Stop current elements text being selected.
}
});
$(".drag-target").mouseup(function() {
if(jsc.ui.dragging) {
jsc.ui.dragging = false;
jsc.ui.drag_element.css("z-index", "98");
$("#overlay").hide();
}
});
However when the object is being dragged, the text inside it has a flickering selection i.e. it is being selected on and off as the element is moved. Is there any way to prevent this, or hide it's effect?
Is there a reason for not using jQuery UI Draggable ? Your code would look like that:
$(".drag-target").draggable();
You should at least check how it's build, it may help you to solve you flickering problem.
I think the extent of the effect depends on your hardware and video driver. To avoid it entirely, you could just draw a rectangle and then redraw the window when the mouse button is released.