Меняем активный пункт меню при прокрутке страницы

Очень часто на одностраничниках можно встретить такую фишку, когда начинаешь прокручивать страницу, верхнее меню плавно едет за вами и в зависимости от того, какой вы сейчас раздел просматриваете, будет выделен соответствующий пункт меню (посмотрите демо).

Все это делается чтобы пользователю было легче ориентироваться на одностраничнике и чтобы он знал, где конкретно сейчас находится.

В принципе реализовать данную идею не так уж и трудно, но ввиду того, что мне на почту приходит не мало писем с вопросами по созданию данного функционала, то я решил написать краткую статью о этом.

HTML

Для реализации идеи, мы должны создать HTML-структуру нашего плавающего меню

        <header class="sticky-header">
<nav class="top-menu">
<ul>
<li><a class="active" href="#home">Главная</a></li>
<li><a href="#about">О нас</a></li>
<li><a href="#service">Услуги</a></li>
<li><a href="#contact">Контакты</a></li>
</ul>
</nav>
</header>

Затем создаем структуру контентной части нашего одностраничника. Она будет состоять из отдельных разделов (секций). Для каждой из этих секций мы обязательно задаем уникальный идентификатор.

Данный идентификатор должен соответствовать значению атрибута href ссылки меню, которая логически ссылается на этот раздел.

<section id="home">
Здесь любой контент...
</section>

<section id="about">
Здесь любой контент...
</section>

<section id="service">
Здесь любой контент...
</section>

<section id="contact">
Здесь любой контент...
</section>

CSS

Теперь добавить немного стилей для нашего меню.

Здесь вы можете задавать все что угодно, единственное, что стоит отметить, это должно быть обязательно указано правило для ссылки с классом .active.

Потому что именно этот класс будет добавляться ссылке меню в момент прокрутки страницы и за счет него мы будем понимать, какая ссылка сейчас является активной.

.sticky-header {
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	background: rgba(0, 0, 0, .8);
}
.top-menu ul {
	margin: 20px 0;
	text-align: center;
}
.top-menu ul li {
	list-style: none;
	display: inline-block;
	margin: 0 15px;
}
.top-menu a {
	color: #fff;
	-webkit-transition: .5s;
	-moz-transition: .5s;
	transition: .5s;
}
.top-menu a:hover,
.top-menu a.active {
	color: #e0a2a2;
}

Для наших разделов (секций) я никаких стилей здесь не привел, потому что они могут быть любыми, это не влияет на функционал.

jQuery

Ну и теперь немного jQuery. За основу я взял скрипт, который был представлен в этой статье. Правда определенные изменения он все же пережил.

var menu_selector = ".top-menu"; // Переменная должна содержать название класса или идентификатора, обертки нашего меню. 
function onScroll(){
	var scroll_top = $(document).scrollTop();
	$(menu_selector + " a").each(function(){
		var hash = $(this).attr("href");
		var target = $(hash);
		if (target.position().top <= scroll_top && target.position().top + target.outerHeight() > scroll_top) {
			$(menu_selector + " a.active").removeClass("active");
			$(this).addClass("active");
		} else {
			$(this).removeClass("active");
		}
	});
}
$(document).ready(function () {
	$(document).on("scroll", onScroll);
	$("a[href^=#]").click(function(e){
		e.preventDefault();
		$(document).off("scroll");
		$(menu_selector + " a.active").removeClass("active");
		$(this).addClass("active");
		var hash = $(this).attr("href");
		var target = $(hash);
		$("html, body").animate({
		    scrollTop: target.offset().top
		}, 500, function(){
			window.location.hash = hash;
			$(document).on("scroll", onScroll);
		});
	});
});

В этом коде, в принципе, ничего менять не нужно, разве только значение переменной menu_selector. Она должна содержать название класса или идентификатора, обертки нашего меню. Если ваше меню обернуто не в тег с классном .top_menu, то измените это значение.

Кстати, если вы больше любите jQuery плагины, то вместо приведенного вверху jQuery кода, вы можете подключить к странице плагин jquery.changeActiveNav.js (он есть в дополнительных материалах).

И затем просто его проинициализируйте.

$(document).ready(function(){
	$(".top-menu").changeActiveNav();
});

Я этот плагин написал сам, хотя по сути, он состоит из того же jQuery кода, который был указан выше. Просто я упаковал его в формат плагина, для удобства использования на разных сайтах.

Теперь, если вы все сделал правильно, у вас должно заработать.

