Simple AJAX Links With Prototype.js

Getting my head wrapped around Prototype.js has been a difficult process. But today I was faced with the simple challenge of creating links that could load external files into divs embedded onto the page. Nothing fancy but useful nonetheless. Here's how I did it:

	var DynamicExtensions = {
	    dynamicize: function(element){
	        element.dHref = element.href;
			element.dTarget = element.target;
			element.href = "#"+element.target;
			element.target = "";
			element.dynamic = dynamicallyLoad.bindAsEventListener(element);
			Event.observe(element, 'click', element.dynamic);
			return element;
	    }
	}

	Element.addMethods(DynamicExtensions);

	function dynamicallyLoad(e) {
		element = Event.element(e);
		new Ajax.Updater(element.dTarget, element.dHref, { 
			method: 'get', 
			onComplete: function() { 
				setLinks(element,element.dTarget);
				new Effect.Highlight(element.dTarget, {duration: 1}); 
			} 
		});
	}


	function setLinks(e, target) {
		(target) ? selector = '#'+target+' ' : selector = '';
		$$(selector+'a.dynamic').invoke('dynamicize');
	}

	Event.observe(window, 'load', setLinks);

Download & Demo

How You Use It

I'm big on javascript that is unobtrusive - so other from including the script (along with prototype.js) all you have to do is assign a class and target to your designated anchor tags. Here's a breakdown:

So the link in the example below would load test.html into the div with the ID of content.

	<div id="content"> This content will be replaced once 
	the link below is clicked.</div>
	<a target="content" href="demo.html" class="dynamic">Load Demo</a>

Important Lessons Learned

I said at the beginning of this post I'm not really a javascript expert so this was a learning process for me. The greatest challenge for me was understanding how binding works - it's something I'm still slightly confused on actually. How to reference functions with callbacks was an additional challenge. Overall I would say there were three main 'gotchas' that stumped me for a while.

Callbacks need to be functions to call functions: if you use a callback like 'onComplete' you can't just say 'onComplete: myfunction()'. Instead you must do something like 'onComplete: function() { myfunction(); }'. If you don't the function get's executed as the assignment to the callback is interpreted. I believe I'm explaining that right. In my case the effect was rendering immediately when you clicked on the link as opposed to delaying until the new content had loaded. This was very bad because if the new content contained dynamic links the script couldn't bind the event behaviors to them.

Elements need to be extracted from an Event object: in my dynamicallyLoad function I wanted to refer to the object that triggered the event as 'this'. I thought I could because I binded the function to it as an event listener but that's where I confused myself. What you need to do is reference the Event object and then get the element from that. That is why the first line of the function reads 'element = Event.element(e);'.

This script is not rocket science, but it is a useful starting place for me in a lot of projects that need to make minor use of AJAX updates on click. I've already expanded this to be less generic to suit the current project I wrote it for. I'll share with you more basic prototype lessons as I get more chances to utilize it in forthcoming projects.

Note: I bundled the source with a compressed version of Scriptaculous and Prototype. You can learn more about these compressed versions of prototype at the Google Code project page.