March 31, 2020

JavaScript Generated Anchor Link Menu From Heading Tags

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"];
Was this helpful?
YesNo
Category: Code

Author

“surfing the web and the waves”. - With a degree in Information Technology (majoring in Networking), a Cert IV in Small Business Management, and having been interested in website design and development since 2010 (With an interest in computers since about age 10) – Ryan has the skills, experience and knowledge with building websites, improving SEO and running online ads.