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.

      html
      <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:

      js
      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:

      js
      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:

      js
      _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:

      js
      {
        title:      "",
        author: "",
        img:     ""
      }
      

      Now let's create the titles:

      js
      _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:

      js
      $(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:

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

      js
      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:

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