Using MooTools 1.2 For Drag, Drop, Sort, Save

My customers love being able to control their website’s content so I build a lot of administrative control into their website. One administrative control I frequently build is a News control. I allow the customer to add, edit, delete, and sort news items. My customers especially love sorting their articles because of the fashion of which they can sort: drag and drop. Here’s how I do it.

The MySQL Table

id title sort_order
1 Article 1 1
2 Article 2 2
3 Article 3 3
4 Article 4 4
5 Article 5 5
6 Article 6 6

My news table contains more fields but these are the important fields per this example.

The PHP / XHTML

<?php
	$query = 'SELECT id, title FROM test_table ORDER BY sort_order ASC';
	$result = mysql_query($query,$connection) or die(mysql_error().': '.$query);
	if(mysql_num_rows($result)) {
?>
<p>Drag and drop the elements below.  The database gets updated on every drop.</p>

<div id="message-box"><?php echo $message; ?> Waiting for sortation submission...</div>

<form id="dd-form" action="<?php echo $_SERVER['REQUEST_URI']; ?>" method="post">
<p><input type="checkbox" value="1" name="auto_submit" id="auto_submit" <?php if($_POST['auto_submit']) { echo 'checked="checked"'; } ?> /> <label for="auto_submit">Automatically submit on drop event</label></p>

<ul id="sortable-list">
	<?php
		$sort_order = array();
		while($item = mysql_fetch_assoc($result))
		{
			echo '<li class="sortme" rel="',$item['id'],'">',$item['title'],'</li>';
			$sort_order[] = $item['sort_order'];
		}
	?>
</ul>
<br />
<input type="hidden" name="sort_order" id="sort_order" value="<?php echo implode($sort_order,'|'); ?>" />
<input type="submit" name="do_submit" value="Submit Sortation" class="button" />
</form>
<?php } else { ?>
	<p>Sorry!  There are no items in the system.</p>
<?php } ?>

We query the database to get every news item. What’s extremely important is that the query sorts the items by their original sort order. We set the “rel” attribute equal to the article’s ID and the list item’s text to the article title.

The CSS

