How to Use AJAX in a WordPress Shortcode?

I have a code to display a random quote. One person wrote a function to implement all of this. But the update data via AJAX for some reason does not work. When you press the button “New Quote”, nothing happens. Maybe someone knows why? What needs to be fixed in the following code, so that when you click “New quote” a new quote is loaded?

The PHP

/wp-content/themes/%your_theme%/js/ajax-load-quote.php

Read More
 <?php
 /* uncomment the below, if you want to use native WP functions in this file */
// require_once('../../../../wp-load.php');

 $array = file( $_POST['file_path'] ); // file path in $_POST, as from the js
 $r = rand( 0, count($array) - 1 );

 return '<p>' . $array[$r] . '</p>';
 ?>

The HTML Structure

In a page content, a widget or a template file:

<div id="randomquotes">
    <p>I would rather have my ignorance than another man’s knowledge,
       because I have so much more of it.<br />
       -- Mark Twain, American author & Playwright</p>
</div>
<a id="newquote" class="button" href="#" title="Gimme a new one!">New Quote</a>

This you can obviously adjust to your liking, but for the sake of this example, this is what we’re going with.
We will generate the above via a shortcode later.

The jQuery

/wp-content/themes/%your_theme%/js/ajax-load-quote.js

function ajaxQuote() {
    var theQuote = jQuery.ajax({
        type: 'POST',
        url: ajaxParams.themeURI+'js/ajax-load-quote.php',
        /* supplying the file path to the ajax loaded php as a $_POST variable */
        data: { file_path: ajaxParams.filePath },
        beforeSend: function() {
            ajaxLoadingScreen(true,'#randomquotes');
        },
        success: function(data) {
            jQuery('#randomquotes').find('p').remove();
            jQuery('#randomquotes').prepend(data);
        },
        complete: function() {
            ajaxLoadingScreen(false,'#randomquotes');
        }
    });
    return theQuote;
}
/* Loading screen to be displayed during the process, optional */
function ajaxLoadingScreen(switchOn,element) {
    /* show loading screen */
    if (switchOn) {
        jQuery(''+element).css({
            'position': 'relative'
        });
        var appendHTML = '<div class="ajax-loading-screen appended">
            <img src="'+ajaxParams.themeURI+'images/ajax-loader.gif"
                alt="Loading ..." width="16" height="16" /></div>';
        if( jQuery(''+element).children('.ajax-loading-screen').length === 0 ) {
            jQuery(''+element).append(appendHTML);
        }
        jQuery(''+element).children('.ajax-loading-screen').first().css({
            'display': 'block',
            'visibility': 'visible',
            'filter': 'alpha(opacity=100)',
            '-ms-filter': '"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"',
            'opacity': '1'
        });
    } else {
        /* hide the loading screen */
        jQuery(''+element).children('.ajax-loading-screen').css({
            'display': '',
            'visibility': '',
            'filter': '',
            '-ms-filter': '',
            'opacity': ''
        });
        jQuery(''+element).css({
            'position': ''
        });
    }
}
/* triggering the above via the click event */
jQuery('#newquotes').click( function() {
    var theQuote = ajaxQuote();
    return false;
});

functions.php

/wp-content/themes/%your_theme%/functions.php

function random_quote( $atts ) {
    /* extracts the value of shortcode argument path */
    extract( shortcode_atts( array(
        'path' => get_template_directory_uri() . '/quotes.txt' // default, if not set
    ), $atts ) );
    $array = file( $path );
    $r = rand( 0, count($array) - 1 );
    $output = '<div id="randomquotes">' .
            '<p>' . $array[$r] . '</p>' .
        '</div>' .
        '<a id="newquote" class="button" href="#" title="Gimme a new one!">New Quote</a>';
    /* enqueue the below registered script, if needed */
    wp_enqueue_script( 'ajax-quote' );
    /* supplying the file path to the script */
    wp_localize_script(
        'ajax-quote',
        'ajaxParams',
        array(
            'filePath' => $path,
            'themeURI' => get_template_directory_uri() . '/'
        )
    );
    return $output;
}
add_shortcode( 'randomquotes', 'random_quote');
/* register the js */
function wpse72974_load_scripts() {
    if ( ! is_admin() ) {
        wp_register_script(
           'ajax-quote', 
            get_template_directory_uri() . '/js/ajax-load-quote.js',
            array( 'jquery' ),
            '1.0',
            true
        );
    }
}
add_action ( 'init', 'wpse72974_load_scripts' );

How you can update the content on the page using AJAX in WordPress?

Related posts

Leave a Reply

