In this tutorial we'll create a grid of CD covers, that we can rearrange by dragging and dropping them. We'll use the great sortable jQuery UI plugin.
See the end result below. Drag and drop the covers to different positions to make new arrangements. Notice how the list on the right gets updated.
Covers
HTML
The markup is quite simple.
<div class="container cf">
<div class="content">
<h2>Covers</h2>
<ul id="titles"></ul>
</div>
<ul class="discs cf" id="discs"></ul>
</div>
The Sortable plugin only needs a list of elements wrapped within another element. A list seems perfect for this. The grid effect is achieved by floating the list items.
In our example, we are placing the list within the discs
element, and we'll build its children via JavaScript.
JavaScript
Let's call our function Covers
and write its constructor so that it accepts a list of CDs:
var Covers = function(data) {
this._data = data;
this._init();
};
The _init
method will set up the original state of our covers. Let's write that as part of Covers
prototype:
Covers.prototype = {
_init: function() {
var self = this;
this._covers = $('#discs');
this._titles = $('#titles');
this._createCovers();
this._createTitles(this._data);
}
};
We cache the DOM elements for covers and titles and then inject the markup. First the covers:
_createCovers: function() {
var covers = [];
for (var i = 0; i < this._data.length; i++) {
var disc = this._data[i];
covers.push(this._createCover(disc, i));
}
this._covers.html(covers.join(''));
},
_createCover: function(disc, i) {
return '<li id="disc-' + i + '"><img src="' + disc.img + '" alt="' + disc.title + '"/></li>';
}
The CD data are made of an array of objects with the following structure:
{
title: "",
author: "",
img: ""
}
Now let's create the titles:
_createTitles: function(data) {
var titles = [];
for (var i = 0; i < data.length; i++) {
titles.push(this._createTitle(data[i]));
}
this._titles.html(titles.join(''));
},
_createTitle: function(disc) {
return '<li>' + disc.title + ' - ' + disc.author + '</li>';
}
Time to instantiate our Covers
class, and we'll do that within a document ready function provided by jQuery:
$(function() {
new Covers([
{ title: "Title", author: "Meghan Trainor", img: "http://ecx.images-amazon.com/images/I/61gLDt2PU%2BL.jpg" },
{ title: "Shadows In The Night", author: "Bob Dylan", img: "http://ecx.images-amazon.com/images/I/51FdrYnOC2L.jpg" },
{ title: "The Firewatcher's Daughter", author: "Brandi Carlile", img: "http://ecx.images-amazon.com/images/I/612sEmlEjjL.jpg" },
{ title: "What a Terrible World, What a Beautiful World", author: "The Decemberists", img: "http://ecx.images-amazon.com/images/I/61yMEEq8YXL.jpg" },
{ title: "Rebel Heart", author: "Madonna", img: "http://ecx.images-amazon.com/images/I/518-GYTUTRL.jpg" },
{ title: "Wallflower", author: "Diana Krall", img: "http://ecx.images-amazon.com/images/I/51hH8ze6UpL.jpg" },
{ title: "Smoke and Mirrors", author: "Imagine Dragons", img: "http://ecx.images-amazon.com/images/I/514No6jBDuL.jpg" },
{ title: "Reflection", author: "Fifth Harmony", img: "http://ecx.images-amazon.com/images/I/51pETxGnE6L.jpg" }
]);
});
(The CD data are taken from amazon.com).
CSS
Let's add some styles to make our list look like a grid:
.cf:before, .cf:after { content:""; display:table; }
.cf:after { clear:both; }
.cf { zoom:1; }
.container {
max-width: 60em;
margin: 3em auto;
}
.content {
float: left;
width: 25%;
}
.content h2 {
font-size: 1em;
font-weight: normal;
text-transform: uppercase;
margin-bottom: 1em;
}
.content ol {
margin-right: 1em;
padding-left: 1em;
}
.content li {
padding-bottom: .5em;
}
.discs {
float: left;
width: 75%;
}
.discs li {
width: 150px;
height: 150px;
display: block;
cursor: move;
float: left;
margin: 0 2px 2px 0;
}
.discs img {
width: 100%;
}
Making the grid sortable
The Sortable plugin makes this very easy. We'll append the code below to the _init
function:
this._covers.sortable({
revert: true,
stop: function() { self._sort(); }
});
The revert
option adds animation to the cover when it is reverted back to its place. You can also pass it an integer, and the plugin will interpret it as the animation duration.
We have also added a handler to the stop
event, which is triggered when dragging stops. The handler will sort the titles on the left:
_sort: function() {
var ids = this._covers.sortable('toArray'),
data = [];
for (var i = 0; i < ids.length; i++) {
var id = parseInt(ids[i].replace('disc-', ''));
data.push(this._data[id]);
}
this._createTitles(data);
}
Here we take advantage of the toArray
method of Sortable, which returns the ids of all the grid items. Earlier we stored the data index in the list item element in the form of disc-[i]
, where i
is the data index. This allows us to extract the index later and create a new data array with the correct titles order.
Check out complete code on github.