{"id":698,"date":"2010-05-15T10:48:56","date_gmt":"2010-05-15T09:48:56","guid":{"rendered":"http:\/\/www.simonbattersby.com\/blog\/?page_id=698"},"modified":"2012-12-09T10:24:59","modified_gmt":"2012-12-09T10:24:59","slug":"drag-and-drop-with-jquery-ui","status":"publish","type":"page","link":"https:\/\/www.simonbattersby.com\/blog\/drag-and-drop-with-jquery-ui\/","title":{"rendered":"Drag and drop with jQuery UI Sortable"},"content":{"rendered":"<p>Last September I wrote a bespoke e-commerce solution for my wife&#8217;s website, including all the accompanying admin screens. One admin screen allows the amendment of the display order of items in a particular category. It was, if I&#8217;m honest, a bit clunky, as you had to type in the display order as a number, and then refresh the screen to see what the resultant re-order looked like. In response to repeated requests of &#8220;Can&#8217;t you make it drag and drop&#8230;?&#8221; I switched from responding with hollow laughter and actually had a look at how this might be done. And surprisingly, it was pretty straightforward. Here&#8217;s how<\/p>\r\n\r\n<h3>Setting up the database<\/h3>\r\n\r\n<p>I already had a database with a table called items, each of which had a unique ID field and another field called Display_Order. The images for each item are all named in the format [ID]-x.jpg to make life easier.<\/p>\r\n\r\n<h3>Displaying the items on the &#8220;Maintain Display Order&#8221; admin page<\/h3>\r\n\r\n<p>I started off just setting up the page to display all the items. Straightforward php, and pretty much what I already had. I&#8217;m retrieving the items from the database and displaying them in an unordered list, which I&#8217;ll use later for the drag and drop.<\/p>\r\n\r\n<pre>$result = mysql_query(&quot;SELECT * FROM items ORDER BY Display_Order ASC&quot;);\r\necho &quot;&lt;ul id='categoryorder'&gt;&quot;;\r\nwhile ($item = mysql_fetch_array($result))\r\n{\r\necho &quot;&lt;li id='ID_&quot;.$item['ID'].&quot;'&gt;&lt;img src='.$item['ID'].&quot;-1.jpg' \r\nheight='90' width='60' alt='No file on server' title='Preview'&gt;&lt;\/li&gt;&quot;;\r\n}\r\necho &quot;&lt;\/ul&gt;&quot;;<\/pre>\r\n\r\n<p>A couple of things to note here &#8211; I&#8217;ve assigned an id <code>categoryorder<\/code> to my list, and each item in the list has its html id set as &#8220;ID_&#8221; followed by the database ID. This is important later on, as we&#8217;ll see &#8211; don&#8217;t miss the underscores. Your resultant html should look like this:<\/p>\r\n\r\n<pre>&lt;li id=\"ID_1\"&gt;...&lt;\/li&gt;\r\n&lt;li id=\"ID_2\"&gt;...&lt;\/li&gt;\r\n<\/pre>\r\n<p>Then, I added a bit of css to make my list into two columns of images to match the real page layout.<\/p>\r\n\r\n<h3>Adding the drag and drop<\/h3>\r\n\r\n<p>I used the <a href=\"http:\/\/jqueryui.com\/demos\/sortable\/\" title=\"jQuery UI sortable\">jQuery UI sortable plugin<\/a>. You can download jQuery, jQuery UI base and the plugin from <a href=\"http:\/\/jqueryui.com\/demos\/sortable\/\">http:\/\/jqueryui.com\/download<\/a>. Having done this, I then need to add links to these javascript files in the head of my page<\/p>\r\n\r\n<pre>&lt;script type=&quot;text\/javascript&quot; src=&quot;\/jquery-1.4.2.min.js&quot;&gt;&lt;\/script&gt;&lt;br&gt;\r\n&lt;script type=&quot;text\/javascript&quot; src=&quot;\/jquery-ui-1.8.1.custom.min.js&quot;&gt;&lt;\/script&gt;<\/pre>\r\n\r\n<p>In order to make my list items draggable then I need to add just the following code:<\/p>\r\n\r\n<pre>&lt;script type=&quot;text\/javascript&quot;&gt;\r\n$(document).ready(function(){\r\n\r\n  $(&quot;ul#categoryorder&quot;).sortable({ \r\n         opacity: 0.6, \r\n         cursor: 'move'  \r\n         });\r\n\r\n});\r\n&lt;\/script&gt;<\/pre>\r\n\r\n<p>All I&#8217;m doing is invoking the &#8220;sortable&#8221; plugin when the page loads and assigning it to items in my <code>#categoryorder<\/code> list.  I&#8217;m also setting the opacity of the dragged item to be 0.6, so it&#8217;s a bit faded when it&#8217;s actually being dragged. Finally, I&#8217;m setting the cursor to change when I&#8217;m dragging an item. And that&#8217;s it for the draggable bit &#8211; my items can now be dragged and dropped into whatever new order I want.<\/p>\r\n\r\n<h3>Giving a visible indication that the order has changed<\/h3>\r\n\r\n<p>Once I&#8217;ve changed the order, it&#8217;d be nice to display a message to say that the are changes that haven&#8217;t yet been saved, and I can do this by using the update Event on sortable:<\/p>\r\n\r\n<pre>&lt;script type=&quot;text\/javascript&quot;&gt;\r\n$(document).ready(function(){\r\n\r\n  $(&quot;ul#categoryorder&quot;).sortable({ \r\n         opacity: 0.6, \r\n         cursor: 'move'&lt;span&gt;,  \r\n         update: function(){\r\n                $('#categorysavemessage').html('Changes not saved');\r\n                $('#categorysavemessage').css(&quot;color&quot;,&quot;red&quot;);\r\n                }&lt;\/span&gt;\r\n         });\r\n\r\n});\r\n&lt;\/script&gt;<\/pre>\r\n\r\n<p>Here I&#8217;m using the update option to populate a predefined message field  (<code>#categorysavemessage<\/code>) with some red text to say &#8220;Changes not saved&#8221; whenever the sort order is changed.<\/p>\r\n\r\n<h3>Updating the database<\/h3>\r\n<p>I don&#8217;t want to update the database every time I move an item, just at the end when I&#8217;m happy, so I&#8217;ve added a &#8220;Save now&#8221; button. I now need to create a javascript function to fire when that button is clicked, and here&#8217;s the javascript I need:<\/p>\r\n\r\n<pre>function saveDisplayChanges()\r\n{\r\nvar order = $(&quot;ul#categoryorder&quot;).sortable(&quot;serialize&quot;);\r\n$('#categorysavemessage').html('Saving changes..');\r\n$.post(&quot;update_displayorder.php&quot;,order,function(theResponse){\r\n$(&quot;#categorysavemessage&quot;).html(theResponse);\r\n$('#categorysavemessage').css(&quot;color&quot;,&quot;green&quot;);\r\n});\r\n}\r\n&lt;\/script&gt;<\/pre>\r\n\r\n<p>Here I&#8217;m first populating a variable called <code>order<\/code> with the &#8220;new&#8221; order of my items, using the &#8220;serialize&#8221; method of the sortable plugin. This looks at the html id of each list item which refers to the ID in my database, and builds a query string.  I&#8217;m then passing the resultant string via AJAX to a separate php file called update_displayorder.php which updates the database. Here I&#8217;m using the jQuery form of AJAX call. Finally, when I get a message back from the php file I&#8217;m updating the message field.<\/p> \r\n\r\n<p>Here&#8217;s the php code which will actually update the database:<\/p>\r\n\r\n<pre>$newOrder = $_POST['ID'];\r\n$counter = 1;\r\n\r\n  foreach ($newOrder as $recordIDValue) {\r\n  $query = &quot;UPDATE items SET Display_Order = &quot; . $counter;  \r\n  $query .= \"WHERE ID = &quot; . (int)$recordIDValue;\r\n  mysql_query($query) or die('Error, insert query failed');\r\n  $counter ++;\r\n  }\r\n\r\necho 'Changes saved';<\/pre>\r\n\r\n<p>So here I&#8217;m just extracting the data from the post, and then stepping through each item, updating the display order for each one with a variable which I&#8217;m incrementing for each item.<\/p>\r\n\r\n<p>And that&#8217;s it &#8211; it works, with very little code, thanks to the jQuery UI plugin. Nice. I&#8217;ve tested in IE6, IE7, IE8, Chrome, FF3, Opera 9, Opera 10 and Safari 4, all on Win\/XP, and Safari 5 on Mac OS X\/Snow Leopard.<\/p>\r\n\r\n<p>Here&#8217;s a <a href=\"\/demos\/draganddrop_demo.htm\" title=\"Drag and drop demo\">little demo<\/a> &#8211; it doesn&#8217;t talk to a real database, but you can see the drag and drop and the resultant code that would be passed back to drive the update.<\/p>\r\n\r\n\r\n\r\n","protected":false},"excerpt":{"rendered":"<p>Last September I wrote a bespoke e-commerce solution for my wife&#8217;s website, including all the accompanying admin screens. One admin screen allows the amendment of the display order of items in a particular category. It was, if I&#8217;m honest, a bit clunky, as you had to type in the display order as a number, and [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"open","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-698","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/pages\/698","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/comments?post=698"}],"version-history":[{"count":2,"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/pages\/698\/revisions"}],"predecessor-version":[{"id":2607,"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/pages\/698\/revisions\/2607"}],"wp:attachment":[{"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/media?parent=698"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}