1

I have a technical blog with very long posts. I'm using pagination (via <!--nextpage-->) to separate the content into multiple pages.

I manually add an html table of contents (TOC) on the top of each page of one of my posts (below is just an example for one post, the content changes from post to post):

<table class="noborder"><tr><td><ol>
<li class="highlight"><a href="http://www.website.com/tutorials/raspberry-pi-i2c-and-spi/1/">Introduction</a></li>
<li class="nohighlight"><a href="http://www.website.com/tutorials/raspberry-pi-i2c-and-spi/2/">I2C Fundamentals</a></li>
<li class="nohighlight"><a href="http://www.website.com/tutorials/raspberry-pi-i2c-and-spi/3/">I2C on the Raspberry Pi</a></li>
<li class="nohighlight"><a href="http://www.website.com/tutorials/raspberry-pi-i2c-and-spi/4/">Enabling I2C on Raspberry Pi</a></li>
<li class="nohighlight"><a href="http://www.website.com/tutorials/raspberry-pi-i2c-and-spi/5/">I2C Transactions Using Python</a></li>
<li class="nohighlight"><a href="http://www.website.com/tutorials/raspberry-pi-i2c-and-spi/6/">Raspberry Pi to AVR I2C Communication</a></li>
</ol></td></tr></table>

For each page in the post I modify the css list class above (to "highlight" or "nohighlight") to indicate to the user which page is being viewed. You can get a better idea what I mean by navigating to one of the links above.

So here's the thing. Whenever I modify the page structure, table of content titles, etc, I need to duplicate then edit the html code for each page. This is a huge pain for posts that have 20+ pages.

Is there a way I can automate this? I'm envisioning a shortcode that would define all of the TOC html formatting and page titles at the top of the post, then another shortcode that I could call the TOC with an index to load the html code but also set the class value to "highlight" or "nohighlight" based on a provided index value.

Is this possible? Can I 'store' data across shortcodes? Or maybe I can store TOC data in a custom field then conditionally change the data in it when accessed?

I'd appreciate a bit of direction. Thanks


EDIT

With help from nmr (see below), I'm posting a working solution using custom fields that works for my site. You'll need to modify the css and html to adapt it for your own purposes.

Step 1) Add the following to functions.php:

function paginated_toc_function() {
    global $numpages;
    $post_link = get_permalink();
    $page_number = get_query_var('page');

    if ($page_number == 0) {
        $page_number = 1;
    }

    $toc_list = get_post_custom_values($key = 'toc');
    $toc = explode(PHP_EOL, $toc_list[0]);
    array_unshift($toc, "");

    $post_link .= substr($post_link, -1) == '/' ? '' : '/';

    $html = '<table class="noborder"><tr><td><ol>';

    for ($i = 1; $i <= count($toc) && $i <= $numpages; ++$i) {
        $highlight = ($i == $page_number) ? 'highlight' : 'nohighlight';
        $html .= '<li class="' .$highlight. '"><a href="' . $post_link . $i . '">' . htmlspecialchars($toc[$i]) . '</a></li>';
    }

    $html .= '</td></tr></table></ol>';

    return $html;
}

add_shortcode('paginated_toc', 'paginated_toc_function');

Step 2) Create new custom field in post. Name it 'toc' and add a list of the table of content headers, each on a new line. For example:

Introduction
I2C Fundamentals
I2C on the Raspberry Pi
Enabling I2C on Raspberry Pi
I2C Transactions Using Python
Raspberry Pi to AVR I2C Communication

Step 3) Add [paginated_toc] on each paginated page of the post. I place it near the top. Done!

1

You split post content into multiple pages with <!--nextpage-->, so you can use $page_number = get_query_var( 'page' ) to get current page number and global variable $numpages to get the number of pages in current post.

Displaying table of content:

global $numpages;
$post_link = get_permalink();
$page_number = get_query_var( 'page' );
$toc = [
   1=>'Introduction', 
   'I2C Fundamentals', 
   'I2C on the Raspberry Pi', 
   'Enabling I2C on Raspberry Pi', 
   'I2C Transactions Using Python', 
   'Raspberry Pi to AVR I2C Communication'];

$post_link .= substr($post_link, -1) == '/' ? '' : '/';
$html = '';
for( $i = 1; $i <= count($toc) && $i <= $numpages; ++$i) {
    $highlight = ($i == $page_number) ? 'highlight' : 'nohighlight';
    $html .= '<li class="' .$highlight. '"><a href="' . $post_link . $i . '">' . $toc[$i] . '</a></li>';
}
  • Thanks very much for writing this up. I'm guessing that this would go into functions.php and I would then access it using a unique shortcode. Could I, instead, store the TOC array strings in a custom field in each post, then read the custom field in the PHP function to populate the array? That way I could avoid editing functions.php each time the TOC changes. – RadianDynamics Jul 25 '18 at 7:16
  • @RadianDynamics Yes, put it in functions.php. You can use this as shortcode or call as function. Storing TOC in custom field is good solution, this is example so I write table of content data this way. Storing TOC as custom field allow to easily edit it. If different posts have a different-looking TOC (format) or not all posts should have TOC you should use shortcode, otherwise only put the function call in single.php file. – nmr Jul 25 '18 at 7:32
  • Just posted a working solution using custom fields in my original question. Thanks again for the help! – RadianDynamics Jul 25 '18 at 8:42
  • @RadianDynamics You read TOC from custom field so it should be escaped before display. – nmr Jul 25 '18 at 9:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.