#sortable-list				{ padding:0; }
li.sortme 		 			{ padding:4px 8px; color:#000; cursor:move; list-style:none; width:500px; background:#ddd; margin:10px 0; border:1px solid #999; }
#message-box				{ background:#fffea1; border:2px solid #fc0; padding:4px 8px; margin:0 0 14px 0; width:500px; }

I use the above CSS to format the news items so that the customer knows each news item may be dragged. None of the CSS is essential to this system.

The MooTools Javascript

/* when the DOM is ready */
window.addEvent('domready', function() {
	/* create sortables */
	var sb = new Sortables('sortable-list', {
		/* set options */
		clone:true,
		revert: true,
		/* initialization stuff here */
		initialize: function() { 

		},
		/* once an item is selected */
		onStart: function(el) {
			el.setStyle('background','#add8e6');
		},
		/* when a drag is complete */
		onComplete: function(el) {
			el.setStyle('background','#ddd');
			//build a string of the order
			var sort_order = '';
			$$('#sortable-list li').each(function(li) { sort_order = sort_order +  li.get('rel')  + '|'; });
			$('sort_order').value = sort_order;

			//autosubmit if the checkbox says to
			if($('auto_submit').checked) {
				//do an ajax request
				var req = new Request({
					url:'',
					method:'post',
					autoCancel:true,
					data:'sort_order=' + sort_order + '&ajax=' + $('auto_submit').checked + '&do_submit=1&byajax=1',
					onRequest: function() {
						$('message-box').set('text','Updating the sort order in the database.');
					},
					onSuccess: function() {
						$('message-box').set('text','Database has been updated.');
					}
				}).send();
			}
		}
	});
});

We use Moo 1.2’s Sortables plugin class to select all element within the list and make them sortable (drag and drop). Every time the sort order is changed, the hidden “sort_order” element is built and reset using a “|” as a separator. If the checkbox is checked, an ajax call is made to update the sort order in the database. Otherwise, the regular form submission via submit button will also save the sortation.

The “Header” PHP / MySQL

/* on form submission */
if(isset($_POST['do_submit']))
{
	/* split the value of the sortation */
	$ids = explode('|',$_POST['sort_order']);

	/* run the update query for each id */
	foreach($ids as $index=>$id)
	{
		if($id != '')
		{
			$query = 'UPDATE test_table SET sort_order = '.$index.' WHERE id = '.$id;
			$result = mysql_query($query,$connection) or die(mysql_error().': '.$query);
		}
	}

	/* now what? */
	if($_POST['byajax']) { die(); } else { $message = 'Sortation has been saved.'; }
}

The header is where the new sort order is commited. We split the “sort_order” form value by the “|” and perform a query for each item to update its order. Lastly, if the “byajax” flag is sent, we just die out the PHP script — if not, we continue to load the page.

Hot system, right? Check out the example. Dragging and dropping is by far the fastest way to sort a list of items. What are your thoughts? Have any ideas for improvements?

Comments

this is EXACTLY what i was looking for, thanks.

justin | August 6th, 2008 @ 10:24 pm

Extremely helpful. I was trying to accomplish smth similiar to what u did and definitely i couldnt have done it without this guide. Thanks.

joni | August 7th, 2008 @ 6:03 am

To simplify things, one should use store the article id in the list item’s id instead of rel, then use sb.serialize() to quickly and easily get the order.

Mike | August 14th, 2008 @ 7:51 pm

Thats really cool! This might be a little cheeky but is there any way of having multiple lists on the same page (not being able to move out of the list but within their own lists)

Cody | August 22nd, 2008 @ 1:36 am

Nice Script. Are there any source scripts that need to be loaded?
Like

Leven W | August 23rd, 2008 @ 5:07 pm

Everything works fine “oh yeah” script needed was mootools.js, lol
Problem - when i grab an item from the list. it grabs every item and moves them. Any ideas.
Thanks in advance.

Leven W | August 23rd, 2008 @ 6:03 pm

where does $connection come into play?
I get msq query error with that in statement. $result = mysql_query($query,$connection) or die(mysql_error().’: ‘.$query);

Leven W | August 23rd, 2008 @ 9:18 pm

in other words. What does $connection link to?

Leven W | August 23rd, 2008 @ 9:19 pm

OK. I have everything working.
But I have a problem.
When I make a change in my list then save it, it doesnt save to database.
But. If I make the same change again, then save again. It does update.
Any ideas as to what be going on here.

Leven W | August 24th, 2008 @ 8:44 am

@Leven: Do you have an example URL to provide?

admin | August 26th, 2008 @ 4:57 am

Hi,

Thanks for this code - you have a slight bug however:

$sort_order[] = $item['sort_order'];

should rather be:

$sort_order[] = $item['id'];

The sort_order value that gets submitted should contain the id’s of the rows rather than the order in which they appear as we get the order dynamically in the foreach loop.

Your code works in your example only because your sort_order and id fields match.

Regards
Adriaan

Adriaan | August 27th, 2008 @ 5:56 am

thanks a lot for the code. was just what i was looking for.

saurab | August 27th, 2008 @ 7:38 pm

Hello:

Great script! I’ve gotten the example to work on my server. However, I can’t seem to make it update when using the checkbox or the submit button. Can I send you my code? Thank you!

Hugo | September 10th, 2008 @ 12:12 pm

can i use this with excerpts too?

mitza | September 10th, 2008 @ 12:55 pm

If anyone has gotten this script to work with regard to updating the list can you please get in touch? My email address is yvhv9848@gmail.com

I would greatly appreciate any help. I have the script up and running but I doubt this comment box will allow code. Send me an email and I will send you my working code. Everything works just as the example except for the updating part. Neither the checkbox or the submit button update the list on the database. Thank you in advance for any help!

Hugo | September 10th, 2008 @ 3:30 pm

Nice tutorial and code snippets. Searched Google for sortable lists and yours was the best I found. Thanks!

Nathanael | September 11th, 2008 @ 10:40 am

Great example!! Love it. I’m having trouble getting it to auto save to my database though. Any thoughts?

Scott | September 19th, 2008 @ 11:30 am

Damn, I ‘ve been looking this for months!! Thx a lot!!

saludos desde Argentina! :D

facundo | September 21st, 2008 @ 10:58 am

could you post an example with blocks of items organized by category where you could drag an item from one category to another, like Basecamp do?

tks in advance by your post! :-)

Glad | September 24th, 2008 @ 7:49 am

hello, this is very useful stuff! thanks for sharing!

Olli | September 25th, 2008 @ 3:08 am

Are we sure this works as far as the code on this page? I know the tutorial works but so far I have not been able to get this to work and I have gone through it three times from start to finish.

Is Adriann right about the $sort_order[] = $item['sort_order']; issue?

Stephen | September 26th, 2008 @ 9:24 pm

The script works like a charm! Thanx.

Yes, Adriann is right about the little change.

Also you should insert the right url in the ajax request to be able to change sort order with ajax..

