1

I'm using the code below to asynchronously load posts on the same index page via AJAX. I would like to be able to update the URL as well but am unsure how I can go about adding that to what I currently have.

The end goal is an effect similar to this. If you click on a post, you'll be able to see the posts loading in the hidden div above and you'll see that the URL updates in the address bar as well.

Can somebody help me out?

Index Template

<div id="single-post-container"></div>

<?php if (have_posts()) : while (have_posts()) : the_post(); ?>

    <a class="post-link" rel="<?php the_ID(); ?>" href="<?php the_permalink(); ?>">

       <?php the_title(); ?> 

    </a>

<?php endwhile; endif; ?>

Single Post Template

<?php

    $post = get_post($_POST['id']);

?>
    <div id="single-post post-<?php the_ID(); ?>">

    <?php while (have_posts()) : the_post(); ?>

                <?php the_title();?>

                <?php the_content();?>

    <?php endwhile;?> 

    </div>

jQuery

   $(document).ready(function(){

        $.ajaxSetup({cache:false});
        $(".post-link").click(function(){
            var post_link = $(this).attr("href");

            $("#single-post-container").html("content loading");
            $("#single-post-container").load(post_link);
        return false;
        });

    });

Edit: Also, I read from a couple sources that using WordPress' built in admin-ajax.php was recommended. Would it be beneficial for me to use admin-ajax.php and if so, how would I "switch it out" with what I currently have?

Update (1/4/2015):


Index Template

<div id="project-container"></div>

<?php if (have_posts()) : while (have_posts()) : the_post(); ?>

    <a class="post-link" rel="<?php the_ID(); ?>" href="<?php the_permalink(); ?>">

       <?php the_title(); ?> 

    </a>

<?php endwhile; endif; ?>

Single Post Template

<div id="single-post post-<?php the_ID(); ?>">

<?php while (have_posts()) : the_post(); ?>

    <?php the_title();?>

    <?php the_content();?>

<?php endwhile;?> 

</div>

Functions.php

/**
 * Enqueue scripts and styles.
 */
function starter_scripts() {
    wp_deregister_script( 'jquery' );
    wp_register_script( 'jquery', includes_url( '/js/jquery/jquery.js' ), false, NULL, true );
    wp_enqueue_script( 'jquery' );

    wp_enqueue_style( 'starter-style', get_stylesheet_uri() );
    wp_enqueue_script( 'includes', get_template_directory_uri() . '/js/min/includes.min.js', array('jquery'), '', true );
    wp_enqueue_script( 'gray', get_template_directory_uri() . '/js/min/jquery.gray.min.js', array('jquery'), '', true );
    wp_localize_script( 'includes', 'site', array(
                'theme_path' => get_template_directory_uri(),
                'ajaxurl'    => admin_url('admin-ajax.php')
            )
    );
}
add_action( 'wp_enqueue_scripts', 'starter_scripts' );

/**
 * AJAX nopriv 
 */
add_action('wp_ajax_load-single-post', 'prefix_ajax_single_post');
add_action('wp_ajax_nopriv_load-single-post', 'prefix_ajax_single_post');

function prefix_ajax_single_post() {
    $pid = (int) filter_input(INPUT_GET, 'pID', FILTER_SANITIZE_NUMBER_INT);
    if ($pid > 0) {
        global $post;
        $post = get_post($pid);
        setup_postdata($post);
        printf('<div id="single-post post-%d">', $pid);
        the_title();
        the_content();
        echo '</div>';
    }
    exit();
}

jQuery (includes.js)

// Load posts via AJAX
(function($, D){

$.ajaxSetup({cache:false});

    $(".post-link").click(function(){

        var postID = $(this).attr('rel');
        var $container = $("#project-container");
        $container.html("content loading");
        $.get(D.ajaxurl, {action: 'load-single-post', pID: postID}, function(content) {
          $container.html(content);
        });

    });

})(jQuery, site);
0

