DevHeart Web Development jQuery: Customizable layout using drag and drop

jQuery: Customizable layout using drag and drop

jQuery: Customizable layout using drag and drop

Amazed by features like drag and drop, customizable content and AJAX? Learn how you can combine them to create a customizable website layout, saving preference using cookies.

Wanna cut to the chase? Check out the final example or download them all.

View the final example Download all examples

 

Requirements

My approach includes using jQuery along with the jQuery UI Sortable plugin for drag and drop functionality and the jQuery Cookie plugin for storing item positions.

These files are used in examples related to this article.

jQuery needs to be loaded before jQuery UI and jQuery Cookie.

<script type="text/javascript" src="jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="jquery-ui-1.8.custom.min.js"></script>
<script type="text/javascript" src="jquery.cookie.js"></script>

Note: Don't forget that JavaScript and cookies has to be enabled!

1. Getting started with sortable lists

Before we can create a customizable layout we need to be able to change the order of items.

A single sortable list

Using the Sortable plugin of jQuery UI, we can create a "sortable list", which enables you to re-order items in the list using drag and drop.

// Syntax
$('selector').sortable();
 
// Example usage
$('ul.sortable-list').sortable();

View Example

 

Sortable and connectable lists

Using the parameter connectWith (jQuery selector) we can define which other lists that items within a list can be dragged and dropped to.

// Syntax
$('selector').sortable({
    connectWith: 'selector'
});
 
// Example usage
$('ul.sortable-list').sortable({
    connectWith: 'ul.another-sortable-list'
});

View Example

 

Now this isn't exactly what you would call a customizable layout, however it is one step in the right direction. Imagine that the list was filled with sidebar items instead of plain containers.

What's left is in some way to get, save and load the item positions, but before I cover how to do that, I'll do a short demonstration of some of the other options available for Sortables.

Sortable and connectable lists with visual helper

Using a visual helper to indicate where the item will be positioned when dropped is good for usability. This is enabled using the option placeholder (CSS class).

// Syntax
$('selector').sortable({
    placeholder: 'class name'
});
 
// Example usage
$('ul.sortable-list').sortable({
    placeholder: 'my-placeholder'
});

View Example

 

Sortable and connectable lists (within containment)

Sometimes it's unnecessary to be able to drag items freely around a website, using the containment (DOM element, 'parent', 'document', 'window', or a jQuery selector) option we can choose to contrain drag within the bounds of the specified element.

// Syntax
$('selector').sortable({
    containment: 'selector'
});
 
// Example usage 1
$('ul.sortable-list').sortable({
    placeholder: '#restricted-drag-area'
});
 
// Example usage 2
$('ul.sortable-list').sortable({
    placeholder: 'parent'
});

View Example

 

2. Saving and loading items

Beeing able to customize a layout is great, however it's useless if you had to customize it all over again when you re-visit the page. We need to remember the positions of the items, this is where the jQuery Cookie plugin comes in.

Get items

First of all, we need a method to retrieve the items within a Sortable list. Because we indend to save the items in a cookie, we need a string representation of the items.

This can be achieved by combining the JavaScript function join(), which joins all elements in an array separated with a specified separator, with the .sortable('serialize') method which serializes the sortable's item id's into an array.

<ul id="my-list">
    <li id="A">Item A</li>
    <li id="B">Item B</li>
    <li id="C">Item C</li>
</ul>

var items = $('#my-list').sortable('toArray');
// items  = array('A', 'B', 'C')
 
var itemStr = items.join(',');
// itemStr = 'A,B,C'

That's nice, but we also need a way to store items when we have connected lists (columns). We can do this by looping through each list within a container and for each list its items and then join all the items together with a separator.

<div id="wrapper">
    <ul class="column sortable-list">
        <li id="A">Item A</li>
        <li id="B">Item B</li>
    </ul>
    <ul class="column sortable-list">
        <li id="C">Item C</li>
        <li id="D">Item D</li>
    </ul>
    <ul class="column sortable-list">
        <li id="E">Item E</li>
    </ul>
