News

HTML5: How to implement drag and drop

Use HTML5’s native drag-and-drop functionality to create a simple, sortable image grid

draganddrop

draganddrop

Anyone who has been using a computer for any length of time is familiar, perhaps to the point of no longer even noticing, with drag and drop as a means of organising files and data.

Until the release of HTML5, implementation of similar functions within the web browser environment involved weighty jQuery plug-ins. This wasn’t an easy or pleasant option and needed a certain amount of coding knowledge to really turn the idea into a workable principle. Sure, certain content within all webpages is draggable by default. Try it on an image or and you’ll see a ghost image of the element being dragged around the page.

This will drop back into place when the mouse button is released but this is not what we are trying to recreate here. If you want the ability to be able to actually do something more functional with a draggable element, you can call on the HTML5 drag and drop API to handle it.

In this tutorial, we’ll look at some of the ways you can use HTML5’s drag-and-drop functionality. We look at how to order and manipulate images on a webpage and drag into their own specified areas.

DOWNLOAD TUTORIAL FILES

Create an image grid

Firstly we need to create an image grid. It’s entirely up to you how many images you want to include in your grid. Our tutorial grid has 20 images; five rows of four. Give each frame class a width of 25% and auto centre the containing grid. Style everything else to taste.

001    <div id=”wrap”>
002        <h1>My Image Library</h1>
003        <div id=”grid”>
004            <div class=”frame”><img src=”images
/pic1.jpg” alt=””/></div>
005            <div class=”frame”><img src=”images
/pic2.jpg” alt=””/></div>
006            <div class=”frame”><img src=”images
/pic3.jpg” alt=””/></div>
007            <div class=”frame”><img src=”images
/pic4.jpg” alt=””/></div>
008            <div class=”frame”><img src=”images
/pic5.jpg” alt=””/></div>
009            <div class=”frame”><img src=”images
/pic6.jpg” alt=””/></div>
010            <div class=”frame”><img src=”images
/pic7.jpg” alt=””/></div>
011            <div class=”frame”><img src=”images
/pic8.jpg” alt=””/></div>
012            ...
013        </div>014    </div>

The Image Library

Once you have put together your grid, complete with any other styling you wish to add, we are left with a nice looking set of images. Nice, but pretty static. Now the intention is to be able to rearrange the order of our image grid using some HTML5 magic.

Define draggable elements

Any element can be made draggable by simply setting the draggable=”true” attribute to it. This must be added to every image that we wish to make draggable. There will be no visible results just yet. The ghost image when we try to drag the element is normal and browser specific.

001    <div id=”grid”>
002        <div class=”frame”><img draggable=”true”
src=”images/pic1.jpg” alt=””/></div>
003        <div class=”frame”><img draggable=”true”
src=”images/pic2.jpg” alt=””/></div>
004        <div class=”frame”><img draggable=”true”
src=”images/pic3.jpg” alt=””/>
</div>        ...
005    </div>

Understanding the events

The drag-and-drop process involves a number of events firing in a certain order. The three main events are dragStart, dragOver and drop. These three direct the taking, moving and delivering of the draggable element and can be supplemented by dragEnter, dragLeave and dragEnd, to define behaviours during and after the drag and drop.

dragStart function

Open up a script tag and begin placing the following inside it. We begin by declaring our source element, which is the element that will be dragged and moved. The element in question will change depending on which of our images is being moved at any given point in the process.

001    <script>
002    function handleDragStart(e) {
003        // ‘source’ is the element to be dragged.
004        source = this;
005    }

dataTransfer function

The dataTransfer property dictates what action the dragging element can perform. It can be set to none, copy, copyLink, copyMove, link, linkMove or move, and move is what we want to use. The setData property controls what data from the element is carried. ‘Text’ should be set, rather than ‘html’, as an IE doesn’t work with ‘html’.

001    <script>
002    function handleDragStart(e) {
003      // ‘source’ is the element to be dragged.
004        
005        source = this;
006        e.dataTransfer.effectAllowed = ‘move’;
007        e.dataTransfer.setData
(‘text’, this.innerHTML);
008    }
009    </script>

dragOver function

Below the dragStart, we can add our initial dragOver function. We will add particular dragOver effects later in the process, but for now it’s good practice to use the property to prevent the browser from following its default behaviours, which usually results in an unwanted redirect.

001    ...
002    function handleDragOver(e) {
003      if (e.preventDefault) {
004        e.preventDefault(); 
005      }
006      return false;
007    }
008    ...

drop function

Now we can put in place the penultimate step in the first draft of our demo, the drop function. First, another default browser preventative needs to be planted. This particularly helps with Firefox, which links to the image by default. Then we swap the source data with the target data, using the dataTransfer’s getData.

001    ...
002    function handleDrop(e) {
003        if (e.preventDefault) {
004            e.preventDefault(); 
005        }
006            source.innerHTML = this.innerHTML;
007            this.innerHTML = e.dataTransfer.getData(‘text’);
008        }
009    ...

Set event listeners

Adding event listeners for our three functions, targeted to the frame classes, will get the whole thing up and running. Now you can drag each thumbnail image and place it in the space occupied by another. They will then swap places.

001    ...
002    var fras = document.querySelectorAll
(‘.frame’);
003    [].forEach.call(fras, function(fra) {
004        fra.addEventListener
(‘dragstart’, handleDragStart, false);
005        fra.addEventListener(‘dragover’, 
handleDragOver, false);
006        fra.addEventListener
(‘drop’, handleDrop, false);
007    });
008    </script>

CSS additions

Now we can start ironing out some user experience details in the CSS. Firstly, we need to ensure that users see a cursor change when they hover over our draggable elements. By placing ‘cursor:move;’ into the containing div, users will see the crosshair to indicate a moveable element. The ‘[draggable]’ elements should also be unselectable.

001    .frame {
002        ...
003        cursor: move;
004            
005    } 
006    [draggable] {
007        -moz-user-select: none;
008        -khtml-user-select: none;
009        -webkit-user-select: none;
010        user-select: none;
011    }

Adding dragOver effects

The dragOver function can add a class to whichever element the source image hovers over. The class can then be given a style in the CSS. In this case, a reduced opacity for the target. Don’t forget to add the listener to the group. For the moment, elements stay this way once hovered over.

001    .over {
002        opacity:0.1;
003    }
004     function handleDragOver(e) {
005        
006        if (e.preventDefault) {
007            e.preventDefault(); // Necessary. Allows us to drop.
008        }
009        this.classList.add(‘over’);
010        return false;
011    }
012    

dragLeave function

By adding a dragLeave function, we can ensure that any classes applied during the dragEnter are cancelled out after the hovering element has left. Without restoring the original opacity, you will leave a trail of faded images in your draggable element’s wake, although there may be a time when this effect is actually more desirable!

001    function handleDragLeave(e) {
002        this.classList.remove
(‘over’);  // this / e.target is 
previous target element.
003    }
004     fra.addEventListener
(‘dragleave’, handleDragLeave, false);
005    

Restore target opacity

It’s also a good idea to add the same class removal function to the drop function, too. Without this, the dropped element tends to retain its 0.1 opacity.

001     function handleDrop(e) {
002        if (e.preventDefault) {
003            e.preventDefault(); // Necessary. Allows us to drop.
004        }
005        this.classList.remove(‘over’); 
006    ...

Add dragStart effects

We won’t just leave the novel effects with a simple hover. You can choose when and where you add a variety of effects, starting when an element is ‘picked up’. In this case, we’ll add a CSS3 animation that will cause the source image to ‘wobble’ once it is dragged. But we’ll need to end the case, as well.

001    @-webkit-keyframes wobble  {
002        0%  { -webkit-transform:  rotate(0deg); }
003        20%  { -webkit-transform:  rotate(8deg); }
004        50%  { -webkit-transform:  rotate (-8deg); }
005        100%  { -webkit-transform:  rotate (0deg); }
006    }
007    .wobble { -webkit-animation: wobble 5s 
infinite; }
008    function handleDragStart(e) {
009        this.classList.add(‘wobble’);  
010        ...
011    }

dragEnd event

We’ll use the dragEnd function to turn off the wobble effect once a single drag-and-drop move has been completed, otherwise you could eventually have a gallery of shaking images. Once again, remember to add the Event Listener to the end of the script.

001    function handleDragEnd(e) {
002            this.classList.remove(‘wobble’);
003    }
004    fra.addEventListener (‘dragend’, handleDragEnd, false);

Custom drag image

It’s possible to replace the default ‘ghost’ image that appears when dragging an element with a custom image or icon of your own. All you need to do is add the code inside the handleDragStart function, below the setData declaration. The x and y co-ordinates set distance from the cursor.

001    var dragIcon = document.createElement (‘img’);
002    dragIcon.src = ‘images/paperplane.png’;
003    e.dataTransfer.setDragImage
(dragIcon, -5, -5);

Applications

At first glance this function may seem fun but ultimately gimmicky, however drag and drop has been an integral part of the web experience for longer than you may realise. HTML5 drag and drop is admittedly still a little rough around the edges, and very browser inconsistent, but still offers a variety of plug-in-free solutions.

×