julesbou
julesbou

Reputation: 5780

html5 draggable hide original element

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

Answers (7)

PaulCrp
PaulCrp

Reputation: 982

How to hide your element

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.

When to hide it

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.

Code exemple

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

arielhad
arielhad

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

Filipe
Filipe

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

Jason Yin
Jason Yin

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

didxga
didxga

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

Michel Jansson
Michel Jansson

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

Hatchet
Hatchet

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

Related Questions