this is my very first question here. So please don't be too angry, if I miss something out or make mistakes while using the plattform. I was, of course, trying to find an answer on my problem first. Didnt work out too good :)

My Problem: I'm trying to implement more dynamic content on a page, so I'm working around with ajax-function-calls in a shortcode.

I managed to follow a lot of tutorials and successfully created a single shortcode with a post-type-parameter, which outputs f.e. the last 5 posts of a type and a pagination. I created the needed ajax function call in js and everything. Its working and so far I thought, I understand the logic behind it.

But when I'm using the shortcode, with another post-type-parameter, twice or more on the same page, only the content of the last shortcode is loaded.

A single shortcode with a given post type is always loading the content correctly.

So now I'm stucked and I think, maybe I didnt get the idea behind ajax-calls and localization for js in wordpress, yet.

If you add the script via action, like you should, its loaded only once, of course. How does localization work in general, when an "ajax.js" is called more than once on a page?

Do I maybe have to create ajax_actions dynamically for every shortcode in a case like that?

I will post my code, if you wish, but maybe the general answer already helpes me to understand the idea behind it.

greetings to everybody


Okay, here is the code:


require_once KNE_THEME_DIR . '/includes/helpers/view-classes/class-kne-theme-data-slider.php';
        $slider = new KNE_DATA_SLIDER();
        add_shortcode('kne_data_slider', array($slider, 'shortcode'));

        // creating Ajax call for WordPress
        add_action('wp_ajax_nopriv_kne_ajax_posts', array($slider, 'get_data_callback'));
        add_action('wp_ajax_kne_ajax_posts', array($slider, 'get_data_callback'));

the dataslider-class


