IntersectionObserver не работает на маленьких экранах для длинных разделов JS

Этот скрипт выдает активный класс для активного раздела. Недавно заметил, что перестает работать на маленьких экранах. Еще в консоли разработчика в хроме начну увеличивать размер экрана и он появится, как только начну уменьшать сразу перестанет работать (пропадает активный класс). Но только на один длинный участок, на более коротких все работает. Как это можно исправить?

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

const links = document.querySelectorAll('.nav-link');
const sections = [... document.querySelectorAll('.forJS')];

const callback = (entries) => {
  links.forEach((link) => link.classList.remove('active'));
  const elem = entries.find((entry) => entry.isIntersecting);
  if (elem) {
    const index = sections.findIndex((section) => section === elem.target);
    links[index].classList.add('active');
  }
}

let observer = new IntersectionObserver(callback, {
  rootMargin: '0px',
  threshold: 0.5
});

sections.forEach((section) => observer.observe(section));
section {
  height: 100vh;
  scroll-y: auto;
}
.long{
height: 300vh;
}
.nav-link.active{
  color: red;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"/>
<body>
<header class="fixed-top">
  <nav class="navbar navbar-expand-lg navCustom">
    <div class="container">

          <ul class="navbar-nav justify-content-center">
            <li class="nav-item">
              <a class="nav-link" href="#main">Main</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#about">About us</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#portfolio">Portfolio</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#contacts">Contacts</a>
            </li>
          </ul>
    </div>
  </nav>
</header>

<section class="forJS text-center">Some info 1</section>
<section class="forJS text-center">Some info 2</section>
<section class="forJS text-center long">Some info 3</section>
<section class="text-center">Some info 4</section>
<section class="text-center">Some info 5</section>
<section class="text-center">Some info 6</section>
<section class="text-center">Some info 7</section>
<section class="text-center">Some info 8</section>
<section class="text-center">Some info 9</section>
<section class="forJS text-center">Some info 10</section>
</body>


person vcxbgfx    schedule 20.02.2021    source источник


Ответы (1)


Основная проблема заключается в следующем: threshold: 0.5. Это говорит наблюдателю срабатывать, как только 50% элемента становится видимым в окне просмотра. Для вашего длинного элемента, поскольку его высота составляет 300 дюймов, а ваше окно просмотра имеет высоту 100 дюймов, максимальная видимость, которую он имеет, составляет 100 дюймов / 300 дюймов = 33%, поэтому наблюдатель никогда не срабатывает.

Чтобы справиться с этим, вы можете настроить порог на что-то меньшее, например 0,25. Это исправит поведение для длинного раздела, но приведет к преждевременному изменению активной ссылки для более коротких разделов. Поэтому я предлагаю вам добавить 2 наблюдателя: 1 для коротких разделов с порогом 0,5 (.forJS:not(.long)), а другой для более длинных разделов с порогом 0,25 (.forJS.long).

const links = document.querySelectorAll('.nav-link');
const sectionsShort = [...document.querySelectorAll('.forJS:not(.long)')];
const sectionsLong = [...document.querySelectorAll('.forJS.long')];
const sections = [...document.querySelectorAll('.forJS')];

const callback = entries => {
    links.forEach((link) => link.classList.remove('active'));
    const elem = entries.find((entry) => entry.isIntersecting);
    if (elem) {
        const index = sections.findIndex((section) => section === elem.target);
        links[index].classList.add('active');
    }
}

const observerShort = new IntersectionObserver(callback, {
    rootMargin: '0px',
    threshold: .5,
});
const observerLong = new IntersectionObserver(callback, {
    rootMargin: '0px',
    threshold: .25,
});
sectionsShort.forEach((section) => {
    observerShort.observe(section)
});
sectionsLong.forEach((section) => {
    observerLong.observe(section)
});
person chiliNUT    schedule 20.02.2021
comment
Спасибо за ответ! Есть более простой скрипт без использования InsertsectionObserver, который делает все то же самое. Но суть в том, что когда пользователь находится, например, не в разделе портфолио, то ссылка на портфолио не должна быть активной, верно? В вашем случае всегда активная ссылка предыдущего активного раздела. - person vcxbgfx; 21.02.2021
comment
Этот скрипт делает именно то, что делает ваш пример: stackoverflow.com/questions/66166602/ - person vcxbgfx; 21.02.2021
comment
@vcxbgfx Я не был уверен, было ли это предполагаемым поведением или нет. Смотрите обновление к ответу, активные ссылки остаются прежними - person chiliNUT; 21.02.2021
comment
@vcxbgfx делает это по-другому. Использование IntersectionObserver - лучший способ сделать это, чем другой ответ, который вы связали, потому что он был специально разработан для этого варианта использования, тогда как событие прокрутки должно прослушивать любую прокрутку на странице и включает ручную проверку смещений прокрутки и смещений элементов - person chiliNUT; 21.02.2021
comment
Все работает хорошо, но в этих двух фрагментах кода есть небольшой конфликт. После перезагрузки страницы или ее обновления активный класс пропадает, даже если мы находимся в активной зоне. Если удалить один из них, все работает нормально. Может, нужна какая-то проверка перед их выполнением? sectionsShort.forEach((section) => {observerShort.observe(section)}); sectionsLong.forEach((section) => {observerLong.observe(section)}); - person vcxbgfx; 21.02.2021
comment
Да, я не знаю, я думаю, что это связано с этим другим вопросом, на который даже нет ответа: stackoverflow.com/questions/57946043/ - person chiliNUT; 21.02.2021