Подтвердить что ты не робот

Позиция таргетинга: липкие элементы, которые в настоящее время находятся в "застрявшем" состоянии

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

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

Существует ли какой-либо псевдоселектор (например, ::stuck) для целевых элементов, которые имеют position: sticky и в настоящее время? Или у поставщиков браузеров есть что-то подобное в конвейере? Если нет, где бы я его запросил?

NB. javascript решения не подходят для этого, потому что на мобильном телефоне вы обычно получаете только одно событие scroll, когда пользователь отпускает свой палец, поэтому JS не может знать точный момент, когда был пропущен порог прокрутки.

4b9b3361

Ответ 1

В настоящее время нет селектора, который предлагается для элементов, которые в настоящее время "застревают". Модуль Postioned Layout, где position: sticky определен, также не упоминает о таком селекторе.

Запросы функций для CSS можно отправить в список рассылки www-style. Я считаю, что псевдокласс класса :stuck имеет больше смысла, чем псевдоэлемент ::stuck, так как вы ищете, чтобы нацеливать сами элементы, находясь в этом состоянии. На самом деле, псевдо-класс :stuck обсуждался некоторое время назад; было обнаружено, что основное осложнение - это то, что касается любого предлагаемого селектора, который пытается сопоставить на основе визуализированного или вычисленного стиля: круговые зависимости.

В случае псевдокласса :stuck простейший случай округлости будет иметь место со следующим CSS:

:stuck { position: static; /* Or anything other than sticky/fixed */ }
:not(:stuck) { position: sticky; /* Or fixed */ }

И может быть много других случаев, которые трудно было бы решить.

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

Ответ 2

На самом деле это не поклонник использования js-хаков для стилизации (например, getBoudingClientRect, прослушивания прокрутки, изменения размера), но именно так я сейчас решу проблему. Это решение будет иметь проблемы со страницами с минимализуемым/максимизируемым контентом (<details>) или вложенной прокруткой или действительно любыми шарами кривой. При этом это простое решение, когда проблема проста.

let lowestKnownOffset: number = -1;
window.addEventListener("resize", () => lowestKnownOffset = -1);

const $Title = document.getElementById("Title");
let requestedFrame: number;
window.addEventListener("scroll", (event) => {
    if (requestedFrame) { return; }
    requestedFrame = requestAnimationFrame(() => {
        // if it sticky to top, the offset will bottom out at its natural page offset
        if (lowestKnownOffset === -1) { lowestKnownOffset = $Title.offsetTop; }
        lowestKnownOffset = Math.min(lowestKnownOffset, $Title.offsetTop);
        // this condition assumes that $Title is the only sticky element and it sticks at top: 0px
        // if there are multiple elements, this can be updated to choose whichever one it furthest down on the page as the sticky one
        if (window.scrollY >= lowestKnownOffset) {
            $Title.classList.add("--stuck");
        } else {
            $Title.classList.remove("--stuck");
        }
        requestedFrame = undefined;
    });
})

Ответ 4

Кто-то в блоге разработчиков Google утверждает, что нашел эффективное решение на основе JavaScript с IntersectionObserver.

Соответствующий бит кода здесь:

/**
 * Sets up an intersection observer to notify when elements with the class
 * '.sticky_sentinel--top' become visible/invisible at the top of the container.
 * @param {!Element} container
 */
function observeHeaders(container) {
  const observer = new IntersectionObserver((records, observer) => {
    for (const record of records) {
      const targetInfo = record.boundingClientRect;
      const stickyTarget = record.target.parentElement.querySelector('.sticky');
      const rootBoundsInfo = record.rootBounds;

      // Started sticking.
      if (targetInfo.bottom < rootBoundsInfo.top) {
        fireEvent(true, stickyTarget);
      }

      // Stopped sticking.
      if (targetInfo.bottom >= rootBoundsInfo.top &&
          targetInfo.bottom < rootBoundsInfo.bottom) {
       fireEvent(false, stickyTarget);
      }
    }
  }, {threshold: [0], root: container});

  // Add the top sentinels to each section and attach an observer.
  const sentinels = addSentinels(container, 'sticky_sentinel--top');
  sentinels.forEach(el => observer.observe(el));
}

Я не повторил это сам, но, может быть, это поможет кому-то наткнуться на этот вопрос.

Ответ 5

В некоторых случаях простой IntersectionObserver может сделать свое дело, если ситуация позволяет придерживаться пикселя или двух вне его корневого контейнера, вместо того, чтобы должным образом противостоять. Таким образом, когда он находится прямо за краем, наблюдатель стреляет, и мы убегаем.

const observer = new IntersectionObserver( 
  ([e]) => e.target.toggleAttribute('stuck', e.intersectionRatio < 1),
  {threshold: [1]}
);

observer.observe(document.querySelector('nav'));

Вставьте элемент только что из его контейнера с top: -2px, а затем top: -2px цель с помощью атрибута stuck...

nav {
  background: magenta;
  height: 80px;
  position: sticky;
  top: -2px;
}
nav[stuck] {
  box-shadow: 0 0 16px black;
}

Пример здесь: https://codepen.io/anon/pen/vqyQEK