HTML5 drag and drop events - dragLeave fires before drop - javascript

In drag and drop the dragLeave event sometimes fires before the drop event.
This is causing problems because the target is getting the listeners in dragEnter with dragLeave and drop removing the listeners. If dragLeave fires before drop, then there is no listener for the drop.
I think the reason has something to do with another contra-intuitive: the dragEnter sometimes fires multiple times for the same target, even with propagation off. With multiple dragEnters, one would spawn a drop while the others would spawn a dragLeave. If this is the case, perhaps I could associate the dragLeave with the dragEnter - but I see no means of that coordination.
function dragEnter( e ) {
e.stopPropatation();
// is multiple fires of dragEnter for same cell
if( curCell == this ) return;
curCell = this;
curCell.addEventListener( 'drop', drop, true );
curCell.addEventListener( 'dragover', dragOver, true );
curCell.addEventListener( 'dragleave', dragLeave, true );
...
}
function dragLeave( e ) {
e.stopPropagation();
curCell.removeEventListener( 'drop', drop, true );
curCell.removeEventListener( 'dragover', dragOver, true );
curCell.removeEventListener( 'dragleave', dragLeave, true );
}
function drop( e ) {
// do the actual work
dragLeave( e );
}
Here's a list of calls:
begin drag dragstart
drag enter: this=e9 - e.target=IMG
drag enter: this=e9 - e.target=TD
drag enter: this=e8 - e.target=TD
drag enter: this=e8 - adding listeners
drag enter: this=e8 - e.target=IMG
drag leave: this=e8
clearing listeners: this=e8
If the "clearing listeners" were not performed, the next step would have been:
drop: this=e8

try this
<!DOCTYPE HTML>
<html>
<head>
<style type="text/css">
#div1 {width:350px;height:70px;padding:10px;border:1px solid #aaaaaa;}
</style>
<script>
function allowDrop(ev)
{
ev.preventDefault();
}
function drag(ev)
{
ev.dataTransfer.setData("Text",ev.target.id);
}
function drop(ev)
{
ev.preventDefault();
var data=ev.dataTransfer.getData("Text");
ev.target.appendChild(document.getElementById(data));
}
</script>
</head>
<body>
<p>Drag it</p>
<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<br>
<img id="drag1" src="img_logo.gif" draggable="true" ondragstart="drag(event)" width="336" height="69">
</body>
</html>

There's rarely a reason to manage your event listeners in this way.
If you bind to drop, dragover, dragleave at the same time as you bind to dragenter do you still see the problem?
The HTML dnd api is a little bit weird when you first look at it. Depending on what you are trying to do something simple like
onDragOver=function(e) { e.stopPropagation() }
onDrop=function(e) { /* handle drop */ }
may be all the listeners you need.

Related

Capture all event targets from mouseover when user clicks, then stop

I'd like to record all event.target that mouse encounters after user clicks anywhere and stop recording after he releases click. So far I've come up with this which doesn't stop recording after mouseup and I don't know why.
document.addEventListener('click', function() {
document.addEventListener('mouseover', record);
document.addEventListener('mouseup', removeListener);
})
function record(e) {
console.log(e.target);
}
function removeListener() {
document.removeEventListener('mouseover', record);
document.removeEventListener('mouseup', removeListener);
}
<div class='toto'>Toto</div>
<div class='toto'>Toto</div>
<div class='toto'>Toto</div>
<div class='toto'>Toto</div>
EDIT : Answer & Explanation
addEventListener('click') triggers on mouseup, therefore the sequence was as follow :
document.addEventListener('click', function() {
//Following would start once mouseup
document.addEventListener('mouseover', record);
//Following never triggers cause mouse is already up
document.addEventListener('mouseup', removeListener);
})
Solution as stated in the answer is to replace 'click' with 'mousedown'. It triggers immediatly after mouse is click is pressed, not released :
document.addEventListener('mousedown', function() {
document.addEventListener('mouseover', record);
document.addEventListener('mouseup', removeListener);
})
Instead of click event, you should use mousedown
I forked your codepen and you can see the result: https://codepen.io/Lazzaro83/pen/EeoxEW
document.addEventListener('mousedown', function() {
document.addEventListener('mouseover', record);
document.addEventListener('mouseup', removeListener);
})
Your problem is because you are putting one listener inside another, this isn't a reliable way to do so because terms of ms of execution, remember JS is not that "sequential" do not worry and let the three listener to live, a better way of do the thing you want to is make a global variable that works like a switch :
let switch = false;
document.addEventListener('click', function(e) {
e.stopPropagation();
switch = true;
});
document.addEventListener('mouseover', function(e){
e.stopPropagation();
if (switch){
console.log(e.target);
}
});
document.addEventListener('mouseup', function(e){
e.stopPropagation();
switch = false;
}) ;
this is a project i did with a blackboard, have many techniques as relegation:
https://codepen.io/LeonAGA/pen/eyWpMV
regards!

