Collapsible Drag & Drop Panels Using jQuery
Aug 25th, 2009 - 50 Comments »
Posted in jQuery
Drag n Drop panels are great to let the user control how he/she wants to see the information as he can arrange various information blocks according to his preference. This type of functionality is often provided by web portals or personal homepage services like iGoogle.

Similarly, WordPress dashboard also lets user control how the various boxes are displayed.

Today, i am going to show you how to create collapsible, drag and drop panels easily using jQuery and jQuery UI libraries. Here’s what the final result will look like.
Let’s begin with it.
Include The Libraries
We’ll be using the all powerful jQuery library and the jQuery UI libraries, so grab the latest version of both and add it to your page header.
The HTML Structure
We’ll be using two vertical columns defined by div with class="column" in this example that will hold our panels. You can increase the number of panels by adjusting the width in the stylesheet for .column. Each panel is a div with class="dragbox". And the content of each panel resides within div with class="dragbox-content".
Here’s the complete HTML structure for our example, i will be using 5 panels within two columns.
<div class="column" id="column1"> <div class="dragbox" id="item1" > <h2>Handle 1</h2> <div class="dragbox-content" > <!-- Panel Content Here --> </div> </div> <div class="dragbox" id="item2" > <h2><span class="configure" ><a href="#" >Configure</a></span>Handle 2</h2> <div class="dragbox-content" > <!-- Panel Content Here --> </div> </div> <div class="dragbox" id="item3" > <h2>Handle 3</h2> <div class="dragbox-content" > <!-- Panel Content Here --> </div> </div> </div> <div class="column" id="column2" > <div class="dragbox" id="item4" > <h2>Handle 4</h2> <div class="dragbox-content" > <!-- Panel Content Here--> </div> </div> <div class="dragbox" id="item5" > <h2>Handle 5</h2> <div class="dragbox-content" > <!--Panel Content Here--> </div> </div> </div>
You can add a link to configuration of a panel within the <h2> tag using <span class="configure">. This will show up when mouse hovers over the panel header.
Update
Each column now has a unique id and every panel also has a unique id, this will help in saving the order of panels if the user changes them.
The CSS Styles
Here are the CSS styles, you can modify it as you wish but remember to adjust the width of .column in case you increase the number of columns.
.column{
width:49%;
margin-right:.5%;
min-height:300px;
background:#fff;
float:left;
}
.column .dragbox{
margin:5px 2px 20px;
background:#fff;
position:relative;
border:1px solid #ddd;
-moz-border-radius:5px;
-webkit-border-radius:5px;
}
.column .dragbox h2{
margin:0;
font-size:12px;
padding:5px;
background:#f0f0f0;
color:#000;
border-bottom:1px solid #eee;
font-family:Verdana;
cursor:move;
}
.dragbox-content{
background:#fff;
min-height:100px; margin:5px;
font-family:'Lucida Grande', Verdana; font-size:0.8em; line-height:1.5em;
}
.column .placeholder{
background: #f0f0f0;
border:1px dashed #ddd;
}
.dragbox h2.collapse{
background:#f0f0f0 url('collapse.png') no-repeat top right;
}
.dragbox h2 .configure{
font-size:11px; font-weight:normal;
margin-right:30px; float:right;
}
The JavaScript Code
To make the boxes draggable and droppable, we’ll be using the Sortables function of jQuery UI library. Here’ the code you need to use, add this to $(document).ready() function:
$('.column').sortable({
connectWith: '.column',
handle: 'h2',
cursor: 'move',
placeholder: 'placeholder',
forcePlaceholderSize: true,
opacity: 0.4,
})
.disableSelection();
Explanation: I used some options with sortable function . the first one connectWith lets you move panels across different columns. The handle defines the tag which is used to drag the panel. The placeholder parameter is the CSS class to use for the panel placeholder which is displayed when you drag a panel to show the possible position of the dragged panel. forcePlaceholderSize makes sure that placeholder size is equal to the size of the dragged panel and at last opacity sets the opacity of the panel while dragging.
Collapsing The Panels
To collapse the content of a panel when you click on the panel header and show the configure link on panel header on hover, use the below code in jQuery document ready function.
$('.dragbox').each(function(){
$(this).hover(function(){
$(this).find('h2').addClass('collapse');
}, function(){
$(this).find('h2').removeClass('collapse');
})
.find('h2').hover(function(){
$(this).find('.configure').css('visibility', 'visible');
}, function(){
$(this).find('.configure').css('visibility', 'hidden');
})
.click(function(){
$(this).siblings('.dragbox-content').toggle();
})
.end()
.find('.configure').css('visibility', 'hidden');
});
One thing you’ll notice after adding this code is that when you drag the panel, the content of the panel toggles as when you drag the click event of panel header also fires that toggles the state of content. To fix this, you need to add another parameter to sortable so that when drag is finished, state of content does not toggle.
$('.column').sortable({
connectWith: '.column',
handle: 'h2',
cursor: 'move',
placeholder: 'placeholder',
forcePlaceholderSize: true,
opacity: 0.4,
stop: function(event, ui){
$(ui.item).find('h2').click();
}
})
.disableSelection();
Here i added another parameter stop that defines the function to use when drag is complete. Once the drag is complete, click event of panel header is fired so that the original state of content is restored.
Saving State
Update (Sep 16, 2009)
Many of the commentors below asked about how to save the state of panels. I have written a follow up post on how to save state using database. Be sure to check it if you want to know.
There you are with nice multi-column collapsible drag drop panels.
Bookmark n Share
Subscribe to Full RSS Feed
If you found this article
useful, then consider subscribing to our
RSS Feed or
e-mail updates to stay updated with
latest Web Design/ Development articles. You can also follow @webdevplus on twitter for latest updates.
50 Responses (Add Your Comment)
Comments are closed for this post.



How would you save the positions of the panels?
Ben, I’ve updated the post, check it now
Very good (y) A friend of mine has been trying to do something like that for weeks. Ill pass it on
Great article! Thanks for taking the time to explain the Drag and drop panels through Jquery. I’ve been thinking about similar topics lately, and it’s good to see that I’m not alone. What do you think about paging with Jquery?
It does not seem to work in opera 10. It will move and pop up that it has been changed but move it to a weird location. Wordpress used to have this same problem a version or two ago but seemed to have fixed it sense then. Works great in the other browsers!
i haven’t tested it in opera, will look into it. Thanks!
typo in the title!
eh!! fixed it. thanks!
Hi i wanna now how u can have such a nice designed comment-Box. I want it too! :/ can u tell me how you did this?
Hi, its great article but it is not working in IE 7 but its working fine with firefox.
I think javascript is not working in the IE 7. I need it very badly, can you please tell me how to fix it
Thanks so much for the great write-up! I already had something similar to this working using the interface library, but it was having IE issues so I figured I would try this one, worked immediately… Almost. IE8 seems to only recognize the area directly over the h2 text as a handle, _until_ you drag/drop it, at which time the entire gray box becomes draggable. Weird. I’ll post again if I figure it out.
Nice one, thanks. I’ve done a similar trick with jQuery, jQuery UI and ist-ui-panel (googled recently).
Thanks for this but I also have the same problem where it does become active until you drag it once… also its seem to drag easier from top-bottom than from bottom-top.
Also how do you propose to reload the saved order (from wherever I was thinking writing that array to a cookie) on each postback?
thanks in advance
M.H.
Hi
I have been trying in vain for hours to get the data saved to a database. Any chance you can give some pointers on this area of the script please?
cheers
Mark
Refer to the Saving State section in the article above, you can pass the sortorder variable to the server side script either using GET or POST, e.g.
$.get('serversidescript', sortorder, function(){ //do something });and on the server side, you’ll have
$_GET["column1"], $_GET["column2"]etc. variables with values corresponding to panels which you can save to database.Hi
Thank you for your help. I now have a partially working script to update my database. If I could beg your help once more I would be ever grateful.
In my database I have the following rows.
id, item_id, column_id, item_order and name.
In my update.php script I have this
$col1 = $_GET['column1'];$col2 = $_GET['column2'];
$col3 = $_GET['column3'];
$col4 = $_GET['column4'];
$query = "UPDATE sg_desktop_widgets SET column_id = " . $_GET['column1']; . " WHERE item_id = 3";
mysql_query($query) or die('Error, insert query failed');
Obviously my query is completely wrong. Do you have any pointers I could follow to make this work properly
TIA
Mark
Oh big programming god,
could you inform a bit better what to do to have it saved?
Many many thanks for this script. It’s great !!!
I’m sorry to ask, but I’m so excited to get this working
Should I create a file with chmod755 on the server?
And what code do I need to add specific.
Greets from a designer, not a coder
Hi. THanks for this wonderful effect. I need to store this positions . Can you give me the sample update coding…(with explanation)… Once again thanks for this beautiful one…………….
All greetings!
My browser is Opera, it blocks the movement after he takes not a correct position.
What could be the problem, the script itself or customize css?
i did not test it in opera, but now i have seen it, looks like there are issues with working of this script in Opera
Here’s a solution to this problem, in the stop function of sortable, add this line of code,
ui.item.css({'top':'0','left':'0'});This will fix the problem in opera
Excellent work!
Thank you very much …
Hi,
I am in an unfortunate position in that I have to cater for IE6. I have noticed that in this browser the handle only works on the text of the header and also the expand / collapse arrows don’t work. Are these things that can be changed with css selectors or would they need a bigger fix?
Excellent tutorial and many thanks for sharing.
Is it possible to show us how you save the state in cookies and then retrieve the state from those cookies on postback or refersh.
Thanks again.
Hi All
I now have a working version of the database update script. I changed it from GET to POST as well. I am not a fan of GET, no idea why really
You will also need to update the JS script to send the data to the php file. see further below for the full modified JS
$val){$ind = preg_replace ('/[^\d\s]/', '',$ind); //strip non-numeric characters
$ind = (int)$ind;//make it an integer
$insertions = prep_data($ind,$val);
mysql_query("$insertions") or die('Error, insert query failed');
}
echo '> Widget position updated';
?>
Obviously you will need to change the database table and row details but all should be good. works absolutely fine for me. If you need a little more help drop me a mail and I will walk you through it.
here is the JS
// MAIN STAGE DRAGGABLE BOXES
$(function(){
$('.dragbox')
.each(function(){
$(this).hover(function(){
$(this).find('h2').addClass('collapse');
}, function(){
$(this).find('h2').removeClass('collapse');
})
.find('h2').hover(function(){
$(this).find('.configure').css('visibility', 'visible');
}, function(){
$(this).find('.configure').css('visibility', 'hidden');
})
.click(function(){
$(this).siblings('.dragbox-content').toggle();
})
.end()
.find('.configure').css('visibility', 'hidden');
});
$(document).ready(function(){function stageResponse(){
setTimeout(function(){
$("#response").removeClass("processing");
}, 2000);}
$('.column').sortable({connectWith: '.column',
handle: 'h2',
cursor: 'move',
placeholder: 'placeholder',
forcePlaceholderSize: true,
opacity: 0.4,
stop: function(event, ui){
$(ui.item).find('h2').click();
ui.item.css({'top':'0','left':'0'});
var sortorder='';
$('.column').each(function(){
var item_order=$(this).sortable('toArray');
var columnId=$(this).attr('id');
sortorder+=columnId+'='+item_order.toString()+'&';
});
/*Pass sortorder variable to server using ajax to save state*/
$.post('updateDesktop.php', sortorder, function(updateDesktopResponse){
$("#response").addClass('processing');
$("#console").fadeIn("5000").append(''+updateDesktopResponse+'');
stageResponse();
});
}
})
.disableSelection();
});
});
Thanks should go to “kratsg” from phpfreaks for his help debugging and fixing the script that saves the data.
Hi Mark, thanks for your effort!
could you please mail the code at contact@webdeveloperplus.com so that i can make it part of the post, i think there’s some problem with the code you pasted here, the php script looks incomplete, it might could have been stripped off by wordpress
I forgot to add, that script will only update which column the widget is in, it DOES NOT currently update the order in the column or remember the open/closed state
I am working on the last few things to make sure it works in every way possible.
Anybody out there have any idea how to get the widget order working?
email has been sent. thank you
Working on integrating this on my works development site, but have a problem where the original div’s will only sort in the column it is originally in.
The site uses Blueprint CSS as the framework for the layout and has 3 columns. The 2 sidebars (left and right of the content) need to be sortable and be able to move panels from 1 side to the other.
Are there any known issues with Blueprint CSS or is it another factor?
After a little bit more research multiple column sorting doesn’t work until you upgrade to jQuery 1.3.2
Unfortunately I am limited to jQuery 1.2.6, you may want to add a caveat to let people know.
hey everyone, many of you were asking about how to save the state of panels, i have gone ahead and coded the complete solution to that problem. You can check it out here.
great work!
i have a question about cross browser compatibility between firefox and ie7. in the stop function, there’s a $(ui.item).find(‘h2′).click(), which seems to be forcing the panel to open back up after dragging, i imagine because it’s being dragging is erroneously also registering a click triggering a toggle. the problem is that with that line in, it works great in firefox, but not in ie7. take the line out, and the reverse is true. is there a fix to make it work for both?
also, i’m noticing that in ie7, the first time you try to drag a panel, the handle only seems to apply to the text in the header. after the first move, then you can use the entire header as a handle. is this a known jquery issue?
thanks in advance
for the drag handle weirdness in ie7 i was mentioning earlier, an effective fix is to specify “zoom: 1″ in the css for the header. still trying to fix the issue of the click line in ’stop’ fixing the issue in firefox, but making it collapse on drag and drop in ie7.
All these issues have been fixed, you should read this – Saving State For collapsible drag drop panels
thank you, satbir, i didn’t realize the follow up code had fixes to the previous functionality unrelated to saving state
Great Work !
This work in Firefox but not in IE6,IE7,IE8 can u please help me its urgent.
I am having the same issue with IE, did you find any work around for this issue?
check out this followup article Saving State For Collapsible Drag Drop Panels, where most issues hae been fixed.
Hi this is great tutorial, just what Iam looking for.
I’m tweaking with your code and jquery for hours and Now I just have one problem how can I lock the first and or second cell in the left side side column I want it static just like in the customization layout of the blogspot.com.
any hint or help how can this be done.
many thanks
Nick Ace
1. How do I insert dynamically new widget windows… with different div Id’s??
2. Also how do i remove a particular widget if I follow your approach. ??