Add a Time to Read To Your WordPress Posts

Popular on posts these days is a ‘time to read’ appearing at the top to give readers an idea how long it will take to read through a potential opus. Plugins exist to do this but, when it’s so easy to do, why do it?

Add the following to your functions.php (or equivalent) and add styling to taste. 300 is the generally accepted number of words per minutes that can be read online by the average person. Again, this can adjusted to whatever you feel most comfortable with.

Note – this will only show at the top of single posts – you will need to change the second line to adjust this behaviour.

function time_to_read( $content ) {

    if ( is_single() ) {
        $time = str_word_count( strip_tags( $content ) ) / 300;
        if ( $time == 0 ) { $time = 0.1; } // If there is no content, report < 1 minute
        $rounded = ceil( $time );
        $content = '<p>Time to read: ' . ( $time<1?'<':'' ) . $rounded . ' minute' . ( $rounded>1?'s':'' ) . '<p>' . $content;
    }

    return $content;
}

add_filter( 'the_content', 'time_to_read' );

So short but it does so much. Apart from working out how long it will take to read based on the number of words, it will also display both plural and singular versions of the word ‘minute’. Also, if it will take less than a minute it will indicate this as ‘<1 minute’. Oh, and it excludes HTML from the word count.

Part 2 – Taking it outside of your content

The above works brilliantly but there is just one downside to it – when search engines crawl your site, they will include the ‘Time to read’ text, as it’s the first part of the contents. Having it appear in your SEO results is not ideal, so now I’ll show you how to move it so that it’s excluded.

First of all, we need to revise the above code, which you still add to functions.php, but now it doesn’t automatically inject itself into the top of the post content…

function time_to_read() {

    $time = str_word_count( strip_tags( get_the_content() ) ) / 300;
    if ( $time == 0 ) { $time = 0.1; } // If there is no content, report < 1 minute
    $rounded = ceil( $time );
    $output = 'Time to read: ' . ( $time<1?'<':'' ) . $rounded . ' minute' . ( $rounded>1?'s':'' );
    
    return $output;
}

That’s the easy bit. Now, you need to find where in your theme you want to display the result – often, you have a heading at the top of each post, along with some meta data (such as category, tag, etc), so you may want to add it there. Usually, this exists in the file single.php but may be elsewhere. Themes will vary so you’ll need to do your own research here.

When you find the right place in the code for the ‘Time to read’ to display, add the following line…

if ( function_exists( 'time_to_read' ) ) { echo time_to_read(); }

As before, you may wish to wrap some CSS around this to taste.

Part 3 – One code to rule them all

All this is great but we have 2 different pieces of code to do the same thing, just in different ways. What if we want to change between them? Well, let’s simplify it and produce a single piece of code that can be easily used in either way.

First, add the following to functions.php

function time_to_read( $content = false ) {

    if ( is_single () ) {

        if ( !$content ) { $content = get_the_content(); $add = false; } else { $add = true; }

    	$time = str_word_count( strip_tags( $content ) ) / 300;
        if ( $time == 0 ) { $time = 0.1; } // If there is no content, report < 1 minute
    	$rounded = ceil( $time );
        $output = 'Time to read: ' . ( $time<1?'<':'' ) . $rounded . ' minute' . ( $rounded>1?'s':'' );

        if ( $add ) { $content = $output . $content; } else { $content = $output; }

    }

    return $content;
}

Now, you simple need to add either one of the following, depending on where you wish to add the ‘time to read’ output.

For adding to the top of your content, add the following into your functions.php (ideally below the above code)…

add_filter( 'the_content', 'time_to_read' );

Or, if you wish to add it to directly to your theme, above the content, then add this to the relevant theme file…

if ( function_exists( 'time_to_read' ) ) { echo time_to_read( false ); }

Part 4 – Caching

For this final part we’re going to tidy up the code (with some added comments) and extend it with some caching.

How will this help? Well, when you fetch the content, it uses a database read – you then have to calculate the word count. A fetch from the cache will also read from the database but it doesn’t then require the word count to be calculated.

In the case of when we add the ‘time to read’ directly to the content, we won’t be caching as the content doesn’t need to be read (it’s already provided by the filter), so the benefits are lost.

With regard to how long to keep the cache, we need to store when the post was last updated – if this changes, we can refresh the cache. Oh, and it will be beneficial to not use the cache at all if we’re previewing the post.

So, based on this, here is the final version of the code…

<?php
function time_to_read( $content = false ) {
if ( is_single () ) {
// If content has not been passed in (via the filter), fetch it
if ( !$content ) { $content = get_the_content(); $add = false; } else { $add = true; }
// Get the cache, if the content was not provided and this is not a preview
$cache = false;
if ( !$add && !is_preview() ) { $cache = get_transient( 'time_to_read_' . get_the_id() ); }
// If cache was found, see if the post has updated. If so, trash it
if ( $cache ) { if ( isset( $cache[ 'updated' ] ) && $cache[ 'updated'] != get_the_modified_date() ) { $cache = false; } }
// If there is a cache, use it!
if ( $cache ) {
$content = $cache[ 'content'];
} else {
// Generate output
$time = str_word_count( strip_tags( $content ) ) / 300;
if ( $time == 0 ) { $time = 0.1; } // If there is no content, report < 1 minute
$rounded = ceil( $time );
$output = 'Time to read: ' . ( $time<1?'<':'' ) . $rounded . ' minute' . ( $rounded>1?'s':'' );
$generated = 'Code generated';
if ( $add ) { $content = $output . $content; } else { $content = $output; }
// Save cache, if content was not provided and this is not a preview
if ( !$add && !is_preview() ) {
$cache[ 'content' ] = $content;
$cache[ 'updated '] = get_the_modified_date();
set_transient( 'time_to_read_' . get_the_id(), $cache );;
}
}
}
return $content;
}
?>
view raw functions.php hosted with ❤ by GitHub

To get it to display, you’ll need to use the appropriate line of code detailed in Part 3, depending on where you wish to situate it.

Part 5 – Post Formats

If use different post formats, it may not be relevant to display a ‘time to read’ for some formats (e.g. links, galleries, etc.). In this case, taking the final code in part 4 as a base, you would need to replace line 3 with the following…

    $format = get_post_format() ? : 'standard';

    if ( is_single () && ( $format == 'standard' or $format == 'aside' ) ) {

In this case,  I’m only displaying it on standard and aside post formats. You’d need to add your own, as required.

2 responses

  1. What’s your CSS for the little blue block you have there? I like the look of that.

    1. Hi Joe. I’m using the plugin Bootstrap Shortcodes to add various design elements to my site and the CSS has come from that.

Talk to me!

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Discover more from David Artiss

Subscribe now to keep reading and get access to the full archive.

Continue reading