Content Overview and Example
(this index list is already a real example of what the shortcode does)
What does the WordPress ShortCode do?
Before I start with that: Keep in mind that this article was written rather quickly, having experienced users in mind …!
Well first of all, the overview just before this chapter is showing the output of using the ShortCode – WordPress generated the anchors and the overview for this post based on the shortcode we will discus here.
Drafts vs Published …
If you use this shortcode to just display the anchor overview for THIS post, then this will work in previews as well (the post is still a draft).
If you want to display a category, then anchors and titles of “draft” posts in that category will NOT display.
If none of the posts are “published” in the given category, then the list will remain empty.
Well, it started with making a list of all <h1>
, <h2>
,…, <h6>
tags that I used in a post. So the code should generate a list with the found <hx>
tags, and I wanted them in an unordered list (<ul>
), and with each higher number in the<hx>
tag, jump a level deeper.
Something like this:
h2 chapter
h2 chapter
h3 chapter
h3 chapter
h2 chapter
h3 chapter
h4 chapter
h5 chapter
h5 chapter
h5 chapter
h6 chapter
h4 chapter
h2 chapter
Yes I skipped the <h1>
tag, since I reserved that on my website for the page title.
Obviously, maintaining the anchors (<a id="anchorname"></a>
) was a pain as well, so I decided to add this as well, and since anchors seems to work with just the “id” as well, I used: <h2 id="TitleofthisChapter">Title of this Chapter</h2>
.
To make things more complicated, for example for a course, I wanted to add multiple articles to the list. For this approach I used a dedicated category for a given “course” or group of articles. The tag now looks at all the articles in the defined category and lists them (optionally) as well.
Skipped Chapters …
Chapter tags (<Hx>…</Hx>) that contain markup like <strong> and <em> will be skipped with this version!
So if you need italics or bold, better modify your CSS classes.
The purpose of this article is to show you how to do this with your own WordPress setup, so you will not need to install a plugin. There are some awesome plugins out there, but unfortunately tons of them get abandoned over time … so I prefer to use my own code.
This article could give you some ideas how to create your own, better and improved version. Feel free to post comments when you have improvement or suggestions. My code can always be improved and made better.
Ad Blocking Detected Please consider disabling your ad blocker for our website.
We rely on these ads to be able to run our website.
You can of course support us in other ways (see Support Us on the left).
The simpelest shortcode is:
[t4a_content_list]
It will not show the entire category, and only the chapters and anchors of this current post, and will give it the title “Content Overview”.
The most extended way of using the ShortCode is:
[t4a_content_list category="2" anchors="all" title="Overview of this fantastic article"]
This will generate an overview of all posts in the category with ID=2, sorted by post titles, and will show the anchors of all posts in that category. The title for this will be “Overview of this fantastic article“.
ShortCode Parameters
Parameter |
Purpose |
Example |
category |
List the articles in the category with this ID.
Default the ID=-1 which means do not show the articles in this category, only the current post will be shown. |
category=”-1″
category=123 |
anchors |
List the anchors of this post of all posts (if category is listed).
Default this is only for this post. |
anchors=”all”
anchosr=”post” |
title |
Here you can enter the title for this overview.
The default title is “Content Overview”. |
title=”Just an Overview” |
Using parameters is optional – when a parameter is not added, then it will go to it’s default value.
How does it work in the background?
This shortcode works in two steps, and you have to understand these to make it work right.
Most plugins do all this on the fly, but I’m not a fan of that when it can be done more efficient, but there is a small price to pay for this increased efficiency.
Note that I posted this one rather quickly, and the code is not explained in the greatest detail.
I do consider this an article for those with at least a little bit of PHP, HTML and CSS experience.
Creating the Anchor List for a Post
The first step occurs when we write a new post.
The moment you save a post (draft or publish), WordPress will trigger an action called “save_post“. In this “action” the code will first cleanup all <hx>
tags and remove the id="anchorname"
from them – either you added them or the code added them at a previous time.
Next it will grab the title and clean it from all HTML, spaces, special characters etc, so we can use it as an anchor name.
After that it will replace all <hx>
tags with tags like so: <hxid="anchorname">
so we can jump to those locations with the use of simple links.
Finally it will generated an unordered list in the format we’d like, and include the titles and links to the appropriate chapters.
That list will then be stored as meta-data for a post, so that when we call the shortcode, we do not have to dig through the code again to get the list.
The reason why I do this when we save a post, is to make sure it’s always up to date of course.
Another advantage is that when we’d like to list an entire category, that we do not have to start digging through all the posts involved – we can simply pull the metadata (also called “Custom Fields”) instead. The metadata is stored as “t4a_AnchorList” for each post that is saved after we applied the changes in the code, which I will show you below.
You might see why this function is good to have for all posts – you might refer to an entire category in the future and at that moment, at least the lists are already done.
Update Posts after making these code changes!
The fact that these lists will be generated at the point that you save or update a post, means that if you already had a few posts, before making the code changes, that for those posts you briefly have to open and save/update them so that the list is being generated and stored as metadata.
Of course changing the content of a post while saving a post would trigger this function again, so we temporary have to remove this action, save the changes and re-add this action again. Other wise we’d end up in an endless loop where changing the content would trigger this action again, which would change the content again, and trigger this code again, etc …
This is the code that handles that. Paste it into the “functions.php” file of the theme you’re using.
UPDATE: Added a line to remove ALL existing Anchors as well.
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
| // Convert H-tag text to anchor
function t4a_content_title2anchor($TitleText) {
$anchor = strip_tags($TitleText); // remove all HMTL
$anchor = preg_replace('/[^A-Za-z0-9]/','', $anchor); // Only use A-Z, a-z and 0-9, remove other
return $anchor;
}
// Build Content list and store it in 't4a_AnchorList' metadata
function t4a_content_buildanchorlist($PostID) {
$HTML = '<p>';
// Get the post object (for status, title, and content)
$thePost = get_post($PostID);
$chapter_counter = 0; // Counters for H2 ... H6
$prev_level = 2; // We start with NO level (= H1)
//if ($thePost->post_status === 'publish')
{
// ***** First remove all old "id"s
$Content = preg_replace('%\<h([1-6]) id="(.+?)">%', '<h$1>', $thePost->post_content);
// ***** Remove all anchors
$Content = preg_replace('%<a id="(.+?)"><\/a>%', '', $Content);
// ***** CREATE STYLES for LI's, if post is PUBLISHED and has hx-tags
if (preg_match_all('%\<h([1-6])>(.+?)[^ </i>]<\/h\1>%', $Content, $matches)>0)
{
$chapters = $matches[0];
$chaptersHTML = '<ul class="t4a_overview_first">'; // Create initial list
foreach($chapters as $chapter) // For each chapter we found ...
{
// Create Content List
$new_level = substr($chapter,2,1); // Get chapter number (depth/level so h2->2, h3->3, etc.)
if($new_level!==$prev_level) // Change in chapter depth
{
if($new_level>$prev_level) // Go one or more levels UP (ie. h2->h3, or h2->h5 etc)
{
while($new_level>$prev_level) // create and style first and following <ul> lists
{
if($new_level!=='2')
{ $chaptersHTML .= '<ul class="t4a_overview_next">'; }
else
{ $chaptersHTML .= '<ul class="t4a_overview_first">'; }
$prev_level++;
}
}
else
{ // Go one or more levels DOWN (ie. h3->h2, h5->h2, etc)
while($new_level<$prev_level)
{
$chaptersHTML .= '</ul>';
$prev_level--;
}
}
}
if($new_level==2) // if at base level (h2), increase chapter number and add span tag for it
{
++$chapter_counter;
$chapterTXT = '<span>'.$chapter_counter.'</span>';
}
else
{ $chapterTXT = ''; }
$chaptersHTML .= '<li>'.$chapterTXT.'<a href="#'.t4a_content_title2anchor($chapter).'" class="t4a_overview_level_'.$new_level.'">'.preg_replace(['%<h([1-6])>%','%</h([1-6])>%','%<strong>%','%</strong>%'],'', $chapter).'</a></li>';
}
while($new_level>1) // Return to base level, for example when the last chapter was h5 go back to h2.
{
$chaptersHTML .= '</ul>';
$new_level--;
}
$chaptersHTML .= "</p>";
}
$HTML .= $chaptersHTML;
update_post_meta($PostID, 't4a_AnchorList', $HTML); // Store content list as meta data so we don't calculate it over and over again
// Create new "id"s
if (preg_match_all('%\<h([1-6])>(.+?)[^ </i>]<\/h\1>%', $Content, $matches)>0)
{
$chapters = $matches[0];
foreach($chapters as $chapter) // For each chapter we found ...
{
// insert id="" in found chapter
$new_chapter = preg_replace('%\<h([1-6])>%','<h$1 id="'.t4a_content_title2anchor($chapter).'">', $chapter);
$Content = str_replace($chapter, $new_chapter, $Content);
}
//echo $Content;
}
// unhook this function so it doesn't loop infinitely
remove_action( 'save_post', 't4a_content_buildanchorlist' );
$UpdatedPost = array (
'ID' => $PostID,
'post_title' => $thePost->post_title,
'post_content' => $Content
);
wp_update_post( $UpdatedPost );
// re-hook this function
add_action( 'save_post', 't4a_content_buildanchorlist' );
}
}
add_action('save_post', 't4a_content_buildanchorlist' ); // Update Content when saving content |
Suggestions for improvements are most welcome – I’m sure this can be done cleaner/better/faster etc., even though it works really well this way.
You’ll see two functions here;
t4a_content_title2anchor() which grabs the actual title and strips it down to a sequence of characters that can be used as an anchor.
t4a_content_buildanchorlist() which actually builds the lists, cleans the <H> tags and adds “id”‘s to the <H> tags.
The add_action('save_post', 't4a_content_buildanchorlist' );
line adds the “t4a_content_buildanchorlist” function to the “save_post” action, so it will be executed when we save or update a post.
Handling the ShortCode in your Post
The ShortCode will be handled by the following function (t4a_content_list()).
It will try to read parameters that you might have written in the ShortCode, and defaults to it’s default values if it cannot find the parameters.
It will generate a list for this post only, or for a category. With the category we have the option to only list the chapters of this post or of all posts. This code will also create the title for this overview chapter.
Code to add the shortcode to WordPress, is again added to the “functions.php” file of the WordPress theme you’re using.
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 51
| function t4a_content_list($atts, $content = null) {
$attributes = shortcode_atts( array(
'category' => '-1', // Category ID, zero (default) means do not display category, else it's the category ID
'anchors' => 'post', // Anchors, post (default) means only anchors from this post { post, all }
'title' => __('Content Overview') // Title for this "chapter" (h2)
), $atts );
$showCategory = $attributes['category'] >= 0; // If NO category (-1), and NO anchors (none):
$showAllAnchors = $attributes['anchors'] === 'all'; // create list anyway though
$thePost = get_post();
if(!$showCategory)
{
return '<h2>'.$attributes['title'].'</h2>'.get_post_meta($thePost->ID, 't4a_AnchorList', TRUE);
}
else
{
$HTML = '<h2>'.$attributes['title'].'</h2><ul class="t4a_content_postslist">';
// Show cats, either ALL anchors, or only anchors from this post
$catposts = get_posts('category='.$attributes['category'].'&posts_per_page=-1&order=ASC&orderby=title'); // get posts in category
foreach($catposts as $catpost) {
if(in_category($attributes['category'],$catpost->ID))
{
$post_title = $catpost->post_title;
$short_text = get_post_meta($catpost->ID,'ShortText',TRUE);
$HTML .= '<li class="t4a_content_posts"><a href="'.get_permalink($catpost->ID).'"'.
' title="'.$post_title.' - '.$short_text.'">'.$post_title.'</a></li>';
if( ($showAllAnchors) || ($thePost->ID === $catpost->ID))
{
if($thePost->ID === $catpost->ID)
{
$HTML .= get_post_meta($catpost->ID, 't4a_AnchorList', TRUE);
}
else
{
$HTML .= str_replace(' href="#', ' href="'.get_permalink($catpost->ID).'#', get_post_meta($catpost->ID, 't4a_AnchorList', TRUE));
}
}
}
}
$HTML.='</ul>';
return $HTML;
}
}
add_shortcode("t4a_content_list", "t4a_content_list"); |
CSS to make it look better
Now a simple bullet list can be quite boring, so I’ve added some CSS classes, which you can modify as you see fit.
A little CSS experience will be helpful if you want to give it your preferred look. This is just an example.
An example (add this to the CSS of your theme):
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 51
| .t4a_overview_first {
list-style-type: none;
margin-left: 30px !important;
padding-left: 0px !important;
}
.t4a_overview_first span {
margin-right: 10px;
font-weight: bold;
padding: 4px;
background-color: #E8CFC3;
margin-bottom: -5px !important;
border-radius: 100px;
width: 20px;
height: 20px;
display: inline-block;
text-align: center;
}
.t4a_overview_next {
list-style-type: disc;
margin-left: 40px !important;
}
.t4a_overview_level_3, .t4a_overview_level_4, .t4a_overview_level_5, .t4a_overview_level_6 {
color: #777 !important;
}
.t4a_content_postslist {
margin-left: 20px !important;
list-style: none;
}
.t4a_content_posts {
font-size: large;
list-style-position: initial;
font-weight: bold;
color: #cb6335;
}
li.t4a_content_posts {
width: 100%;
display: inline-block;
background-color: #E2E2E2;
border: 1px #CECECE solid;
padding: 5px;
padding-left: 18px;
border-radius: 100px;
}
li.t4a_content_posts a {
border: 0px !important;
} |
Some extra CSS info …
Just a quick note:
- <h2> tags will have a number in front of it, which is tagged in a <span>.
- <h2> tags, in the list, get the “t4a_overview_first” CSS class.
- Tags higher than <h2> (so: <h3>, <h4>, <h5>, <h6>) will not have a number.
- Tags higher than <h2>, get the “t4a_overview_next” class.
- Tags higher than <h2>, have their own CSS class for links (t4a_overview_level_x).
- When using categories, each title of a post gets the “t4a_content_posts” CSS class (<li>).
Ad Blocking Detected Please consider disabling your ad blocker for our website.
We rely on these ads to be able to run our website.
You can of course support us in other ways (see Support Us on the left).
Since I tend to forget the parameters, even from my own ShortCode, I created a little button for TinyMCE – the editor used when you’re editing a post in WordPress.
There are several steps to add a button.
We need an icon, which is a 18px × 18px PNG file, which I called “t4a_add_anchoroverview.png“.
Next we need tell WordPress that we have a new button, which needs to be attached and placed in tinyMCE – which is done in the “functions.php” file of our theme and a Javascript file which I called “tinybuttons.js“.
Finally we need some code to handle the form in which we will set our parameters to build the ShortCode and insert it into our post – which is basically a HTML file with some JavaScript in it. I called that file “tinymce_addanchorlist.php” – I used PHP because I need to use some trickery to retrieve a list of available categories.
To keep things clean and organized, I’ve created a directory called “js” in my WordPress setup, in which I store the form.
In that same directory, I created a directory called “images” to store the PNG icon in.
This is the icon:
(you can right click it and download it if you’d like).
So the file structure in the theme directory of the theme your website is using would be something like this:
...
functions.php
...
js/
| tinybuttons.js
| tinymce_addanchorlist.php
|
/ images /
| t4a_add_anchoroverview.png
...
The icon you’ve seen, so I assume that you have it in the /js/images/ directory now.
To get the button available in TinyMCE, we need this file (“tinybuttons.js“):
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
| (function() {
tinymce.create('tinymce.plugins.t4a_boxes', {
init : function(ed, url) {
ed.addButton('add_anchorlist', {
title : 'Strip and add ID anchors to Chapter(H) tags and create overview of these anchors',
image : url+'/images/t4a_add_anchoroverview.png', onclick : function() {
ed.windowManager.open({ file : "/wp-content/themes/tweaking4all/js/tinymce_addanchorlist.php", title : 'Tweaking4All - Create Anchorlist Overview',
width : 800,
height : 155,
inline : "yes",
close_previous: "no",
win : window});
}
});
// You can still add more here if you'd like
},
createControl : function(n, cm) {
return null;
},
});
tinymce.PluginManager.add('t4a_boxes', tinymce.plugins.t4a_boxes);
})(); |
In line 7 you’ll see we refer to the icon, and in line 9 we refer to the “tinymce_addanchorlist.php” file which handles the form:
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| <html dir="ltr"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Add Anchors and Content Overview</title>
<script language="javascript" type="text/javascript" src="/wp-includes/js/tinymce/tiny_mce_popup.js"></script>
<script language="javascript" type="text/javascript">
// We need: [t4a_content_list category="0" anchors="post" title="Overview of our Arduino Programming Course"]
var ed = tinyMCEPopup.editor; //My editor
function insertCode()
{
var cat = '';
var title = '';
var showall = '';
if (document.addanchorlistprefs.category.value!=-1)
{
cat=' category="'+document.addanchorlistprefs.category.value+'"';
}
if (document.addanchorlistprefs.chaptertitle.value!='')
{
title = ' title="'+document.addanchorlistprefs.chaptertitle.value+'"';
}
if (document.addanchorlistprefs.showall.checked)
{
showall = ' anchors="all"';
}
else
{
showall = ' anchors="post"';
}
var message = ed.selection.getContent();
if (message=='') { message='Message'; }
var theHTML = '[t4a_content_list'+cat+title+showall+']';
ed.execCommand('mceInsertContent', false, theHTML);
tinyMCEPopup.close();
}
</script></head>
<style>
select, input {
font-size: inherit;
}
</style>
<body contenteditable="true">
<?php require_once('../../../../wp-load.php'); ?> <form name="addanchorlistprefs">
<table>
<tr>
<td align="left" valign="top" nowrap>Select category to include: </td>
<td align="left" valign="top" nowrap>
<?php // Generate a list of categories, id of this is "category" wp_dropdown_categories(array( 'show_option_none' => '** Do not show an entire Category **', 'option_none_value' => '-1', 'orderby' => 'name', 'order' => 'ASC', 'hide_empty' => 0, 'echo' => 1, 'name' => 'category', 'class' => 'postform', 'depth' => 0, 'hierarchical' => 1, 'tab_index' => 0, 'taxonomy' => 'category', 'hide_if_empty' => false, 'value_field' => 'term_id',)); ?> </td>
</tr>
<tr>
<td align="left" valign="top" nowrap>Title for Content: </td>
<td align="left" valign="top" nowrap><input name="chaptertitle" type="text" id="chaptertitle" value="Content Overview" size="30"></td>
</tr>
<tr>
<td align="left" valign="top" nowrap>Show anchors of all posts: </td>
<td align="left" valign="" nowrap><input type="checkbox" name="showall" id="showall"></td>
</tr>
</table>
</p>
<p>
<input type="submit" name="insert" id="insert" value="Apply Box" onclick="javascript:insertCode();">
<input type="submit" name="cancel" id="cancel" value="Cancel" onclick="javascript:tinyMCEPopup.close();">
</p>
</form>
</body>
</html> |
The code should be relatively easy to understand.
Line 52 is where the WordPress trick starts – we include wp-load.php to have WordPress available in this little file.
Lines 60-76 is a WordPress call “wp_dropdown_categories” which generates a dropdown in HTML containing all categories.
Functions.php
Now that we have these files in place, add the following code to your theme’s “functions.php” file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| // Add the JavaScript file which calls the Anchorlist form
function add_tinymce_t4a_plugin($plugin_array)
{
$the_js_file = get_bloginfo('template_url').'/js/tinybuttons.js';
$plugin_array['t4a_boxes'] = $the_js_file;
return $plugin_array;
}
add_filter('mce_external_plugins', 'add_tinymce_t4a_plugin');
// Add the Anchor list button to the 1st button row of tinyMCE
function set_tinymce_buttons_row1($buttons)
{
array_push($buttons, "add_anchorlist");
return $buttons;
}
add_filter('mce_buttons', 'set_tinymce_buttons_row1'); |
All this combined should get the button in your post editor. Nice and easy, don’t need to remember those silly parameters or shortcode name.
![Anchorlist Button in TinyMCE](//www.tweaking4all.com/wp-content/uploads/2016/03/anchorlist-button-in-wordpress-tinymce.png)
Anchorlist Button in TinyMCE
![Anchorlist Popup in TinyMCE](//www.tweaking4all.com/wp-content/uploads/2016/03/anchorlist-popup-in-wordpress-tinymce.png)
Anchorlist Popup in TinyMCE
Examples – Screenshots
3 examples as I have use it on Tweaking4All, with the CSS example from above.
Note that the overview of this page also uses this ShortCode … ![](/wp-content/themes/tweaking4all/css/images/emoticons/t4a_grin.png)
Just listing the chapters of the current post
The first one only shows a content overview for posts in this current post.
![Anchor Overview - Just Chapters of THIS post](//www.tweaking4all.com/wp-content/uploads/2016/03/anchor-list-overview-onlypost.jpg)
Anchor Overview – Just Chapters of THIS post
Show all Category Posts, but only Chapters for the current post
This one shows all posts in the category ID you’ve mentioned in the shortcode.
The chapters for this current post however are lifted out and posted individually.
As you can see; for the posts “Part 1 – Start” and “Part 4 – If then”, none of the chapters are listed – just the titles of the posts.
![Anchor Overview - Entire Category, but only Chapters of THIS post](//www.tweaking4all.com/wp-content/uploads/2016/03/anchor-list-overview-category-onlypost.jpg)
Anchor Overview – Entire Category, but only Chapters of THIS post
Show all Chapters, in all Posts of a given Category
In this last example we list it all … quite often though this can become a long list.
I did not take a full screenshot, but it gives you an idea.
![Anchor Overview - An entire category with all chapters of all posts](//www.tweaking4all.com/wp-content/uploads/2016/03/anchor-list-overview-category-all.jpg)
Anchor Overview – An entire category with all chapters of all posts
Comments
There are 2 comments. You can read them below.
You can post your own comments by using the form below, or reply to existing comments by using the "Reply" button.
UPDATE:
Fixed some code issues, since WordPress like to eat backslashes
hans
UPDATE:
Code modified so all existing anchors (<a id=”…”></a>) will be removed as well.
hans