How To Empower Your WordPress Visitors With Post Reading Times

When you get a visitor to your site, you want them to stay there as long as possible and information that you can give them that will help is worth the effort.

Estimated reading time for posts is a great example of the extra information and is used by recent new sites Medium.com and by ManageWP.org to let their users know what time investment is required for each post.

In this article, I’ll show you how to add reading time to your site, without having to edit your theme.

Reading times provide valuable information and greatly improve the user experience
Reading times provide valuable information and greatly improve the user experience

There are a few plugins in the WordPress plugin repository that cover reading time but none do exactly what I am looking for.

For the same reason that you shouldn’t add analytics to the theme, I want the reading time insertion to be independent of the theme so that I can swap themes and still have the reading time appear. Or that I can stop displaying reading time by simply disabling the plugin.

I found a plugin that was close, Post Reading Time by Bostjan Cigan, and using that as my base I’ve altered it to better suit my requirements and will hopefully be useful for you to.

I’ve made the following changes to the original plugin:

  1. Calculating and storing a word count for when the post is published and storing it in a custom field, rather than calculating it at the time of output.
  2. Automatically adding the reading time message to the title
  3. Adding a stylesheet to the page header to allow styling of the reading time text
  4. Changing the settings to allow a format for the reading time message to be specified

I’ve kept the original plugin’s widget so you can also display the reading time in a sidebar.

And, of course, it’s all tested on WordPress 3.7.1.

The Reading Time Calculation

The calculation for the reading time is as simple as:

number of words / words read per minute

So, 1000 words at 200 words per minute is 5 minutes. You can set the words per minute in the Settings > Post Reading Time

Formatting the Output

I’ve introduced some simple formatting for the output message. The format uses  %min% and %sec% as placeholders for minutes and seconds respectively.

If you don’t use %sec% then the minutes will be rounded up.

For example, for a 4 minute 35 second reading time,

  • %min% minute read = 5 minute read
  • Reading time: %min% mins %sec% secs = Reading time: 4 mins 35 secs

If the minute is 1 then “minutes” becomes “minute” and “mins” becomes “min” if used in the format.

Download the post-reading-time plugin and see what you think.

For those of you with an interest in plugins, I’ll quickly step through the more interesting parts: I’m not going to worry about the calls to the Widget and Settings APIs.

Calculating and Storing the Post Word Count

The original plugin was calculating the reading time for a post every time the post_read_time function was called.

I think this is overkill, so I’ve added code to the publish_post hook to calculate and store the word count in a custom field when a post is published or updated which shortens the reading time calculation. I’ve used the word count so that the format of the reading time message can be changed and all posts will immediately reflect that change.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Calculate the number of words for a post and add this to a custom field

function readtime_publish_post( $post_id ) {

// get the post content

$content = apply_filters('the_content', get_post_field('post_content', $post_id));

// count the words
$num_words = str_word_count(strip_tags($content));

// store the result in a custom field
update_post_meta($post_id, 'word_count' , $num_words );

}

// hook the function to the publish_post action
add_action( 'publish_post', 'readtime_publish_post' );

To cater for existing content, this same function is called when the reading time message is being generated in the front end and the word count is missing, meaning that the calculation still takes place and that the word count is stored on the post.

Here’s how the reading time message is created:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
function set_readtime( $post_id ) {

// if this is not a post then don't worry about a reading time message. You might need to update this if you have custom post types

if ( get_post_type( $post_id ) != 'post' ) return '';

// get the global options for words per minute and format

$words_per_second_option = get_option('post_readtime_wpm');
$format = get_option('post_readtime_format');

// get the word count value for the post
$num_words = get_post_meta( $post_id , 'word_count' , single);

// if the word count is not set - existing content - then let's set it now
if ( $num_words == '') {

readtime_publish_post( $post_id );

$num_words = get_post_meta( $post_id , 'word_count' , single);

// still no word count? outta here
if ( $num_words == '' ) return '';
}

// calculate the minutes and seconds

$minutes = floor($num_words / $words_per_second_option);

$seconds = floor($num_words % $words_per_second_option / ($words_per_second_option / 60));

// Check the format for %sec% - if not found in format then round up the minutes
if( strpos( $format , '%sec%' ) === false ) {
if( $seconds >= 30 ) {
$minutes = $minutes + 1;
$seconds = 0;
}
}

// construct the reading time message

$readtime = str_replace( '%min%' , $minutes , $format );
if ( $seconds > 0 ) $readtime = str_replace( '%sec%' , $seconds , $readtime );
if ( $minutes == 1 ) {
$readtime = str_replace( 'minutes', 'minute' , $readtime );
$readtime = str_replace( 'mins', 'min' , $readtime );
}
return $readtime;

}