Trigger Javascript event on different target

I have a element which adds a child to itself when a mousedown or touchstart event occurs. This child is resizable (jQuery ui widget). I use http://touchpunch.furf.com/ wich enables jquery-ui widgets for touch devices.
I want to resize the child when it gets created without lifting the mouse/touch and clicking again. It works for mouse devices but i fail to trigger it when using a touch device.
Check the snippet (works for mouse, fails with touch).
Mousedown on blue element (red element is created)
Keep mouse down and drag to the right (red element gets resized)
I fail making it work for a touch device.
I create the element on touchstart, but i am not able to resize it whithout lifting my finger.
I basically want to achieve the same with touch as with the mouse. Problem is I don't know how the event must look like which I have to trigger on the resize-handle.
I check if it is a touch event and try to change the event.target but this does not work.
if (event.type === "touchstart") {
console.log("here i am stuck")
event.target = handler[0];
item.trigger(event);
}
$(function(){
$(document).on("mousedown touchstart", ".resizeCreator", function(event){
if ($(this).find("div").length){
return;
}
//Add resizable div
$(this).append("<div>resize me</div>");
$(this).find("div").resizable({handles: "e"});
simulateHandleEvent($(this).find("div"), event)
});
$(document).on("click", "button", function(){
$(".resizeCreator").find("div").remove();
});
})
var simulateHandleEvent = function(item, event){
var handler = item.find(".ui-resizable-e").first();
if (event.type === "touchstart") {
console.log("here i am stuck")
event.target = handler[0];
item.trigger(event);
}else{
handler.trigger("mouseover");
handler.trigger({
type: "mousedown",
which: 1,
pageX: handler.offset().left,
pageY: handler.offset().top
});
}
}
.resizeCreator{
width:200px;
height:200px;
padding:5px;
border:1xp solid black;
background-color:blue;
}
.resizeCreator div{
background-color:red;
display:inline-block;
padding:5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="https://raw.githubusercontent.com/furf/jquery-ui-touch-punch/master/jquery.ui.touch-punch.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css" rel="stylesheet"/>
<div class="resizeCreator">
</div>
<button>reset</button>

How can I make a nested element not draggable in a draggable container?

I'm using HTML5 drag and drop on a parent container, but I want to disable the drag effect on some of its children, specifically an input so that users can select/edit input content easily.
Example:
https://jsfiddle.net/Luzub54b/
<div class="parent" draggable="true">
<input class="child" type="text" value="22.99"/>
</div>
Safari seems to do this for inputs by default so try it on Chrome or Firefox.
I was looking for something similar and found a possible solution using the mousedown and mouseup events. It's not the most elegant solution but it's the only one that worked consistently for me on both chrome and firefox.
I added some javascript to your fiddle:
Fiddle
;
(function($) {
// DOM Ready
$(function() {
$('input').on('mousedown', function(e) {
e.stopPropagation();
$('div.parent').attr('draggable', false);
});
$(window).on('mouseup', function(e) {
$('div.parent').attr('draggable', true);
});
/**
* Added the dragstart event handler cause
* firefox wouldn't show the effects otherwise
**/
$('div.parent').on({
'dragstart': function(e) {
e.stopPropagation();
var dt = e.originalEvent.dataTransfer;
if (dt) {
dt.effectAllowed = 'move';
dt.setData('text/html', '');
}
}
});
});
}(jQuery));

Get notified about dragged images within a contenteditable div

I have a div which has contenteditable="true" and which contains some html. This html may include images.
Since contenteditable="true" the user can move images by dragging them to a new position. But I need my code to be notified each time a image is moved, in a way where I get both the image element which is being moved, and the target node where the image is dropped. How do I do that?
My current solution adds a Drop listener to my div element which is contenteditable, and then I do get a drop event each time the user moves an image, but I can't get the dom node with the image which the user moved.
Also: Dragging an image, seems to copy the DOM node, instead of moving it. Is this true? (Tested in firefox).
I would suggest a following pure JavaScript solution
HTML:
<div id="space" contenteditable="true">
Hello <img width="300" class="contentImg" src='http://www.independent.co.uk/incoming/article9859555.ece/alternates/w620/Dr-Talyor.jpg'/> dude!
</div>
CSS:
#space {
width: 500px;
height: 500px;
background-color: #000000;
color: #ffffff;
}
JavaScript:
var draggedImg;
document.addEventListener("dragstart", function( event ) {
// IE uses srcElement, others use target
var targ = event.target ? event.target : event.srcElement;
if (targ.className == 'contentImg'){
draggedImg = event.target;
}
}, false);
document.addEventListener("dragend", function( event ) {
if(draggedImg){
alert("It's moved!");
console.log('Here is data', draggedImg);
draggedImg = null;
}
}, false);
You'll find an image node in draggedImg variable.
Please review a working example here: http://jsfiddle.net/o09hLtch/2/
jQueryUI features draggable and droppable interactions. Draggable has drag event, which gives you the dragged element and droppable has drop event, which gives you the dropped element as well as where it was dropped.
Quick example: clickety
$('#content .dr').draggable(
{
addClasses: false,
drag: function(event, ui)
{
$('#notify').text('Bird (#' + $(this).attr('id') + ') being dragged: ' + JSON.stringify(ui));
},
stop: function(event, ui)
{
$('#notify').text('');
}
});
I think You looking for this,
$(function () {
$(".image").draggable({helper: 'original'});
$(".container").droppable({
drop: function (event, ui) {
$(this).append(ui.draggable.css({position: 'static'}));
alert('dropped!');
}
});
});
For JSFiddle Demo Let's see
Good Luck ['}

Cannot drag image inside div

i created a div an want to drag & drop pictures in it... the main problem is, that if it works the image cannot be dragged inside that div.
Code working drag & drop
$('div[role="textbox"]').on(
'dragover',
function (e) {
e.preventDefault();
e.stopPropagation();
}
);
$('div[role="textbox"]').on(
'dragenter',
function (e) {
e.preventDefault();
e.stopPropagation();
}
);
$('div[role="textbox"]').on(
'drop',
function (e) {
});
Code drag & drop not working but drag inside div works
//$('div[role="textbox"]').on(
//'dragover',
//function (e) {
// e.preventDefault();
// e.stopPropagation();
//}
// );
//$('div[role="textbox"]').on(
// 'dragenter',
// function (e) {
// e.preventDefault();
// e.stopPropagation();
// }
//);
$('div[role="textbox"]').on(
'drop',
function (e) {
//logic
})
Is there a way to combine these two to make both work?
The image is simply inserted as an .
This should be an example of what you want to do:
<!DOCTYPE HTML>
<html>
<head>
<script>
function allowDrop(ev){ev.preventDefault()}
function drag(ev){ev.dataTransfer.setData("text", ev.target.id)}
function drop(ev){
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
ev.target.appendChild(document.getElementById(data));
}
</script>
</head>
<body>
<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<img id="drag1" src="img_logo.gif" draggable="true" ondragstart="drag(event)" width="336" height="69">
</body>
</html>
Source:
http://www.w3schools.com/html/html5_draganddrop.asp
Also from my cross browser experience, to drag/drop nested elements inside a div one needs to cancel all its drag events on the parent div.
element.ondragenter = function(event){event.preventDefault()};
element.ondragleave = function(event){event.preventDefault()};
element.ondragover = function(event){event.preventDefault()};

Categories