Collapsible Drag & Drop Panels Using 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 connect With 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. force, Place, holder, and Size 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

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.