defined('ABSPATH') or die('No script kiddies please!');

    /** Data Slider Konstruktor */
    public function __construct()

    /** Shortcode */

    public function shortcode($atts)
        extract(shortcode_atts(array(), $atts));

        $posts_total = (isset($atts['posts_total']) ? $atts['posts_total'] : -1);
        $posts_per_page = (isset($atts['posts_per_page']) ?$atts['posts_per_page'] : -1);
        $post_type = (isset($atts['post_type']) ? $atts['post_type'] : 'post');

        $kne_unique = rand(1, 9999);
        $nonce = wp_create_nonce('kne_ajax_nonce');
        wp_register_script('kne_ajax_js', KNE_THEME_URI . '/assets/js/ajax-implementation.js', array('jquery'),'', false);

        $data_string = [
            'ajaxurl' => admin_url('admin-ajax.php'),
            'security' => $nonce,
            'page' => $page = (isset($page) ? $page : 1),
            'post_type' => $post_type,
            'posts_total' => $posts_total,
            'per_page' => $posts_per_page,
            'kne_unique_id' => $kne_unique

        wp_add_inline_script('kne_ajax_js', 'var kne_ajax_object = ' . json_encode($data_string), 'after');
        // wp_localize_script('kne_ajax_js', 'kne_ajax_object', $data_string);


        ob_start(); ?>
            <div class="kne-posts-container-<?php echo $kne_unique ?>">
                <span class="kne-loader">Loading...</span>
                    <div class="kne-post-content">
                <div class="kne-post-content_name">
        <?php return ob_get_clean();


    /** Get Data Callback */

    public function get_data_callback($atts)
        // check the nonce - still too simple
        check_ajax_referer('kne_ajax_nonce', 'security');

        //get the data from ajax() call

        if (isset($_POST['page'])) {
            $post_type = $_POST['post-type'];
            $page = $_POST['page'];
            $page -= 1;
            $per_page = (isset($_POST['per_page']) ? $_POST['per_page'] : -1);  //set the per page limit
            $start = $page * $per_page;

            $all_the_posts = new WP_Query(array(
                'post_type'         => $post_type,
                'post_status '      => 'publish',
                'orderby'           => 'post_date',
                'order'             => 'DESC',
                'posts_per_page'    => $per_page,
                'offset'            => $start

            if ($all_the_posts->have_posts()) {
                while ($all_the_posts->have_posts()) {
                    echo the_title('<h3>', '</h3>', true);

    } // end of data-callback-function

} // end of class

the js

jQuery(document).ready(function ($) {

    console.log('Ajax Implementation loading');

    var post_data = {
        'action' : 'kne_ajax_posts',
        'security' : kne_ajax_object.security,
        'page' : kne_ajax_object.page,
        'post-type': kne_ajax_object.post_type,
        'kne_unique_id' : kne_ajax_object.kne_unique_id,
        'per_page' : kne_ajax_object.per_page,

        url: kne_ajax_object.ajaxurl, 
        data: post_data,
        type: 'post',
            (response) => {
                    var container = $('.kne-posts-container-' + kne_ajax_object.kne_unique_id);
                    // var name_active_link = $(container + ' .kne-post-content' + '_name' + ' .kne_posts_pagination li.active');
                    // $(name_var).html(response);
                    // $(container + ' .kne-loader').css('display', 'none');
                    // $(name_active_link).on('click',function(){
                    //     console.log('clicked');
                    //     var page = $().attr('p');
                    //     kne_posts_make_ajax_call(kne_ajax_object, page);
                    // });
                (response) => {

    // $( document.body ).trigger( 'post-load' );
  • there's no code to debug in your question, can you edit it to include all of the code, both your shortcode, AJAX handler, and your javascript?
    – Tom J Nowell
    Feb 1, 2023 at 17:35
  • done. sorry. I should have posted it from the very beginning. thanks for letting me know.
    – Diddi_83
    Feb 1, 2023 at 18:00

1 Answer 1


The problem is that your JS needs to know the post type for each shortcode, but rather than looking at the shortcodes output, it looks at the main global kne_ajax_object, and since there is only one kne_ajax_object, there can only be one post type.

Additionally, your javascript code only runs one time, and only makes one AJAX call, which means there can only be one post type and one shortcode.

Instead, store the AJAX URL/security stuff in kne_ajax_object but put the rest of the information on the HTML the shortcode generates.

E.g. instead of this:

<div class="kne-posts-container-<?php echo $kne_unique ?>">

Why not this?

<div class="kne-posts-container" data-post-type="mycpt">

Then do the equivalent of this in JS:

foreach element that matches `.kne-posts-container`
    post type = element.data('post-type')
    do the ajax call
        on success
            element.append( result )

Or even better, use the REST APIs that WordPress already provides. WP already has AJAX friendly data only URLs that return all this information as JSON, with pretty URLs like example.com/wp-json/wp/v2/posts.

Additional Notes

echo the_title('<h3>', '</h3>', true);

the_title already echo's so this is the same as echo echo get_title(, you don't need to use echo on most functions that begin with the_

Using -1 for posts_per_page is bad, I know you want to list all posts, but can your server load an infinite number of posts? If it can handle 500 or less posts, why not set it to 500? Why chance that your server will fallover trying to send thousands of posts? Set posts_per_page to a high number you never expect to reach, but you know your server can actually handle.

There's no point using offset, this isn't going to apply some old MySQL pagination speed trick. Just used the standard parameter 'paged' => $page, it might even be faster.

Finally, escaping. This is bad code:

echo '<a href="'. $url . '">';

Are you sure $url is a URL? Do you trust that? What if your site was hacked and it actually contains "><script>nasty stuff goes here</script>?

Instead escape:

echo '<a href="'. esc_url( $url ) . '">';

Now it is always a URL, it is guaranteed, we no longer need to trust $url. If it contains something nasty, it'll be neutered, cut down to a URL shaped size, and the worst case scenario is now a broken link.

Finally, this:

$slider = new KNE_DATA_SLIDER();
add_shortcode('kne_data_slider', array($slider, 'shortcode'));

This is not object oriented programming, it's just functions being put inside a class. If you removed the class and turned them into pure functions it would work exactly the same. It's just additional boilerplate.

If you're going to create classes and objects, do it because you need to. If you don't plan to create more than one of an object, and your class has no internal state, it's usually a sign you don't need a class/objects for the task. Namespaces will serve the same purpose just as well. A lot of developers think that as they get more experienced everything becomes objects and OOP, and use classes for everything regardless of what it is or wether it benefits tem, but that's not true. OOP is a tool that can be very useful, but it's not a universal hammer that does everything, use it when it's needed but for most things in WordPress like simple shortcodes or action hooks it's overkill.

  • Holy Shhhhhleep, I'd like to marry you now. :D I couldnt even read all of it, but after flying over it, I already know, it will help me a lot. Will look into it tommorow. But I get the idea behind it now. Have a nice evening ;)
    – Diddi_83
    Feb 2, 2023 at 21:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.