Using MooTools 1.2 For Drag, Drop, Sort, Save

By  on  

This post has been updated: Using jQuery or MooTools For Drag, Drop, Sort, Save. The code on this page is no longer best practice.

The following is a repost of an article that ran on Script & Style a few months ago....

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
<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" alt="',$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 */
/* 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('alt')  + '|'; });
			$('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 committed. 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? Dragging and dropping is by far the fastest way to sort a list of items. What are your thoughts? Have any ideas for improvements?

Recent Features

  • By
    LightFace:  Facebook Lightbox for MooTools

    One of the web components I've always loved has been Facebook's modal dialog.  This "lightbox" isn't like others:  no dark overlay, no obnoxious animating to size, and it doesn't try to do "too much."  With Facebook's dialog in mind, I've created LightFace:  a Facebook lightbox...

  • By
    How to Create a Twitter Card

    One of my favorite social APIs was the Open Graph API adopted by Facebook.  Adding just a few META tags to each page allowed links to my article to be styled and presented the way I wanted them to, giving me a bit of control...

Incredible Demos

  • By
    Checkbox Filtering Using MooTools ElementFilter

    When I first wrote MooTools ElementFilter, I didn't think much of it. Fast forward eight months later and I've realized I've used the plugin a billion times. Hell, even one of the "big 3" search engines is using it for their maps application.

  • By
    CSS :target

    One interesting CSS pseudo selector is :target.  The target pseudo selector provides styling capabilities for an element whose ID matches the window location's hash.  Let's have a quick look at how the CSS target pseudo selector works! The HTML Assume there are any number of HTML elements with...

Discussion

  1. On FF3 the dragging animation is slow and not fluid…

  2. @nulll: Works for me.

  3. FF 3.0.4 – works fine.
    Don’t have time to go trough the whole post now, but the final example looks nice ;)

  4. Rob

    Wouldn’t it be an idea to use an ordered list, so you can use the alt property for an ID? IMHO, that’d make it even more useful.

  5. Rob

    Argh, nevermind. Misread what the alt field was already used for.

  6. Nice example, but isn’t it easier to use the native ‘serialize‘ function from the Sortables class onComplete?

  7. Works somehow, had another sort of SQL connection so had to remove the $connection in ($query, $connection) but weird thing, say I drag ‘6’ to position ‘1’ and then submit it jumps back to its former position, after that when I do it for the second time, it updates to my DB. Whats wrong? :p

  8. Hugo

    @null: Sometimes Firefox leaks memory if you have it open for too long or if you have way too many tabs open. Not sure what your computer configuration is like but perhaps there isn’t enough memory available when you’re running Firefox and trying to use drag & drop examples. But this work of art works really well! I’ve combined David’s work with some of my PHP.MySQL apps and the drag&drop sorting makes for a very great app! Cheers!

  9. With Opera works fine but with my up to date FF 3.0 on Kubuntu 8.04 is not fluid while dragging the objects…even on a new fresh launched istance of FF

  10. Hey nice effect, thanks ;)

    How can I use this if I have multiple users in a system, and I want to let them create their own list using this sorting method. I have a table called “users” with columns id, email, username and password. Is it possible to attach a “sort_list” to every email in my system?

    Thanks in advance!

  11. Thanks! Great post..

    What if I want to implement more than just one list?

    I have a site with a number of users, and each of these should be able to create their own list, from data from the same database.

    I have a table called “users” with columns; id, username, email, password. I have a table called “sources” with columns; id, name, rss. These users should be able to make their own list out of those data from the “sources”-table.

    Is it possible to throw the sort_order in a columns in the “users”-tables, so that it says 1,1|6,2|7,3|13,4 .. id 1 from sources should be placed as #1 on the list, id 6 from sources should be placed as #2 on the list, id 7 from sources should be placed as #3 etc.

  12. Rex

    Very nice!!! I’ve been looking for something exactly like this. Kudos, mate!

  13. rpflo

    Sweet, you made this much quicker for me.

    My sortables also have ajax requests in a click event. Even if I didn’t drag, simply clicking caused the onComplete of the sortables to fire, so I was constantly requesting the sorting script even when I wasn’t sorting.

    I added a variable sorted = false. Then put in:

    onSort: function(){
      sorted = true;
    }
    

    then in my onComplete I put:

    if(sorted) {
      sorted = false; // clear it out again
      //ajax request to the sorting php script
    }
    
  14. Hugo

    @rpflo: would you be willing to share you code? I am looking to use the code in this example to submit an form via ajax when there has been a change to any of the form’s input fields. I have been experimenting with different scripts but am having a difficult time figuring out how to do an ‘onChange’ type of deal versus an ‘onClick.’ I also tried the periodical stuff but I don’t want to constantly send the form via ajax…I only need the forms submitted when there’s been a change.

  15. @Hugo

    Here’s my code, but reading your question I’m not sure it has anything to do with this!
    Feel free to click my name to visit my site and get a hold of me.

    Basically I’ve got a click event on the same li’s as my sortables. When I simply clicked them I’d get two requests: the click event request (which I wanted) and the request to re-sort them (which I didn’t want unless I actually resorted them).

    // My 'click' event on the same li's that I'm making sortable later.
    $$$$('#categories li').addEvent('click',function(){
        var req = new Request.HTML({
            url: 'properties.php',
            update: $$('properties_ul'),
            data: {'category' : category},
            method: 'get',
            onRequest: function(){
                $$$$('#properties div.loading').setStyle('display','block');
            },
            onComplete: function(response) {
                $$$$('#properties div.loading').setStyle('display','');
            }
        }).send();
    });
    
    // variable to keep track of whether or not we actually sorted anything
    var sorted = false;
    
    // making the li's sortable, same elements that have the click event above
    category_sortable = new Sortables('categories_ul', {
        onStart: function(el) { 
            el.setStyles({
                'background-color':'#585858',
                'color':'#fff',
                'cursor':'move'
            });
        },
        onSort: function(){
            // if things got sorted, make it true
            sorted = true;
        },
        onComplete: function(el) {
            el.setStyles({
                'background-color': '',
                'color': '',
                'cursor': ''
            });
    
            // if things got sorted, then do do stuff, otherwise don't do anything because I didn't sort anything
            if(sorted){
                sorted = false; // set it back to false so the code is useful more than once :)
                var sort_order = '';
                $$$$('#categories_ul li').each(function(li) { sort_order = sort_order +  li.get('rel')  + '|'; });
                $$('categories_sort').set('value',sort_order)
                var req = new Request({
                    method:'post',
                    data:{'sort_order':sort_order,'list':'categories'},
                    url: 'sort.php',
                    onRequest: function() {
                        $$$$('#categories div.loading').setStyle('display','block');
                    },
                    onComplete: function() {
                        $$$$('#categories div.loading').setStyle('display','');
                    }
                }).send();
            }
        }
    });
    
  16. Hi David! There’s any way to sort the list when I read the data from the database?
    I save all the div positions into a database, now I want to create a form with that order.
    Thanks in advanced.

  17. rpflo

    @John

    In your table you should have a field called ‘sort_order’. In your select query write:

    $$query = “SELECT stuff FROM yourTable ORDER BY sort_order ASC”;

  18. @rpflo yes, I have something like that in my db, but this is my problem: I’m using this to sort colors, the people can drag & drop colors and save into a database under his user, but when they try to edit I need to put all the colors in the same order than they save. There’s an option in the javascript code to set those values from the db? Something like an “initial order”

    Thanks in advanced.

  19. @John

    but when they try to edit I need to put all the colors in the same order than they save.

    I’m lost … If you’re saying you need two orders, have two fields, sort_order and edit_order.

    But again, I’m lost. Not sure why you’d want to allow somebody to sort things like this and then not want it to sort?

  20. This demo looks really cool, I’ve been looking for something like this (physically available) for a while.
    As an aside, have you seen the Great Big Massive demo? The guy claims to use mootools aswell and his demos show it inserting objects before, after and EVEN swapping. Looks ace, I tried to get his code but he says its secret until release. Pffft :(
    Worth a look! –> http://www.youtube.com/watch?v=pvvmqP89lb8

    If you get any code, gimme some :D~~~

  21. Zoran

    Hey David, this is really cool, i am new at javascript libraries, so i have problem with this tutorial, cause i don’t know which Moo-Tools library to include.
    I checked their website and the Sortables class is contained in the …more.js plugin and addEvent method is in the core, so how i can include all of the used classes and methods into one js file. I tried including both, the more and the core, but it doesn’t work for me…
    Thank you in advance.
    Cheers!!!

  22. Zoran

    No need of replay, i got it work, thanks anyway…
    I think seriously to start using MooTools over others libraries, maybe include Jquery too.

  23. Zoran

    Hey David, again me… I remade your task here, so you can drag and drop the items and the order gets updated in the hidden input and when the user is ready, i added a button to update the database via ajax. I am using bit of OOP PHP to print the list dynamic on each update. I was wondering if i could post it here, cause i was looking for something similar online, but never found it, so if someone finds it useful can take it from here.
    If anybody wants the zip file, you can email me at u4zoran2002@yahoo.com
    Thanks again for this useful example David.

  24. guillem

    Is there a way to save the modifications into a cookie set by mootools (hash.cookie)?
    How can we do it with your piece of code?

    Thanks a lot!

  25. Hi there,

    I’m kinda new in this kind of stuff!! Never the less, i’ve managed to create a sortable div structure with your script. You can see it when you click on my website… (http://www.unikoop.into-cms.nl/into-cms/frontpage/index.php?dragable=true). The left column works like a charm (no update in a database yet, but that will come later on). My main question here is, can I use several sortables on one page similar like the one i’ve created? I want the user to create the order by replacing the items. So the order of all the columns on the site needs to be sortable. Is this possible with this script? Or is there an other script that can handle something like this. I’ve searched the web for all kinds of solutions, but your script was the one that answered my needs for this site…

    I hope to here from you soon!!

    Greets,
    Douwe

  26. Brent

    I can’t figure out how to ‘plugin’ my db within the PHP above… as in i’m not seeing how above, the PHP knows what db to get the table info from! Doesn’t the php need to address (username/pwd)?

  27. hi all., Do you know what is this line for? specially this php code “”

    “<input type=”hidden” name=”sort_order” id=”sort_order” value=”” />”

    Co’z im using Smarty and Php.,

    thanks you all!

  28. Hitesh

    its really good but can u please help me
    i find it not working when i put mootools-core-1.2 with it

  29. Can we use this to swap instead sorting?
    Great post indeed!

  30. I’ve built facebook like photo order sorting stuffs by using this example – you can see the details here
    http://kodegeek.wordpress.com/2009/07/22/facebook-like-photo-order-sorting-in-mootools/

    Thnx David!

  31. David,

    Do you have any suggestions for nested sortables? I am going insane!

  32. @ Jumanji

    It is putting turning the array of integers into a pipe delimited string. You should assign that string value to smarty and output it instead.

  33. fedeisas

    Hi! Awesome piece of code. I’m not a php/ajax pro, so this is giving me a headache: I have a list of items order from 1 to infinite, but the system keeps updating the order giving 0 to the first element. How can I modify the script to start in 1 ??

    Thanks a lot!

  34. Alexander

    Great code,

    Already implemented it.

    However, i have found a problem maybe you can help me with this.

    If you add a text input inside the LI tag in Firefox you wont be able to change ths value of it, in IE6 it can be done.

    I tried to add an event on every textbox so when the user clicks on it it selects all the text inside it, but this is not happening.

    I guess some code from the Sortables class is preventing firing the event.

    Regards,
    Alex

  35. phpnoob

    Can you write a version that is using jquery? Looking forward to your next awesome tutorial. You are my javascript teacher, thank you, arigatogozaimasu, kamsahamdina :)

  36. Brian

    is this compatible with 1.2.3
    I’m having no luck, then again I could be missing something besides fixing the $, $$ issue

  37. Brian

    i don’t think its compatible with 1.2.1 – 1.2.3 actually

  38. BG

    on FF3 works like a charm for me…

  39. @BG

    They are talking about the MooTools version number, not a browser.

  40. yasin

    Can someone upoad a folder where all the Demofiles are in? With : The PHP / XHTML, The CSS, The MooTools Javascript…

  41. bayard baudoin

    I too have had no luck getting this code to work, and it seems to have nothing to do with mootools, and something with the php? perhaps its something I’m missing with
    I query the database? Seems very useful though, something I’ve been trying to solve for a while. Posting the files would be of great help.
    -bay

  42. bayard baudoin

    I too have had no luck getting this code to work, and it seems to have nothing to do with mootools, and something with the php? perhaps its something I’m missing with
    I query the database? Seems very useful though, something I’ve been trying to solve for a while.
    -bay

  43. John Doish

    Man, you just don’t check ANYTHING in IE, do you?

    I appreciate your resources… however… don’t pollute the web with half working scripts.

  44. John Doish: Works for me in IE6, IE7, and IE8. How fitting that your last name is so close to “douche”!

  45. David, didn’t you make this post back in your unenlightened Windows/IE days anyway? Your screenshot reeks of windows font rendering.

  46. Ed Kelly

    I’ve used this code in many of my projects. Are you sure it works in IE 8, with compatibility view off? For me (using your demo) it loses the sortable item’s container styling when dragging. It would be great if there was a workaround for that.

  47. MK

    Hey, I figured out why styling is lost when dragging in IE 8. The problem is with css, so I changed following style

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

    to

    #sortable-list li{ padding:4px 8px; color:#000; cursor:move; list-style:none; width:500px; background:#ddd; margin:10px 0; border:1px solid #999; }

    It worked for me with compatibility view on/off.

  48. MrGroovy

    Alexander said: “If you add a text input inside the LI tag in Firefox you wont be able to change ths value of it, in IE6 it can be done.”

    Having the same problem, anyone knows how to fix it ?

  49. Is there anyone who can help me getting this worked with 2 columns…. I can’t get it worked….?

    Please contact me, thanks A LOT!!

  50. elron

    it isnt let me drag!
    can you give a full page, not slices?
    thank you

  51. joe

    @Chris: did you find the reason for the missbehaviour thats every thing works fine only after the second time?

  52. It should be great if you tell us what components are included in the “more” library, so that we can rebuild it

  53. Gabriel

    Hi,
    thanks to David for the code :)
    For who have to use input text inside sortable item, use handle option:
    var mySortables = new Sortables(‘#list-1, #list-2′, {
    constrain: true,
    clone: false,
    revert: true,
    handle:’whatever’
    });
    – handle – (string: defaults to false) A selector to select an element inside each sortable item to be used as the handle for sorting that item. If no match is found, the element is used as its own handle.
    bye

  54. Faiz

    Somebody please help me
    i get the error

    Notice: Undefined variable: message in sort-save.php on line 185
    Waiting for sortation submission…

    while the coding on that line is

    Waiting for sortation submission…

    Thanx

  55. Faiz

    Waiting for sortation submission…

  56. Faiz

    @Faiz: Waiting for sortation submission…

  57. Faiz

    the coding is in div message box.. when am pasting it here its not showin in the comments.. sorry for the duplicate lines.. plz help

  58. faceless

    Thank you very much for the tutorial, this helped me a lot and saved some time :)

    @Faiz: try wrapping changing the echo $message to if(isset($message)){echo $message; }

  59. Andy

    I appreciate the tutorial. I’m wondering: how do you customize this for each visitor?

    Right now if I sort something, it gets saved to the database, but this ‘sortation’ is then applied for all users who view it. In your opinion, what would be a suitable way to make it so that for each user they see their own custom sorting?

  60. xin

    great tutorial. but i have one problem, I was implementing drag/drop to one new project. and the problem occurs when drag a element from main view and drop it to a div that has fixed position on the bottom of browser’s window. All elements that are not behind the fixed div could be dropped on this div, any other elements that need to use browser’s scroll bar to view couldn’t be dropped. The draggable element that behind the fixed div somehow ignores the div as drop zone.

    the problem only occurs when using fixed position

    Any ideas?

  61. Mark

    Hi Guys,

    Wondered if I could get a little help. I just cannot get this code to update the database. Infact I cannot get it to interact with the url location at all.

    I use a piece of Ajax on the page before thats works fine

    window.addEvent('domready', function(){
      $('trackForm').addEvent('submit', function(e){
        e.stop();                      
        var req = new Request.HTML({
          url: '/campaigns/createtracklistajax',
          data: $('trackForm'),
          update: $('trackList') 
        }).send();                    
      });
    });
    

    BUT the code on this page appears to work as the list sorts when I drag and drop it, it states that the database has been updated by the onSuccess function, but it appears to not interact with the controller/action. I cant understand how it reports success when its obviously not succeeding.

    A bit of a headache this!

    Many thanks

    Mark

  62. Hi David,
    Sorry to bug you in the middle of the gunners game – when you get a moment could you let me know if your moo sortables script can be used for a dynamic amount of UL’s
    and if so, would you have a small implementation of it as I just can’t get any more than the first

    any help would be v.greatfuly appreciated.
    pSouper

  63. AngelEyez

    Is there any way to get the browser to scroll with this? I’m using it to sort a gallery and when there are more thumbnails than will fit on the screen, I need the browser to automatically scroll when dragging the thumbnail. Thanks!

  64. That pretty much explains the logic behind all the pieces of this Sortable List example. I know it’s not very flashy, but the purpose of this example was to illustrate some of the cool things that can be done with a Sortable List and when they will be executed.

  65. Jan

    Can please someone explain how to create the correct MySql Table?
    Thanks !!

  66. pSouper

    for this example it simply needs a table called “test_table” with two fields: id and sort_order

  67. redmike

    Hi David! I have problem on using mootools on my site – it destroys my layout. Is it because I also run jquery?

  68. ken mc grath

    Hi Dave

    Im really sorry to bother you but i could really do with your help.
    Im trying to make a simple list sortable by drap and drop but the darn thing wont work for me.

    If you could help me out i would be very grateful as it has held me up the last few days.

    I can send you my index file if you can help me out.
    I have used this
    http://jqueryui.com/demos/sortable/
    but it wont work for me :-(

    Much appreciated
    Ken

  69. Great example,
    @redmike if you use both of jQuery and MooTools, you have to use jQuery.noConflict and wrap your jQuery code in

    (function($){
    
    })(jQuery);
    

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!