I have 2 WordPress shortcodes I am working on:

  1. A Chapter. [chapter name="the begining"]...content...[/chapter]
  2. A table of contents [toc][/toc]. The toc needs to display a simple list of the chapters.


  • There can be many chapters in a post.
  • There can be one, two or no toc shortcode in a post.
  • The toc can be either before or after the chapters or both before and after. It is up to the post writer so I don't know in advance.
  • I cannot use nested shortcodes as those are difficult for writers to work with.

I thought of using a static toc array to add chapters at each chapter tag and then output it in the toc shortcode. Alas, the toc shortcode can appear BEFORE the chapters and then the array will be empty.

The following post html will not show the toc:

Here is some content
[chapter name="hello"]xxx[/chapter]
In between content may come here
[chapter name="world"]yyy[/chapter]
Some more stuff

This is my starting code (embeded in a class):

public static function register_chapter_shortcode($atts, $content=null){
    $atts = shortcode_atts (
        array (
            'name'       => '',
        ), $atts );

    $name = $atts["name"];
    if (self::$toc == null){
        self::$toc = array ($name);
    } else {
        array_push(self::$toc, $name);
    return '

public static function register_toc_shortcode($atts, $content=null){
    $items = "";
    foreach (self::$toc as $item){
        $items = $items. '<li><a href="#">'.$item.'</a></li>';
    return '

2 Answers 2


Does the TOC need to be a shortcode? Perhaps giving the editors a meta box with a set of radio boxes for none, below and above options would be cleaner?

You could then generate the TOC via the static variable approach you mentioned and output it during page render.

  • Thanks. TOC needs to be a shortcode. It can have content itself and users of the plugin would like to determine where to place it.
    – checklist
    Sep 21, 2017 at 13:18

As these shortcodes will (mostly) be evaluated inside the loop, you can use loop functions. Most importantly get_the_content(). Having the complete content of a post, you just need to parse it to get all the chapters and their names.

Regular expressions probably aren't the best for this, but I found the following code to be working

add_shortcode('toc', function($atts, $content=null){
  $content = get_the_content();
  if ( preg_match_all('/\[chapter.*?name="(.*?)".*?\]/i', $content, $matches) ) {
    $chapters = $matches[1];

  // $chapters has all the "name"s

See the regular expression explained here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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