This post will teach you how to make a blog post navigation menu that self-generates based on the structure of your post’s H2 and H3 headers. The menu contains links that let you jump to different sections of your blog post. Here’s an example of what it looks like on my website:
You can try out the navigation menu here.
What’s the Problem?
Some of my blog posts are quite large (3,000+ words) and contain multiple sub-sections. As a result, I wanted to find a way to allow users to jump to a certain section.
If an element has an ID, it’s easy to jump to that section of the webpage. The problem is that if you write your blog post within a rich text field of a CMS item, the headers don’t have IDs.
What’s the Solution?
To overcome this, you need to use JavaScript to assign an ID to each header. The way I chose to do this was by assigning the title of each header to the header’s ID property. I also sanitized the text, replacing spaces and special characters with hyphens that would otherwise cause issues.
For example, an H2 header gets converted from this:
<h2>What Is a Preamp?</h2>
Into this:
<h2 id="what-is-a-preamp-">What Is a Preamp?</h2>
At this point, it’s simply a matter of generating a hierarchical navigation menu based on the structure of your H2s and H3s, adding some styling, and making a few adjustments to page scroll behavior.
Adding the Code to Your Website
Implementing the code is pretty straightforward. First, wrap the content on your blog post in a div with an id of “content”. Rather than including every H2 and H3 header on your page, you will only include the H2 and H3 headers found within your blog post, wrapped in the “content” div. This eliminates headers from your sidebar and footer.
To do this in Webflow, add a div to your page, click on the div, and then type “content” in the div’s ID field:
Paste the following code before the </body>
tag of your blog post template page.
<!-- Generate a navigation menu based on H2 and H3 headers -->
<script>
// Disable the browser's default smooth scroll feature
document.body.setAttribute("data-scroll-time", "0");
// Helper function to sanitize text for use as an id
function sanitizeId(text) {
return text.replace(/\W+/g, '-').toLowerCase();
}
// Select the div where the navigation menu will be inserted
var nav = document.getElementById('navigation');
// Select all h2 and h3 elements in the content div
var headers = document.querySelectorAll('#content h2, #content h3');
// Create a ul element to hold the navigation items
var ul = document.createElement('ul');
var currentLi;
// Loop through each header
for (var i = 0; i < headers.length; i++) {
// Create a new list item
var li = document.createElement('li');
// Create a link
var a = document.createElement('a');
// Generate an id for the header if it doesn't already have one
if (!headers[i].id) {
headers[i].id = sanitizeId(headers[i].textContent);
}
a.href = "#" + headers[i].id;
a.textContent = headers[i].textContent;
// Append the link to the list item
li.appendChild(a);
// Check if the header is an H2 or an H3
if (headers[i].tagName.toLowerCase() === 'h2') {
// If it's an H2, append the list item to the main ul
ul.appendChild(li);
currentLi = li;
} else if (headers[i].tagName.toLowerCase() === 'h3') {
// If it's an H3, append the list item to a nested ul within the current li
var nestedUl = currentLi.querySelector('ul') || currentLi.appendChild(document.createElement('ul'));
nestedUl.appendChild(li);
}
}
// Append the ul to the navigation div
nav.appendChild(ul);
</script>
For reference, you want to paste the code here (Pages > CMS Collection Pages > Blog Posts Template (yours might be called something different) > Before </body>
tag):
Embed the following code into a single blog post, or add it to your blog post template, to automatically generate a blog post navigation menu:
<div id="navigation"></div>
I don’t want all my blog posts to have a navigation menu, so I only embed the code in my long blog posts using Webflow’s HTML Embed Code Editor:
You now have a semi-functional navigation menu embedded in your blog posts that self-generates based on the structure of the H2s and H3s in your blog post content.
Style the navigation menu by adding the following code to the <head>
tag of your blog post template:
<style>
#navigation {
<!-- Style the navigation div here using CSS -->
}
#navigation > ul {
<!-- Style the H2 list here using CSS -->
}
#navigation > ul > li {
<!-- Style the H2s individually here using CSS -->
}
#navigation > ul > li > ul {
<!-- Style the H3 lists here using CSS -->
}
#navigation > ul > li > ul > li {
<!-- Style the H3s individually here using CSS -->
}
</style>
The last issue we need to overcome involves the navigation menu at the top of most web pages. When you click on a link in the nav menu of your blog post, it will jump to the associated header and likely be hidden by the nav menu of your website.
You can deal with this using a simple CSS trick. Add some padding to the top of your headers and then apply an equal amount of negative top margin. This causes the header elements to appear further up the page to the browser than they visually appear.
Paste the following code into the <head>
tag of your blog post template and adjust the padding values to accommodate the height of your website’s nav menu. You may need to select your h2 and h3 elements differently depending on the structure of your page.:
<style>
#content > div > h2,
#content > div > h3 {
padding-top: 80px;
margin-top: -80px;
}
@media screen and (max-width: 990px) {
#content > div > h2,
#content > div > h3, {
padding-top: 20px;
margin-top: -20px;
}
}
</style>
You now have a fully functional blog post navigation menu that you can customize to fit the theme of your website!