Author: Stasyuk Eugene 84 Tags: 14.12.2022

What’s this?

On some sites, you can periodically find something like this:

nav

This thing works like this:

  • Each content item is an article heading h2, h3, h4…
  • By clicking on any of these items, we move with the help of an anchor link to the area of the page we need.
  • Also, when we scroll through the page and a certain area with a heading enters the user’s field of view, this navigation item is somehow highlighted, making it clear where we are.

You can download the finished file from this link

How it works?

And now we will analyze everything in stages. We will collect the script file in pieces.

Our navigation markup will look like this:

<ul>
	<li>
		<a href="#article_title1">Title H2</a>
		<ul>
			<li>
				<a href="#article_title2">Title H3</a>
			</li>
			<li>
				<a href="#article_title3">Title H4</a>
			</li>
		</ul>
	</li>

	<li>
		<a href="#article_title4">Title H2</a>
		<ul>
			<li>
				<a href="#article_title5">Title H3</a>
			</li>
			<li>
				<a href="#article_title6">Title H4</a>
			</li>
		</ul>
	</li>
</ul>

As you can see from the example, our navigation will be nested.

1) We create a variable in which we place the content headers in the area we need:

const titles = document.querySelector('.post-content__content .text').querySelectorAll('h2,h3,h4');

2) Adding id to our headers:

let i = 0;
titles.forEach(title=>{
    title.setAttribute('id', `articles_title${i}`);
    i++;
});

3) We create an object and a variable that will help us make nesting

let titlesObj = {},
        h2;

for(let i = 0; i < titles.length; i++){

    if(titles[i].classList.contains('no-active')){
        continue;
    }

    if(titles[i].tagName == 'H2'){
        titlesObj[titles[i].textContent] = {
            'id': titles[i].getAttribute('id')
        };

        if(titles[i+1] && titles[i+1].tagName != 'H2'){
            h2 = titles[i].textContent;
            titlesObj[titles[i].textContent]['titles'] = {};
        }
    }

    if(titles[i].tagName != 'H2'){
        titlesObj[h2]['titles'][titles[i].textContent] = {
            'id': titles[i].getAttribute('id')
        }
    }
}

4) Building the structure of our content

let navigation = '';

for(let item in titlesObj){
    let subLinks = '';

    if(titlesObj[item]['titles']){
        subLinks = subLinks + '<ul>';

        for(let sub in titlesObj[item]['titles']){
            subLinks = subLinks + `<li><a href="#${titlesObj[item]['titles'][sub]['id']}">${sub}</a></li>`;
        }

        subLinks = subLinks + '</ul>';
    }

    navigation = navigation + `<li><a href="#${titlesObj[item]['id']}">${item}</a>${subLinks}</li>`;
}

5) Adding navigation markup to the area we need

document.querySelector('#articles_nav ul').innerHTML = navigation;

6) We create and call a function that will be:
– smoothly move the page to the area with the title when clicking on the navigation item.
– will highlight the desired menu item when scrolling the page.

postContentNavigation();

function postContentNavigation(){
    // 6.1) We store all navigation links in a variable
    const smoothLinks = document.querySelectorAll('#articles_nav a');

    if(smoothLinks){

            // 6.2) We hang a click event on each link
            for (let smoothLink of smoothLinks) {
                    smoothLink.addEventListener('click', function (e) {
                            e.preventDefault();
                            const id = smoothLink.getAttribute('href');

                            document.querySelector(id).scrollIntoView({
                                    behavior: 'smooth',
                                    block: 'start'
                            });
                    });
            };

            // 6.3) Highlight menu items
            $(window).scroll(function(){
                    var $sections = $('.post-content__content h2, .post-content__content h3, .post-content__content h4');
                    $sections.each(function(i,el){
                            var top  = $(el).offset().top-50;
                            var bottom = top +$(el).height();
                            var scroll = $(window).scrollTop();
                            var id = $(el).attr('id');
                            if( scroll > top && scroll < bottom){
                                    $('li.active').removeClass('active');
                                    $('a[href="#'+id+'"]').parents('li').addClass('active');

                            }
                    });
            });
    }
}

Other articles

Simple star rating stars

Simple star rating

Simple star rating

In this article, I will show you how to make the visual part of the star rating. Namely: Finished option: Markup As a markup, we have a container block with the .rate class inside which I have placed 5 links. I used a font as a star. But most likely in practice you will have […]

Wp Favs – plugin manager for WordPress

Wp Favs – plugin manager for WordPress

Wp Favs – plugin manager for WordPress

When you create WordPress templates on a regular basis, you come across one procedure that takes up some of your time – downloading and activating plugins. The WP Favs plugin solves this problem quite successfully. You simply create a list of plugins that you use regularly and then bulk upload them to your site. The […]