Статьи

CSS «позиция: липкая» — Введение и Polyfills

Если вы читали статью « Очевидный дизайн всегда побеждает » Аннариты Транфики, вы можете согласиться с ее утверждением:

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

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

Иногда из-за запроса клиента или из-за того, что мы определили, что это лучший подход, мы можем потребовать, чтобы основная навигация оставалась видимой на странице постоянно, без фиксации на месте, по существу следуя содержимому страницы. В последние годы многие решения, основанные на JavaScript, увидели свет, потому что только CSS не смог решить эту задачу.

В этой статье мы обсудим position: sticky , новое решение CSS для этой проблемы.

Какую проблему мы решаем?

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

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

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

Чтобы достичь этого с помощью традиционного кода, нам нужно добавить немного JavaScript. Мы прослушиваем событие прокрутки страницы и используем JavaScript, чтобы изменить значение position и свойства top соответствии с текущей позицией области просмотра. В частности, нам нужно добавить top: 0 и position: fixed в меню, когда оно находится в верхней части окна просмотра, а затем вернуть свойства обратно к их значениям по умолчанию.

Альтернативный, но похожий подход состоит в том, чтобы создать класс в нашем CSS, где присутствуют эти значения, а затем добавить и удалить класс по мере необходимости с помощью JavaScript, который может выглядеть следующим образом:

 var menu = document.querySelector('.menu') var menuPosition = menu.getBoundingClientRect().top; window.addEventListener('scroll', function() { if (window.pageYOffset >= menuPosition) { menu.style.position = 'fixed'; menu.style.top = '0px'; } else { menu.style.position = 'static'; menu.style.top = ''; } }); 

Обратите внимание, что этот фрагмент не относится к более старым версиям Internet Explorer. Если вам нужно разобраться с этими браузерами (бедняжка!), Я предоставлю несколько вариантов полизаполнения, которые вы можете рассмотреть.

Демонстрация этого второго шага показана ниже:

Но ждать! Можете ли вы определить проблему, вызванную этим кодом? Многие реализации, которые я видел, включая ту, которую мы разработали до сих пор, не учитывают важную проблему. Когда мы меняем положение элемента на fixed , он покидает поток страницы, поэтому элементы под ним «подпрыгивают» на количество пикселей, примерно равное высоте элемента (высота этого «прыжка» зависит на полях, границах и тд).

Возможное решение состоит в том, чтобы внедрить элемент-заполнитель того же размера, что и тот, который мы хотим «прикрепить», чтобы при обновлении стиля закрепленного элемента не было прыжка. Кроме того, мы не хотим переназначать значения снова и снова без причины, если правильные значения уже установлены. Наконец, мы хотим использовать описанную мной технику с использованием класса CSS.

Окончательная версия кода JavaScript приведена ниже:

 var menu = document.querySelector('.menu'); var menuPosition = menu.getBoundingClientRect(); var placeholder = document.createElement('div'); placeholder.style.width = menuPosition.width + 'px'; placeholder.style.height = menuPosition.height + 'px'; var isAdded = false; window.addEventListener('scroll', function() { if (window.pageYOffset >= menuPosition.top && !isAdded) { menu.classList.add('sticky'); menu.parentNode.insertBefore(placeholder, menu); isAdded = true; } else if (window.pageYOffset < menuPosition.top && isAdded) { menu.classList.remove('sticky'); menu.parentNode.removeChild(placeholder); isAdded = false; } }); 

И это блок объявления для sticky класса:

 .sticky { top: 0; position: fixed; } 

Окончательный результат показан в следующей демонстрации:

Теперь, когда вы хорошо понимаете, в чем заключается проблема и каково возможное решение на основе JavaScript, пришло время принять современность и обсудить, в чем заключается эта position: sticky — это все.

Какое position: sticky ?

Если вы были настолько смелы, чтобы внимательно следовать предыдущему разделу, вы можете спросить: «Почему браузеры не могут сделать это для меня?» Рад, что вы спросили!

stickyэто новое значение, введенное для свойства CSS position . Предполагается, что это значение будет вести себя как position: static внутри своего родителя, пока не будет достигнут заданный порог смещения, и в этом случае оно действует так, как если бы значение было fixed . Другими словами, используя position: sticky мы можем решить проблему, обсужденную в предыдущем разделе, без JavaScript.

Вспоминая наш предыдущий пример и используя это новое значение, мы можем написать:

 .menu { margin: 0; padding: 0; width: 100%; background-color: #bffff3; position: sticky; } 

И браузер сделает все остальное! Это так просто.

Поддержка браузера и Polyfills

Поддержка этого нового значения в настоящее время довольно скудна. Вот как все складывается для каждого браузера:

  • Firefox 26+ — поддерживается установкой css.sticky.enabled в значение «true» в about:config .
  • Chrome 23+ — поддерживается включением «экспериментальных функций веб-платформы» в chrome://flags .
  • Chrome 38 (?) — команда Chrome недавно удалила эту функцию из Blink , поэтому в настоящее время она недоступна в Chrome Canary (версия 38.x), даже с флагом. Вы можете прочитать отчет об ошибке, объясняющий удаление, но мы подозреваем, что эта функция будет вскоре реализована и, возможно, без сбоев в поддержке стабильной версии.
  • Safari 6.1+ — поддерживается с использованием префикса -webkit vendor для значения (т position: -webkit-sticky )
  • Opera 23+ — поддерживается включением «экспериментальных функций веб-платформы» в about:flags .
  • Internet Explorer — не поддерживается ( см. Статус )

Смотрите положение: наклейка «Могу ли я использовать …» для всех деталей.

К счастью, есть несколько вариантов заполнения:

демонстрация

Следующая демонстрация показывает position: sticky в действии. Как уже упоминалось, чтобы увидеть, как это работает, и в зависимости от браузера, который вы используете, вам нужно активировать флаг.

Вывод

Хотя новая функция не имеет большой поддержки браузера, и вы, возможно, не решитесь ее заполнить, она будет постепенно снижаться до position: static по умолчанию position: static или position: fixed , если вы определяете альтернативный стиль с помощью Modernizr.

Если вы экспериментировали с этим свойством или знаете о других полифиллах, сообщите нам об этом в комментариях.