Simple Draggable Element Persistence with jQuery

At some point you may need to create a draggable element within your web application. This is great functionality, however you may want or find that you need the element to stay in place after being dragged around. In this tutorial I will show you how to easily drag an element and make it stick, even after a page reload, by grabbing and storing its X and Y coordinates.

Scenario

So you have an element in your web application. You can drag it around, put it here and put it there. But, when the page is reloaded in any way, the element returns to its default position. While you want the element to be draggable, you don’t want it to move after its been dragged. Let’s look at a simple solution to give us this ability.

Getting Started

For this tutorial we are going to need the jQuery library, jQuery UI, and the jQuery-JSON plugin by Brantley Harris. We will also be using some PHP and a MySQL database to parse and store our data. If your new to jQuery, no worries. jQuery is a highly extensible, fast, and lightweight JavaScript library which is both fun and easy to use. The library has very nicely structured documentation, and a huge community. This is my first tutorial on jQuery and JavaScript, so please bear with me. I hope to explain everything as best as I can and if you have any questions, please feel free to ask.

The HTML amd CSS

I wanted to start off with the HTML and styling for this tutorial since the effect is applyed to HTML elements, it helps to visualize what we are going to do, right off the bat. First the CSS:

html, body {
	background:#151515;
	margin:0 0 0 0;
	padding:0 0 0 0;
}

#glassbox {
	background:#333;
	border:1px solid #000;
	height:400px;
	margin:30px auto auto auto;
	position:relative;
	width:960px;
	-moz-border-radius: 10px;
	-webkit-border-radius: 10px;
}

#element {
	background:#666;
	border:1px #000 solid;
	cursor:move;
	height:143px;
	padding:10px 10px 10px 10px;
	width:202px;
	-moz-border-radius: 10px;
	-webkit-border-radius: 10px;
}

#respond{
	color:#fff;
	margin:0 auto 0 auto;
	width:960px;
}

The CSS is very simple. We set the html and body properties to clear margins and padding, and continue by setting some heights, widths, and other properties to our elements so it doesn’t look so bland. -moz-border-radius and -webkit-border-radius are two properties which allow us to create rounded borders (applicable only to Mozilla Firefox and Safari 3 at the moment) for our elements. Let’s take a look at the HTML:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Simple Draggable Element Persistence with jQuery</title>

<link rel="stylesheet" href="style.css" type="text/css" />
<script type="text/javascript" src="js/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="js/jquery-ui-1.7.2.custom.min.js"></script>
<script type="text/javascript" src="js/jquery.json-2.2.min.js"></script>

</head>

<body>

	<div id="glassbox">
		<div id="element"><img src="nettuts.jpg" alt="Nettuts+" />Move the Box<p></p></div>
	</div>

	<div id="respond"></div>

As you can see we just setup a very nice and simple page which calls in our CSS, JavaScript library and plugins, and contains the elements which we will be using to apply some effects and events to. As a note,the jquery-ui file is a custom build which only includes the core and the draggable interaction functionality.

The Javascript

Now for some juicy interaction! Lets first take a look at some of the basic functions we will be using to apply some effects to our elements. Let’s tear it down.

<script type="text/javascript">
	$(document).ready(function() {
		$("#element").draggable({
				containment: '#glassbox',
				scroll: false
		 })

First we tell the browser, “Hey, this is some code we want to run; it’s not HTML, it’s JavaScript.” We then want to wait for the document to load before we do anything else, once thats happened, we call a function to select our #element DIV, and add the draggable handler with some basic options. The containment options will keep our element within the parent DIV, and we set scroll to false because we don’t want any scrolling to happen. Let’s move on:

	.mousemove(function(){
		var coord = $(this).position();
		$("p:last").text( "left: " + coord.left + ", top: " + coord.top );
	})

With this tidbit, we call the event handler mousemove and tell it, “When the mouse moves, set the variable ‘coord’ to equal our selected #element’s position.” Then we select a paragraph (“p:last”), the last one in #element, and print some text which will read out the left(x) and the top(y) properties of our element relative to the parent object (which is #glassbox).

	.mouseup(function(){
				var coords=[];
				var coord = $(this).position();
				var item={ coordTop:  coord.left, coordLeft: coord.top  };
			   	coords.push(item);
				var order = { coords: coords };
				$.post('updatecoords.php', 'data='+$.toJSON(order), function(response){
						if(response=="success")
							$("#respond").html('<div class="success">X and Y Coordinates Saved!</div>').hide().fadeIn(1000);
							setTimeout(function(){ $('#respond').fadeOut(1000); }, 2000);
						});
				});

	});
</script>

