AJAX for WordPress


Can of Ajax Cleanser
April 2013

Contents


This is an unusually long and dense article! It is necessary to adequately cover this topic. You will often see Next to Contents links. Of course, the "to Contents" link will bring you back here. The "Next" link just takes you to the section immediately below. Not that useful, except that it makes it easy to bookmark where you left off so you can return straight to it at a later time.


1. Overview


AJAX is a common technique used in modern web applications, so many developers are familiar with it's use. Unfortunately, how it is implemented in WordPress is unique, so seasoned web developers new to WordPress will need to alter their ways slightly. If you are new to AJAX, getting it working in your WordPress plugin by referring to the CodexLink is to outside of this site examples will likely be more confusing than helpful. I hope to remove this potential confusion here and properly illustrate the "WordPress Way" of using AJAX to developers of all skill levels. You experienced types can skim ahead now while I get our novices caught up.

AJAX is the abbreviation for Asynchronous JavaScript And XML. In contrast to the traditional HTML model where entire pages of data are sent for each request, AJAX makes a request in response to various events on a page, then only updates the effected elements of the page based on the response. Not only does this reduce the amount of data that needs to be sent, but the user experience becomes much more interactive.

WordPress uses AJAX for many Administration Panel functions, but the default theme (currently twentytwelve) uses none at all for the public side of WordPress. However, many themes do utilize AJAX for various public user experiences.

In order to successfully enable an AJAX interchange, you must create both client side jQuery (or JavaScript) code and server side PHP code. While JavaScript alone without the jQuery libraries can be used for AJAX, this article will focus on jQuery primarily because the selectors provide much more flexibility in choosing HTML DOM elements and the libraries are already loaded by default for WordPress. AJAX without jQuery can be problematic due to inconsistent browser implementations (looking at you, IE!) of straight JavaScript AJAX.

An AJAX exchange always begins with some sort of event on the user's page in their browser, typically a mouse click, but any event could serve as a trigger. The event initiates an associated jQuery function which sends a request to your server, including any pertinent information the server needs to process the request.

When the server receives such a request, it does what it's supposed to do based on the information received. What this is is entirely up to you and only limited by the PHP language, your specific server limitations, and the data available. Though not required, the server would typically send a response (often formatted as XML) once it's programmed tasks are completed.

The jQuery function that sent the initial request will receive the response and do something with it, usually displaying something to the user as confirmation that the initial event either succeeded or failed to produce the desired sequence of events. That's AJAX in a nutshell, now on to actually making it work.

1.1 Files

The code for AJAX can be contained within one of a few WordPress features: Plugin, Theme, or Child Theme. The examples used in this article are for implementation as a Plugin, though they can be easily adapted for theme use. PHP files are typically stored in the plugin's or theme's main folder. For themes, the code would almost always be in the functions.php file. The plugin file can be named anything, but the main plugin PHP file must have specific data in the initial comments identifying it as the main plugin file. The code could also be in an auxillary file that is referenced in the main plugin file with an include or require statement of some sort.

The JavaScript or jQuery code can be echoed into the page's header via PHP script, but I prefer to compartmentalize JavaScript code in separate .js files contained in a /js/ sub-folder under the main plugin or theme folder. This arrangement makes maintenance of the code easier and follows the arrangement used by many programmers. The following examples reflect this arrangement.
Next to Contents

2. jQuery (Client side)


jQuery Logo This section covers the jQuery code required to both send a request and handle the response. The server side PHP code for receiving the request and handling a response is covered in the PHP section.

2.1 Wrapper

While much of this section covers client side AJAX in general, one aspect specific to WordPress and a few other applications is the dollar sign ($) jQuery shortcut is not directly available for you to use in your code. This because the jQuery library is loaded by WordPress in the "no conflict"Link is to outside of this site mode. You can still use the $ shortcut in your code if your code is contained within a "no conflict" wrapper:
jQuery(document).ready(function($) {
	// $() will work as an alias for jQuery() inside of this code block "wrapper"
});

2.2 Selector

Causing your jQuery code to react to an event begins by using jQuery selector arguments to specify which DOM object should be monitored for an event. These are the CSS like descriptors contained in the parenthesis immediately following the initial $ or jQuery code. Selectors are well documentedLink is to outside of this site elsewhere, so we will not get into any detail here. Here is a basic example of selecting a table cell with a class value of "author" (<td class="author">):
$(td.author);
Note that each line of code in JavaScript and jQuery, as well as PHP and mySQL, all terminate with a semi-colon (;).

2.3 Event

Mouse clip art There are many events that your code can watch for to initiate an AJAX exchange. One commonly used event is a mouse click. Another common one is a form element change in value. There are also keyboard and window events available to trigger events. Events are also well documentedLink is to outside of this site elsewhere, so we will not cover them in detail. This is an example of having the function toggle_onoff() execute when a click event occurs on a table cell of the class "author":
$(td.author).click(toggle_onoff);
Since event handler functions are rarely reused elsewhere, instead of naming a function to pass to the event method, it's very common to define an anonymous function right inside the method's parenthesis. This is sometimes referred to as a closure, because variables in closures have a special scope which you would not see in named functions. This can give closures a big advantage over named functions, especially ones related to AJAX. An anonymous event handler would look like:
$(td.author).click(function() {
	// insert code here to execute when table cell is clicked on
});
Notice JavaScript and PHP both share the same type of C style comment delimiters: // and /* */

When a DOM element's event triggers a handler function, you can use the variable this to refer to the element. This is very useful because there may be several <td> elements of class "author", but only one of them was clicked on, the this one. There may not be any other selector that works for this one object. For example, even if there are many table cells of the class "author", the following code will change the color of only the cell that was clicked on to red:
$(td.author).click(function() {
	$(this).css('color', 'red');
});
Next to Contents

2.4 Post

Mailbox jQuery uses two different ways to send a request to the server, GET and POST. We will focus on the POST method because it affords a little more privacy and is more conducive for submitting form data. While event handlers can execute any valid JavaScript code, AJAX events will all at least contain a .get(), .post() or related method. Very often, one or the other is the only code that needs to be executed. Adding the .post() method into our event handler results in something like this:
$(td.author).click(function() {
	$.post(server_url, submit_data, callback_handler);
});
Let's now take a look at the different parameters passed in the .post() method. There is actually a fourth optional parameter that specifies the data type returned from the server. The default of letting jQuery make an intelligent guess typically works fine for nearly every situation, so you will rarely see the fourth parameter.

2.4a URL

The server_url parameter for the .post method is the PHP page which will handle the request. In WordPress, this is always the same page. Conveniently, WordPress defines a local AJAX class object to be used as a receptacle for AJAX exchanges. The class object is instantiated in WordPress's PHP localize scriptLink is to outside of this site function, we will name ours ajax_object. It keeps the url in a property we will name ajax_url. Assigning these names will be covered in a related PHP section later. You don't want to hard code an URL here because it would only work for one site, you couldn't offer your plugin for anyone else to use without the need to edit every URL in every AJAX call. You instead construct the proper URL for each site with PHP and save it in the localized object. We will also cover this later. For now, you just need to know that you access the url in JavaScript as:
ajax_object.ajax_url
For admin side AJAX, the server_url is also available as the global variable ajaxurl. Since this is not defined for public side AJAX, for the sake of consistency, I suggest you always pass the URL in the localized AJAX object and not use the global version.
Next to Contents

2.4b Data

The submit_data parameter will vary for each application, though you will always see one or two common elements. This is the data you pass to the server, which it will act on in some way. The data passed is often the contents of a form. All the data is assembled into an associative array to be passed to .post() as a single element.
2.4b(1) Nonce
Any AJAX request that could result in a change to the database needs to be secured by a nonce, a portmanteau of "Number used ONCE". It is essentially a unique hexadecimal serial number assigned to each instance of any form served. When the form's data is submitted, the nonce is checked for validity by the server. Only forms with a valid nonce are accepted for processing. For details on defining a nonce, see the PHP Nonce section. For now, all you need to know is a nonce value the server creates is added to the ajax_object object as a named property. You will know the name because you will define it in the PHP part of the AJAX implementation. Let's say it's name will be author.

The default array key for nonces is _ajax_nonce. You can use a different key, but it must be coordinated with the PHP portion or else the nonce check will not know how to access the passed value. The beginning of our array definition looks like this:
{_ajax_nonce: ajax_object.author}
2.4b(2) Action
The required action parameter is the name appended to the AJAX action hook in PHP to channel the request to the correct code. If our action in our example is change, our data array becomes:
{action:    "change",
_ajax_nonce: ajax_object.author}
Again, this parameter is coordinated with the PHP code so everything works together.
2.4b(3) Data
Additional data is optional. Sometimes just the fact a request came in with a particular action is enough information. Other times, form data needs to be sent. It all depends on the application. If we want to send an author name in our request, our data array becomes:
{action:      "change",
_ajax_nonce:   ajax_object.author,
 author_name: "Elvis"}
If you wanted to get fancier, you could get the contents of the element clicked on by using the this variable, but that's for another jQuery lesson.
Next to Contents

