Что это?
На некоторых сайтах периодически можно встретить нечто подобное:
Работает эта штука следующим образом:
- Каждый пункт содержимого — это заголовок статьи h2, h3, h4 …
- Кликая на какой-либо из этих пунктов мы перемещаемся с помощью якорной ссылки в необходимую нам область страницы.
- Также, когда мы пролистываем страницу и в поле зрения пользователя попадает определенная область с заголовком, этот пункт навигации каким-либо образом подсвечивается давая понять где мы находимся.
Скачать готовый файл можно по этой ссылке
Как это работает?
А теперь разберем все поэтапно. Будем собирать файл скрипта по кускам.
Разметка нашей навигации будет выглядеть следующим образом:
<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>
Как видно из примера, наша навигация будет иметь вложенность.
1) Создаем переменную, в которую помещаем заголовоки контента в необходимой нам области:
const titles = document.querySelector('.post-content__content .text').querySelectorAll('h2,h3,h4');
2) Добаввляем к нашим заголовкам id:
let i = 0;
titles.forEach(title=>{
title.setAttribute('id', `articles_title${i}`);
i++;
});
3) Создаем объект и переменную, которые помогут нам сделать вложенность
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) Создаем структуру нашего содержимого
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) Добавляем разметку навигации в необходимую нам область
document.querySelector('#articles_nav ul').innerHTML = navigation;
6) Создаем и вызываем функцию которая будет:
— плавно перемещать страницу в область с заголовком при клике на пункт навигации.
— будет подсвечивать нужный пункт меню при скролле страницы.
postContentNavigation();
function postContentNavigation(){
// 6.1) Заносим все ссылки навигации в переменную
const smoothLinks = document.querySelectorAll('#articles_nav a');
if(smoothLinks){
// 6.2) Навешиваем на каждую ссылку событие клика
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) Подсвечиваем пункты меню
$(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');
}
});
});
}
}