Hostingheaderbarlogoj
Join InMotion Hosting for $3.49/mo & get a year on Tuts+ FREE (worth $180). Start today.
Advertisement

Simple Draggable Element Persistence with jQuery

by
Gift

Want a free year on Tuts+ (worth $180)? Start an InMotion Hosting plan for $3.49/mo.

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!


Advertisement