2.4c Callback

Telephone clip art The callback handler is the function to execute when a response comes back from the server after the request is made. Once again, we usually see an anonymous function here. The function is passed one parameter, the server response. The response could be anything from a yes or no to a huge XML database. JSON formatted data is also a useful format for data. For our example, the response will either be "yes" or "no". If the response is "yes", we simply change the CSS color of the DOM element. The anonymous function looks like this:

function(data) {
	if ('yes' == data) {
		$(this2).css('color', 'red'); 
	}
}
Notice the use of this2 to specify the <td> element clicked on, it is a copy of the initiating event's this, that special variable mentioned earlier that differentiates the one element clicked on from all other elements matching the same selector that could have been clicked on and handled here. The callback is out of scope to use the original this, so we make a copy of it within scope of the action function for callback use by inserting the following line before the call to .post() :
var this2 = this;
You can see how this fits into the complete code listing in the jQuery Summary.
2.4c(1) Nonces
Note that when our code executes, we will have used the initial nonce contained in the ajax_object.author property by sending it back to the server. It normally cannot be used again. In order to make another request before a page reload, the server would typically need to send a new nonce to use in the next request. Our callback function would need to take this new nonce and place it in the property ajax_object.author.

As far as WordPress is concerned, it's "nonce" is not number used once, it is number used as much as you want in 24 hours. It is not a true nonce implementation. So there is not really a need to refresh the nonce on each request in WordPress. I mention how to refresh the value simply to illustrate the proper use of a true nonce.
2.4c(2) XML
While XML is the X in AJAX, and it is the classic way to structure data, it's rather unwieldy and modifying the structure by script can be difficult. Many programmers prefer JSON for it's light weight and ease of use.

How an XML string is parsed depends on the browser, so for full compatibility, you need to implement both parsers. Use the Microsoft.XMLDOM ActiveX object's .loadXML()Link is to outside of this site method for Internet Explorer and the DOMParserLink is to outside of this site object's .parseFromString() method for all other browsers.
2.4c(3) JSON
This format is based on JavaScript, so you can actually convert JSON text to an object simply with the eval() function. However, using eval() carries some significant security risks, so you should use a JSON parser, which will be faster as well. The global instance of the parser is JSON, and it's parse method is .parse()Link is to outside of this site, so do something like so:
obj = JSON.parse(txt);
To ensure the JSON parser is available to the browser, it must be enqueued on the originating PHP page. An example is provided in the related PHP Section.
2.4c(4) Other
As long as the data format is coordinated, it can be any format you like, such as comma delimited, tab delimited, or any kind of structure that works for you.
Next to Contents

2.5 Summary

Putting together all the various elements covered in this section, our .js file has the following contents:
jQuery(document).ready(function($) {		//the wrapper
	$(td.author).click(function() {		//the selector and event
		var this2 = this;		//copy 'this' so that the callback can use it
		$.post(ajax_object.ajax_url, {	//the server_url
			 action:      "change",	//the submit_data array
			_ajax_nonce:   ajax_object.author,
			 author_name: "Elvis"
		}, function(data) {		//the callback_handler
			if ('yes' == data) {
				$(this2).css('color', 'red'); 
			}
		});
	});
});
Next to Contents

3. PHP (Server side)


Bank of computers This section covers the PHP code needed to handle an AJAX request and respond to it. The client side jQuery code is covered in the jQuery section. Most of this PHP code is specific to WordPress and has no application in other environments.

3.1 Enqueue

In order for WordPress to recognize and load our .js file we created in the jQuery section, we need to enqueue it. This is done by hooking either the 'admin_enqueue_scripts' action for administration pages or the 'wp_enqueue_scripts' action for other pages. Login pages require the use of the 'login_enqueue_scripts' action. The action call passes the name of the page being loaded to your hook function, so you can control which page you load your script on so that it is not unnecessarily loaded on every page. An example hook for the edit-comments.php admin page looks like this:
function my_enqueue($hook) {
	if( 'edit-comments.php' != $hook) return;
	// add add enqueue stuff here
}
add_action('admin_enqueue_scripts', 'my_enqueue');

3.1a wp_enqueue_script()

You call the wp_enqueue_script()Link is to outside of this site function to enqueue your JavaScript file. It takes up to 5 parameters, but you typically only need to provide the first 3. The first is an arbitrary tag to identify your script, which can be almost anything. Let's call ours 'ajax_script'. The second parameter is a complete path to our JavaScript file. For flexibility, you never hard code path specifications, you use tags such as plugins_url().Link is to outside of this site The third parameter is an array of dependent script tags. These are other scripts your script needs in order for it to function properly. Any tags listed will have their associated files included or loaded when your script loads. Our script is dependent only on jQuery, but we must still pass it in an array. A sample enqueue script line looks like this:
wp_enqueue_script( 'ajax-script',
	plugins_url( '/js/myjquery.js', __FILE__ ), 
	array('jquery')
);

