Saving a reordered list

Update 05/11: version 0.2 of the DHTML Library has a helper function to “serialize” the order of a list:

ToolMan.junkdrawer().serializeList(myList)

It returns a string representation of the list as described in the remainder of this post.


Tim Erfle and several others have asked a question like the following:

Is there anyway to report the order of the list after it has been sorted by the user? Its a super cool script, but it would be helpful if store the user modified order in the database.

Because the sorting is done by rearranging elements in the DOM, all you have to do is iterate over the parent element’s children to extract the new order. For example:

function foo(listID) {
    var list = document.getElementById(listID);
    var items = list.getElementsByTagName("li");
    var itemsString = "";
    for (var i = 0; i < items.length; i++) {
        if (itemsString.length > 0) itemsString += ":";
        itemsString += items[i].innerHTML;
    }
    alert(itemsString);
}

To use this in a form submit replace alert(itemsString) with something like myHiddenInput.value = itemsString. Or you could stuff itemsString in a cookie or pass it back to the server with XMLHttpRequest. The above example uses items[i].innerHTML which works fine so long as your list items are plain text. A better approach is to give each LI element a unique identifier. You can use the ID attribute or — if you’re not concerned about valid XHTML — invent a new attribute to use. A list would look something like this:

<ul>
    <li listID="325"><a href="...">Copper</a></li>
    <li listID="27"><a href="...">Gold</a></li>
    <li listID="131"><a href="...">Gold</a></li>
    ...
</ul>

And use a script like this to extract the order:

function foo(listID, formInput) {
    var list = document.getElementById(listID);
    var items = list.getElementsByTagName("li");
    var itemIDs = "";
    for (var i = 0; i < items.length; i++) {
        if (itemIDs.length > 0) itemIDs += ":";
        itemIDs += items[i].getAttribute("listID");
    }
    // do something with itemIDs
}

