HTML5 Drag and Drop – sorting photos between albums

HTML5 Drag and Drop – sorting photos between albums

19 162615
HTML5 Drag and Drop - sorting photos between albums

HTML5 Drag and Drop – sorting photos between albums

Today, I would like to tell you about Drag and Drop and HTML5. As you know (I hope), all modern browsers (it should be FF, Safari, Chrome, possible Opera) have native support of this useful feature (as drag and drop). It means that we can use it in our projects, the code won’t be very difficult. And, as the example of practical implementation, let assume that we have to sort some media (images) between some albums. I think that this is the one of trivial tasks in web development of different CMSs or photo websites. Today I have prepared nice example how to achieve it.

As the first, I would suggest you to download the source files and keep the demo opened in a tab for better understanding.

Live Demo
download result

So, lets start


Step 1. HTML

index.html

<div class="albums">
    <div class="album" id="drop_1" droppable="true"><h2>Album 1</h2></div>
    <div class="album" id="drop_2" droppable="true"><h2>Album 1</h2></div>
    <div class="album" id="drop_3" droppable="true"><h2>Album 3</h2></div>
</div>
<div style="clear:both"></div>
<div class="gallery">
    <a id="1" draggable="true"><img src="images/1.jpg"></a>
    <a id="2" draggable="true"><img src="images/2.jpg"></a>
    <a id="3" draggable="true"><img src="images/3.jpg"></a>
    <a id="4" draggable="true"><img src="images/4.jpg"></a>
    <a id="5" draggable="true"><img src="images/5.jpg"></a>
    <a id="6" draggable="true"><img src="images/6.jpg"></a>
    <a id="7" draggable="true"><img src="images/7.jpg"></a>
    <a id="8" draggable="true"><img src="images/8.jpg"></a>
    <a id="9" draggable="true"><img src="images/9.jpg"></a>
    <a id="10" draggable="true"><img src="images/10.jpg"></a>
    <a id="11" draggable="true"><img src="images/11.jpg"></a>
    <a id="12" draggable="true"><img src="images/12.jpg"></a>
</div>
<script src="js/main.js"></script>

You can see here three droppable objects (our virtual albums) and twelve images. I have marked droppable albums with attribute ‘droppable’, and draggable objects with attribute ‘draggable’.

Step 2. CSS

Now, its time to style our example. It possible that you have noticed that styles of our today’s lesson looks like the styles of our previous demonstration (where I described how to create pure css3 gallery). I updated those styles for today’s lesson.

css/main.css

