Published on March 31, 2020

JavaScript Generated Anchor Link Menu From Heading Tags

Ryan Jones

An anchor link is a link that allows users to flow through a website page. It helps to scroll and skim read easily. A named anchor can be used to link to a different part of the same page (like quickly scrolling) or to a specific section of another page.

It could also be called “Jump to Section”.

A Simple Example

If you want to just be able to run a JavaScript function to automatically create the wrappers you could do something like this:


<div class="anchormenu">
<span><b>Quick Jump Menu</b></span>
<span id="submenu">Loading...</span>
</div>

<script>
document.addEventListener("DOMContentLoaded", function(){
(function(targetId) {
var headingtags = ["h2", "h3", "h4", "h5", "h6", "h7", "h8"];
var all_el = document.getElementsByTagName('*');
var target = document.getElementById(targetId);
var arr = [];
for (var i = 0, n = all_el.length; i < n; i++) {
if (headingtags.indexOf(all_el[i].nodeName.toLowerCase()) > -1){
arr.push(all_el[i]);
}
}
var headings = arr;
console.log(headings);
if(headings.length > 1) {
var menuList = document.createElement("ul");
for(var i=0; i < headings.length; i++) {
var anchorName = "";
if(headings[i].id) {
anchorName = headings[i].id;
} else {
anchorName = "section_" + i;
headings[i].id = anchorName;
}
var headingTag = headings[i].nodeName.replace("H", "");;
var level = "";
var levelcount = 0;
for(var a=0; a < headingTag-2; a++) {
level = level + "- ";
levelcount = levelcount+1;
}
var headingText = level + " "+headings[i].innerText;
var menuLink = document.createElement("a");
menuLink.setAttribute("href", "#" + anchorName);
menuLink.appendChild(document.createTextNode(headingText));
var listItem = document.createElement("li");
listItem.appendChild(menuLink);
menuList.appendChild(listItem);
}
while(target.hasChildNodes()) target.removeChild(target.firstChild);
target.appendChild(menuList);
}
})
("submenu");
});
</script>

Anchor Link Menu with Dropdown

A suggestion could be you turn it into a Clickable Dropdown or a Curtain Menu for fancy styling and UX.

As an example:

<div class="dropdown">
<span onclick="submenudropdown()" class="dropbtn">Jump To</span>
<div id="submenu" class="dropdown-content">
</div>
</div>

<script>
function submenudropdown() {
document.getElementById("submenu").classList.toggle("show");
}

window.onclick = function(event) {
if (!event.target.matches('.dropbtn')) {
var dropdowns = document.getElementsByClassName("dropdown-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}

document.addEventListener("DOMContentLoaded", function(){
(function(targetId) {
var headingtags = ["h2", "h3", "h4", "h5", "h6", "h7", "h8"];
var all_el = document.getElementsByTagName('*');
var target = document.getElementById(targetId);
var arr = [];
for (var i = 0, n = all_el.length; i < n; i++) {
if (headingtags.indexOf(all_el[i].nodeName.toLowerCase()) > -1){
arr.push(all_el[i]);
}
}
var headings = arr;
console.log(headings);
if(headings.length > 1) {
var menuList = document.createElement("span");
for(var i=0; i < headings.length; i++) {
var anchorName = "";
if(headings[i].id) {
anchorName = headings[i].id;
} else {
anchorName = "section_" + i;
headings[i].id = anchorName;
}
var headingTag = headings[i].nodeName.replace("H", "");;
var level = "";
var levelcount = 0;
for(var a=0; a < headingTag-2; a++) {
level = level + "- ";
levelcount = levelcount+1;
}
var headingText = level + " "+headings[i].innerText;
var menuLink = document.createElement("a");
menuLink.setAttribute("href", "#" + anchorName);
menuLink.appendChild(document.createTextNode(headingText));
menuList.appendChild(menuLink);
}
while(target.hasChildNodes()) target.removeChild(target.firstChild);
target.appendChild(menuList);
}
})
("submenu");
});
</script>
<style>
.dropbtn {
font-size: 16px;
border: none;
cursor: pointer;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f1f1f1;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown-content a:hover {background-color: #ddd}
.show {display:block;}
</style>

Targeted Content Only

Finally, we can edit the code to only take headings tags from the content of the page instead of the header or footer (which often have heading tags as well).

An live example of this can be found at the top of this page.


<div class="dropdown" id="dropdown">
<span onclick="submenudropdown()" class="dropbtn">Jump To</span>
<div id="submenu" class="dropdown-content">
</div>
</div>

<script>
function submenudropdown() {
document.getElementById("submenu").classList.toggle("show");
}

window.onclick = function(event) {
if (!event.target.matches('.dropbtn')) {
var dropdowns = document.getElementsByClassName("dropdown-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}

function hasParentWithMatchingSelector (target, selector) {
return [...document.querySelectorAll(selector)].some(el =>
el !== target && el.contains(target)
)
}

document.addEventListener("DOMContentLoaded", function(){
(function(targetId) {

var headingtags = ["h2", "h3", "h4", "h5", "h6", "h7", "h8"];
var all_el = document.getElementsByTagName('*');
var target = document.getElementById(targetId);
var arr = [];
for (var i = 0, n = all_el.length; i < n; i++) {
if (headingtags.indexOf(all_el[i].nodeName.toLowerCase()) > -1){
if(hasParentWithMatchingSelector(all_el[i], '.et-l--body')){
arr.push(all_el[i]);
}
}
}
var headings = arr;
console.log(headings);
if(headings.length > 1) {
var menuList = document.createElement("span");
for(var i=0; i < headings.length; i++) {
var anchorName = "";
if(headings[i].id) {
anchorName = headings[i].id;
} else {
anchorName = "section_" + i;
headings[i].id = anchorName;
}
var headingTag = headings[i].nodeName.replace("H", "");;
var level = "";
var levelcount = 0;
for(var a=0; a < headingTag-2; a++) {
level = level + "- ";
levelcount = levelcount+1;
}
var headingText = level + " "+headings[i].innerText;
var menuLink = document.createElement("a");
menuLink.setAttribute("href", "#" + anchorName);
menuLink.appendChild(document.createTextNode(headingText));
menuList.appendChild(menuLink);
}
while(target.hasChildNodes()) target.removeChild(target.firstChild);
target.appendChild(menuList);
}
else
{
document.getElementById("dropdown").style.display = "none";
}
})
("submenu");
});
</script>
<style>
.dropbtn {
font-size: 16px;
border: none;
cursor: pointer;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f1f1f1;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown-content a:hover {background-color: #ddd}
.show {display:block;}
</style>

Just edit the .et-l–body on this line here (Line 40) to your content body class:


if(hasParentWithMatchingSelector(all_el[i], '.et-l--body')){

Note: This class is Divi’s main content class.

A Quick Tip

Use top level headings instead of all heading tag levels to keep a simple and easy to use jump to menu. For longer blog posts, a super long jump-to menu may just clutter the site.

You can edit the heading tags used here:


var headingtags = ["h2", "h3", "h4", "h5", "h6", "h7", "h8"];
Add To Favorites
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x
That Website Is Me

That Website Is Me

Typically replies within an hour

I will be back soon

Contact Us

General Contact Form
That Website Is Me
Hey there 👋
How can I help you?
Start Chat with:
chat