Создаем фиксированное меню на CSS, jQuery + решение проблемы с якорями
С момента публикации первой версии данной заметки прошло уже несколько лет, но текущая информация все еще актуальна. Просто хочу дополнить ее разбором одной нетипичной и сложной ситуации, с которой на днях столкнулся по фрилансу. Новичкам озвученные здесь приемы точно пригодятся, плюс запишу их для себя, т.к. похожие задачи возникают время от времени.
В статье я не буду вдаваться в подробности всех деталей кода, укажу лишь основные нюансы. Можете скопировать эти примеры и попрактиковаться с ними на своих проектах, ну а полным новичкам — не помешает дополнительно глянуть сервис обучения верстке Interneting is Hard.
В посте представлены такие вопросы:
-
Как фиксировать меню при прокрутке страницы (это когда блок навигации «прижимается» к верху экрана). Рассмотрим:
-
. .
Фиксированное меню на CSS + HTML
В общем случае при создании простого горизонтального фиксированного меню для сайта вам нужно использовать CSS свойства position:fixed и top:0. Также основному блоку контента придется задать верхний отступ margin-top.
Итак, возьмем простую структуру HTML страницы:
В файл стилей CSS добавляете:
В результате получится такая картинка:
Ничего оригинального, но главное, что работает. Дабы увидеть эффект фиксированного меню при прокрутке страницы вам нужно будет добавить больше текста в блок контента (чтобы появилась полоса прокрутки).
Основные детали кода выше, как я уже говорил, это position:fixed, top:0 и margin-top: 60px. Верхний отступ может быть другим, если высота меню у вас меньше/больше.
Кстати, если требуется зафиксировать меню внизу страницы, заменяете код на:
Все то же самое, только вместо top указывается bottom:0. Единственное, нужно будет погуглить как правильно сделать отступ контенту снизу чтобы он тот скрывался за плашкой меню. Решение с margin-bottom:30px; почему-то не сработало.
Фиксированное меню с jQuery и CSS
Второй пример чуть более сложный — с применением библиотеки jQuery. Она встречается на многих сайтах, плюс в большинстве шаблонов WordPress и других CMS подключена изначально. Из Javascript функций здесь используется всего 2, поэтому данное решение можно считать достаточно легким по сравнению с некоторыми другими из интернета. Вот что в итоге должно получиться:
Алгоритм внедрения данного фиксированного меню с jQuery состоит из трех шагов. Первым делом добавляем HTML код:
Если вы хотите внедрить данный метод на уже готовое собственное меню, тут 2 пути: либо в CSS и JS ниже подставляете свои стили, либо подгоняете имеющийся HTML под вариант сверху.
В стилях размещаете следующие строки:
Кроме непосредственно фиксации меню при прокрутке есть CSS для размещение ссылок в одну строку.
Логика работы функций следующая — когда пользователь находится или прокручивает страницу ниже 136 пикселей сверху, то для меню добавляется класс f-nav, а в нем прописаны знакомые нам position:fixed и top:0.
Значение в 136 пикселей можно менять в зависимости от вашего дизайна. Кроме того, если вы интегрируете данный пример в свое готовое горизонтальное фиксированное меню на сайте, то указывайте соответствующие значения классов в JS и CSS. Если у вас возникли какие-то нюансы с реализацией кода, загляните в комментарии к оригинальной статье — там есть парочка подсказок.
Напоследок предлагаю ознакомиться с еще одним вариантом реализации нашей задачи. Не буду особо детализировать его, просто размещаю вставку из онлайн редактора кода Codepen.
Прокрутка до якоря с фиксированным меню
Для начала пару слов о самой задаче. Допустим у вас есть сайт, где вы решили фиксировать меню при прокрутке. При этом в навигации или тексте используются так называемые якоря (Anchor). При переходе по этим ссылкам экран страницы автоматически перемещается к соответствующим местам на странице (где расположен якорь). Однако в таком случае часть контента закрывается блоком меню.
Данная ситуация чаще всего возникает в лендингах. На скриншоте проблема заметна более наглядно:
На StackOverflow найдено два решения задачи, которые отличаются лишь CSS.
Чтобы создать якорь с отступом первым делом добавим ему определенный стиль, например, anchor:
Далее для него в стилях прописываете:
Здесь 55 пикселей — высота меню + отступ, которые нужны чтобы текст контента был виден. Указывайте значения, подходящие вашему сайту. В работе я применял именно этот вариант.
Альтернативный метод предлагает реализацию через padding, там вообще получается одна строка кода:
В дополнение предлагаю глянуть эту заметку где автор привел сразу 5 разных решений как правильно использовать якоря с отступом для горизонтального фиксированного меню при прокрутке страницы: через псевдоэлементы, негативные отступы и т.п.
Динамическое фиксированное меню + якоря содержания (в мобильной версии)
Уже по заголовку видно, что ситуация у меня возникла не простая. Возможно, и существует какое-то более изящное решение, но за день поисков ничего лучше найти не удалось. Оно еще и работает в отличии от остальных!
Пару слов о самой задаче. Вот как выглядит мое меню:
- Во-первых, в начале статьи с помощью плагина Easy Table of Contents автоматически выводится содержимое той или иной публикации. При клике на соответствующие ссылки вы попадаете в нужное место статьи. ВАЖНО! Если у вас установлен этот же модуль, то там для мобильных версий есть специальная настройка и ничего дополнительно внедрять НЕ нужно.
- Во-вторых, на мобильных устройствах при прокрутке экрана появляется специальное меню с темным фоном, которое фиксируется сверху экрана.
Я перепробовал много разных вариантов: и рассмотренный выше сдвиг якоря вниз страницы с помощью CSS, и добавление специальной переменной, срабатывающей при клике на содержимое Easy Table of Contents, но безрезультатно.
Сложность была в том, что оба действия (якорь и появление мобильного меню) были привязаны к обработке события scroll. Я пытался их разделить, но переход на якорные ссылки все равно влиял на scroll. Читал, что можно сделать подобные якоря без перегрузки страницы с помощью hashchange, но в эту тему не сильно вникал (хотя в примере ниже эта фишка присутствует).
В итоге мне помогла эта запись на Stackoverflow. Ниже представлена версия с jQuery для более гладкого перехода. Визуально решение позволяет после перехода на якорь “отмотать” назад содержимое экрана чтобы меню его не перекрывало.
Вот работающий финальный код + возможность посмотреть результат в реальном времени:
Продублирую на всякий случай его в статье. По HTML ничего особенного – сначала идет меню, а затем 5 разных блоков DIV.
<div My fixed bar </div> <div <div <a href="#target-2">Jump to target 2</a> </div> <div <a href="#target-3">Jump to target 3</a> </div> <div <a href="#target-4">Jump to target 4</a> </div> <div <a href="#target-5">Jump to target 5</a> </div> <div <a href="#target-1">Jump to target 1</a> </div> </div>
Стили тоже вполне стандартные – каждый DIV занимает всю высоту видимой области экрана + прописываются детали фиксированного меню.
Ну, и в завершение – JavaScript. Тут, признаться я мало что могу объяснить, т.к. слабо понимаю, что конкретно происходит. Если бы не комментарии разработчика, то все это походило бы на какую-то магию.
(function(document, history, location) < var HISTORY_SUPPORT = !!(history && history.pushState); var anchorScrolls = < ANCHOR_REGEX: /^#[^ ]+$/, OFFSET_HEIGHT_PX: 50, /** * Establish events, and fix initial scroll position if a hash is provided. */ init: function() < this.scrollToCurrent(); $(window).on('hashchange', $.proxy(this, 'scrollToCurrent')); $('body').on('click', 'a', $.proxy(this, 'delegateAnchors')); >, /** * Return the offset amount to deduct from the normal scroll position. * Modify as appropriate to allow for dynamic calculations */ getFixedOffset: function() < return this.OFFSET_HEIGHT_PX; >, /** * If the provided href is an anchor which resolves to an element on the * page, scroll to it. * @param href * @return - Was the href an anchor. */ scrollIfAnchor: function(href, pushToHistory) < var match, anchorOffset; if(!this.ANCHOR_REGEX.test(href)) < return false; >match = document.getElementById(href.slice(1)); if(match) < anchorOffset = $(match).offset().top - this.getFixedOffset(); $('html, body').animate(< scrollTop: anchorOffset>); // Add the state to history as-per normal anchor links if(HISTORY_SUPPORT && pushToHistory) < history.pushState(, document.title, location.pathname + href); > > return !!match; >, /** * Attempt to scroll to the current location's hash. */ scrollToCurrent: function(e) < if(this.scrollIfAnchor(window.location.hash) && e) < e.preventDefault(); >>, /** * If the click event's target was an anchor, fix the scroll position. */ delegateAnchors: function(e) < var elem = e.target; if(this.scrollIfAnchor(elem.getAttribute('href'), true)) < e.preventDefault(); >> >; $(document).ready($.proxy(anchorScrolls, 'init')); >)(window.document, window.history, window.location);
У меня лично код сработал без каких-либо дополнительных правок и манипуляций – я просто добавил его в файл скриптов своего шаблона script.js. Важно заменить в коде переменную OFFSET_HEIGHT_PX, в которой задается высота вашего меню (т.е. итоговый отступ сверху экрана).
Плагин Page scroll to id
Кстати, если вы работаете с WordPress, то вас может заинтересовать этот модуль. Нашел его чуть раньше, и он частично перекликается с текущей задачей. Плагин внедряет более плавную и красивую анимацию перехода по ссылкам якорям и при этом обладает некоторыми приятными фишками:
- вертикальным и горизонтальным скроллингом;
- разными настройками анимации;
- поддержкой переходов по ссылкам на другие страницы;
- подсветкой ссылок, заданием отступов, длительности перехода и т.п.;
Вот как выглядит пример меню:
Думаю, Page scroll to id будет полезен, если вам надо сделать навигацию на одностраничном лендинге, а базовый шаблон не обладает подобной опцией (либо она ограничена в настройках).
Если знаете еще какие-то интересные сниппеты или есть, что добавить по теме, пишем в комментариях.