/* Photo Gallery styles */
.gallery {
    margin: 50px auto 0;
    width: 840px;
}
.gallery a {
    display: inline-block;
    height: 135px;
    margin: 10px;
    opacity: 1;
    position: relative;
    width: 180px;
    -khtml-user-drag: element;
    /* CSS3 Prevent selections */
    -moz-user-select: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    user-select: none;
    /* CSS3 transition rules */
    -webkit-transition: all 0.5s ease;
    -moz-transition: all 0.5s ease;
    -o-transition: all 0.5s ease;
    transition: all 0.5s ease;
}
.gallery a img {
    border: 8px solid #fff;
    border-bottom: 20px solid #fff;
    cursor: pointer;
    display: block;
    height: 100%;
    left: 0px;
    position: absolute;
    top: 0px;
    width: 100%;
    z-index: 1;
    /* CSS3 Box sizing property */
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    -o-box-sizing: border-box;
    box-sizing: border-box;
    /* CSS3 transition rules */
    -webkit-transition: all 0.5s ease;
    -moz-transition: all 0.5s ease;
    -o-transition: all 0.5s ease;
    transition: all 0.5s ease;
    /* CSS3 Box Shadow */
    -moz-box-shadow: 2px 2px 4px #444;
    -webkit-box-shadow: 2px 2px 4px #444;
    -o-box-shadow: 2px 2px 4px #444;
    box-shadow: 2px 2px 4px #444;
}
/* Custom CSS3 rotate transformation */
.gallery a:nth-child(1) img {
    -moz-transform: rotate(-25deg);
    -webkit-transform: rotate(-25deg);
    transform: rotate(-25deg);
}
.gallery a:nth-child(2) img {
    -moz-transform: rotate(-20deg);
    -webkit-transform: rotate(-20deg);
    transform: rotate(-20deg);
}
.gallery a:nth-child(3) img {
    -moz-transform: rotate(-15deg);
    -webkit-transform: rotate(-15deg);
    transform: rotate(-15deg);
}
.gallery a:nth-child(4) img {
    -moz-transform: rotate(-10deg);
    -webkit-transform: rotate(-10deg);
    transform: rotate(-10deg);
}
.gallery a:nth-child(5) img {
    -moz-transform: rotate(-5deg);
    -webkit-transform: rotate(-5deg);
    transform: rotate(-5deg);
}
.gallery a:nth-child(6) img {
    -moz-transform: rotate(0deg);
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
}
.gallery a:nth-child(7) img {
    -moz-transform: rotate(5deg);
    -webkit-transform: rotate(5deg);
    transform: rotate(5deg);
}
.gallery a:nth-child(8) img {
    -moz-transform: rotate(10deg);
    -webkit-transform: rotate(10deg);
    transform: rotate(10deg);
}
.gallery a:nth-child(9) img {
    -moz-transform: rotate(15deg);
    -webkit-transform: rotate(15deg);
    transform: rotate(15deg);
}
.gallery a:nth-child(10) img {
    -moz-transform: rotate(20deg);
    -webkit-transform: rotate(20deg);
    transform: rotate(20deg);
}
.gallery a:nth-child(11) img {
    -moz-transform: rotate(25deg);
    -webkit-transform: rotate(25deg);
    transform: rotate(25deg);
}
.gallery a:nth-child(12) img {
    -moz-transform: rotate(30deg);
    -webkit-transform: rotate(30deg);
    transform: rotate(30deg);
}
.gallery a:hover img {
    z-index: 5;
    /* CSS3 transition rules */
    -webkit-transition: all 0.5s ease;
    -moz-transition: all 0.5s ease;
    -o-transition: all 0.5s ease;
    transition: all 0.5s ease;
    /* CSS3 transform rules */
    -moz-transform: rotate(0deg);
    -webkit-transform: rotate(0deg);
    -o-transform: rotate(0deg);
    transform: rotate(0deg);
}
.gallery a.hidden {
    height: 0;
    margin: 0;
    opacity: 0;
    width: 0;
}
.albums {
    margin: 40px auto 0;
    overflow: hidden;
    width: 840px;
}
.album {
    border: 3px dashed #ccc;
    float: left;
    margin: 10px;
    min-height: 100px;
    padding: 10px;
    width: 220px;
    /* CSS3 transition rules */
    -webkit-transition: all 1.0s ease;
    -moz-transition: all 1.0s ease;
    -o-transition: all 1.0s ease;
    transition: all 1.0s ease;
}
.album a {
    display: inline-block;
    height: 56px;
    margin: 15px;
    opacity: 1;
    position: relative;
    width: 75px;
    -khtml-user-drag: element;
    -webkit-user-drag: element;
    -khtml-user-select: none;
    -webkit-user-select: none;
    /* CSS3 Prevent selections */
    -moz-user-select: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    user-select: none;
    /* CSS3 transition rules */
    -webkit-transition: all 0.5s ease;
    -moz-transition: all 0.5s ease;
    -o-transition: all 0.5s ease;
    transition: all 0.5s ease;
}
.album a img {
    border: 4px solid #fff;
    border-bottom: 10px solid #fff;
    cursor: pointer;
    display: block;
    height: 100%;
    left: 0px;
    position: absolute;
    top: 0px;
    width: 100%;
    z-index: 1;
    /* CSS3 Box sizing property */
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    -o-box-sizing: border-box;
    box-sizing: border-box;
    /* CSS3 transition rules */
    -webkit-transition: all 0.5s ease;
    -moz-transition: all 0.5s ease;
    -o-transition: all 0.5s ease;
    transition: all 0.5s ease;
    /* CSS3 Box Shadow */
    -moz-box-shadow: 2px 2px 4px #444;
    -webkit-box-shadow: 2px 2px 4px #444;
    -o-box-shadow: 2px 2px 4px #444;
    box-shadow: 2px 2px 4px #444;
}

Step 3. JS

js/main.js

