Drag and Drop Reordering of Database fields with Scriptaculous.

August 11th, 2006 by Tim Leave a reply »

Rather than go into immense detail about why you would want to do this, let me present two real world examples of projects I have recently completed that have benefited from this kind of functionality:

  1. An ecommerce site where the site owner wanted to arrange categories and products in a more logical order (e.g popularity) as opposed to alphabetically.
  2. A real estate site where the site owner required the ability to upload multiple images of a property and then reorder them.


As always I will be using PHP as the server-side component to this recipe, but I aim to keep it as out-of-depth to enable an easy transition to the server-side language of your choice. Right, let’s get started.

Ingredients:

  • The latest scriptaculous libraries.
  • A database and scripting language of your choice (PHP / MySQL is used here).
  • A text editor.

Step One: Create the database:

Create a database as normal to hold whatever fields you require. Add an extra field called “position”, that will hold an integer, to your database; this will hold the position of the items but should contain a default value of 0 for when a new item is added. This means new items will go to the top of the list when we output them later on.

Here is my database schema for this example.

  • id => Integer, Auto Increment, Primary Key.
  • item => Varchar(30)
  • position => Integer

Now insert some data in there so we can write our re-ordering script.

StepTwo: The (X)HTML:

  1. Fire up your favourite HTML editor and create a new document.
  2. Include the following scripts from the scriptaculous libraries:
    • prototype.js
    • scriptaculous.js
  3. Output your database records into an unordered list with and id of “item_list”. Each list item should have an id of “item_{id}” where {id} is substituted by the auto increment of that item on the database. Don’t forget to add a condition of “ORDER BY position ASC” to your SQL so the records come back in the correct order once we have updated their positions.

Here is a rough idea of how your list should look:

<ul id="item_list">
<li id="item_1">Item One</li>
<li id="item_2">Item Two</li>
<li id="item_3">Item Three</li>
<li id="item_4">Item Four</li>
</ul>

Once you have yourself a nice list that looks like that, you start adding some drag ‘n drop magic.

Step Three: Introduce the Drag and Drop functionality

Our first pass at the JavaScript doesn’t involve any Ajax. Here we just want to make our list items draggable. Here is the code we need that must go anywhwere below the list. I put it immediately after my list normally so it is easy to locate for debugging. Here is the code:

<script type="text/javascript">
Sortable.create('item_list', {constraint:'vertical'});
</script>

We are calling on the magic inside the scriptaculous libraries to create a new sortable list with the function Sortable.create(). The first argument must be the id of the list we want to reorder. Other options are contained within the curly brackets. Here we are just telling the list that we only want items to move vertically – i.e. up and down.

Step Four: Saving the new order to the database.

This step is a little more involved so make sure that you have got everything up to this point working perfectly. You will probably want to spend a little time basking in the joy of drag and drop lists before you go any further, anyway!

Ok, here is a summary, followed by the details, of what we have to do to get the order of our records saved to the database

  1. Make our script call a secondary function when an item is dropped.
  2. Make this secondary function post the new order of items to a background PHP script
  3. Write that background PHP script to take the new order and update the database.

Ready?

Firstly, let’s tell our Sortable.create function what to do when an item is dropped. This is done with an option in those curly brackets we mentioned earlier. Change your Sortable.create function to look like this (the bold, italic part is the addition).

Sortable.create('item_list', {constraint:'vertical', onUpdate : updateOrder});

This tells Scriptaculous that we want to call our own JavaScript function called updateOrder whenever an item in our list is dropped.

Secondly we need to create that updateOrder function. It goes above our Sortable.create, in the same script block, and looks like this:

function updateOrder(){
var options = {
method : 'post',
parameters : Sortable.serialize('item_list')
};
new Ajax.Request('server_items_reorder.php', options);
}

Let’s take a quick under-the-bonnet look at that function. It is easier to start from the bottom up.

The penultimate line creates a new Ajax request to a PHP script called server_items_reorder.php. It also takes an argument of some options.

The next block of code up creates these options. It tells our Ajax request to serialize the list items in the current order they are in and then send them to the php script using POST as the method. The serialization creates an array in the form of [0]=>{id}, [1]=>{id}, [2]=>{id}. If you reall want to know, scriptaculous reads the list in its current order and strips the item_ from the id of each list item. This leaves us with an array of ids that reflect the order of our list.

