AJAX in Plugins « WordPress Codex

  • For a more recent overview of AJAX in plugins, please refer to the AJAX page of the Plugin Handbook.
  • If you’re looking to use AJAX with the Gutenberg editor, please refer to the Gutenberg Handbook.
  • AJAX can also be used with the REST API, visit the REST API developer handbook to learn more.

Introduction

This article, aimed at plugin developers, describes how to add Ajax to a plugin. Before reading this article, you should be familiar with the following:

  • Ajax – Overview of the technology
  • Writing a Plugin – How to write a plugin
  • Plugin API – Filters and actions – what they are and how to use them
  • How to add HTML to the appropriate WordPress page, post, or screen — for instance, if you want to add Ajax to administration screens you create, you will need to understand how to add administration menus to WordPress; if you want to add Ajax to the display of a single post, you’ll need to figure out the right filters and actions to add HTML to that spot on viewer-facing blog screens. This article does not cover these topics.

Ajax on the Administration Side

Since Ajax is already built into the core WordPress administration screens, adding more administration-side Ajax functionality to your plugin is fairly straightforward.

This short example uses PHP to write our JavaScript in the footer of the page. This script then triggers the AJAX request when the page is fully loaded:

<?php
add_action( 'admin_footer', 'my_action_javascript' ); // Write our JS below here

function my_action_javascript() { ?>
	<script type="text/javascript" >
	jQuery(document).ready(function($) {

		var data = {
			'action': 'my_action',
			'whatever': 1234
		};

		// since 2.8 ajaxurl is always defined in the admin header and points to admin-ajax.php
		jQuery.post(ajaxurl, data, function(response) {
			alert('Got this from the server: ' + response);
		});
	});
	</script> <?php
}


NOTE: Since ajaxurl can be used in case you want to separate your JavaScript code from php files into JavaScript only files. This is true on the administration side only. If you are using AJAX on the front-end, you need to make your JavaScript aware of the admin-ajax.php url. A best practice is documented in the fourth example, below using wp_localize_script().

Since Version 2.8 , The JavaScript global variablecan be used in case you want to separate your JavaScript code from php files into JavaScript only files.If you are using AJAX on the front-end, you need to make your JavaScript aware of theurl. A best practice is documented in the fourth example, below using

Then, set up a PHP function to handle the AJAX request.

<?php 

add_action( 'wp_ajax_my_action', 'my_action' );

function my_action() {
	global $wpdb; // this is how you get access to the database

	$whatever = intval( $_POST['whatever'] );

	$whatever += 10;

        echo $whatever;

	wp_die(); // this is required to terminate immediately and return a proper response
}

Notice how the ‘action’ key’s value ‘my_action’, defined in our JavaScript above, matches the latter half of the action ‘wp_ajax_my_action’ in our AJAX handler below. This is because it is used to call the server side PHP function through admin-ajax.php. If an action is not specified, admin-ajax.php will exit, and return 0 in the process.

You will need to add a few details, such as error checking and verifying that the request came from the right place ( using check_ajax_referer() ), but hopefully the example above will be enough to get you started on your own administration-side Ajax plugin.

Notice the use of wp_die(), instead of die() or exit(). Most of the time you should be using wp_die() in your Ajax callback function. This provides better integration with WordPress and makes it easier to test your code.

Separate JavaScript File

The same example as the previous one, except with the JavaScript on a separate external file we’ll call js/my_query.js. The examples are relative to a plugin folder.

jQuery(document).ready(function($) {
	var data = {
		'action': 'my_action',
		'whatever': ajax_object.we_value      // We pass php values differently!
	};
	// We can also pass the url value separately from ajaxurl for front end AJAX implementations
	jQuery.post(ajax_object.ajax_url, data, function(response) {
		alert('Got this from the server: ' + response);
	});
});

With external JavaScript files, we must first wp_enqueue_script() so they are included on the page. Additionally, we must use wp_localize_script() to pass values into JavaScript object properties, since PHP cannot directly echo values into our JavaScript file. The handler function is the same as the previous example.

<?php
add_action( 'admin_enqueue_scripts', 'my_enqueue' );
function my_enqueue($hook) {
    if( 'index.php' != $hook ) {
	// Only applies to dashboard panel
	return;
    }
        
	wp_enqueue_script( 'ajax-script', plugins_url( '/js/my_query.js', __FILE__ ), array('jquery') );

	// in JavaScript, object properties are accessed as ajax_object.ajax_url, ajax_object.we_value
	wp_localize_script( 'ajax-script', 'ajax_object',
            array( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'we_value' => 1234 ) );
}