</div>

// Get all items from a container
function getItems(container)
{
    var columns = [];
 
    $(container+ ' ul.column').each(function(){
        columns.push($(this).sortable('toArray').join(','));
    });
 
    return columns.join('|');
}
 
var itemStr = getItems('#wrapper');
// itemStr = 'A,B|C,D|E'

Now we have the item string ready!

View Example

 

Save items

Using jQuery Cookie to get, set and delete cookies - is a piece of cake ;).

// Syntax
$.cookie('name'); // Get cookie
$.cookie('name', value); // Set cookie
$.cookie('name', null); // Delete cookie
 
// Example usage
$.cookie('items', getItems('#wrapper'));
// $.cookie('items') = 'item-A,item-B|item-C,item-D|item-E'

View Example

 

Save items automaticly

We don't want to push a "Save positions" button whenever we have changed the order of items, instead we want it to happen automaticly.

The Sortable event update, is executed each time sorting has occurred, we just have to enter a little bit of code that saves the item order to a cookie whenever this event is triggered.

// Syntax
$('selector').sortable({
    update: function(event, ui) { ... }
});
 
// Example usage
$('selector').sortable({
    update: function(){
        $.cookie('items', getItems('#wrapper'));
    }
});

Now, whenever sorting has occurred, the item positions will be saved to a cookie named "items".

Note: we don't need to include the parameters event and ui since we aren't using them.

View Example

 

Load items

Loading items is somewhat the reverse of saving items, we need to extract columns and items from our item string and then render each item.

I have separated the loading process into two functions, loadItemsFromCookie() and renderItems().

// Load items
function loadItemsFromCookie(name)
{
    if ( $.cookie(name) != null )
    {
        renderItems($.cookie(name), '#wrapper');
    }
    else
    {
        alert('No items saved in "' + name + '".');
    }
}
 
// Render items
function renderItems(items, container)
{
    var html = '';
    var columns = items.split('|');
 
    for ( var c in columns )
    {
        html += '<div class="column"><ul class="sortable-list">';
 
        if ( columns[c] != '' )
        {
            var items = columns[c].split(',');
 
            for ( var i in items )
            {
                html += '<li id="' + items[i] + '">Item ' + items[i] + '</li>
 
';
            }
        }
 
        html += '</ul></div>';
    }
 
    $('#' + container).html(html);
}
 
// Use a button to load items
$('#my-button').click(function(){
    loadItemsFromCookie('items');
});

Running this code would result in the same HTML we had before:

<div id="wrapper">
    <ul class="column sortable-list">
        <li id="A">Item A</li>
        <li id="B">Item B</li>
    </ul>
    <ul class="column sortable-list">
        <li id="C">Item C</li>
        <li id="D">Item D</li>
    </ul>
    <ul class="column sortable-list">
        <li id="E">Item E</li>
    </ul>
</div>

View Example

 

That's basically it, we now know how to sort, save and load items. This is all we need to know in order to create a customizable layout. Finally we can take our knowledge and put together and apply it to a real design.

3. Implementation in a real design

I've chosen to use a modified version of my website template Simple Organization for this example.

jQuery: Customizable layout using drag and drop

Making this design customizable is really straight forward, the only changes we have to make is to put all items that we want to cusomtize into Sortable lists and change our functions a little bit. For example we don't need to bother about columns (connected lists).

Loading content using AJAX

To make it more realistic, I have put the contents of the customizable items into separate HTML files and load them using AJAX in the rendering proccess. All we have to do is to change a bit of code in renderItems().

// Render items
function renderItems(id, itemStr)
{
    var list = $('#' + id + '-list');
    var items = itemStr.split(',')
 
    for ( var i in items )
    {
        html = '<li class="sortable-item';
 
        if ( id == 'splash' )
        {
            html += ' col3 left';
        }
        html += '" id="' + items[i] + '"><div class="loader"></div>
</li>
 
';
        list.append(html);
 
        // Load html file
        $('#' + items[i]).load('content/' + items[i] + '.html');
    }
}