3.1b Nonce

As mentioned above under jQuery Data, a nonce serves to ensure data is submitted from a valid form. You construct one here so you can pass it to the jQuery function so it can prove it's identity. You use the function wp_create_nonce().Link is to outside of this site It takes one parameter, an arbitrary string which is also used to check the nonce when it is submitted. You assign the returned value to a variable for use in the localize function described next. Example:
$author_nonce = wp_create_nonce( 'author_example' );

3.1c Localize

To localize a script, you create an object that will be local to the JavaScript or jQuery script you enqueued previously. You can attach any needed properties to this object using PHP and they will be available to your JavaScript functions. This is one of the few ways PHP can pass parameters to JavaScript. You localize by calling wp_localize_script().Link is to outside of this site The function takes 3 parameters. The first is the tag you used to enqueue the script. The second is an arbitrary name for your AJAX object. The third parameter is an associative array of properties you want available in your object. For the example in the next section, we are only passing a nonce and the URL for the PHP WordPress AJAX handler page.
3.1c(1) URL
All WordPress AJAX requests get handled by the same file: wp-admin/admin-ajax.php. Even for front end public functions that are not admin related, the request still goes to this page. This has caused issues for people that have hardened access to the /wp-admin/ folder for security purposes. Hardened access is fine, but if your theme uses front end AJAX, the admin-ajax.php file must be excluded from the hardened access. The file needs to be accessible to all users. Also, once again, you never hard code URLs, you use the appropriate tags, in this case admin_url().Link is to outside of this site Our localize function will look like this:
wp_localize_script( 'ajax-script', 'ajax_object', array(
	'ajax_url'	=> admin_url( 'admin-ajax.php' ),
	'author'	=> $author_nonce,	//it is common practice to comma after
));						//the last array item for easier maintenance
All the names used here must be coordinated with the jQuery script for everything to properly come together. In jQuery, the data assigned here is accessed as object properties. So the data we assigned in wp_localize_script() will be referenced as ajax_object.ajax_url and ajax_object.author in JavaScript.
Next to Contents

3.2 AJAX Action

AJAX can As noted above, all AJAX requests are routed to the same page. That page extracts the action parameter contained in the request and invokes a custom action who's tag name is formed by the action parameter's value being prefaced by either "wp_ajax_" or "wp_ajax_nopriv_". Which preface is used depends on whether the current user is logged in or not. For front end requests where the user may or may not be logged in, you should hook both actions. Since our request has an action value of "change", and assuming the user must be logged in to do this action, the hook used to handle our request will look like:
function my_change() {
	// handle the ajax request
	die; // all ajax handlers should die when finished
}
add_action('wp_ajax_change', 'my_change');
Which will result in the function my_change() being called when our request is received. Attentive readers might wonder if they could use an anonymous function or closure to define the action handler. You can, but closures are a recent PHP feature that is not supported in older versions. For maximum backwards compatibility, you should avoid using closures for now.

Hooks are a significant part of WordPress development. If you are unfamiliar with this concept, take a look at the CodexLink is to outside of this site to get caught up on this important topic.

3.2a Referrer

For security purposes, if a nonce is used to secure the data exchange, one of the first things you should do is verify the nonce. You use check_ajax_referer()Link is to outside of this site. It takes three parameters. The first is the exact same string we used for wp_create_nonce()Link is to outside of this site. The second is the key name under which the request's nonce is stored. If the default _ajax_nonce was used as a key, no name need be specified. The default behavior when the nonce fails to verify is to simply die. If you want the function to return true/false so additional code can be executed, pass true as the third parameter. In our example, we can simply check the nonce with:
check_ajax_referer('author_example');
Note. The correct English spelling is 'referrer' with 4 'r's. When the HTTP header specification was written, no one noticed the the misspelled 'referer' with 3 'r's. So now, all code related to the 'referer' header is typically intentionally misspelled to match the specification. The Referer Header is supposed to contain the page URL that sent the HTTP request, but this header is easily spoofed, so the wp_check_referer() wants to see that the nonce value is correct before deciding the request came from a legitimate page.

3.2b Capability

