Add visual feedback

Add visual feedback when you drag something, when the mouse enters a drop zone, etc.

We can associate some CSS styling with the lifecycle of a drag and drop. This is easy to do as the drag and drop API provides many events we can listen to, and can be used on the draggable elements as well as in the drop zones:

    • dragstart: this event, which we discussed in a previous section, is used on draggable elements. We used it to get a value from the element that was dragged, and copied it onto the clipboard. It’s a good time to add some visual feedback – for example, by adding a CSS class to the draggable object.
    • dragend: this event is launched when the drag has ended (on a drop or if the user releases the mouse button outside a drop zone). In both cases, it is a best practice to reset the style of the draggable object to default.

The next screenshot shows the use of CSS styles (green background + dashed border) triggered by the start of a drag operation. As soon as the drag ends and the element is dropped, we reset the style of the dragged object to its default. The full runnable online example is a bit further down the page (it includes, in addition, visual feedback on the drop zone):

drag n drop colorful

Source code extract:

  1. <style>
  2.   .dragged {
  3.      border: 2px dashed #000;
  4.      backgroundcolor: green;
  5.   }
  6. </style>
  7.   function dragStartHandler(event) {
  8.      // Change CSS class for visual feedback
  9.      event.target.style.opacity = ‘0.4’;
  10.      event.target.classList.add(‘dragged’);
  11.      console.log(‘dragstart event, target: ‘ + event.target);
  12.      // Copy to the drag’n’drop clipboard the value of the data* attribute of the target,
  13.      // with a type “Fruits”.
  14.      event.dataTransfer.setData(“Fruit”, event.target.dataset.value);
  15.   }
  16.   function dragEndHandler(event) {
  17.      console.log(“drag end”);
  18.      // Set draggable object to default style
  19.      event.target.style.opacity = ‘1’;
  20.      event.target.classList.remove(‘dragged’);
  21.   }
  22. script>
  23. ol ondragstart=“dragStartHandler(event)” ondragend=“dragEndHandler(event)” >
  24.     li draggable=“true” datavalue=“fruit-apple”>Applesli>
  25.     li draggable=“true” datavalue=“fruit-orange”>Orangesli>
  26.     li draggable=“true” datavalue=“fruit-pear”>Pearsli>
  27. ol>

Notice at lines 12 and 24 the use of the classlist property that has been introduced with HTML5 in order to allow CSS class manipulation from JavaScript.

Other events can also be handled:

    • dragenter: usually we bind this event to the drop zone. The event occurs when a dragged object enters a drop zone. So, we could change the look of the drop zone.
    • dragleave: this event is also used in relation to the drop zone. When a dragged element leaves the drop zone (maybe the user changed his mind?), we must set the look of the drop zone back to normal.
    • dragover: this event is also generally bound to elements that correspond to a drop zone. A best practice here is to prevent the propagation of the event, and also to prevent the default behavior of the browser (i.e. if we drop an image, the default behavior is to display its full size in a new page, etc.)
    • drop: also on the drop zone. This is when we actually process the drop (get the value from the clipboard, etc). It’s also necessary to reset the look of the drop zone to default.

Complete example with visual feedback on draggable objects and the drop zone

The following example shows how to use these events in a droppable zone. 

Try it in your browser below or directly at CodePen:

Complete source code (for clarity’s sake, we put the CSS and JavaScript into a single HTML page):

  1.    
  2.      div {
  3.         height: 150px;
  4.         width: 150px;
  5.         float: left;
  6.         border: 2px solid #666666;
  7.         backgroundcolor: #ccc;
  8.         marginright: 5px;
  9.         borderradius: 10px;
  10.         boxshadow: inset 0 0 3px #000;
  11.         textalign: center;
  12.         cursor: move;
  13.      }
  14.      .dragged {
  15.         border: 2px dashed #000;
  16.         backgroundcolor: green;
  17.      }
  18.      .draggedOver {
  19.         border: 2px dashed #000;
  20.         backgroundcolor: green;
  21.      }
  22.      function dragStartHandler(event) {
  23.         // Change css class for visual feedback
  24.         event.target.style.opacity = ‘0.4’;
  25.         event.target.classList.add(‘dragged’);
  26.         console.log(‘dragstart event, target: ‘ + event.target.innerHTML);
  27.         // Copy in the drag’n’drop clipboard the value of the data* attribute of the target,
  28.         // with a type “Fruits”.
  29.         event.dataTransfer.setData(“Fruit”, event.target.dataset.value);
  30.      }
  31.      function dragEndHandler(event) {
  32.         console.log(“drag end”);
  33.         event.target.style.opacity = ‘1’;
  34.         event.target.classList.remove(‘dragged’);
  35.      }
  36.      function dragLeaveHandler(event) {
  37.         console.log(“drag leave”);
  38.         event.target.classList.remove(‘draggedOver’);
  39.      }
  40.      function dragEnterHandler(event) {
  41.         console.log(“Drag enter”);
  42.         event.target.classList.add(‘draggedOver’);
  43.      }
  44.      function dragOverHandler(event) {
  45.         //console.log(“Drag over a droppable zone”);
  46.         event.preventDefault(); // Necessary. Allows us to drop.
  47.      }
  48.      function dropHandler(event) {
  49.         console.log(‘drop event, target: ‘ + event.target);
  50.         // reset the visual look of the drop zone to default
  51.         event.target.classList.remove(‘draggedOver’);
  52.         var li = document.createElement(‘li’);
  53.         // get the data from the drag’n’drop clipboard, with a type=”Fruit”
  54.         var data = event.dataTransfer.getData(“Fruit”);
  55.         if (data == ‘fruit-apple’) {
  56.             li.textContent = ‘Apples’;
  57.         } else if (data == ‘fruit-orange’) {
  58.             li.textContent = ‘Oranges’;
  59.         } else if (data == ‘fruit-pear’) {
  60.             li.textContent = ‘Pears’;
  61.         } else {
  62.             li.textContent = ‘Unknown Fruit’;
  63.      }
  64.      // add the dropped data as a child of the list.
  65.      document.querySelector(“#droppedFruits”).appendChild(li);
  66.    }
  67.   
  68. </head>
  69. <body>
  70. <p>What fruits do you like? Try to drag an element!</p>
  71. <ol ondragstart=dragStartHandler(event) ondragend=dragEndHandler(event) >
  72.     <li draggable=“true” data-value=“fruit-apple”>Apples</li>
  73.     <li draggable=“true” data-value=“fruit-orange”>Oranges</li>
  74.     <li draggable=“true” data-value=“fruit-pear”>Pears</li>
  75. </ol>
  76.  id=“droppableZone” ondragenter=dragEnterHandler(event) ondrop=dropHandler(event)
  77.       ondragover=dragOverHandler(event) ondragleave=dragLeaveHandler(event)>
  78.       Drop your favorite fruits below:
  79.       
       id=“droppedFruits”>
  80.  
  • <body>
  • <html>