简体   繁体   中英

Change content inside <div> from “sort posts by category” menu without page load

My first post here on this website and I am seeking some guidance for my first custom wordpress theme. I am extremely new to jQuery/AJAX and it hurts my brain at this moment as this entire process has been one learning curve after the next. So please forgive my inability to see between the lines and understand what is or could be already there. There are some results on this subject when I search however I am unable to translate these examples into working results for my situation...

I wish to sort my displayed posts by category on the "News" section...There is a submenu in the <div id="#container"> ... my intent is to have the sub menu links change the content specifically in my <div id="contentbox-news"> ...

ie "latest news" , "recent build projects", "press release announcments" etc...

Here is some code from "page-news.php" and original link Cars & Coffee HFX

<div id="news_submenu_container"><!-- begin submenu placement -->
            <ul id="news_submenu">
              <li>VIEW</li>
              <li><a href="#" onClick="get_posts_all();">ALL</a></li>
              <li><a href="#" onClick="get_posts_news();">NEWS</a></li>
              <li><a href="#" onClick="get_posts_builds();">BUILDS</a></li>
              <li><a href="#" onClick="get_posts_pr();">PR</a></li>
            </ul>
        </div><!-- end submenu placement -->

        <script>
            function get_posts_all() {
                var posts_all_var = '<?php echo "Insert Wordpress Loop here identified by Category-ID" ?>';
            document.getElementById("contentbox-news").innerHTML = posts_all_var;
            return false;
            }

            function get_posts_news() {
                var posts_news_var = '<?php echo "Huh?" ?>';
            document.getElementById("contentbox-news").innerHTML = posts_news_var;
            return false;
            }

            function get_posts_builds() {
                var posts_builds_var = '<?php echo "Cant get loop to work" ?>';
            document.getElementById("contentbox-news").innerHTML = posts_builds_var;
            return false;
            }

            function get_posts_pr() {
                var posts_pr_var = '<?php echo "DAMNIT!" ?>';
            document.getElementById("contentbox-news").innerHTML = posts_pr_var;
            return false;
            }
        </script>

I am understanding now that JavaScript and PHP do not work together in the way I thought... Apparently I need to use a method through AJAX... I found this article on PHP AJAX and MySQL however it is too advanced for me, I'm having trouble understanding it and creating a working example for myself...

But now, since yesturday I have made new code that gets me this far, I can PHP echo...thats about it.

            function get_posts_builds() {
                var posts_builds_var = '<?php query_posts('cat=6'); 
                                              while (have_posts()):
                                              the_post(); 
                                              the_content(); 
                                              endwhile; ?>';
            document.getElementById("contentbox-news").innerHTML = posts_builds_var;
            return false;
            }

Once I replace the PHP echo with this PHP wordpress loop, it doesn't seem to do the trick for me and breaks.

You indeed need to use AJAX to achieve this. Wordpress have its own way to handle ajax request that you can use for this by going through few steps detailed below. I will try to show some WP best practices on this subject - so as there could be faster/easier/dirtier way to achieve this (probably even through a plugin), this will make a good introduction for Wordpress theme/plugin development for you. Feel free to follow the docs links to understand better how this works.


1. Building your categories menu

In your code sample the menu is static, though you should better use a dynamic menu. One solution could be to use get_the_category_list , but it doesn't allow you to have full control on category links. I would suggest to define a new navigation menu like this (the following code go into your theme functions.php ):

add_action('after_setup_theme', 'add_categories_menu');
function add_categories_menu() {
  register_nav_menu('categories_menu', __('Categories Menu', 'your-theme-slug'));
}

Then in your template, replace your static menu with the following in order to display your brand new menu:

<?php wp_nav_menu(array('theme_location' => 'categories_menu')); ?>

Now to add the categories to your menu, log into your Wordpress administration, go into
Appearance > Menus , create a new menu, select the theme location "Categories Menu" for it, and add the categories to it.

Last step for this, we'll add a filter in order to add onclick attribute on the menu links that will send the category slug to a js function called showPostsFromCategory() that we'll define later.

This go in your functions.php :

function add_onclick_attr_categories_menu($atts, $item, $args) {
    if($args->theme_location == 'categories_menu' && $item->object == 'category') {
        $category = get_category($item->object_id);
        if($category !== null) {
            $atts['onClick'] = 'showPostsFromCategory("' . $item->slug . '")';
        }
    }
    return $atts;
}
add_filter('nav_menu_link_attributes', 'add_onclick_attr_categories_menu', 10, 3);

You may want to ask, why we're keeping the category link on the menu item? It's for SEO and accessibility purpose: for a browser without javascript (like a screen reader) or a search crawler, the categories pages will still be accessible.