var req = new Request({
url:’!HERE YOUR SUBMIT URL!’,
method:’post’, …….

AlfaOne | October 4th, 2008 @ 10:30 am

How would I use this with images stored in a database?

Forrest | October 8th, 2008 @ 1:06 pm

Great work mate.

rubel | October 9th, 2008 @ 2:29 am

I can’t get it to update either (via checkbox or submit). I also noticed the checkbox doesn’t work in the demo.

dude | October 22nd, 2008 @ 2:23 am

Great script - however - I cannot get the dragging to work at all.

http://neoscapefilms.com/film/demo/sort-save.php

Any help?

malaki1974 | October 22nd, 2008 @ 7:31 am

@malaki1974, have you viewed your source? Your form is above your DOCTYPE. It goes in the body.

dude | October 22nd, 2008 @ 8:55 am

Figured it out - my main moo.js had the wrong name. But like (most) everyone else - updating not working. Can anyone confirm it worked and if so post the right syntax for the update?

malaki1974 | October 22nd, 2008 @ 9:02 am

AHHHH, I found the other bug. change: rel to: alt in the while loop.

So bugs are:

change: rel to: alt

and the one mentioned above:

change: $sort_order[] = $item['sort_order'];

to: $sort_order[] = $item['id'];

$disorder[] = $item['id'];

dude | October 22nd, 2008 @ 9:30 am

whoops, disregard the last line, $disorder[]… that’s my code that I didn’t strip out of the post, sorry about that.

dude | October 22nd, 2008 @ 9:31 am

@malaki1974, yes, I have it working with the submit button, I haven’t got the the auto_update working yet though. See the two bugs mentioned above.

dude | October 22nd, 2008 @ 10:06 am

Alright, I’m crazy, the auto_update is working for me. I do however see an issue I’ll have with this script (maybe you folks too). Once the sort_order goes through the array, it begins with zero, not 1. For me this throws off an auto increment counter I have in another table. The fix here would be to change the array to base-1 (rather than base-0 the default), I’ve yet to accomplish this though.

dude | October 22nd, 2008 @ 9:46 pm

Alright, I got this worked out now. The answer to the problem I mentioned above where the value of the sorted sort_order beginning with zero rather than 1 is this:

$newindex = $index +1;
$query = ‘UPDATE test_table SET sort_order = ‘.$newindex.’ WHERE id = ‘.$id;

So now the sorted sort_order begins with 1 like it was prior to being sorted.

dude | October 23rd, 2008 @ 12:04 am

Ah, one last note. The reason I thought the auto_submit wasn’t working is because I was refreshing the page to see if it had worked. When using the auto_submit function the changes are discarded (actually reversed) after a page refresh. This is also why I thought it wasn’t working in the demo.

dude | October 23rd, 2008 @ 1:23 am

Alright - mine is working also. New question: How to add more fields from the database? My table has 14 columns and I would like to see them all instead of the one I have currently. Any ideas?

malaki1974 | October 23rd, 2008 @ 6:10 am

$query = ‘SELECT * FROM test_table ORDER BY sort_order ASC’;

the * means all, but know this, by selecting all when you don’t need all, you’re slowing your query down. So always be conservative about what columns you select.

dude | October 23rd, 2008 @ 12:53 pm

Note, neither “rel” or “alt” are valid attributes of elements. To use this and remain valid you could switch the li.get(attribute) to “title”.

You’d switch this in two spots…

One in the JS:

$$(’#sortable-list li’).each(function(li) { sort_order = sort_order + li.get(’title’) + ‘|’; });

And the other in the PHP while loop:

echo ”,$item['title'],”;

dude | October 24th, 2008 @ 2:42 am

Whoops… *Note, neither “rel” or “alt” are valid attributes of “” elements.

dude | October 24th, 2008 @ 2:44 am

Oh well, it’s cutting code out of my posts…

I WAS saying… LI tags do not have alt or rel attributes, use title instead (if you’re interested in having valid code).

dude | October 24th, 2008 @ 2:47 am

The “style” attribute is another option for the LI attribute instead of “title” or the invalid “rel” or “alt”. This is the attribute I’m using because using the title might confuse users. Just be sure to change it in both the PHP and JS.

dude | October 29th, 2008 @ 8:48 pm

Great Post thank you . But ı am looking for multi row drag drop saving ?

Murat | November 4th, 2008 @ 5:09 am

I stand corrected on my last post. I just tested my sorting again and it wasn’t saving. Apparently I didn’t test it since I changed the attribute to “style”. Silly me, I forgot that JavaScript CSS overrides inline CSS… so by using the style attribute the value being passed via AJAX was the JS style, not the inline style value. So, I guess to make this valid you either need to use the title attribute or write your own DTD for “rel” or “alt” on LI elements.

dude | November 12th, 2008 @ 1:49 am

Well, been checking your bug updates.
Works now for me, but only automaticly.
I dont care bout submit then :p needed auto only.

Chris | November 23rd, 2008 @ 6:50 am

Wow! This is GREATEST I ever found!
Thanks!

I found this link fisrt at:
http://davidwalsh.name/mootools-drag-drop
and they only repost WRONG code.
It waste so much my time.
Thank you guys for the debug.
Any one need images drag-n-drop, let me know.

Joe | December 5th, 2008 @ 11:27 pm

can you send the complete code for image drag n drop..thnk u n advance!..

johny | January 22nd, 2009 @ 12:39 am

Hi,
Greate work but can you post the project for other programming language users.
is it posible?

Resul | January 30th, 2009 @ 12:31 am