При прокрутке странице, как только в поле зрения посетителя появляется соответствующий раздел (секция), в нашем меню будет найдена ссылка, соответствующая этому разделу и затем к ней будет добавляться класс .active.

Так как, мы с вами для класса .active задали определенные стили в CSS, то теперь этот пункт меню будет выделяться соответствующим образом.

Если же мы просто кликнем по пункту меню, то будет сделана прокрутка к соответствующему разделу.

Добавить комментарий

  • Здравствуйте, возможно вы сможете мне как новичку помочь. Вопрос такой: при клике на якорь мне необходимо скроллить не ровно до начала данного блока, а на 70px выше, так как к верху страницы приклеена шапка. Никак не могу разобраться как бы сделать, чтобы работало

    • Попробуйте изменить вот эту строчку http://joxi.ru/RmzKOjJT0y78N2
      А именно укажите target.offset().top — 70

      • 17.03.2018 21:49

        Простите, пожалуйста, совершенно забыла уточнить, что использую jquery.changeActiveNav.min.js

      • Тогда подключите вот этот скрипт http://files.webcareer.ru/2016/01/jquery.changeActiveNav.min.rar

        Он как раз должен работать так, как вам и нужно.

      • 17.03.2018 23:11

        В том-то и проблема, что неправильно работает: при клике изначально скороллит до нужного момента, но сразу после остановки отскакивает вверх и получается заголовок блока скрыт под шапкой. Я уже полдня экспериметирую с этим плагином и никак не получается (
        Но всё равно спасибо вам за помощь))

      • 17.03.2018 23:28

        Ура!
        Удалось решить проблему (код плагина маленько переписанный вставляю), вдруг кому пригодится.
        !function (t) {
        var e = {
        init: function (n) {
        return this.each(function () {
        var n = t.extend({menu: t(this)}, n);
        t(document).on("scroll.changeActiveNav", {myOptions: n}, e.scroll), t("a[href^=#]").on("click.changeActiveNav", {myOptions: n}, e.click)
        })
        }, destroy: function () {
        return this.each(function () {
        t(document).off(".changeActiveNav")
        })
        }, scroll: function (e) {
        var n = e.data.myOptions.menu, o = t(document).scrollTop();
        n.find("a").each(function () {
        var e = t(this).attr("href"), i = t(e);
        topOffset = i.position().top — 70;
        topOffset <= o && topOffset + i.outerHeight() > o ? (n.find("a.active").removeClass("active"), t(this).addClass("active")) : t(this).removeClass("active")
        })
        }, click: function (n) {
        n.preventDefault();
        var o = n.data.myOptions.menu;
        o.find("a.active").removeClass("active"), t(this).addClass("active");
        var i = t(this).attr("href"), a = t(i);
        t("html, body").animate({scrollTop: a.offset().top — 70}, 1000
        // , function () {
        // window.location.hash = i, t(document).on("scroll.changeActiveNav", {myOptions: n.data.myOptions}, e.scroll)
        // }
        )
        }
        };
        t.fn.changeActiveNav = function (n) {
        return e[n] ? e[n].apply(this, Array.prototype.slice.call(arguments, 1)) : "object" != typeof n && n ? void t.error("Method " + n + " does not exist on jQuery.changeActiveNav") : e.init.apply(this, arguments)
        }
        }(jQuery);

  • 31.10.2017 21:26

    Здрасте,а как сделать плавную смену цвета?

    • За это отвечает стиль "transition".
      Например:
      a.active {
      background:#fff;
      transition: 1s;
      }

  • 08.03.2016 14:32

    Поздравляю вас, Александр, с днем рождения. Всего вам наилучшего!

  • С праздником, Александр!

  • С Днем рождения, Александр! Всего самого доброго и светлого! И спасибо огромное за Ваши уроки! Очень нравится Ваша манера объяснения темы! Очень все подробно, неторопливо и предельно ясно! Еще раз Вас с 25-летием !

  • 11.01.2016 20:35

    С Днём Рождения Александр! Море удачи и дачу у моря вам 🙂 По чаще радуйте нас своими супер — пупер уроками 🙂 Спасибо!

  • Александр! С днем рождения! Счастья, здоровья, исполнения всех Ваших желаний. Вы один из лучших преподавателей. Ждем Ваших новых уроков.

  • 11.01.2016 18:01

    Здравствуйте, Александр! С Днем Рождения, дорогой! Всех благ и больших успехов на ниве web-программирования!