Вместо прослушивания события прокрутки вы должны взглянуть на Intersection Observer (IO). Это было разработано для решения таких проблем, как ваша. И это намного эффективнее, чем прослушивание событий прокрутки и последующий расчет позиции.
Конечно, вы можете продолжать использовать только события прокрутки, официальный Polyfill от W3C использует события прокрутки для эмуляции ввода-вывода для старых браузеров. Прослушивание события прокрутки и расчета позиции не является производительным, особенно если есть несколько элементов. Так что, если вы заботитесь о пользовательском опыте, я действительно рекомендую использовать IO. Просто хотел добавить этот ответ, чтобы показать, каким будет современное решение для такой проблемы.
Я потратил время на создание примера на основе ввода-вывода, это должно помочь вам начать.
В основном я определил два порога: один для 20 и один для 90%. Если элемент в окне просмотра составляет 90%, то он сохраняет, предполагая, что он будет покрывать заголовок. Поэтому я установил класс для заголовка для элемента, который находится на 90%.
Второй порог для 20%, здесь мы должны проверить, виден ли элемент сверху или снизу. Если он виден на 20% сверху, он будет перекрываться с заголовком.
Настройте эти значения и измените логику, как вы видите.
Редактировать: отредактировал его в соответствии с вашим комментарием, обратите внимание, что вы можете увидеть эффект лучше, если вы удалите console.log из моего кода, чтобы они не загромождали ваш вид.
Я добавил один div, где заголовок не меняется (зеленый)
const sections = document.querySelectorAll('.menu');
const config = {
rootMargin: '0px',
threshold: [.2, .9]
};
const observer = new IntersectionObserver(function (entries, self) {
entries.forEach(entry => {
console.log(entry); // log the IO entry for demo purposes
console.log(entry.target); // here you have the Element itself.
// you can test for className here for example
if (entry.isIntersecting) {
var headerEl = document.querySelector('header');
if (entry.intersectionRatio > 0.9) {
//intersection ratio bigger than 90%
//-> set header according to target
headerEl.className=entry.target.dataset.header;
} else {
//-> check if element is coming from top or from bottom into view
if (entry.target.getBoundingClientRect().top < 0 ) {
headerEl.className=entry.target.dataset.header;
}
}
}
});
}, config);
sections.forEach(section => {
observer.observe(section);
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.g-100vh {
height: 100vh
}
header {
min-height: 50px;
position: fixed;
background-color: green;
width: 100%;
}
header.white-menu {
color: white;
background-color: black;
}
header.black-menu {
color: black;
background-color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<header>
<p>Header Content </p>
</header>
<div class="grid-30-span g-100vh menu" style="background-color:darkblue;" data-header="white-menu">
<img
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1.414 1'%3E%3C/svg%3E"
data-src="/images/example_darkblue.jpg"
class="lazyload"
alt="<?php echo $title; ?>">
</div>
<div class="grid-30-span g-100vh no-menu" style="background-color:green;" data-header="black-menu">
<h1> Here no change happens</h1>
<p>it stays at whatever state it was before, depending on if you scrolled up or down </p>
</div>
<div class="grid-30-span g-100vh menu" style="background-color:lightgrey;" data-header="black-menu">
<img
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1.414 1'%3E%3C/svg%3E"
data-src="/images/example_lightgrey.jpg"
class="lazyload"
alt="<?php echo $title; ?>">
</div>