1 Answer 1

2

Updating the url via javascript, is not related to WordPress, however you just need to modify window.location.hash in your jQuery code.

Regarding the usage of admin-ajax.php it is recommended way to do tasks via AJAX that need to load WordPress environment, instead of manually requiring wp-load.php.

In your case you are sending a request to a regular WordPress url, so it's not too bad, however your current code have some issues.

The "$_POST['id']" Issue

In your singular post you have

$post = get_post($_POST['id']);

that is not needed at all, because you are sending a request to singular post url, it means post variable is automatically set by WordPress.

Moreover, $_POST['id'] may be not set, especially if you use pretty permalinks, that will cause a PHP warning to be thrown.

The "Single View" Issue

Your singular posts will still have own permalinks. If you visit one of those permalinks, you'll see a broken html page that doesn't contain any <html>, <head> or <body> tag, but just the content of the page.

You can fix that issue in 2 ways:

  • use the 'post_link' hook to change the permalink of posts and make it pointing to index page with a proper hash, and also put in place a redirect to send users that enter singular url in the address bar to index page with a proper hash. That may be fine, but in that way your post can never be seen in singular view.

  • second way is to edit your single post template and conditionally output proper html tags when the page is loaded in a regular (i.e. non-AJAX) request.

    function is_ajax() {
      return isset($_SERVER['HTTP_X_REQUESTED_WITH'])
             && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
    }
    
    if (is_ajax()) {
       // open here <html>, <head> and <body> tags
    }
    
    // page content here
    
    if (is_ajax()) {
       // close here <html>, <head> and <body> tags
    }
    

In this way when requested using regular requests your posts are shown in well formed html page.

Performance Implications

Using one of the two ways suggested above you can solve main issue of your current code, but there is still a performance issue.

Requests to WordPress urls are a bit more expensive in performance than requests to admin-ajax.php. Even if both load full WP environment, for regular requests WordPress need to resolve the permalink to a query. That is done loading a set of rewrite rules from database and programmatically performing a regex check against any of the rules untill one of them matches current url.

If you can send an ajax request that contains post ID as part of the request itself, you will be able to output the post content skipping that check and so improving performance.

To obtain that, you should use WordPress AJAX API. Something like:

in functions.php

add_action('wp_enqueue_scripts', function() {
  // $script_url is the full url to your js file
  wp_enqueue_script('myjs', $script_url, array('jquery'), true, null, true);
  wp_localize_script('myjs', 'myData', array('ajaxurl' => admin_url('admin-ajax.php));
});

add_action('wp_ajax_load-single-post', 'prefix_ajax_single_post');
add_action('wp_ajax_nopriv_load-single-post', 'prefix_ajax_single_post');

function prefix_ajax_single_post() {
  $pid = (int) filter_input(INPUT_GET, 'pID', FILTSER_SANITIZE_NUMBER_INT);
  if ($pid > 0) {
    global $post;
    $post = get_post($pid);
    setup_postdata($post);
    printf('<div id="single-post post-%d">', $pid);
    the_title();
    the_content();
    echo '</div>';
  }
  exit();
}

jQuery (goes in the file whose url is the $script_url var in code above)

(function($, D){

  $.ajaxSetup({cache:false});

  $(".post-link").click(function(){

    var postID = $(this).attr('rel');
    var $container = $("#single-post-container");
    $container.html("content loading");
    $.get(D.ajaxurl, {action: 'load-single-post', pID: postID}, function(content) {
      $container.html(content);
    });

  });

})(jQuery, myData);

Your index template code can stay the same.

Using this approach all issues are solved and, as you can see, the single post template is not involved at all, it means you can use it to display the singular post view as you like. (If you don't want any singular post view, the first suggestion under The "Single View" Issue can be still used).

0

This site is temporarily in read-only mode and not accepting new answers.

Not the answer you're looking for? Browse other questions tagged .