Javascript, JSON and PHP

March 29, 2009

JSON data is commonly used as a simple way to send data back to client side javascript from the server. For example, a PHP script may output the following JSON to confirm that an action was taken:

{success: true, message: "It worked!"}

or in the case of failure:

{success: fase, message: "It didn't work!"}

Below is a basic javascript callback function to process the JSON data, displaying the returned message:

function callback(data) {
    if(data.success) alert(data.message);
    else alert('ERROR:' + data.message);
}

Sometimes it’s actually useful to send back javascript code to in the JSON data, to be run on the client. For example, if an Ajax request is made that updates some account information we could return javascript code to update several parts of the page with the new information. Here’s PHP code that outputs JSON data which includes javascript code:

$response = array('success' => 'true',
    'code' => "jQuery('#id').html('Success!');");
echo json_encode($response);
// Outputs {success: true, code: "jQuery('#id').html('Success!');"}

Note how in the returned JSON data “code” is actually a string, so it won’t be possible to run the code simply by doing “data.code()” on the client. Instead we must explicitly tell javascript to evaluate the string as code. Below is an example of how we can do this using eval:

function callback(data) {
    if(data.success && data.code) eval(data.code);
}

If we wanted to update the page with HTML more complicate the the simple “Success!” message in the previous example we must be careful to escape any quotes in the HTML correctly. If we wanted to set the content to: “Javascript”, “json”, “PHP” then we’d need to do the following:

$response = array('success' => 'true',
    'code' => "jQuery('#id').html('\"Javascript\", \"json\", \"PHP"');");
echo json_encode($response);

And It’s easy to imagine a far more complicated example. The code becomes less readable, and if you accidentally forget to escape one of the quotes, or escape of the quotes that shouldn’t be escaped, then the whole thing breaks. A nice alternative is ty store the HTML as a separate JSON property and then to reference it, avoiding the need to escape the quotes and making the code easier to read:

$response = array('success' => 'true',
    'html' => '"Javascript", "json", "PHP"',
    'code' => "jQuery('#id').html(this.html);");
echo json_encode($response);

However, to make the “this.html” reference work the clients side callback function will need to changed, as eval() runs the code in global scope. This means that “this.html” will refer to a global html variable rather than the one in our JSON data. We can get around this problem by using the apply function as follows:

function callback(data) {
    if(data.success && data.code) {
        var f = new Function(data.code);
        f.apply(data);
    }
}

And here’s a complete example which uses jQuery to perform the Ajax call:

jQuery.get('update.php', {} function(data) {
    if(data.success && data.code) {
        var f = new Function(data.code);
        f.apply(data);
    }, 'json');

jQuery Draggable Implementation

February 16, 2009

jQuery UI is an excellent add-on library for jQuery that provides all sorts of UI widgets, effects and behaviours. One particularly useful function is provides is to make any element on the page draggable, so that it can be moved about with the mouse.

Because of all the features that jQuery UI provides the library is fairly large, about 450KB uncompressed. It is possible to only include specific parts of the library, but even the bare minimum required for the draggables feature weighs in at just over 40KB uncompressed.

Therefore I decided to write my own lightweight draggable implementation using only jQuery. The code is shown below:

// Make an element draggable using jQuery
var makeDraggable = function(element) {
	element = jQuery(element);

	// Move the element by the amount of change in the mouse position
	var move = function(event) {
		if(element.data('mouseMove')) {
			var changeX = event.clientX - element.data('mouseX');
			var changeY = event.clientY - element.data('mouseY');

			var newX = parseInt(element.css('left')) + changeX;
			var newY = parseInt(element.css('top')) + changeY;

			element.css('left', newX);
			element.css('top', newY);

			element.data('mouseX', event.clientX);
			element.data('mouseY', event.clientY);
		}
	}

	element.mousedown(function(event) {
		element.data('mouseMove', true);
		element.data('mouseX', event.clientX);
		element.data('mouseY', event.clientY);
	});

	element.parents(':last').mouseup(function() {
		element.data('mouseMove', false);
	});

	element.mouseout(move);
	element.mousemove(move);
}

The element you pass to makeDraggable should already have absolute or fixed CSS positioning, and a top and left CSS value set. Something like the following:

<div id='draggable' style='position: absolute; top: 100; left: 200; padding: 50px; background: black; border: 2px solid #aaa;'>
This box is draggable!
</div>
<script type="text/javascript">
makeDraggable(jQuery('#draggable'));
</script>

Then just click on the div and move the mouse to drag it around.

Obviously it isn’t as flexible as the jQuery UI version, but it is a useful alternative if you want to save on the amount of data your user’s will need to download and you don’t require any other jQuery UI features.

Converting Ext to jQuery

July 24, 2008

I’ve recently been working on a fairly large javascript project that was using the Ext javascript library. Ext is a powerful javascript library that provides lots of UI widgets, in addition to a DOM manipulation API. The complete Ext library comes in at 546KB, and on this project we were only really using the Ext DOM functions, which made the library a bit overkill. The decision was made to use jQuery instead, which weighs in at only 16KB.

I set about replacing all the existing Ext code with jQuery equivalents. The majority of Ext functions map directly onto jQuery equivalents, which made things easier. For example:

Ext.get('id') -> $('#id')
Ext.get('id').query('.class') -> $('#id .class')

Some things don’t map quite so well, but are still relatively simple. The position functions are one such example:

Ext.get('id').getAbsoluteXY() -> [$('#id').offset().left, $('#id').offset().top]
Ext.get('id').bottom() -> $('#id').offset().top + $('#id').height()

However, some Ext functions have no equivalents at all in jQuery. Two Ext functions we were using with no jQuery equivalent were serialiseForm and decodeJson. I came up with the replacement functions below:

$.fn.decodeJson = function(string) {
		// if we don't have a string then there isn't any decoding to be done, return as is
		if(typeof(string) != "string") return string;
		// make sure the string starts with { and ends with }
		if (string.substr(0,1) == "{" && string.substr(string.length - 1,1) == "}") {
			eval("var decoded = " + string);
			return decoded;
		}
		return {};}

$.fn.serialiseForm = function(form) {
		// We might have been passed a form ID, or a form object
		if(typeof(form) == "string") form = jQuery('#' + form);

		// lets try ourselves!
		var getValuesFromForm = function(form) {
			var serialised = [];
			jQuery(form).children().each(function() {
				var child = jQuery(this);

				if (child.children().length > 0) {
					var t = getValuesFromForm(child);
					if(t.length > 0) jQuery.merge(serialised, t);
				}

				var name = child.attr('name');
				var type = child.attr('type');
				var val = child.val();

				if(name) {
					if (type != 'checkbox') {
						serialised.push(encodeURI(name) + '=' + encodeURI(val));
					} else {
						var checked = child.attr('checked');
						if (checked) {
							serialised.push(encodeURI(name) + '=' + encodeURI(val));
						}
					}
				}
			});

			return serialised;
		}

		var data = getValuesFromForm(form).join("&");
		return data;
}

The only feature of Ext being used without a relatively simple replacement was Ext.basicDialog, a UI widget. The jQuery BlockUI plugin came to the rescue:

dialog.hide() -> $.unblockUI();

During this rewrite I came to appreciate just how powerful jQuery selectors are. The codebase was considerably smaller after the rewrite, due to the expressiveness of jQuery. Many 3 or 4 liners in Ext would condense down to a single expression in jQuery!