// add event handler
var addEvent = (function () {
  if (document.addEventListener) {
    return function (el, type, fn) {
      if (el && el.nodeName || el === window) {
        el.addEventListener(type, fn, false);
      } else if (el && el.length) {
        for (var i = 0; i < el.length; i++) {
          addEvent(el[i], type, fn);
        }
      }
    };
  } else {
    return function (el, type, fn) {
      if (el && el.nodeName || el === window) {
        el.attachEvent('on' + type, function () { return fn.call(el, window.event); });
      } else if (el && el.length) {
        for (var i = 0; i < el.length; i++) {
          addEvent(el[i], type, fn);
        }
      }
    };
  }
})();
// inner variables
var dragItems;
updateDataTransfer();
var dropAreas = document.querySelectorAll('[droppable=true]');
// preventDefault (stops the browser from redirecting off to the text)
function cancel(e) {
  if (e.preventDefault) {
    e.preventDefault();
  }
  return false;
}
// update event handlers
function updateDataTransfer() {
    dragItems = document.querySelectorAll('[draggable=true]');
    for (var i = 0; i < dragItems.length; i++) {
        addEvent(dragItems[i], 'dragstart', function (event) {
            event.dataTransfer.setData('obj_id', this.id);
            return false;
        });
    }
}
// dragover event handler
addEvent(dropAreas, 'dragover', function (event) {
    if (event.preventDefault) event.preventDefault();
    // little customization
    this.style.borderColor = "#000";
    return false;
});
// dragleave event handler
addEvent(dropAreas, 'dragleave', function (event) {
    if (event.preventDefault) event.preventDefault();
    // little customization
    this.style.borderColor = "#ccc";
    return false;
});
// dragenter event handler
addEvent(dropAreas, 'dragenter', cancel);
// drop event handler
addEvent(dropAreas, 'drop', function (event) {
    if (event.preventDefault) event.preventDefault();
    // get dropped object
    var iObj = event.dataTransfer.getData('obj_id');
    var oldObj = document.getElementById(iObj);
    // get its image src
    var oldSrc = oldObj.childNodes[0].src;
    oldObj.className += 'hidden';
    var oldThis = this;
    setTimeout(function() {
        oldObj.parentNode.removeChild(oldObj); // remove object from DOM
        // add similar object in another place
        oldThis.innerHTML += '<a id="'+iObj+'" draggable="true"><img src="'+oldSrc+'" /></a>';
        // and update event handlers
        updateDataTransfer();
        // little customization
        oldThis.style.borderColor = "#ccc";
    }, 500);
    return false;
});

As you can see – the code is not very difficult. In the beginning, the script selects all draggable and droppable elements. And, I bind ‘dragstart’ event to all draggable elements in order to setData to dataTransfer object. And, to all droppable areas I bind next events: ‘dragover’, ‘dragleave’ and ‘drop’. In case of the first two events, scripts performs small css customization to active drop area. When we drop draggable object, our script duplicates that draggable object and put it inside active droppable area (album) and remove that dropped object and finally, we update event handlers again (for new draggable objects).


Live Demo
download result

Conclusion

Thats all, today we have implemented native Drag and Drop functionality the lesson, which could be your practical task. Hope that our tutorial has helped you. Feel free to share our tutorials with your friends. Good luck!

SIMILAR ARTICLES

Understanding Closures

0 15850

19 COMMENTS

  1. I’ve tried to use the demo in Chrome and it doesn’t work. The images are not moved to the albums. I’ve also tested in Firefox and it works as expected.

    • Hello Lee,
      Are you sure? Because I have already tested this demo in Chrome too, and it works fine. What is your Chrome version? Mine is v18

  2. Hi there! Nice tutorial. Works fine. But…
    I tried to adapt your code onto a list. Works fine in FF and Safari, but Chrome…
    Your original code does work in Chrome though. I altered it step by step. When I change the .album div to ul – no prob. Altering the a to li – no prob. It stops working, when I delete the img and insert text instead. It’s not even fully draggable anymore. I have no clue why that behaviour comes up…

    Can you help me out?

    • Hello Eric,
      But please pay attention to our script, in drop event handler script takes image SRC in order to save it value and to recreate image in the code below. Check it at your customized version.

  3. Hi, Thank you for the great tutorial.

    I have a question – how would I limit the album capacity to just one image please – so that I could only put one image in each album?

    • Hello Mary,
      In your case, you should check how many objects are in active album (at JS level). Pay attention to ‘drop’ event handler. You should add new checking code there (somewhere at line 78). Before hiding of dragged image. Just find how many inner objects are in active drop area.

  4. Hi, it also does not work in Chrome for me. My version is the latest v19 – is there any workaround please?

  5. Great tutorial. Very intuitive and looks slick. I was tempted to want to pull images out of the albums and back into the main view.Is there a way to pull the image back out of the album into the library? Would I mod a function or create a new one? Thanks. Jimmy

    • Hi Simone,
      Unfortunately IE doesn’t support native Drag and Drop methods. You should use extra customized (for IE) methods to work with drag and drop

  6. Hi, your blog is awesome, congrats. II have two questions, i) how can use it on IE, maybe using some html5 elements?, not sure. ii) how can I enable image removing from the albums, not just moving to other album, just removing from the album. Also can them be sorted or even update a mysql db? Thanks, jorge

    • Hello Jorge,
      Did you read any comments? I already mentioned that it won’t work for IE browser. Because IE doesn’t support native Drag and Drop methods.
      as for removing, this is pretty easy – just add some ending with possibility to remove (maybe some image, or button, or even – text link). And yes, you can sort your images or albums if you need.

  7. Hello Andrew

    a great jobs has done
    thank you

    i have several question
    how do i collaborate it with
    edit form and then edit database in php -mysql ?
    and how to make the album still editable after the user have make the picture inside?

    • Hi Rizky,
      You can achieve it using AJAX(+JSON) interaction with the server (which uses the mySQL). Just add this interaction to all necessary places (of our JS code).

Leave a Reply to admin Cancel reply