The div with class loader (loading spinner image) is replaced with the contents of the HTML file as soon as the AJAX-call is complete.

View Example

 

Additional options

For the final example, I have used some options that I haven't explained before:

Code from example

// Sidebar
$('#sidebar .sortable-list').sortable({
    axis: 'y',
    containment: 'parent',
    forceHelperSize: true,
    forcePlaceholderSize: true,
    handle: '.section-title',
    opacity: 0.8,
    placeholder: 'placeholder',
    update: function(){
        $.cookie('sidebar-cookie', getItems('sidebar'));
    }
});

  • handle (jQuery selector) - defines where on an item dragging is possible.
  • axis (x/y) - defines in which direction dragging is allowed.
  • opacity (float) - opacity of the item beeing dragged.
  • forceHelperSize (boolean) - forces the helper (droppable area) to have a size.
  • forcePlaceholderSize (boolean) - forces the placeholder (visible helper) to have a size.

Learn more about these options among others in the jQuery Sortable documentation.

The End

I hope you enjoyed my article and that your have learned something. I'm new to writing articles so any feedback is very appreciated.

Good luck with creating customizable layouts!

 
 
 

Comments: 16

Feel free to share your thoughts on this article.

  • Shay

    Hey!
    Thanks for the guide, it helped me alot :)
    I’d like to ask how did you configure in the final example that a sortable will only be able to be grabbed on its top and not on the entire block?

     
     
     
    • I used the handle option, I’ll update the article describing the additional options used asap.

       
      • Thanks a lot, it works :)
        Another question – do you have any idea how to make the cookie not expire after the browser closes? I want it to stay indefidently.

         
      • Yes, you can specify expiration using a third parameter (accepts an object with properties). Example $.cookie(name, value, { expires: 999 }), this will set the cookie expire in 999 days.

         
  • James

    In Example 2.3, how do I change the code so the Cookie is loaded automatically? Without the use of a button.

     
     
     
    • You can bind a function to be executed whenever the page has loaded successfully using $(document).ready().

      So to automaticly load items from a cookie on page load, just call loadItemsFromCookie() from the function placed within the $(document).ready() event.

      Example:

      $(document).ready(function(){
      loadItemsFromCookie('cookie-name');
      })

       
  • Morteza Rostami

    Hi,

    Great and clean tutorial :-)

    Thanks.

     
     
     
  • Pranav

    This is indeed a great tutorial. Thanks a lot for this. I will surely create a POC. Will let you know if have any question.

     
     
     
  • Axinte Adrian

    This is awesome!! Thanks a lot.
    I will use this for a project ;)

     
     
     
  • Victor

    Hi. Your final example seems to work in IE7, but other examples don’t work. I tried to troubleshoot but didn’t have any clue whatsoever. Do you mind to look into it?

    This is a great work though. Thanks.

     
     
     
  • Victor

    Ok, I have found the out what’s the problem with IE.

    connectWith must be put at the very last:

    $(‘#content .sortable-list’).sortable({
    opacity: 0.7,
    placeholder: ‘placeholder’,
    connectWith: ‘#content .sortable-list’
    });

    This works now. Thanks.

     
     
     
  • Strange, these items not saved for me. Latest FF, cookies enabled

    I found another sample which working fine. http://www.daviferreira.com/blog/exemplos/draganddrop/exemplo2.html

     
     
     
  • Oh, that cool. Thanks the guide for me

     
     
     
  • tom

    thanks a lot for this, has helped me as well !!

     
     
     
  • bram

    Very cool and clear. Thank you.
    It is very helpful article.

     
     
     
  • Raj

    Thanks for sharing your code, very nice article.

     
     
     
  • Leave a Reply
     
    Your Name
     
     
     
     
     
 
© 2014 DevHeart and the authors.
Powered by WordPress | Created by Arcsin