Ok now for some devilry! In this snippet we are going to do a couple of things. First we want to setup an empty array, and then get some values to fill it with. By calling the event handler .mouseup() we are telling the browser to look for the event when you unclick your mouse. We set the variable coords to equal our empty array, and again set the variable coord to equal the position handler of our #element. Then we need to create a list of items, these will be coordTop: and coordLeft: respectfully equaling our #element’s left and top positions. With coords.push(item) we are literally pushing our item list and filling the coords array with it. Then set the variable order as a new list where the coords key will equal our coords array. Now for some AJAX.

$.post is an AJAX request handler which loads a remote page using an HTTP POST method. This function looks for the parameters: url, data, callback and data type to be returned. In this tutorial we specify the updatecoords.php file as our URL because this is where we want to send our post data, we then define our datatype by including the $.toJSON function defined in our jquery-JSON plugin and setting our variable order as the data to be handled by .toJSON. Next we create a callback which checks for a return response from our PHP file upon success, and add a bit of flavor by saying, “If whats returned is equal to success then…” We keep this html hidden by using the effect handler .hide, and tell it to fade in at 1000 milliseconds, wait with a timeout for 2000 milliseconds, and tell it to fade out again. In the end our JavaScript should look like this:

<script type="text/javascript">
	$(document).ready(function() {
		$("#element").draggable({
				containment: '#glassbox',
				scroll: false
		 }).mousemove(function(){
				var coord = $(this).position();
				$("p:last").text( "left: " + coord.left + ", top: " + coord.top );
		 }).mouseup(function(){
				var coords=[];
				var coord = $(this).position();
				var item={ coordTop:  coord.left, coordLeft: coord.top  };
			   	coords.push(item);
				var order = { coords: coords };
				$.post('updatecoords.php', 'data='+$.toJSON(order), function(response){
						if(response=="success")
							$("#respond").html('<div class="success">X and Y Coordinates Saved!</div>').hide().fadeIn(1000);
							setTimeout(function(){ $('#respond').fadeOut(1000); }, 2000);
						});
				});

		});
</script>

Place the JavaScript below the HTML, right after the closing body tag.

The PHP

Alright, now lets get to business on doing something with the data being posted from our jQuery. First lets create a simple database to store our coordinates, which we will then later retrieve to define the position of our element. Second will be our config.php file which will store our database connection settings, and then we will finish with updatecords.php.

Database: 'xycoords'

CREATE TABLE IF NOT EXISTS `coords` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `x_pos` int(4) NOT NULL,
  `y_pos` int(4) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1;

config.php

<?php
/*Database Settings*/

$db_host ="localhost"; //this will likely stay the same
$db_name = "xycoords"; //name of the database we will be using
$db_usr = "database_username"; //db username
$db_pass = "database_password"; //db password

//Connect to the database
$link = mysqli_connect($db_host, $db_usr, $db_pass) or die("MySQL Error: " . mysqli_error());
//Select our database
mysqli_select_db($link, $db_name) or die("MySQL Error: " . mysqli_error());
?>

updatecoords.php

<?php
if(!$_POST["data"]){
	echo "Nothing Sent";
	exit;
}

include ('config.php');

//decode JSON data received from AJAX POST request
$data = json_decode($_POST["data"]);

foreach($data->coords as $item) {
	//Extract X number for panel
	$coord_X = preg_replace('/[^\d\s]/', '', $item->coordTop);
	//Extract Y number for panel
	$coord_Y = preg_replace('/[^\d\s]/', '', $item->coordLeft);
	//escape our values - as good practice
	$x_coord = mysqli_real_escape_string($link, $coord_X);
	$y_coord = mysqli_real_escape_string($link, $coord_Y);

	//Setup our Query
	$sql = "UPDATE coords SET x_pos = '$x_coord', y_pos = '$y_coord'";

	//Execute our Query
	mysqli_query($link, $sql) or die("Error updating Coords :".mysqli_error());
}

//Return Success
echo "success";

?>

This is pretty simple to follow. The first thing we want to do is check to make sure our post data is being passed to the file. If that happens we include our config file for our database connection and set the variable $data to json_decode(passed post variable); json_decode is a PHP function implemented in PHP 5.2.0 which allows us to decode a JSON string.

Since our $data variable contains an array of data, we need to tear it apart to get the values we need. To do this we take foreach $data->coords (which is from our order variable in our JavaScript) as an item. This takes each key and value pair and creates an item object from the array, we then specify and create a variable out of it. We use this in conjunction with preg_replace so that we can take out the characters we don’t need. We then, as good practice and a measure of security, escape our values to prepare them for insertion into the database. If all goes well, we need to return success to our JavaScript to let it know everything went just fine.

Lastly

Now that we have what we need in place, to grab the position coordinates from our element and pass it to PHP for storing, we need to modify our HTML to reflect the position of our element. To do this we change the basic element HTML and instead create it with PHP:

<div id="glassbox">
<?php
		//Create a query to fetch our values from the database
		$get_coords = mysqli_query($link, "SELECT * FROM coords");
		//We then set variables from the * array that is fetched from the database
        while($row = mysqli_fetch_array($get_coords)) {
			$x = $row['x_pos'];
			$y = $row['y_pos'];
			//then echo our div element with CSS properties to set the left(x) and top(y) values of the element
			echo '<div id="element" style="left:'.$x.'px; top:'.$y.'px;"><img src="nettuts.jpg" alt="Nettuts+" />Move the Box<p></p></div>';
		}
?>
</div>
<div id="respond"></div>

Here we setup a basic query to the database to select all the rows from the table coords. We then invoke a while loop which specifies each row we select as $row. Now we can set some variables to equal each individual row we pull from the database, and echo them in the proper place within the elements style (left and top).

Wrapping up

Well I hope that you enjoyed this tutorial as much as I did writing it! It may not be perfect. While this is just one way to get this functionality in a draggable element, there are other ways (and perhaps better) to achieve it. Once such way could be to store the coordinate values in a cookie, to keep calls to the database at a minimum. You could also serialize the values passed from jQuery to PHP instead of using JSON. This tutorial is just one example from which you can expand upon. Thanks for reading!


Related Posts

Add Comment