5 comments

  1. Nice question!

    But, as your code starts with a bad practice – require_once('wp-load.php'); -, I decided to pick one of my working snippets and adapt it.

    Observations:

    • Differently from your code, no external quotes.txt is being grabbed, here a Post Type is used as source (post), in the method get_random_post
    • There can only be one instance of the shortcode in a given page, as it is based on specific element ID’s (#newpost-shortcode and #randomposts)
    • As usual, it’s better to create a plugin for this. Follow the code comments.

    /wp-content/plugins/so-ajax-shortcode/so-ajax-shortcode.php

    <?php
    /**
     * Plugin Name: (SO) Ajax Shortcode
     * Description: Demonstration of WordPress Ajax working as a shortcode.
     * Plugin URI:  http://stackoverflow.com/a/13614297/1287812
     * Version:     2013.10.25
     * Author:      Rodolfo Buaiz
     * Author URI:  https://wordpress.stackexchange.com/users/12615/brasofilo
     * License:     GPLv3
     */
    
    add_action(
        'plugins_loaded',
        array ( B5F_SO_13498959::get_instance(), 'plugin_setup' )
    );
    
    class B5F_SO_13498959
    {
        private $cpt = 'post'; # Adjust the CPT
        protected static $instance = NULL;
        public $plugin_url = '';
        public function __construct() {}
    
        public static function get_instance()
        {
            NULL === self::$instance and self::$instance = new self;
            return self::$instance;
        }
    
        /**
         * Regular plugin work
         */
        public function plugin_setup()
        {
            $this->plugin_url = plugins_url( '/', __FILE__ );
            add_shortcode( 'randomposts', array( $this, 'shortcode') );
            add_action( 'wp_enqueue_scripts', array( $this, 'enqueue' ) );
            add_action( 'wp_ajax_query_rand_post', array( $this, 'query_rand_post' ) );
            add_action( 'wp_ajax_nopriv_query_rand_post', array( $this, 'query_rand_post' ) );
        }
    
        /**
         * SHORTCODE output
         */
        public function shortcode( $atts ) 
        {
            # First post
            if( ! $random_post = $this->get_random_post() )
                $random_post = __( 'Could not retrieve a post.' );
            # Prepare output
            $output = sprintf(
                '<div id="randomposts">%s</div>
                 <button id="newpost-shortcode" type="button" title="%s">%s</button>',
                $random_post,
                __( 'Gimme a new one!' ),
                __( 'New random post' )
            );
            return $output;
        }
    
        /**
         * ACTION Enqueue scripts
         */
        public function enqueue() 
        {
            # jQuery will be loaded as a dependency
            ## DO NOT use other version than the one bundled with WP
            ### Things will BREAK if you do so
            wp_enqueue_script( 
                 'ajax-random-post',
                 "{$this->plugin_url}ajax.js",
                 array( 'jquery' )
            );
            # Here we send PHP values to JS
            wp_localize_script( 
                 'ajax-random-post',
                 'wp_ajax',
                 array( 
                     'ajaxurl'      => admin_url( 'admin-ajax.php' ),
                     'ajaxnonce'   => wp_create_nonce( 'ajax_post_validation' ),
                     'loading'    => 'http://i.stack.imgur.com/drgpu.gif'
                ) 
            );
        }
    
        /**
         * AJAX query random post
         * 
         * Check for security and send proper responses back
         */
        public function query_rand_post()
        {
            check_ajax_referer( 'ajax_post_validation', 'security' );
            $random_post = $this->get_random_post();
            if( !isset( $random_post ) )
                wp_send_json_error( array( 'error' => __( 'Could not retrieve a post.' ) ) );
            else
                wp_send_json_success( $random_post );
        }
    
        /**
         * AUX FUNCTION 
         * Search a random Post Type and return the post_content
         */
        public function get_random_post()
        {
            $array = get_posts( 
                array( 
                      'post_type' => $this->cpt,
                      'numberposts' => -1 
                ) 
            );
            if( empty( $array ) )
                return false;
    
            # Select a random post index number from the current array
            $r = rand( 0, count($array) - 1 );
            return $array[$r]->post_content;
        }
    }
    

    /wp-content/plugins/so-ajax-shortcode/ajax.js

    /* 
     * @plugin SO Ajax Shortcode
     */
    
    jQuery( document ).ready( function( $ ) 
    { 
         var data = {
             action: 'query_rand_post',
             security: wp_ajax.ajaxnonce
         };
         var image = '<img src="' + wp_ajax.loading + '" alt="Loading ..." width="16" height="16" />';
    
        $( '#newpost-shortcode' ).click( function(e) 
        {
            e.preventDefault();
            $( '#randomposts' ).html( image );
            $.post( 
                wp_ajax.ajaxurl, 
                data,                   
                function( response )
                {
                    // ERROR HANDLING
                    if( !response.success )
                    {
                        // No data came back, maybe a security error
                        if( !response.data )
                            $( '#randomposts' ).html( 'AJAX ERROR: no response' );
                        else
                            $( '#randomposts' ).html( response.data.error );
                    }
                    else
                        $( '#randomposts' ).html( response.data );
                }
            ); 
        }); // end click
    });
    
  2. Here sample plugin as answer to the question. Using ajaxurl at frontend.

    so-random-quotes.php

    <?php
    /*
    Plugin Name: SO Random Quotes
    Plugin URI: http://azzrael.ru
    Description: Reference to http://stackoverflow.com/questions/13498959/how-to-use-ajax-in-a-wordpress-shortcode
    Version: 1.0.0
    Author: Azzrael
    Author URI: http://azzrael.ru
    */
    
    new SoRandomQuotes();
    
    /**
     * Class SoRandomQuotes
     */
    class SoRandomQuotes{
    
        const SHORTCODE_KEY = 'randomquotes'; // usage [randomquotes path='/path/to/file/another.quotes.csv']
        const AJAX_ACTION = 'so_getnewquote'; // ajax action
        const DOM_TARGET =  'randomquotes'; // dom element to put the quotes
    
        /**
         * SoRandomQuotes constructor.
         * init actions
         */
        function __construct() {
            // adding shortcode
            add_shortcode('randomquotes', array($this, 'addShortcode'));
    
            // adding ajax callbacks
            add_action( 'wp_ajax_'.self::AJAX_ACTION, array($this, 'getQuoteAjax')); // admin
            add_action( 'wp_ajax_nopriv_'.self::AJAX_ACTION, array($this, 'getQuoteAjax')); // front
        }
    
        /**
         * Shortcode callback
         * @param $atts
         * @return string
         */
        public function addShortcode($atts){
    
            // getting path value from shortcode atts
            $got =shortcode_atts( array(
                'path'    => plugin_dir_path( __FILE__ ).'quotes.txt',
            ), $atts );
    
            // shortcode replacement
            $out = sprintf(
                        '<div id="%s">%s</div><a id="newquote" class="button" href="#" title="Gimme a new one!">New Quote</a>',
                        self::DOM_TARGET,
                        $this->getQuote($got['path'])
                   );
    
            // loading js
            // jquery depends
            wp_enqueue_script('sorandquo-js', plugin_dir_url( __FILE__ ).'quote-loader.js', array('jquery'));
            // passing to js needed vars
            wp_localize_script( 'sorandquo-js', 'ajaxParams',
                array(
                    'path'      => $got['path'], // path to qoutes file
                    'targetDom' => '#'.self::DOM_TARGET, // dom path to put resulting qoute
                    'ajaxurl'   => admin_url( 'admin-ajax.php'), // for frontend ( not admin )
                    'action'    => self::AJAX_ACTION, //
                )
            );
    
            // render shortcode replacement
            return $out;
        }
    
        /**
         * Ajax Callback
         */
        public function getQuoteAjax(){
            echo $this->getQuote($_POST['path']);
            die();
        }
    
        /**
         * Getting random Qoute from the file
         * @param $path
         * @return mixed
         */
        public function getQuote($path){
            $quotesFile = is_file($path) ? file_get_contents($path):"File {$path} not found";
            $quotesArr = $quotesFile ? explode("n", $quotesFile):['Quotes File is empty'];
            return $quotesArr[array_rand($quotesArr)];
        }
    
    }
    

    quote-loader.js

    jQuery.noConflict();
    jQuery(document).ready(function($) {
        $(document).on('click', '#newquote', function (e) {
            e.preventDefault();
    
            $.post(ajaxParams.ajaxurl, {
                'action':ajaxParams.action,
                'path'  :ajaxParams.path
            }, function (ret) {
                $(ajaxParams.targetDom).html(ret);
            }, 'html');
        });
    });
    
  3. WordPress shortcode same like function where you give parameters,

    for creating ajax request you can use jQuery.ajax or xmlhttp in your header or function file with add_action wp_head hook.

    you should create ajax.php in your theme folder and on the top of file you should include wp-load.php. and place all your ajax functions in proper manner.

  4. Add this function in function.php:

        <?php
        add_action('wp_head','ajaxurl');
        function ajaxurl()
        {
        ?>
            <script type="text/javascript">
            var ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>';
            </script>
        <?php
        }?>