Reputation: 5780
When starting to drag an element using HTML5 draggable attribute, original element is still visible, so I end up having two elements visible instead of one.
How can I do to have only the element being dragged visible (the original one should be momentarily hidden).
<script>
function startDrag() {
// hide initial element
}
function endDrag() {
// reset initial element
}
</script>
<div class="draggable" draggable="true"
ondragstart="startDrag(event)"
ondragend="endDrag(event)"
></div>
Here's a jsfiddle to show the problem https://jsfiddle.net/gjc5p4qp/
Upvotes: 40
Views: 29526
Reputation: 982
If you want to hide your element, you can use the CSS property visibility:hidden
or display:none
.
I recommend using the visibility
property because it's hides an element without changing the layout of the document. But, if you're element have childrens and you want to hide them to, you need to set up visibility:inherit
on each of childrens.
You were right to use the dragstart
event. But the clone of the element who is created by the draggable attribute appears at the end of the dragstart
event.
Because JavaScript engine is a single-threaded interpreter if you choose to hide it in here, you're element will be masked as its clone which will copy the property visibility:hidden
. In fact, to avoid this you need to hide it after the creation of the clone in the Javascript callstack.
To do it, use the setTimout()
function and set it to 0 ms. This way, the masking of the original element is put at the end of the stack, after the creation of his clone.
At the end of the drag, to make it reappear, you just need to set the element visible by calling visibility:visible
in the dragend
event.
For you're exemple, the code can look like this :
<div
class="draggable"
draggable="true"
ondragstart="startDrag(event)"
ondragend="endDrag(event)"
style="background: #e66465; color:white; width:80px; height:20px; text-align:center;">
Drag me
</div>
<script>
function startDrag(e) {
setTimeout(function(){
e.target.style.visibility = "hidden";
}, 0);
}
function endDrag(e){
e.target.style.visibility = "visible";
}
</script>
Upvotes: 5
Reputation: 2163
You can add to your dragStart event this code:
event.dataTransfer.setDragImage(new Image(), 0, 0); // Hide the dragged element
It will hide your target dragged element.
Upvotes: 1
Reputation: 1808
You may succeed this with a hacky solution. The native draggability doesn't allow CSS styles like: opacity:0;
, visibility:hidden
or display:none
.
But you can do it using: transform:translateX(-9999px)
.
function startDrag(e) {
let element = e.target;
element.classList.add('hide');
}
function endDrag(e) {
let element = e.srcElement;
element.classList.remove('hide');
}
.draggable {
border-radius: 4px;
background: #CC0000;
width: 40px;
height: 40px;
}
.hide {
transition: 0.01s;
transform: translateX(-9999px);
}
<div
class="draggable"
draggable="true"
ondragstart="startDrag(event)"
ondragend="endDrag(event)"
/>
I've updated your JSFiddle with the solution.
[EDIT]:
Updated JSFiddle example with Andrew Hedges suggestion by using requestAnimationFrame
instead setTimeout
.
[EDIT 2]:
Updated with a better solution by Jason Yin adding transition
CSS property instead using requestAnimationFrame
, it moves the processing from scripting to rendering.
Upvotes: 30
Reputation: 21
Filipe's answer is really helpful. If setTimeout
or requestAnimationFrame
doesn't work for you. try adding transition.
transition: transform 0.01s;
transform: translateX(-9999px);
Upvotes: 2
Reputation: 6125
This could be achieved without hacky trick from the previous answers.
The key point is to listening to the drag
event, instead of the dragstart
.
//listen to drag event, not dragstart!
document.querySelector(".draggable").addEventListener("drag", (e)=>{
e.target.classList.add("dragging");
});
document.querySelector(".draggable").addEventListener("dragend", (e)=>{
e.target.classList.remove("dragging");
});
.draggable {
width: 200px;
height: 30px;
background-color:#5856d6;
text-align:center;
line-height:30px;
}
.dragging {
background: transparent;
color: transparent;
border: 1px solid #5856d6;
}
<div draggable="true" class="draggable">Drag me!</div>
Upvotes: 18
Reputation: 2256
Found this: https://stackoverflow.com/a/35120001/1807542
With a clean solution to hide the source/original element in the dragover handler instead.
var dragging;
function dragstart(e) {
dragging = $(this);
}
function dragover(e) {
dragging.hide();
}
function dragend(e) {
if (dragging) {
dragging.show();
dragging = undefined;
}
}
Upvotes: 0
Reputation: 5428
Since simply setting visibility: hidden
doesn't work, I found another somewhat hacky solution: set visibility: hidden
one millisecond after the drag event starts.
function startDrag(e) {
setTimeout(function() {
e.target.style.visibility = "hidden";
}, 1);
}
function endDrag(e) {
setTimeout(function() {
e.target.style.visibility = "";
}, 1);
}
body {
display: inline-block;
outline: 1px solid black;
}
.draggable {
border-radius: 4px;
background: #CC0000;
width: 40px;
height: 40px;
}
<div class="draggable" draggable="true" ondragstart="startDrag(event)" ondragend="endDrag(event)">
</div>
Upvotes: 15