javascriptdrag-and-drophtml5-draggable

HTML5 native Drag and Drop : Is it possible to change the preview while dragging an element?


I would like to change the preview while dragging an element. (see setDragImage)

Like using another element if the user hit ctrl or shift for instance while dragging ?

This is an example here. This example display an image when you start the drag action. However if you want to change it on the fly it's quite chanllenging !

Is it even possible ?


Solution

  • I don think it is possible by using only the native API since the setDragImage docs begin by "When a drag occurs, ...". Invoking setDragImage on other events does not thow error but does not seems to work.

    However it is possible to implement a dynamic dragImage by using a custom sprite :

    const areas=[
      {
        dom:document.querySelector('.zone-rabbits'),
        headSprite:'🐰',
        bodySprite:'🐇',
      },
      {
        dom:document.querySelector('.zone-rats'),
        headSprite:'🐭',
        bodySprite:'🐀',
      }
    ];
    const traveller={
      current:0,
      dom: document.querySelector('.traveller')
    };
    const sprite=document.querySelector('.drag-sprite');
    const blank=document.querySelector('.drag-blank');
    
    const mouseMove=evt=>{
      // Change sprite position
      sprite.style.left=(evt.pageX-15)+'px';
      sprite.style.top=(evt.pageY-15)+'px';
    };
    
    traveller.dom.draggable = true;
    traveller.dom.ondragstart = evt => {
      // Sets blank image so we can replace it with our sprite
      evt.dataTransfer.setDragImage(blank, 0, 0);
      // Init sprite contents ,visibility and position
      sprite.innerHTML=areas[traveller.current].headSprite;
      sprite.style.display='block';
      mouseMove(evt);
    };
    traveller.dom.ondragend = evt => {
      // Hide sprite
      sprite.style.display='none';
    };
    
    areas.forEach((area,id)=>{
      area.dom.ondragover = evt => {
        evt.preventDefault();
        // Move sprite
        mouseMove(evt);
      };
      area.dom.ondragenter = evt => {
        evt.preventDefault();
        // Modify sprite
        // NB : Sprite behaviour can be changed at any
        // moment between dragstart & dragend 
        sprite.innerHTML = areas[id].headSprite;
      };
    
      area.dom.ondrop = evt => {
        evt.preventDefault();
        traveller.current=id;
        traveller.dom.innerHTML=areas[id].bodySprite;
        area.dom.appendChild(traveller.dom);
      };
    });
    .world{
      display:flex;
    }
    .zone{
      width:100px;
      height:100px;
      border:solid 1px;
    }
    .zone-rats{
      color:#00f;
      background-color:#eef;
    }
    .zone-rabbits{
      color:#f00;
      background-color:#fee;
    }
    .traveller{
      cursor:default;
    }
    .drag-sprite{
      position:fixed;
      /* drag image sprite and its contents
         must not interfere with pointer events */
      pointer-events:none;
    }
    .drag-blank{
      width:'1px';
      height:'1px';
      opacity:0;
    }
    <div class="world">
      <div class="zone zone-rabbits">
        Rabbits
        <div class="traveller">
        &#128007;
        </div>
      </div>
      <div class="zone zone-rats">
        Rats
      </div>
    </div>
    <div class="drag-sprite"></div>
    <div class="drag-blank"></div>