// Same handler function...
add_action( 'wp_ajax_my_action', 'my_action' );
function my_action() {
	global $wpdb;
	$whatever = intval( $_POST['whatever'] );
	$whatever += 10;
        echo $whatever;
	wp_die();
}

Ajax on the Viewer-Facing Side

Since WordPress 2.8, there is a hook similar to wp_ajax_(action):

  • wp_ajax_nopriv_(action) executes for users that are not logged in.

So, if you want it to fire on the front-end for both visitors and logged-in users, you can do this:

add_action( 'wp_ajax_my_action', 'my_action' );
add_action( 'wp_ajax_nopriv_my_action', 'my_action' );

Note 1: Unlike on the admin side, the ajaxurl javascript global does not get automatically defined for you, unless you have BuddyPress or another Ajax-reliant plugin installed. So instead of relying on a global javascript variable, declare a javascript namespace object with its own property, ajaxurl. You might also use wp_localize_script() to make the URL available to your script, and generate it using this expression: admin_url( 'admin-ajax.php' )

Note 2: Both front-end and back-end Ajax requests use admin-ajax.php so is_admin() will always return true in your action handling code. When selectively loading your Ajax script handlers for the front-end and back-end, and using the is_admin() function, your wp_ajax_(action) and wp_ajax_nopriv_(action) hooks MUST be inside the is_admin() === true part.

Ajax requests bound to either wp_ajax_ or wp_ajax_nopriv_ actions are executed in the WP Admin context. Carefully review the actions you are performing in your code since unprivileged users or visitors will be able to trigger requests with elevated permissions that they may not be authorized for.

if ( is_admin() ) {
    add_action( 'wp_ajax_my_frontend_action', 'my_frontend_action' );
    add_action( 'wp_ajax_nopriv_my_frontend_action', 'my_frontend_action' );
    add_action( 'wp_ajax_my_backend_action', 'my_backend_action' );
    // Add other back-end action hooks here
} else {
    // Add non-Ajax front-end action hooks here
}

Here the Ajax action my_frontend_action will trigger the PHP function my_frontend_action_callback() for all users. The Ajax action my_backend_action will trigger the PHP function my_backend_action_callback() for logged-in users only.

The post-load JavaScript Event

Plugins and themes that insert content via Ajax must trigger the post-load event on document.body after content is inserted. Other scripts that depend on a JavaScript interaction after content insertion, such as AddToAny or a jQuery Masonry script, listen for the post-load event to initialize their required JavaScript. When the post-load event is fired from Jetpack’s Infinite Scroll, for example, AddToAny displays the share buttons for each post, and jQuery Masonry positions elements on the page.

JavaScript triggering the post-load event after content has been inserted via Ajax:

jQuery( document.body ).trigger( 'post-load' );

JavaScript listening to the post-load event:

jQuery( document.body ).on( 'post-load', function () {
    // New content has been added to the page.
} );

Note on jQuery: Beware of using jQuery’s load method with a selector expression to insert content because it will cause <script> blocks in the response to be stripped out.

Error Return Values

If the Ajax request fails in wp-admin/admin-ajax.php, the response will be -1 or 0, depending on the reason for the failure. Additionally, if the request succeeds, but the Ajax action does not match a WordPress hook defined with add_action('wp_ajax_(action)', ...) or add_action('wp_ajax_nopriv_(action)', ...), then admin-ajax.php will respond 0.

Debugging

To parse AJAX, WordPress must be reloaded through the admin-ajax.php script, which means that any PHP errors encountered in the initial page load will also be present in the AJAX parsing. If error_reporting is enabled, these will be echoed to the output buffer, polluting your AJAX response with error messages.

Because of this, care must be taken when debugging Ajax as any PHP notices or messages returned may confuse the parsing of the results or cause your JavaScript to error.

One option if you can’t eliminate the messages and must run with debug turned on is to clear the buffer immediately before returning your data.

    ob_clean();
    echo $whatever;
    wp_die();

It is also possible to use tools such as FirePHP to log debug messages to your browsers debug console. An alternative approach is to use a debugging proxy such as fiddler.

Further Reading – External Resources