IntersectionObserver не работает на маленьких экранах для длинных секций
Не так давно мне помогли написать такой код. Он выдает класс active для активной секции. Недавно заметил, что он перестает работать на маленьких экранах. Вот даже в консоли разработчика в хроме я начну увеличивать размер экрана и оно появится, только назад сразу перестает работать (класc active исчезает). Но только для одной длинной секции, в более коротких все работает. Как это можно исправить?
В сниппете я поставил большую фиксированную высоту, поэтому ссылка portfolio не получает класс active, на моем же примере при увеличении ширины секции ее высота уменьшается, поэтому в определенный момент все начинает работать.
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>
Спасибо!
Ответы (1 шт):
На англоязычном SO, пользователь с ником @chiliNUT помог в общем решить проблему, но остался один баг. При перезагрузке либо первичной загрузке страницы, код не всегда срабатывает, нужно поскролить, чтобы он начал работать. Есть идеи как это можно решить?
Текст объяснения можете почитать по ссылке, но в кратце все из за threshold. 0.5 это 50% видимой области, если секция очень длинная и 50% никогда не попадают во view порт, IntersectionObserver не сработает. @chiliNUT предложил для длинной секции изменить threshold, как он это сделал смотрите в коде.
Код из ответа скопирую сюда:
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)
});
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>