Category: WordPress

  • Avoid showing same WordPress post in multiple loops

    Your WordPress theme might have multiple loops in the page. For example, there may be a ‘Featured’ section at the top of your homepage, and a ‘Recent Posts’ section below that.

    These are separate loops, but could contain the same post. For example, your most recent post might also be in the Featured category.

    You only want the post to appear once. How do you manage it?

    Avoid showing duplicate WordPress posts

    The answer is surprisingly simple. Put the following in your functions.php.

    <?php
    add_filter('post_link', 'track_displayed_posts');
    add_action('pre_get_posts','remove_already_displayed_posts');
    
    $displayed_posts = [];
    
    function track_displayed_posts($url) {
      global $displayed_posts;
      $displayed_posts[] = get_the_ID();
      return $url; // don't mess with the url
    }
    
    function remove_already_displayed_posts($query) {
     global $displayed_posts;
     $query->set('post__not_in', $displayed_posts);
    }

    We only want to hide posts which have been displayed already – so we hook into the post_link() function call and make a note of the post we’re displaying.

    We then want to hook into any new WordPress query to tell the query to not include any of the posts we’ve already seen. We do this by hooking into the pre_get_posts function and passing it our list of already-displayed posts.

    Let’s make life even easier!

    I’ve just been through WordPress’ slightly laborious review publishing process to make this an easy-to-install plugin for you! You can install Avoid Repeating Posts and all of your problems will go away:

    If you’d like to check out the latest source code, have a look on GitHub.

    And if you find this plugin or this code snippet useful, please comment below and let me know!

    Want some more great WordPress tools? Check out my other plugin: Secretary.

    This post was updated in December 2019 to refer to the new way of detecting which posts have been displayed, using post_link rather than the_title as a hook, as post previews don’t always use the_title (they may be thumbnail only) but they DO always have a link.

  • Use RequireJS with WordPress (plugins) & jQuery (UI)

    I recently found myself really wanting to use RequireJS with WordPress, to manage the various JavaScript dependencies a client site had. Unfortunately, this was easier said than done.

    WordPress is historically not very compatible with RequireJS, as it provides jQuery and a myriad of other JavaScript files out of the box, which are difficult to shoehorn into your RequireJS configuration. There are loads of WordPress plugins out there which rely on jQuery being a global variable in the page.

    Also, any attempt at loading RequireJS into the page will often result in errors because many JavaScript modules first check if require is defined before they execute. This means that the very act of including RequireJS in the page will change how some code is executed, and cause you lots of headaches!

    We need require to be defined so that we can make lots of lovely require calls throughout our webpage, but we can’t actually define require until all of WordPress’ native dependencies and plugin JS files have sorted themselves out.

    I managed to hack together a solution, and it seems to work rather well.

    Edit your header

    First of all, edit header.php (I put this just above the wp_head() call):

    <script>
    window.queueForRequire = [];
     
    window.r = function (deps, callback) {
        window.queueForRequire.push({
            deps:     deps,
            callback: callback
        });
    };
    </script>

    We’re going to store any require() calls in a queue: queueForRequire.

    Then require your modules as normal. Well, as semi-normal: as mentioned previously, many JavaScript files will check if require is defined and misbehave if it is, so instead of mocking require, I’ve made a custom function r which we’ll use instead.

    <script>
    r(['subscribe-cta'], function (subscribe) {
        console.log('This is my callback');
    });
     
    <?php if (is_home() && apply_filters('require_slider', $shouldLoadSlider)) : ?>
    r(['slider']);
    <?php endif; ?>
     
    <?php if (is_archive() || is_home()) : ?>
    r(['infinite-scroll']);
    <?php endif; ?>
    </script>

    At this stage, all we’re doing is adding require calls to a queue – nothing is being downloaded just yet.

    Edit your footer

    Now edit footer.php (put this right before the closing </body> tag):

    <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.2.0/require.min.js"></script>
    <script>
        require.config({
            baseUrl: '/wordpress/wp-content/themes/tc-magazine-core/js',
            paths: {
                'readmore': 'https://cdnjs.cloudflare.com/ajax/libs/Readmore.js/2.1.0/readmore.min'
            },
            waitSeconds: 15
        });
     
        if (typeof jQuery === 'function') {
            define('jquery', function () { return jQuery; });
        }
     
        for (var i = 0; i < window.queueForRequire.length; i++) {
            require(window.queueForRequire[i].deps, window.queueForRequire[i].callback);
        }
    </script>

    Only at the very end of the document do we go “now I’m ready to load RequireJS. WordPress has done its thing, so it should be safe for us to do ours now”.

    So, this is pretty standard stuff: we’re downloading the RequireJS library from a CDN and initialising it with a config. jQuery is a strange special case handled in lines 11-13: we want our modules to be able to pull in jQuery as a module, but know jQuery is probably a global already, so here we’re creating a jQuery AMD module on the fly.

    The magic happens on lines 15-17: now that we have RequireJS, we can go ahead and download all the things we’ve queued already (and call their callbacks if those were passed as arguments).

    RequireJS with WordPress

    There you have it – RequireJS working nicely with WordPress and all its plugins!