Discussion 63 Comments

  1. Huckleberry says:

    Wow, 1st one to get to comment.
    This looks like just the thing I’ve been looking for!
    Thanks for a great tut!

  2. Jhon says:

    Very cool and become handy

  3. Hamza Oza says:

    Nice tut. Enjoyed reading it.

  4. Ryan says:

    in the mouseup function, I think you meant to say:

    var item={ coordTop: coord.top, coordLeft: coord.left };

    not:

    var item={ coordTop: coord.left, coordLeft: coord.top };

  5. Ethan says:

    Very slick. Will definitely use this.

  6. Zoran says:

    Thank you very much for this Dustin, it is very helpful, i wonder if i could apply this to my CodeIgniter project, i am building small CMS HTML editor for my company, but didn’t have any idea how to position some elements in parts of the website. If i had 3 parts website with header, main and footer and in the header div i have logo image, could i use this method to position the logo in the header section and write its new position to the database, so when i produce the layout i will read it from the database. I know it is kind of lame to do such thing, but company wants it and i want to do it :) .
    Thanks again

  7. Asmodiel says:

    Hey there!
    Nice tut. But think about that:
    User X moves it around, it gets saved in the DB.
    In this moment user Y reloads the box and the div is on a completely different position!

    Either you should go with … well saving it to the DB along with the IP or you’d have to go with Cookies which in my opinion would be the better oppinion. Like a cookie for a year or so…

  8. Steve Davis says:

    Lots of work for something that’s supposed to be “simple”. But still cool :)

  9. Stefan says:

    Was surprised to see PHP and mySQL… Isn’t this supposed to be the kind of thing you store in a cookie?

    Whether it’s in a cookie or server side definitely depends on the situation you’re using it for.

    But still, my mental statistics tell me most draggable stuff needs to be in cookies. I’m thinking draggable interface elements (menus, modal windows or option boxes). Those would have no place anywhere else than on the client side.

    Hmm… if you had a puzzle game for maybe with draggable pieces, now that would need to go on the server.

  10. Roger Roelofs says:

    Another option would be to store the coordinates in a cookie instead of the db. It totally depends on why you want the user to be able to drag stuff around. Also, it would be better to use the draggable events rather than mousemove/mouseup. Doing that could simplify your event processing. Good first article.

    • Dustin says:

      Right,cookies are another good option, and may suite many more needs. You have to consider how much persistence you want. Cookies are only as persistent as the user allows them to live or what their time to live is set to.

  11. Anand Kumar says:

    I tried it by the source given . there is some bug in the source it is only dragging but not saving the position in the database table ..

    Help me out ..

    • Dustin says:

      That doesnt help solve your problem, could you please be more specific?

      • Anand Kumar says:

        yes i solved
        actually need to stip the slashes in the post request
        chenged the line to from the file updatecoords.php

        $data = json_decode($_POST['data']);

        $data = json_decode(stripcslashes($_POST['data']));

        Now it works fine

        :)

  12. Jeff says:

    How would I make it so I have multiple draggable divs?

  13. Hi really a nice tutorial..
    but i think there is no need to convert data in json.
    i am sending like this-

    $.post(‘updatecoords.php’, ‘data=’+coord.left+’||’+coord.top, function…

    and at updatecoords.php simply –

    $cords = explode(‘||’,$_POST["data"]);
    $sql = “UPDATE coords SET x_pos = ‘”.$cords[0].”‘, y_pos = ‘”.$cords[1].”‘”;
    //Execute Query
    mysql_query($sql,$link) or die(“Error updating Coords :”.mysql_error());

    echo “success”;

  14. Monie says:

    Where is the demo????

  15. Ted says:

    This is really cool! May use this for a flow chart editor I need. Or would you know of any JQuery extension that does flow charting out of the box?
    Thanks for your contribution!

  16. D3rson says:

    ok.. nice tut,

    but where is the Demo???

  17. Brilliant tutorial. Can be implemented with several projects ideas.

  18. Russ Back says:

    Nice tutorial. Makes me think about how browser storage in the more recent versions of Safari et al could pull this kind of personalisation out into the user’s environment and off the server.

  19. esranull says:

    very nice but whree is the demo?

  20. z.Yleo77 says:

    a powerful code that inspire me something

  21. Big J says:

    Can someone explain to me why the use of the json plugin… I’m new to jquery but arent’ you allowed to pass json variables in with your .post anyways?? eg $.post(‘updatecoords.php’, {cordx : cordx, cordy : cordy }…

    Dusin or someone else please explain… Thanks

  22. Hello says:

    How am I suppose to run this, I mean, it says $_POST but what the hell am I posting???

  23. Nice, cool article – I”m with esranull and Monie -> wheres the demo?

  24. Ismail says:

    If it does not work replace

    $data = json_decode($_POST['data']);

    with

    $data = json_decode(stripcslashes($_POST['data']));

    as Anand Kumar pointed out

  25. spido says:

    Very nice tutorial!

    @Marcelo: thank you for your demo, great job!
    ( Demo -> http://www.onlinefacil.com.br/arrastar/savestate.php )

  26. Kalvster says:

    Good tutorial, it can be well adapted into templates.

  27. agilius says:

    I like it. I think I might learn this by following each step, maybe it will help in some of my projects :) .

    Btw, “The HTML amd CSS” ? I think it should be “and” not “amd”…

  28. colin says:

    “if your new to jquery”

    *hiss* huge pet peeve of mine.

  29. XHTML 7 Turbo says:

    Who cares for dragable items on webpages (and to remember by browser what was dropped into another page layout-place)? It is totally unuseful – only good stuff for demonstration (nice looking functionality, but totally not wanted for real web usage – and it requires not only JS and PHP but also DataBase(!)). I do not care for Java Script. I have it disabled in browsers. I was also making great looking ads in JS, but in fact they where always just only ads for users. They need useful and good writen text content only (and only, not just another web page with not intuitive navigation and non-clean optionality). By the way: why can’t author use cookie or cache functionality of html5 (instead of enabling all the sql database engine)?

    • Not to offend, but I believe over 95% of the population runs js, and I believe that the potential to use video and photo multimedia in the web is just beginning to gain momentum. This is a good momentum by the way, as text is far too slow of a communication medium for our information age. Jquery is a marvelous way of combining our beautifully hand crafted sites with media and subconscious experiences…

      Databases are used for virtually every PHP site out there, and a combination of cookies + DB is perfect (with the balance depending on your implementation). I think you could find some very interesting uses for this type of js, as well as so many other js scripts.

    • Dustin says:

      right…nobody likes “drag to share” or anything, you know, its totally useless, these draggable items…

  30. Clinton says:

    Save the coords in a cookie would probably be enough. No database connection needed. Simple to implement. Nice to have the database functionality though.

  31. Johan says:

    jquery-ui-1.7.2.custom.min.js ????? That could be anything right?

    • Dustin says:

      “As a note,the jquery-ui file is a custom build which only includes the core and the draggable interaction functionality.”

      What it is, is noted in the tutorial…

  32. Fatih says:

    No one mentioned it but I think you need to save it into database only when the page is unloading and there is a change in the initial positions. In a real world application, it will kill your server to post data after each drag-drop. On the other hand, using cookies will not have this problem.

  33. Thank you very much! I now have ideas in my head for a fully dynamic CMS using Jquery, dynamic css, and the Zend Framework…

  34. Dennis Mortensen says:

    Nice! Would be useful as layout generator, if you listed the entries and selected a db specific entry, then position the elements to where that specific person left them. Really cool if it could output as xml format.

Add a Comment