Being able to specify the format makes it more flexible and actually replaces three of the original settings.

You’ll also notice that the reading message is only being generated for posts. Ideally, the Settings would let you select from a list of custom post types but it’s not that sophisticated yet!

Displaying the Reading Time Message

I’ve kept the original post_read_time() function so if you do feel the urge to edit your theme you can use this function to output the reading time message.

However, I’ve also made use of the the_title filter to automatically add the reading time message whenever a post title is shown.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function readtime_the_title( $title , $id) {

// if we are in the admin interface then we don't need the reading time message

if ( is_admin() ) return $title;

// we only want to add the message to content, not menus, etc

if ( is_main_query() ) {
$reading_time = set_readtime ( $id );
if ( $reading_time != '' ) {
$title .= '<span class="readingtime">' . $reading_time . '</span>';
}
}
return $title;

}

add_action( 'the_title' , 'readtime_the_title' , 10 , 2);

Key things to note here are:

  • the check for is_main_query(): this stops titles in menus from having the reading time message appended.
  • the add_action call which hooks this call onto the the_title filter
  • the reading time message gets added within the title tag (<h1> or <h2>) so needs to be styled.

Styling the Reading Time Message

There is a small CSS file in the plugin directory that gets added to the header of the page via the wp_enqueue_scripts action.

I’ve used the most basic of styling and you’ll probably want to change it to fit your template:

1
2
3
4
5
6
7
8
9
10
11
12
13
h1 span.readingtime {

display: block;
font-style: italic;
font-size: 70%
}

/* hide message on single post pages */

.single-post span.readingtime {
display: none;

}

As the reading time message is wrapped in a span, I’ve changed the display to block so that it sits below the heading (remember it is actually contained with the heading tags) but you can just as easily keep it on the same line as heading by removing the display clause.

I’ve also hidden the message altogether on single post pages because I’m displaying the reading time message in a widget.  Playing with the CSS in this way, allows quite fine-grained control over where and when the message is displayed.

And that’s really all there is to it.

This is a very simple plugin that provides really quite valuable information to an all-too-often time poor site visitor. And anything that contributes to a better user experience has got to be worth considering.

Do you use reading time on your site? Do reading times affect your reading decisions when you visit a site?

Photo Credit : Fernando de Sousa

Download the plugin: post-reading-time plugin

Tags ,

Comments (5)

  1. Fantastic. Great find!

    Is there a way to modify the plugin so it does a word count for a Custom Post Type instead?

    In fact, even more specifically, a Custom Field OF a Custom Post Type?

    • To calculate the word count on a custom post type, either add to or replace this code:

      // hook the function to the publish_post action
      add_action( ‘publish_post’, ‘readtime_publish_post’ );

      with

      add_action(‘publish_{custom_post_type_name}’,readtime_publish_post);

      To calculate the word count for a custom field, use:

      $num_words = str_word_count(strip_tags( get_post_meta($post_id, ‘{custom field}’, true)) );

  2. Bit gutted about this. Our discussion yesterday about post navigation led me to Medium which led me here. This would be a great feature but the plugin prevents my site from loading – goes to a server 500 error after a long time trying to load. I guess there’s a conflict with a plugin or possibly a static front page causing a problem. Any other thoughts Chris?

    • If you deactivate this plugin then presumably everything is fine?

      The static front page shouldn’t cause an issue because the reading time is only calculated / retrieved for posts, so if your site is hanging on a page then it presumably must be a conflict.

      I’ve just tested it again and it works on an existing site.

      Can you deactivate all your plugins except for the reading time plugin and see if it all works then? Then try reactivating each one until you get the issue?

Participate