39 Responses to “Saving a reordered list”

  1. April 18th, 2005 | 4:09 pm

    Hi Tim… wonderfull script!, but, i want to see an example using this script in a button saving the order…

  2. April 19th, 2005 | 9:01 am

    The next update to my examples may include an demonstration of saving the order.

  3. April 27th, 2005 | 2:39 am

    The new version of DnD Sortable Lists now demonstrates the technique I described for saving a reordered list. All the example lists remember their order (via cookies).

  4. matt55
    May 9th, 2005 | 4:50 am

    Hi Tim I’m building a form in PHP and I want to use your wonderful “drag & drop”. My form sends datas in a SQL database.

    But I don’t know how save the result of the sort.

    Can You help me ?

    Thanks for helping

    Matt

  5. May 9th, 2005 | 1:44 pm

    Matt,

    You need to use a hidden HTML input and JavaScript to fill in the serialized version of the list when the form is submitted.

    This requires that the user still clicks a “Save” button. If you want the reordered lists saved automatically you need to look at something like Ajax.

  6. matt55
    May 10th, 2005 | 8:02 am

    Thanks for this answer. Unfortunately I did not succeed in doing what you propose to me.

    Is it possible to register the result of the list in a field and then that changes automatically when the user made of the modifications ?

    Thanks for helping if you have time.

    Matt

  7. May 11th, 2005 | 2:33 pm

    Matt,

    There are two basic possibilities here. The first step, though, is to write a javascript function that saves a text version of the current order of the elements, like this:

    function saveOrder(listId) {
        var list = document.getElementById(listId);
        var items = list.getElementsByTagName('li');
        var ids = '';
        var hiddeninput = document.getElementById('myhiddenfield'); // the input field storing the order
    
        for (var i = 0; i &lt; items.length; i++)  {
            if (i &gt; 0) ids += '|'; 
            ids += id;
        }
    
        hiddeninput.value = ids;
        return true;
    }
    

    Then choose whether you want to calculate the new order at the end of a drag event (which is probably unnecessary if you’re not using ajax) or just right before you submit the form. If you want it to get calculated before form submission, add an event handler to your tag, as in:

    <form action="..." method="..." onsubmit="return saveOrder('myListsId');">
    

    where “myListsId” corresponds to the “id” attribute of the unordered list. You can then explode() the value in PHP into an array, and iterate over the array to save the order.

  8. May 11th, 2005 | 2:37 pm

    Ack. Some things were mis-auto-translated there by wordpress.

  9. May 13th, 2005 | 1:58 am

    Sorry the post contained code.

    Hi,

    This is a very good function. But when I send my request to the database, results of ordered list are not write in the base.

    I have no error message with my PHP script.

    Someone understands what it is wrong ?

    Here is my code.

    Javascript function:

    function saveOrderList(listId) {
        var list = document.getElementById(listId);
        var items = list.getElementsByTagName('li');
        var ids = '';
        var hiddeninput = document.getElementById('q2'); // the input field storing the order
    
        for (var i = 0; i < items.length; i++)  {
          if (i > 0) ids += '|';
          var id = items[i].getAttribute("itemID");  
          ids += id;
        }
    
        hiddeninput.value = ids;
    
        return true;
    }       
    

    My hidden input:

    <input type="hidden" name="q1">
    

    My submit :

    <form method="post" action="question1.php" onsubmit="return saveOrderList('phoneticlong');">
    

    My list:

    <ul id="phoneticlong" class="boxy">
        <li itemID="a">a</li>
        <li itemID="b">b</li>
        <li itemID="c">c</li>
        <li itemID="d">d</li>   
    </ul>
    

    My php script:

    <?php
    mysql_connect("localhost", "root", "");
    mysql_select_db("database");
    error_reporting(E_ALL ^ E_NOTICE);
    $q1 = mysql_escape_string($_POST['q1']);  
    mysql_query("INSERT INTO question1 VALUES('','$q1')");
    mysql_close();
    ?> 
    

    I hope it works and that you can help me. The Javascript function have been modified by Antares.

    Thanks Matt

  10. May 23rd, 2005 | 10:29 pm

    Looks like either WordPress or the Markdown plug-in is insisting on backslash escaping things in code blocks. Works fine for posts, but not for comments. Hmm…

  11. junado
    June 23rd, 2005 | 11:28 am

    I don’t know if I’m too late, but I’m using the tool-man library for reorganising a list in a user specified order. Had to add a little function in the core.js file to do what I wanted, but here’s how it works.

    Function added to core.js writeListOrder : function(id, dest) { var list = ToolMan.junkdrawer().serializeList(document.getElementById(id)) document.getElementById(dest).value = list },

    Button to save changes: “

    As you must have guessed, numeric is my list’s id, while liste is an hidden input field where I write the ordered list.

    Each item in my list can be disabled/enabled on the main page with a checkbox named checkbox# where # correspond to an increasing integer to identify each field and is set as the unique id of my item in the database (using a loop to load all the items). Then using PHP to update the table, I do this:

    if ($action == "sauvegarder") { $i = -1; $ordre = explode("|", $_POST['liste']); foreach($ordre as $num) { $i += 1; if ($_POST['checkbox'.$num] != 1) { $check = 0; } else { $check = 1; } $sql = "UPDATE artisteici SET ordre='".$i."', actif='".$check."' WHERE numero='".$num."'"; $save = mysql_query($sql) or die(mysql_error()); } }

    My MySQL table uses a column called order where I enter the order took from the list. Therefore, when my page is loaded (the one the user views), I sort my results using the “order” column. I hope I made myself clear enough, if you have any question be sure to ask.

  12. newbee
    August 4th, 2005 | 2:06 pm

    Hi ! I saw your script it a very nice one.

    I am trying to use your script to get the reordered values and save it using PHP.

    Could you show me how to do it step by step?

    Thanks

  13. losmurfs
    August 26th, 2005 | 1:03 pm

    Given:

      One
      Two
      Three
    

    How do I return the li bodies instead of the li ids? That is to say One|Two|Three instead of 1|2|3?

  14. losmurfs
    August 26th, 2005 | 1:06 pm

    In the above, replace the code sample with this:

    &lt;ul id="aList"&gt;
      &lt;li id="1"&gt;One&lt;/li&gt;
      &lt;li id="2"&gt;Two&lt;/li&gt;
      &lt;li id="3"&gt;Three&lt;/li&gt;
    &lt;/ul&gt;
    
  15. losmurfs
    August 26th, 2005 | 1:07 pm

    I give up, I’m sure you’ll know what I mean.

  16. October 1st, 2005 | 12:41 am

    Hey, I don’t know if I’m too late with this, but I know all the hype is built up around AJAX, but if you guys want a simpler implementation regardless of what server-side language why not just use some simple Remote Calling. There is a beautiful example over at 15 seconds by Rajesh Toleti. http://www.15seconds.com/issue/050922.htm Take a look at that, it’s written in .net, but i was able to write it in standard asp within a few minutes. If anyone needs some help just shoot me an email, alan@alancece.com

  17. March 22nd, 2006 | 5:07 pm

    I copy the code to my server and everything runs perfectly except the order is not saved. Has anyone else had this problem?

    I have tried the other examples posted and continue to get errors on the following line:

    var items = list.getElementsByTagName(”li”);

    Any help would be appreciated…

  18. March 23rd, 2006 | 9:12 am

    I did figure it out somewhat… The save works in IE and not Firefox. I was using Firefox and wasn’t seeing the save happening…

    -Greg

  19. March 24th, 2006 | 10:28 am

    I have adapted what has been recommended here to prevent the need for the user to click a form button to make the update. Whenever the user finishes dragging an item, the database is updated. I admit that if the users wants to update every item it will use lots of queries, but at the end of the day, it is nicer for the user!

    I needed to let users sort a bunch of links (their favourites on my site), so in the database each favourite had a position stored with it, so I can display the list in the right order without having to the functionality provided by tool-man (and I use these positions to calculate the most popular favourite across all users etc…)

    To do this, I edited the ‘dragsort.js’ file, and added a call to a function in the onDragEnd function: _onDragEnd : function(dragEvent) { ToolMan.coordinates().create(0, 0).reposition(dragEvent.group.element) updateDB(); } The function updateDB() creates the string of orders as mentioned above (x|y|z): function updateDB() { var list = document.getElementById(’draggable’); var items = list.getElementsByTagName(’li’); var ids = ‘’; for (var i = 0; i 0) ids += ‘|’; var id = items[i].getAttribute(’heading’); ids += id; } if (document.getElementById(’favorder’).value != ids) { document.getElementById(’jsupdater’).src = ‘/js_tracker.php?updateorder=’ + ids; document.getElementById(’favorder’).value = ids; }

    return true;
    

    } The function relies on there being a hidden input field named favorder, and the UL has id=”draggable”. Also, an image with 0px width and height, and id is “js_updater”. This is purely relevant for my site, and could be generalised by passing in IDs…

    So, when the order of the list is changed, the picture’s source is changed to /js_tracker.php?updateorder=x|y|z

    The jstracker file is a PHP script that interfaces with the database: $order = explode(”|”, $GET[updateorder]); if (strlen($order[0]) > 0) { foreach ($order as $position => $id) { $position++; mysql_query(”UPDATE SET position = $position WHERE = $id”); } }

    Hope this helps someone!

  20. tech_india
    April 12th, 2006 | 3:30 am

    Hi, I am looking for using this concept in my web site. But the different thing is that i am not using “li” but i have “div’s” and in these “div’s” i have multiple table’s. I need to shuffle these “div’s” and not the tables. Then i need to store the name of the div’s and there current order in DB and when the page loads next time i want that after retreving the order of div’s from DB i should be able to render it on the page. the table’s in each “div” contains data that is generated dynamically. Can anyone help me on this?

    Thanks

  21. tech_india
    April 12th, 2006 | 3:32 am

    Hi, Please mail me the solution of above query at sachin316@yahoo.com

    Thanks

  22. tech_india
    April 12th, 2006 | 3:34 am

    Hi, The ID is “sachin316@yahoo.com”

    Thanks,

  23. digitalpacman
    May 8th, 2006 | 8:24 pm

    Hello. I was wondering if you would be interested in working with me in order to enhance your software to add the functionality to be able to drag items into sub categories. I’ve been working on it, it seems tough, and am seeking assistance. What I mean is instead of inserting before or after, you’d be inserting inside a new element. For the most part I have alot of it down, but its extremely buggy.

    Let me know if you’re interested. I’m posting this where ever I can find an input box so you can see it.

  24. pmcelhaney
    July 13th, 2006 | 2:14 pm

    If all you want is to save the order when a form is submitted, put a hidden input field inside the list item itself.

      United States
    
    
    
      United Kingdom
    

    Now there’s no Javascript or non-standard HTML. Just post the form; the countryCodes will be posted in the order in which you left them.

    Patrick

  25. scriptfury
    July 28th, 2006 | 1:05 pm

    Hi, first time caller long time listener.

    I’d like to say I am a big fan of the tool-man script, absolutely bang-up job.

    That being said I would like to ask a question about the ordering and arrangement of the 2d lists and I was hoping that you could answer it for me.

    Given that the example arranges the elements left to right top to bottom, like so…

    1 2 3 4 5 6 7 8 9

    Is it possible to arrange the list top to bottom left to right like this?

    1 4 7 2 5 8 3 6 9

    I know by changing the float property to right I can arrange it like this…

    3 2 1 6 5 4 9 8 7

    But I don’t think that CSS any other defined floats (except for none).

    Secondly I noticed that the lists always snap to the RHS of any table cell they are in is there away to center them?

    Thank you for your time, I hope my questions are not to elementary. Once again big fan.

    SF

  26. Ghost
    September 4th, 2006 | 3:24 am

    Using matt55’s example code shown on 5/13/2005, I got the same results… Only I changed the hidden input field name to match from q2 to q1.

    Like grguth on 3/23/2006, it works fine in Internet Explorer, but fails in Firefox.

    Any Ideas? I’m running Firefox 1.5.0.6…

    Thanks,

    -Ghost

  27. Ghost
    September 4th, 2006 | 11:56 am

    My problem is that when I use a hidden input field to send the data to PHP, it works in IE, but not in Firefox.

    I’m using the following code:

    function saveOrderList(listId) { var list = document.getElementById(listId); var items = list.getElementsByTagName(’li’); var ids = ‘’; var hiddeninput = document.getElementById(’listarray’); // the input field storing the order

      for (var i = 0; i  0) ids += '|';
        var id = items[i].getAttribute("itemID");
        ids += id;
      }
    
      hiddeninput.value = ids;
    
      return true;
    

    }

    alpha
    

    bravo charlie delta echo foxtrot golf hotel india juliet kilo lima mike november oscar papa quebec romeo sierra tango uniform victor whiskey xray yankee zulu

    The form submits to:

    \n”; ?>

    Internet Explorer v6.0.2900 displays: hidden value: o|b|c|d|g|i|j|k|l|m|w|y|t|x|z|s|p|h|n|q|v|f|u|a|e|r

    While Firefox v1.5.0.6 displays: hidden value:

    Any help would be greatly appreciated.

  28. Ghost
    September 4th, 2006 | 11:58 am

    Opps, my code example got trashed…

  29. Ghost
    September 4th, 2006 | 12:23 pm

    My problem is that when I use a hidden input field to send the data to PHP, it works in IE, but not in Firefox.

    I’m using the following code:

    function saveOrderList(listId) {
        var list = document.getElementById(listId);
        var items = list.getElementsByTagName('li');
        var ids = '';
        var hiddeninput = document.getElementById('listarray'); 
    
        for (var i = 0; i  0) ids += '|';
          var id = items[i].getAttribute("itemID");
          ids += id;
        }
    
        hiddeninput.value = ids;
    
        return true;
    }
    
    
    
    
      alpha
     bravo
     charlie
     delta
     echo
     foxtrot
     golf
     hotel
     india
     juliet
     kilo
     lima
     mike
     november
     oscar
     papa
     quebec
     romeo
     sierra
     tango
     uniform
     victor
     whiskey
     xray
     yankee
     zulu
    

    The form submits to:

     \n";
    ?&gt;
    

    Internet Explorer v6.0.2900 displays: hidden value: o|b|c|d|g|i|j|k|l|m|w|y|t|x|z|s|p|h|n|q|v|f|u|a|e|r

    While Firefox v1.5.0.6 displays: hidden value:

    I would really appreciate any help you can provide…

  30. September 16th, 2006 | 9:58 am

    Try this one. It work both FF & IE….

    Javascript Function : function post(){ list_var = junkdrawer.serializeList(phoneticlong) document.order2.order.value = list_var order2.submit() }

    In Body : `

    apple bud clock down extra

    `

    In reorder.php : "; for($x=0;$x"; } ?&gt;

    Hope it help… :)

    Thanks

  31. September 16th, 2006 | 11:05 am

    Duh! Click Here! =)

  32. oto
    November 30th, 2006 | 10:40 am

    I have applied some of the information found here to try saving locations of entire moving divs (not restricted to lists). The method was quite successful. Here are the basics in case you’re interested:

    1. I create movable divs with handles.
    2. Using javascript, I use the ‘window.onmouseup’ event to do the following: get the ‘document.getElementById(”boxHandle”).style.left’ & the ‘document.getElementById(”boxHandle”).style.top’ properties.
    3. I then insert these properties in an array which is then inserted into an invisible form element.
    4. I used an asynchronous xmlhttp query to submit this array to a php file. The php file treats the array as a string.
    5. This php file serializes the data and puts it in a text file.

    This is all that is required to save the boxHandle location to a text file. Every time a user moves a box, the new location is saved transparently.

    To load the information: 1. When the main page is loaded or refreshed, php code in the header downloads the data from the text file and deserializes it. 2. In the body of the document, php code inserts the deserialized data in another invisible form field.
    3. The javascript ‘window.onload’ event is used to get the string from the invisible form element and the ‘document.control.loadedLocations.value.split(”,”)’ method is used to transform it back into an array. 4. The array is then used to set the ’style.left’ and ’style.top’ properties of the box.

    The use of an array allows to have as many boxes as you like (only limited by size of xmlhttp query). This is the first time that I use javascript so this might not be the best way to do things (but it works!).

  33. intensivex
    December 7th, 2006 | 11:53 am

    Hello oto,

    I’ve been racking my brain for 2 weeks trying to figure out what you’ve just accomplished … impressive …

    If i’m reading your comments accuratly, youve managed to capture the positions of the dragged elements and save these positions to file …

    Question … would you be so kind as to provide me with a working sample of what youve just done? At this point I’m willing to donate a few bucks to save myself more time. (Please!)

    Thanks in advance …

    Respectfully,

    Chris chrisATintensivexDOTcom

  34. oto
    December 14th, 2006 | 10:00 am

    Sure, no problem. I’d rather not publish it on this blog. Email me mark@otomotion.com

    Mark

  35. LizardMan
    December 28th, 2006 | 5:12 pm

    I’ve integrated the code into something I’m working on and it works quite nicely, the only trouble is that I’ve got a button which will add a new item to the bottom of the list. How do I get new item to register and update the constraints?

    I’ve tried and tried, but either the new item doesn’t work, or I get the new item working and the other items break.

    Any chance of a “rehash()” or “addNew()” function?

  36. director
    March 12th, 2007 | 4:06 pm

    I would like to user your simplest one, two, three list reordering. For newbies, would it be possible for you to create a page with just this example and the code it uses.

    Your examples are very impressive, but having them all in one file makes it a little harder to get started.

    Thanks

  37. Cel
    May 6th, 2007 | 7:52 am

    I have a problem at http://research.psychol.cam.ac.uk/~kl278/experiments/recruitment.php under the heading Favourite Foods : I’m using the script to sort a list of items by drag and drop and it works fine on its own.

    The issue arises when I wish to submit the data in the listboxes and textboxes within the sortable list to a MySQL database to be recorded. Namely, the data that client enters into these listboxes and textboxes are passed through only for those entries that are not sorted within the list by drag and drop i.e. as soon as the user has drag and dropped one of the items, the data user has entered is not recorded in the database for that item - and it doesn’t matter whether the user enters the data before or after the drag and drop. And to be more specific, the data is actually recorded if you just move the list item around on spot (without dragging it across any items), but as soon as you move it across, even without release (drop) and come back to the original location, data is not recorded. In other words it seems that the ‘drag across’ event in the sortable list script seems to erase the user input or seems unable to pass it through form submit … I hope this description is understandable. How do I get all data to pass through, irrespective of how much the user sorts the list ?

    Many thanks !

    p.s. I’m using Form Tools to record the submitted data in my MySQL database (http://www.formtools.org/) and I believe there should be no problems on that side.

  38. […] streamofconsciousness » Saving a reordered list […]

  39. mjabon
    February 14th, 2008 | 5:35 pm

    Hello. I have a question that I would really like help on. I’m trying to understand how you do the repositioning - it seems you use relative position for the list items, but is the table absolutely positioned? I would ideally like to make lists sortable with no other information than the relative positions of the list elements, but I find that when I drag the elements the mouse cursor jumps off the element and also the whole table moves to compensate for the moving element….

Leave a reply

You must be logged in to post a comment.