Unless the action to be performed is something anyone can do, you must verify the current user has the proper capability to do the action. The capability should be related to the action being performed. Custom capabilities can be assigned if need be. In order to allow an action based on role, it's easiest to just identify a capability only owned by the role desired and specify that in the function current_user_can()Link is to outside of this site. It can lead to unpredictable results if you pass a role to this function. Example:
current_user_can('manage_options');

3.2c Action

The action handler function does whatever needs doing. It may involve adding or deleting something in the database, or simply looking something up to send back to the client. In our quite silly example, the handler checks if there is a match between the sent value "author" and an option in the WordPress options table called 'author' and responds accordingly. An example follows in the Summary section.
Next to Contents

3.2d Response

A response is sent to the client by echoing the data. It can take several forms depending on the need.
3.2d(1) XML
XML is a common format to send structured data with. WordPress provides the WP_Ajax_ResponseLink is to outside of this site class to assist in assembling XML responses to AJAX requests.
3.2d(2) JSON
JSON is another format for sending structured data which has some advantages: lightweight and ease of use. There is no native WordPress support for this, but a few plugins exist to fill the void. The PHP function. json_encode()Link is to outside of this site can be used to generate data strings from any PHP data structure, including arrays and objects, but not resources.

To ensure the JSON parser in loaded for your JavaScript running on the client browser, first enqueue it on your PHP page or as part of the 'init' hook.
wp_enqueue_script( 'json2' );
Then specify 'json2' as one of the dependencies when you enqueue your script in PHP.
wp_enqueue_script( 'ajax-script',
	plugins_url( '/js/myjquery.js', __FILE__ ), 
	array('jquery', 'json2')
);
3.2d(3) Other
The response can be in any format as long as it is coordinated with the JavaScript interpreting the response. Our example simply returns either yes or no.
if( $_POST['author_name'] == get_option( 'author')){
	echo 'yes';
} else {
	echo 'no';
}
Early versions of this code had the wrong $_POST key specified, this has since been corrected.

The $_POST array is a PHP super global which contains all the data included in the AJAX POST request. You may see other references that use $_REQUEST instead of $_POST. $_REQUEST is a merge of $_POST, $_GET, and $_COOKIE. Convenient, but a little dangerous due to possible name collisions, where $_COOKIE takes precedence. An attacker could create a cookie in your site's name, perhaps named 'action' which would in effect override any action sent by your form. This would mean the attacker could possibly run any arbitrary PHP code that is on your server, with the credentials WordPress runs under instead of a low privileged user. Highly unlikely to be sure, but insecure none the less. Avoid using $_REQUEST.
3.2d(4) Die
By definition, all AJAX handlers must terminate their response with a call to the die() function when they have completed all processing.
Next to Contents

4. Summary

Our complete PHP code looks like this:
<?php
/* Enqueue required scripts and prepare needed data */
function my_enqueue($hook) {
	if( 'edit-comments.php' != $hook) return;
	wp_enqueue_script( 'ajax-script', 
		plugins_url( '/js/myjquery.js', __FILE__ ), 
		array('jquery')
	);
	$author_nonce = wp_create_nonce( 'author_example' );
	wp_localize_script( 'ajax-script', 'ajax_object', array( 
		'ajax_url' => admin_url( 'admin-ajax.php' ), 
		'author' => $author_nonce,
	));
}
add_action('admin_enqueue_scripts', 'my_enqueue');

/* Handle an AJAX request */
function my_change() {
	check_ajax_referer('author_example'); // dies without valid nonce
	if( current_user_can('manage_options')) {
		if( $_POST['author_name'] == get_option( 'author')){
			echo 'yes';
		} else {
			echo 'no';
		}
	}
	die; // all ajax handlers should die when finished
}
add_action('wp_ajax_change', 'my_change');
//The final ?> in PHP files is commonly omitted and is actually considered good practice
And once again, our myjquery.js file contains:
jQuery(document).ready(function($) {		//the wrapper
	$(td.author).click(function() {		//the selector and event
		var this2 = this;		//copy 'this' so that the callback can use it
		$.post(ajax_object.ajax_url, {	//the server_url
			 action:      "change",	//the submit_data array
			_ajax_nonce:   ajax_object.author,
			 author_name: "Elvis"
		}, function(data) {		//the callback_handler
			if ('yes' == data) {
				$(this2).css('color', 'red'); 
			}
		});
	});
});


Comments, feedback, and questions are always welcome. Email me at JavaScript needs to be enabled .
Under the Hood
If you're curious, the code block syntax highlighting on this page is accomplished with the javascript module google-code-prettifyLink is to outside of this site using a modified variant of the supplied "Desert" style by techto...@.