Как показать дочерний элемент при условии, что большая часть его родительского элемента видна?
Имеется следующая конструкция:
* {
margin: 0;
padding: 0;
}
body {
overflow: hidden;
}
.container {
background-color: #f1f1f1;
border: 2px solid black;
width: 100%;
height: calc(100vh - 60px);
overflow-y: hidden;
overflow-x: auto;
white-space: nowrap;
margin-top: 50px;
}
.item {
display: inline-block;
background-color: gray;
width: 100%;
height: 100%;
vertical-align: top;
margin: 0 5vw;
}
.hidden {
display: none;
position: fixed;
top: 10px;
left: 10px;
font-size: 25px;
}
<div class="container">
<div class="item"><div class="hidden">Один</div></div>
<div class="item"><div class="hidden">Два</div></div>
<div class="item"><div class="hidden">Три</div></div>
<div class="item"><div class="hidden">Четыре</div></div>
</div>
Как показать дочерний элемент .hidden при условии, что большая часть его родительского элемента .item видна? Именно по ширине, высота не имеет значения. В противном случае прятать. Благодарю!
Ответы (2 шт):
Автор решения: OPTIMUS PRIME
→ Ссылка
- Собрать массив = координаты между левыми отступами каждой пары дочерних элеметов;
- При скролле сравнивать
scrollLeftс полученными координатами; - Как только scrollLeft оказался больше одной из координат: Значит элемент с его индексом нужно показывать.
onscroll_toggleItems();
function onscroll_toggleItems() {
let visible_index = null;
let points = getSwitchPoints();
/* Ключевые точки получаются только один раз.
Предполагается, что ничего не будет динамически менятся.
Иначе: можно вызывать функцию при каждом скролле */
let cont = document.querySelector(".container");
cont.addEventListener("scroll", function() {
let new_index;
for( let i = points.length - 1; i >= 0; i-- ) {
if( this.scrollLeft >= points[i] ) {
new_index = i;
break;
}
}
if( new_index !== visible_index ) {
/* Не вызывает функцию каждый раз, а только если правда нужно что-то менять. */
showOnly(new_index);
visible_index = new_index;
}
});
/***/
function showOnly(index) {
let items = document.querySelectorAll(".container .item");
items.forEach((item, i) => {
item.children[0].classList[ i === index ? "remove" : "add" ]('hidden');
});
}
function getSwitchPoints() {
/***
Берет левый отступ {число} каждого дочернего элемента;
Для каждой пары элементов считает среднее между этими числами;
Возвращает массив [0, среднее(0, 1), ..., среднее(N-1, N)];
Первый - 0, потому что он изначально виден целиком.
***/
let parent = document.querySelector(".container");
let children = document.querySelectorAll(".container .item");
let parent_left = parent.getBoundingClientRect().left;
let result = [0];
for( let i = 1; i < children.length; i++ ) {
let prev = children[i-1].getBoundingClientRect().left - parent_left;
let curr = children[ i ].getBoundingClientRect().left - parent_left;
result.push( (prev + curr) / 2 );
}
return result;
}
}
* {
margin: 0;
padding: 0;
}
body {
overflow: hidden;
}
.container {
background-color: #f1f1f1;
border: 2px solid black;
width: 100%;
height: calc(100vh - 60px);
overflow-y: hidden;
overflow-x: auto;
white-space: nowrap;
margin-top: 50px;
}
.item {
display: inline-block;
background-color: gray;
width: 100%;
height: 100%;
vertical-align: top;
margin: 0 5vw;
}
.hidden {
display: none;
position: fixed;
top: 10px;
left: 10px;
font-size: 25px;
}
.orange {
background-color: orange;
height: 100%;
}
<div class="container">
<div class="item"><div class="orange hidden">Один</div></div>
<div class="item"><div class="orange hidden">Два</div></div>
<div class="item"><div class="orange hidden">Три</div></div>
<div class="item"><div class="orange hidden">Четыре</div></div>
</div>
Автор решения: ᅠhᅠ
→ Ссылка
Ещё варианты
const elements = document.querySelectorAll('.item')
const toggleClass = entry => {
if (entry.isIntersecting)
entry.target.firstElementChild.style.display = 'block'
else
entry.target.firstElementChild.style.display = 'none'
}
const callback = entries => entries.forEach(toggleClass)
const options = { threshold: 0.5 }
const observer = new IntersectionObserver(callback, options)
elements.forEach(element => observer.observe(element))
* {
margin: 0;
padding: 0;
}
body {
overflow: hidden;
}
.container {
background-color: #f1f1f1;
border: 2px solid black;
width: 100%;
height: calc(100vh - 60px);
overflow-y: hidden;
overflow-x: auto;
white-space: nowrap;
margin-top: 50px;
}
.item {
display: inline-block;
background-color: gray;
width: 100%;
height: 100%;
vertical-align: top;
margin: 0 5vw;
}
.hidden {
display: none;
position: fixed;
top: 10px;
left: 10px;
font-size: 25px;
}
<div class="container">
<div class="item">
<div class="hidden">Один</div>
</div>
<div class="item">
<div class="hidden">Два</div>
</div>
<div class="item">
<div class="hidden">Три</div>
</div>
<div class="item">
<div class="hidden">Четыре</div>
</div>
</div>
const container = document.querySelector('.container')
const items = document.querySelectorAll('.item')
let item_size, gap
function init() {
item_size = items[0].getBoundingClientRect().width
gap = innerWidth * 0.1
}
function onScroll() {
const n = Math.floor(container.scrollLeft / (item_size + gap) + 0.5)
items.forEach((item, i) => {
item.firstElementChild.style.display = 'none'
if (i === n) item.firstElementChild.style.display = 'block'
})
}
container.addEventListener('scroll', onScroll)
window.addEventListener('resize', init)
init()
* {
margin: 0;
padding: 0;
}
body {
overflow: hidden;
}
.container {
background-color: #f1f1f1;
border: 2px solid black;
width: 100%;
height: calc(100vh - 60px);
overflow-y: hidden;
overflow-x: auto;
white-space: nowrap;
margin-top: 50px;
}
.item {
display: inline-block;
background-color: gray;
width: 100%;
height: 100%;
vertical-align: top;
margin: 0 5vw;
}
.hidden {
display: none;
position: fixed;
top: 10px;
left: 10px;
font-size: 25px;
}
<div class="container">
<div class="item">
<div class="hidden">Один</div>
</div>
<div class="item">
<div class="hidden">Два</div>
</div>
<div class="item">
<div class="hidden">Три</div>
</div>
<div class="item">
<div class="hidden">Четыре</div>
</div>
</div>