2. Preparing your theme for AJAX

In Wordpress, all the AJAX requests need to be sent to wp-admin/admin-ajax.php with an action parameter that will identify the request in order to catch it in functions.php using the wp_ajax_nopriv_my_action (for non logged-in users) and wp_ajax_my_action (for logged in users) hooks.

So one small step before going further is to make that path ( wp-admin/admin-ajax.php ) accessible in your JavaScript. First, create a js file that we'll use for the AJAX process in your theme folder, let's say his name will be ./js/categories-ajax.js . Then, add the following in your functions.php in order to enqueue this new script and make the path accessible through script localization :

add_action('wp_enqueue_scripts', 'ajax_categories_enqueue_scripts');
function ajax_categories_enqueue_scripts() {
    wp_register_script('categories_ajax', get_stylesheet_directory_uri() . '/js/categories-ajax.js', array('jquery'), '', true);
    wp_localize_script('categories_ajax', 'ls', array(
        'ajax_url'                  => admin_url('admin-ajax.php')
    ));
    wp_enqueue_script('categories_ajax');
}

With this the admin-ajax.php path will be accessible in your JS with ls.ajax_url .


3. Trigger the ajax request

It's time to create the showPostsFromCategory() function. So let's write in your new categories-ajax.js file. Personally, to avoid any possible conflict with jQuery in my plugin/theme developments I like to always encapsulate my jQuery code in a JavaScript closure and make the functions accessible through global vars like this:

(function($){
  showPostsFromCategory = function(category_slug) {
    // code function...
  };
})(jQuery);
var showPostsFromCategory;

So I'll assume by now that the function code is inside a closure.
Basically, what we need to do now is to set up a $.post request to admin-ajax.php that will sent the following parameters:

  • action : an identifier of the AJAX action that is being called, in order to make Wordpress know which function should be called later. We'll name this dynamic_categories .
  • category : the category slug that have being clicked in the menu.

So the function code will look like this:

showPostsFromCategory = function(category_slug) {
    $.post(ls.ajax_url, {action: 'dynamic_categories', category: category_slug}, function success(data) {
        $('#your-post-content-wrapper').html(data); // Update the page with the new posts -- change this ID to the correct one
    });
    return false; // this is to cancel the link click, so the page isn't being redirected
}

This is very basic, you may want to add later some error handling and a loader. Here we just replace the actual posts with the AJAX return, that we suppose to be HTML. Nothing more to be done on the JS side, so let's work on the PHP side now.


4. Get the posts for the requested categories

First, add the following lines in your functions.php in order to attach the function get_categories_posts() to the wp_ajax_dynamic_categories and wp_ajax_nopriv_dynamic_categories actions.

add_action('wp_ajax_dynamic_categories', 'get_categories_posts');
add_action('wp_ajax_nopriv_dynamic_categories', 'get_categories_posts');

Now, we need to do the following in the function:

  1. Check if the category slug is valid and it have posts associated, or display an error otherwise.
  2. Get the posts associated to the category identified by the slug being sent by the JS script.
  3. Loop through the posts and display each using a template part . For this, create a template in your theme folder that will contain only the necessary HTML/PHP to display one post. You can see an example of such template in the default wordpress theme (twentysixteen) in
    template-parts/content.php . Do the same kind of file in your theme - maybe there is already one.

    You will need to update your archive template to make use of the template part too. This is very easy, remove the code used to display the post inside the loop and replace it with:

     <?php get_template_part('template-parts/content'); ?> 

So the whole function code will look like this:

function get_categories_posts() {
    // Check if the category slug provided is ok and if it have posts
    if(!isset($_POST['category'])) {
        die('<p class="error">' . __('No category parameter provided', 'your-theme-slug') . '</p>');
    }
    $category_slug = sanitize_text_field($_POST['category']);
    $category = get_category_by_slug($category_slug);
    if(!$category) {
        die('<p class="error">' . sprintf(__('Category %s not found', 'your-theme-slug'), $category_slug) . '</p>');
    }
    $posts = get_posts(array(
        'category' => $category->term_id,
        'posts_per_page' => -1 // get all posts
    ));
    if(empty($posts)) {
        die('<p class="error">' . sprintf(__('The category %s is empty', 'your-theme-slug'), $category->name) . '</p>');
    }

    // Loop through the posts and display them
    foreach ($posts as $post) {
        setup_postdata($post);
        get_template_part('template-parts/content');
    }
    die('');
}

And, that's all! With this you now have a dynamic navigation for your posts categories.

For reference: sanitize_text_field , get_category_by_slug , get_posts , setup_postdata and get_template_part .


Please note that this does not support pagination. If you need this, check this answer I made about this matter.


Further readings

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM