
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.
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.
