'Ajax Loaded Content - Deeplinking URLs

On my WordPress website I have a custom page template containing a post grid (showing the titles only) and on click on the title it is loading the whole content of the post into a div without reloading the page with the help of AJAX. Using history popstate the user can also navigate back.

The issue I'm facing now is:

If you go directly to the single post page using the permalink /single-post/ the single.php template is called. I need to prevent this behaviour, because I want to have everything on my one pager template only. If a user accesses the post like /single-post/ directly, the homepage should be shown (instead of the single page) with the proper loaded ajax content.

How can I achieve this?

This is the current code:

Template File:

$args = array(
    'posts_per_page' => -1
);

$blog_query = new WP_Query( $args );
?>

<div class="post-wrapper">

    <div class="post-container">
        <?php
            if( $blog_query->have_posts() ){
                while( $blog_query->have_posts() ){ $blog_query->the_post(); ?>
                    <div class="post-content">                                                      
                                          
                        <a class="post-link" title="<?php the_title(); ?>" rel="<?php the_ID(); ?>" href="<?php the_permalink(); ?>"><?php the_title(); ?> </a>
                                                                                 
                    </div>
                <?php }
            } else { ?>
                <div class="post-content">                                  
                        <h3>No Items found</h3>                                
                </div>
            <?php }
            wp_reset_postdata(); ?>
    </div>
    
    <div id="single-post-container"></div>

</div>

AJAX Call:

// Load posts via AJAX
(function($, D) {
    
    $.ajaxSetup({
        cache: false
    });

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

        e.preventDefault();
        
        var url = jQuery(this).attr('href'),
        title = jQuery(this).attr('title')
        ;
        document.title = title;
        history.pushState({url:url,title:title}, title, url );
              
        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);
        });

    });
    
    //BACK BUTTON FUNCTIONALITY
    $(window).on('popstate', function (e) {
        var state = e.originalEvent.state;
        if (state !== null) {
            document.title = state.title;
            load(state.url);
        } else {
            document.title = 'Test Seite';
            $("#single-post-container").empty();
        }
    });    
    

})(jQuery, site);

functions.php:

function mysite_enqueue() {
    wp_enqueue_script( 'includes', get_stylesheet_directory_uri() . '/js/includes.js', array('jquery'), '', true );
    wp_localize_script( 'includes', 'site', array(
                'theme_path' => get_stylesheet_directory_uri(),
                'ajaxurl'    => admin_url('admin-ajax.php')
            )
    );
}
add_action( 'wp_enqueue_scripts', 'mysite_enqueue' );

/**
 * 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();
}


Solution 1:[1]

In your AJAX callback create a document object with the returned page and then parse the piece you want to load into the container:

// Load posts via AJAX
(function($, D) {
    
    $.ajaxSetup({
        cache: false
    });

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

        e.preventDefault();
        
        var url = jQuery(this).attr('href'),
        title = jQuery(this).attr('title')
        ;
        document.title = title;
        history.pushState({url:url,title:title}, title, url );
              
        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) {
            let doc = document.implementation.createHTMLDocument("New Document");
            doc.body.parentElement.innerHTML = content;
            const elements = doc.getElementsByClassName('post-container');
            const requiredElement = elements[0];            
            $container.html(requiredElement);
        });

    });

//BACK BUTTON FUNCTIONALITY
$(window).on('popstate', function (e) {
    var state = e.originalEvent.state;
    if (state !== null) {
        document.title = state.title;
        load(state.url);
    } else {
        document.title = 'Test Seite';
        $("#single-post-container").empty();
    }
});    


})(jQuery, site);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Mifo