Author: Stasyuk Eugene 123 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

How to import a large MySQL database into OpenServer

How to import a large MySQL database into OpenServer

How to import a large MySQL database into OpenServer

Perhaps everyone is used to importing a database using phpMyAdmin. But this platform has a limit on the weight of the file that can be uploaded. So if the database dump exceeds this quota, you can’t do it through phpMyAdmin. Today I’m going to show an alternative way to load the database – through the […]

Popup window on the site (modal window) pop-up window

Popup window on the site (modal window)

Popup window on the site (modal window)

When I was just learning the basics of development, I used to make a popup window manually. Yes, yes – instead of finding some ready-made solution, I did this thankless task 🤪. It seemed to me that there was nothing particularly complicated about it. But, only after some time of working on this issue, I […]