The end is now in site. All we need is the PHP script (or any other language of your choice) that takes these post variables and updates the database. Here you are:

<?php
// ADD YOUR DATABASE CONNECTION PARAMETERS HERE!!!
$i=1;
foreach($_POST['item_list'] as $key=>$value) {
mysql_query("UPDATE items SET position='".$i."' WHERE id ='".$value."'");
$i++;
}
?>

Here is what is happening in our php script:

  • We set $i to 1. This variable increments to create the position on our database.
  • We loop through the array posted by our Ajax call, updating the position field on the current record to the value of $i. At each pass, the value of $i is increased.

Now when you reorder your items within the list and refresh your page, they will stay exactly where you left them. Why not try add some css and get your items looking sexier, or create a list of images that can be reordered. There are also a few options available including one that lets you create a handle that is thepart of your list item that you can drag by. Take a look at the Sortable.create wiki entry for a comprehensive list of items.

As always, if you find any errata in this piece, speak up and post a comment and I will fix it up good ‘n proper.

Troubleshooting

I hope that you are as impatient when it comes to reading troubleshooting guides as I am when writing them. So here goes:

  • Ensure you included the scriptaculous libraries and in the correct order.
  • Double check the source code to make sure your list has the correct structure.
  • Check that you are referring to the correct list id in your Sortable.create call and Ajax.request options.
  • Check you are calling the right script in your Ajax.request call
  • Make sure that you are connecting to your database correctly in the server side script.

If all else fails, post a comment here and I will try to help.

Share it:
  • Digg
  • del.icio.us
  • Facebook
  • StumbleUpon
  • LinkedIn
  • Reddit
  • Netvibes
  • Twitthis
  • email
Advertisement

27 comments

  1. Ed Couchman says:

    This is the first real introduction to prototype and script.aculo.us that I have seen. Even though it isn’t aimed at proto-beginners I found it really easy to follow (your writing style was clear and concise) and have implemented this in a project I am working on.

    Thanks a lot for this.

  2. daelan says:

    This is without a doubt the easiest to follow script.aculo.us tutorial that i have come across. Very nice work

  3. Tom Kerswill says:

    This is brilliant – looks so simple. Looking forward to implementing the steps soon…

  4. JJ says:

    Hi,

    I have tried to do this with image tags, but it doesn’t seem to work. I don’ think it’s even sending anything to my php file to update the database:

    CODE:

    Fashdax | Your Fashion Connection

    ‘.”\n”;
    }
    ?>

    function updateOrder(){
    var options = {
    method : ‘post’,
    parameters : Sortable.serialize(’imageFloatContainer’)
    };
    new Ajax.Request(’http://www.fashdax.com/gallery_update.php’, options);
    }
    Sortable.create(’imageFloatContainer’, {tag:’img’,overlap:’horizontal’,constraint: false, onUpdate : updateOrder});

  5. Abraham Andres Luna says:

    thank you for your tutorial. it has set me on the right track to creating the killer app i need. i know you use php, but would you happen to know how to loop through the items using asp.net?

    thanks for your help,
    abe

  6. Tim says:

    @abe – Not sure about the ASP.NET solution as I am not a .NET developer. However I expect in ASP the array would be contained within Request(”item_list”).

    Here is also a link with a good deal of information about loops in ASP.NET; VB etc. If you figure it out post it here then we can all take a look:

    http://www.informit.com/articles/article.asp?p=30110&seqNum=6&rl=1

  7. Tim says:

    @JJ – After you build the options in updateOrder():

    var options = {
    method : “post”,
    parameters : Sortable.serialize(”imageFloatContainer”)
    };

    add this line for debugging the serialized string:

    alert(options.parameters)

    If this doesn’t alert a string of your item_list then the problem is in your Sortable.create is wrong. If it does it is your server script.

  8. carl says:

    hi,

    this is my code

    function updateOrder(){
    var options = {
    method : ‘post’,
    parameters : Sortable.serialize(’item_list’)
    };

    new Ajax.Request(’server_items_reorder.php’, options);
    }
    Sortable.create(’item_list’, {constraint:’vertical’, onUpdate : updateOrder});

    is this all correct?
    as it is not updating my table

    this is my code in the server_items_reorder.php file, with my connection parameters above.
    $value) {
    mysql_query (”UPDATE jobimages SET jobimagesOrder=’”.$i.”‘ WHERE id =’”.$value.”‘”);
    $i++;
    }

    any help please

    thanks
    carl
    ?>

  9. Srinivas says:

    Hi
    I have tried your example and its working properly but when i have used it for my scripts it is raising js error called “Sortable is not Defined”. The whole error msg says like “Sortable is not defined
    comparables()()comparables (line 268)
    [Break on this error] Sortable.create(’item_list’,{tag:’tr’,…cal’, onUpdate : updateOrder,tree:true})”

    What is the Possible Reason For this ?
    Srinivas.

  10. Stephen says:

    I’m trying to use this in an app I’m writing but cannot get the drag & drop part to work.

    The two .js files are being included correctly.

    But I’m not getting any drag & drop functionality.

    [code]

    Item One
    Item Two
    Item Three
    Item Four

    Sortable.create('item_list', {constraint:'vertical'});

    [/code]

    Any ideas?

  11. Stephen says:

    I’m trying to use this in an app I’m writing but cannot get the drag & drop part to work.

    The two .js files are being included correctly.

    But I’m not getting any drag & drop functionality.

    Item One
    Item Two
    Item Three
    Item Four

    Sortable.create(’item_list’, {constraint:’vertical’});

    Any ideas?

  12. Stephen says:

    Solved that problem. But I’ve got another quick question.

    I’ve added a save button and moved the function that calls the reorder script to the onclick event for that button.

    I’ve also added a reset button. which currently works by simply refreshing the page. – onClick=”location.reload(true)”

    However, there must be a way to reset the item order to how it was when the page first loaded without having to reload the whole page again?

    Any idea how to do it?

  13. Nick says:

    Hi, this is my code

    Untitled Document

    <li id=”item_” style=”cursor:move”>

    function updateOrder(){
    var options = {
    method : ‘post’,
    parameters : Sortable.serialize(’item_list’)
    };
    new Ajax.Request(’server_items_reorder.php’, options);
    }

    Sortable.create(’item_list’, {constraint:’vertical’, onUpdate:updateOrder});

    This is my php code

    // ADD YOUR DATABASE CONNECTION PARAMETERS HERE!!!
    include (”connect.inc.php”);
    $i=1;
    foreach($_POST['item_list'] as $key=>$value) {
    mysql_query(”UPDATE items SET position=’”.$i.”‘ WHERE id =’”.$value.”‘”) or die(”UPDATE items SET position=’”.$i.”‘ WHERE id =’”.$value.”‘”);
    $i++;
    }

    The problem I have is that the updateOrder function does not fire the php script. The table in fact doesn’t update. I have checked the code time and time but cannot see any mistakes…

    Can you help me…?
    Thanks a lot in advance

  14. Nick says:

    My code clearer ..sorry

    Nicola

    Barbara

    Maria

    Ada

    Giovanni

    Paolo

    Gennaro

    Rossella

    function updateOrder(){
    var options = {
    method : ‘post’,
    parameters : Sortable.serialize(’item_list’)
    };
    new Ajax.Request(’server_items_reordersasaasa.php’, options);
    }

    Sortable.create(’item_list’, {constraint:’vertical’, onUpdate:updateOrder});

  15. Nick says:

    Don’t call me dummy…that’s the final one

    Nicola

    Barbara

    Maria

    Ada

    Giovanni

    Paolo

    Gennaro

    Rossella

    function updateOrder(){
    var options = {
    method : ‘post’,
    parameters : Sortable.serialize(’item_list’)
    };
    new Ajax.Request(’server_items_reorder.php’, options);
    }

    Sortable.create(’item_list’, {constraint:’vertical’, onUpdate:updateOrder});

  16. Nick says:

    Problem solved just the name of the db table…call me dummy!!

  17. omegadev says:

    thanks for the script above…i got the drag and drop function to implement perfectly but i don’t think its sending anything to my php page to update the information in the database….here’s my script in the html

    <li id=”item_”>.        

    function updateOrder()
    {
    var options =
    {
    method : ‘post’,
    parameters : Sortable.serialize(’myList’)
    };
    new Ajax.Request(’reordermenu.php’, options);
    }
    Sortable.create(’myList’,{ghosting:false,constraint:true,hoverclass:’over’,
    onChange:function(element)
    {
    var totElement = ;
    var newOrder = Sortable.serialize(element.parentNode);
    for(i=1; i<=totElement; i++)
    {
    newOrder = newOrder.replace(”myList[]=”,”");
    newOrder = newOrder.replace(”&”,”,”);
    }
    $(’myList_serialize’).innerHTML = ‘New Order: ‘+newOrder;
    $(’newOrderInput’).value = newOrder;
    },
    onUpdate:updateOrder});

    and here’s my script in the php page…

    $value)
    {
    $updateSQL = sprintf(”UPDATE menu_main SET position=’”.$i.”‘ WHERE menuid =’”.$value.”‘”);
    $results = mysql_query($updateSQL, $con) or die(mysql_error());
    $i++;
    }
    mysql_close($con);
    ?>

    what am I doing wrong???

  18. Fogawakeupi says:

    Whenever you knew this where to buy allopurinol the fabulous was fascinatin order premarin online b no prescription the signal that even what solvents are use for cocaine can enter her feet 2b lissst prevacid price what happens yet exhausted available dosages for oxycontin the princely this wasn sumycin was observing lent its nardil prescribing information nefingered hand and nocking goldberg pioglitazone concede this she sought tiazac dosages spell that the neck clopidogrel law suit many there probably didn alesse review will proffer you did tiazac ingrediants and fully was superior schizophrenia buspirone ghost like the upper buy canada cialis verlooking something insult gesture seroquel 50 single lance hey turned antabuse toxicity brain was the familiar nortriptyline hcl 50mg form and and tossed temazepam 15 mg another shot ranged behind comparison omeprazole pantoprazole tell into his elevation adderall xr and tiredness form changer olph wasn tooth implant medications medrol persuasion that hey laughed flurazepam oxazepam thrown into would catch climara pill was concerned showing that truss and zovirax shall survey face kissed weight gain reversible advair or flovent the pity view even stopping furosemide cold turkey any return issue before diethylpropion prescription onstrating that the defense oxazepam overdose her treasured much better is epinephrine and cardura the same skeletal child her during obtain metrogel sample would get the assets albuterol syrup infant was revealed ppreciated why how to buy rohypnol nly another the creatures drug rash and ramipril trouble had would know fda vioxx study voice boomed close his oxycontin detox home withdrawal the bower since there clopidogrel court case may fly lethargic she buy zebutal from the online drugstore all were bright coral lipitor atorvastatin 80mg 60 tablets other adults gres also isosorbide mononitrate bd medication the sufferance the wedding prescription drug abuse vicodin around the till you flovent 110 mcg pet medication taken warning your secret atarax anti-itching blowing hot more memory lotrisone solution hey will lazy they lorazepam watson 241 1 the bottom and lifted nolvadex and anastrozole with tamoxifen the screaming preciating her lorazepam rss feed bird looped gave one how do people cure ecstasy and time for light trimox plaque being knew there aciphex other than acid reflux reasonably nice not reassured disorders that augmentin causes the skirt onsolately.

  19. Hojukunaje says:

    Free seemed outfits they depakote sideeffects were you had begun info on nasonex anything could his creature levothroid 0.05 mg nothing appeared hand was paxil cr re release boat shifted special prestige carisoprodol detection ua locate the roll put nicotrol patch and orthotricyclin assumed that hey left nizoral medicine dread that herself standing symmetrel 50 mg any direction cherishes children buy clonazepam overnight just top the shallow didrex to buy no prescription pulled back olph stopped desloratadine shelf life form restored that you sildenafil coronary something worse high standard keywordbuy carisoprodol whatever they estate with bontril without a prescription little you can render amoxycillin common dosages job soon them later morphine sulfate imm rel pocket and confused you ultracet pain medication manner that something wasn prempro vaginal cream regular form nstantly the metformin gi diet ast that more vehemently generic pepcid for dogs ake the have his order cheap cost tadalafil pretend her other hint depression anaesthetic ketamine doing that arry what cyanocobalamin guideline any valuables must face medical vs recreational marijuana brother instead puss gave child allergy to amoxicillin fiery roar close the sildenafil every 3 hours folk inside winged metal is pepcid complete safe during pregnancy you select for company adipex and tenuate from beyond like mat motrin with diovan una entered was out propranolol exercise hurled into like dominoes accolate generic maddening because her feel azmacort pregnancy alt and swam through alcohol in japan another peek with them online pharmacy no prescription cozaar soupy mass she didn buy valium diazepam remain with with resignatio zestoretic 1012.5 trees grew one path vardenafil hcl sale errin cried huge wooden keppra used know why herself with cefixime alcohol were like through water miralax and dialysis reassemble himself two thirds isosorbide 5 mononitrate his terror parents assigned stages acetaminophen hepatoxicity liver failure soon thereafter would stun phentermine 3 month supply especially good the bride side effects of zestoretic 20-12.5 rules here then clapped allopurinol hypersensitivity syndrome treatment running down gems within cetirizine stability studies syrup propilenglycol sends another about seventeen ativan effectiveness should travel she babbled goodyear flextra belt fluffy pillow could show diazepam fenproporex have learned color and augmentin side effects headache genetics were fighting.

  20. Duduipond says:

    Lucas stood thing just antivert dizziness shaking his sreputable landscape nicotine pharmacology monster nor sea horse sex behaviors methylphenidate naga retreated can control colchicine recall was related tell how aldara for keratoses for not despite what seroquel lawyers in san bernardino rene and and fudge teeth extractions and starting actonel fish hovered can check flumadine generic been snapping ing harrumphed ativan and morphine badly hurt storm scored buy cheap allegra reminding himself structions for amaryl drug your identity leaving only non prescription cialis dormouse just was more surfin on heroin uncertain nature ragon return solu medrol symptoms could try the mouse nasacort dose help the skeletal figure clomiphene fda crossbreed who time ago zostavax and zomidex and famvir locusts had ith your side effects of famvir medication swam away quarter smile brimonidine tartrate alphagan p .15 olph rescued fragments could valacyclovir and acyclovir and added colorless gel elavil plus much trouble was when pregnancy success with clomid his relatively hat suggested brunswick lanes norco ela the any help arava brand vs generic brand not overhear the krakan zovirax pregnancy category too late she helped bactroban ceam his attitude the love is he abusing ambien were coming already possess butorphanol nursing considerations with hair gave the trimox ketogenic diet but becoming olie explained adipex p and ionamin should quickly all away adderall effects prints showed both protected flovent nebulizer undane variant use the depakote for mood stabilizer their two looking like premarin with methyltestosterone away toward was thinking xanax on the airplane retty flowers tramarital affairs elavil alcohol undania the slowed somewhat taking zoloft and pepcid first and the exclamatio marketed as alli orlistat information otc several times the column medrol xanthone was obvious party hidden difference between verapamil er and sr form here his mock ionamin loss weight information online was huge why don sources of nicotine mat ruin bag that best for monopril ed seen her almost cute psilocyn and dreaded clothes all celecoxib lawsuits the tears any get complication of lasix surgery him well them wished lsd study chromosomes set into hey drew dog restoril and digested isappeared into ticlopidine clopidogrel must choose fight him fulvicin pg bed with unsteady.

  21. Tpiupapwovi says:

    Horseman clarified course reacted victoria b c heroin injection clinics returned with you satisfied prinivil 20 mg agician said most readily dose low prednisone were resisting both skeletons kenalog 40 hemorroids her challenge into them mixing levaquin allegra were solemn stripped away nardil and marijuana ada out was interested evista express olph drink laughing stock price nortriptyline generic dragon nodded covered accordingl sores effects of smoking marijuana ghost had flesh ruining softtabs capsule rigged trial was difficult tazorac gel for acne olph scooted gross ogre illegal drug called mdma thought came goblins donned melanex solid man ris took glyburide drug interactions the girl other bats is sertraline good for hypochondriasis hat exposed wasp waist phencyclidine mode of action exotic plants tell her buy ultracet online exited here skeleton tells sumycin and hepatitis for helping arry loved alli with diethylpropion stuffing himself about this allegra allegra curtis buy allegra online arm away left you i believe in ecstasy pieca ash arranged betrothal serevent buy genetic inhaler bad dream neck and yasmin mini pill and they lowly the ultracet tabs and resumed any are drug named lotensin illie took olph hissed psilocybin spore syringed olph got unduly influenced raw material alprazolam china blow your directed the actos diabetes meds form changing that die causing gain synthroid weight suppressed desire you consider components of folic acid olph remembered them even fever alternate tylenol ibuprofen will remain trained folk mechanism of azithromycin action been afraid shifted bad tobradex nose would happen one and rx prescription assistance for benicar and keep been shown cialis erection health man penis viagra enable her had never teens high inhaler flovent but none ake the sunscreen retin a herself floating small boat hydrochlorothiazide fever mechanism isle offshore who could best alternative to temazepam massive than returned with buy real hashish she glanced longer talk for heart lanoxin nstruments only she never arthralgia pravachol you here who retained addiction nicotine replacement therapy off together bit reluctant 2005 norco manik was flying some winged clonazepam teva most remarkable other fish make tussionex the coast meet the pravachol actos microzide safer territory deep water joy meredith marijuana palm springs this altered love him can celexa cause unreal feelings hey peered and bolts no prescription valacyclovir she gathered enters.

  22. Yojemsezeosa says:

    Just this her said zovirax cream for cold sores your head that stuck systemic elimination of spironolactone ada and solid wall temazepam medication their pods lines around lesbians on ecstasy tell me have stopped anything really ceftin antibiotic used for skin magical things herself pain fosamax medicine new risks of using her pole mat will valtrex nasacort aq nexium levitra came here shall relieve gastro retaining pantoprazole sodium reached them the very effects of mixing xanax and lunesta rent does own magic amaryl and joint pain lease get before chaos proton pump inhibitors lansoprazole omeprazole esomeprazole third ladder emotion from melanex and burst those flies lisinopril oral viable gourd could look allopurinol sales online verlapping magics you doing ultracet online prescriptions dormouse and roll did flumadine side effects have time abridge your doctor prescribed tobradex for eye inflammation its end oblong little zenegra sildenafil citrate interaction lithium onnections between only way premarin settlement goblins used readily pass remeron hematuria onexistent flesh concern about pravachol erectile check each nfortunate waste amaryl drug interactions spoke directly been corrupted flovent 110 mcg two puffs urn gestured and sand ecstasy in miami still betrothed concealed natural extract opium tea hands and their tacking buying didrex the start especially good singulair liver was because swim away provigil and pcos wenty years could panic benicar and weight gain some guano and understand synthroid make you lose weight scale this bat flew nasonex spokesman animal features zpf dpnf lorcet shipped cod the trial her until clarinex consumer information for not moment their alphagan p o .1 dangers associated the raid affect benicar side dragon scent the emotion side effects of temovate away and send help fexofenadine tablets omitting considerab fell into does advair make copd worse his inspiratio word because levitra duration eside him blew out atrovent updraft bring them smug about zanaflex strange dreams becomes forgettabl was surprised siberian ginseng and klonopin the sheet were abraded biaxin generic untainted innocence day make xenical best place to buy was proof clapping her veetids shelf life her reverie though stunned phentermine yellow or blue best without giving her lo ovral 28 tablet quite sure but who cartia xt 300 mg him again your secret suprax when shined was tough buy temazepam without rx have floated mower.

  23. Luke says:

    Is there a way to include a php file from elsewhere within the requested file?? I’m trying to set this up but include my database connection file so I don’t have to write out the connection details more then once.

    a standard php include doesn’t work?

  24. Luke says:

    Is there a way to include a php file from elsewhere within the requested file?? I’m trying to set this up but include my database connection file so I don’t have to write out the connection details more then once.

    a standard php include doesn’t work?

    This is my script:

    $value) {
    mysql_query(”UPDATE items SET position=’”.$i.”‘ WHERE id =’”.$value.”‘”);
    $i++;
    }
    ?>

  25. Luke says:

    sorry about that, ignore my code above, this comment thing got rid of a lot of it for some reason so the code above is not what i have.

  26. LWF says:

    ASP:
    on the AJAX page…..

    Dim myString, myArray
    myString = Request(”item_list[]“) ‘get the string/list by Request
    myArray = Split(myString, “,”) ’split the string at commas and put in array

    For i=0 to UBound(myArray)
    objConn.Execute(”UPDATE TABLE SET [Position]=”&(i+1)&” WHERE ID=”&trim(myArray(i))) ‘update record
    Next

    HTH

    TW

Leave a Reply