CSS Scroll Snap 获取活动项目
CSS Scroll Snap get active Item
我对 CSS scroll snap 有疑问。我想通过 JavaScript 检测捕捉到的元素并分配它,例如 CSS class 或类似的。
遗憾的是,我还没有找到一种方法来检测捕捉到的元素。背景:我有一个包含子项的列表,它是滚动的,列表中的中间项总是应该突出显示:
布局
我已经用 rootMargin 测试了 intersection observer 来检测垂直居中的元素,但它的错误多于有用。
HTML
<div class="timeline-menu-dropdown-years-list-container">
<ul class="timeline-menu-dropdown-years-list timeline-menu-dropdown-years-text" id="yearcontainer">
<li id="2010" class="timeline-dropdown-year" data-target="year-2010">2010</li>
<li id="2009" class="timeline-dropdown-year" data-target="year-2009">2009</li>
<li id="2008" class="timeline-dropdown-year" data-target="year-2008">2008</li>
<li id="2007" class="timeline-dropdown-year" data-target="year-2007">2007</li>
<li id="2006" class="timeline-dropdown-year" data-target="year-2006">2006</li>
<li id="2005" class="timeline-dropdown-year" data-target="year-2005">2005</li>
<li id="2004" class="timeline-dropdown-year" data-target="year-2004">2004</li>
<li id="2003" class="timeline-dropdown-year" data-target="year-2003">2003</li>
<li id="2002" class="timeline-dropdown-year" data-target="year-2002">2002</li>
<li id="2001" class="timeline-dropdown-year" data-target="year-2001">2001</li>
<li id="2000" class="timeline-dropdown-year" data-target="year-2000">2000</li>
</ul>
</div>
CSS
.timeline-menu-dropdown-years-list-container {
max-height: 250px;
overflow: scroll;
scroll-snap-type: y mandatory;
-ms-overflow-style: none; /* Internet Explorer and Edge */
scrollbar-width: none; /* Firefox */
padding-top: 45%;
padding-bottom: 40%;
scroll-padding-top: 45%;
scroll-padding-bottom: 40%;
}
.timeline-dropdown-year {
color: white;
font-size: 14px;
border-bottom: 2px solid white;
margin-right: 11%;
margin-left: 34%;
scroll-snap-align: center;
}
我该如何解决?
最后,您应该可以滚动浏览此时间线。活动元素应始终对齐中心并在视觉上突出显示。
我也遇到了同样的问题。我在这里用 JavaScript 解决了它:Implementation of CSS scroll snap event stop and element position detection
[].slice.call(container.children).forEach(function (ele, index) {
if (Math.abs(ele.getBoundingClientRect().left - container.getBoundingClientRect().left) < 10) {
// The 'ele' element at this moment is the element currently
// positioned. Add class .active for example!
} else {
// The 'ele' element at the moment is not
// the currently positioned element
}
});
将其放入事件滚动条中:
// Timer, used to detect whether horizontal scrolling is over
var timer = null;
// Scrolling event start
container.addEventListener('scroll', function () {
clearTimeout(timer);
// Renew timer
timer = setTimeout(function () {
// No scrolling event triggered. It is considered that
// scrolling has stopped do what you want to do, such
// as callback processing
}, 100);
});
对当前答案的改进是预先存储每个元素的边界,以防止每次触发滚动事件时回流。
let container = document.querySelector('.timeline-menu-dropdown-years-list-container')
let containerBounds = null
let currentItem = 0
// store items as an array of objects
const items = Array.from(document.querySelectorAll('.timeline-dropdown-year')).map(el => ({el}))
const storeBounds = () =>{
// store the bounds of the container
containerBounds = container.getBoundingClientRect() // triggers reflow
// store the bounds of each item
items.forEach((item, i)=>{
item.bounds = item.el.getBoundingClientRect() // triggers reflow
item.offsetY = item.bounds.top - containerBounds.top // store item offset distance from container
})
}
storeBounds() // store bounds on load
const detectCurrent = () => {
const scrollY = container.scrollTop // container scroll position
const goal = container.bounds.height / 2 // where we want the current item to be, 0 = top of the container
// find item closest to the goal
currentItem = items.reduce((prev, curr) => {
return (Math.abs(curr.offsetY - scrollY - goal) < Math.abs(prev.offsetY - scrollY - goal) ? curr : prev); // return the closest to the goal
});
// do stuff with currentItem
// here
}
detectCurrent() // detect current item on load
window.addEventListener('scroll', () => detectCurrent()) // detect current item on scroll
window.addEventListener('resize', () => storeBounds()) // update bounds on resize in case they have changed
进一步的改进是在滚动事件上有一个 debounce
函数,以减少计算的频率。我们真的不需要在每次触发滚动事件时都进行检查,并且可以在每次检查之间设置大约 200 毫秒的间隔。
根据布局是否更改,您可能还需要在调整页面大小时更新边界。
不熟悉浏览器重排工作原理的资源:
我对 CSS scroll snap 有疑问。我想通过 JavaScript 检测捕捉到的元素并分配它,例如 CSS class 或类似的。
遗憾的是,我还没有找到一种方法来检测捕捉到的元素。背景:我有一个包含子项的列表,它是滚动的,列表中的中间项总是应该突出显示:
布局
我已经用 rootMargin 测试了 intersection observer 来检测垂直居中的元素,但它的错误多于有用。
HTML
<div class="timeline-menu-dropdown-years-list-container">
<ul class="timeline-menu-dropdown-years-list timeline-menu-dropdown-years-text" id="yearcontainer">
<li id="2010" class="timeline-dropdown-year" data-target="year-2010">2010</li>
<li id="2009" class="timeline-dropdown-year" data-target="year-2009">2009</li>
<li id="2008" class="timeline-dropdown-year" data-target="year-2008">2008</li>
<li id="2007" class="timeline-dropdown-year" data-target="year-2007">2007</li>
<li id="2006" class="timeline-dropdown-year" data-target="year-2006">2006</li>
<li id="2005" class="timeline-dropdown-year" data-target="year-2005">2005</li>
<li id="2004" class="timeline-dropdown-year" data-target="year-2004">2004</li>
<li id="2003" class="timeline-dropdown-year" data-target="year-2003">2003</li>
<li id="2002" class="timeline-dropdown-year" data-target="year-2002">2002</li>
<li id="2001" class="timeline-dropdown-year" data-target="year-2001">2001</li>
<li id="2000" class="timeline-dropdown-year" data-target="year-2000">2000</li>
</ul>
</div>
CSS
.timeline-menu-dropdown-years-list-container {
max-height: 250px;
overflow: scroll;
scroll-snap-type: y mandatory;
-ms-overflow-style: none; /* Internet Explorer and Edge */
scrollbar-width: none; /* Firefox */
padding-top: 45%;
padding-bottom: 40%;
scroll-padding-top: 45%;
scroll-padding-bottom: 40%;
}
.timeline-dropdown-year {
color: white;
font-size: 14px;
border-bottom: 2px solid white;
margin-right: 11%;
margin-left: 34%;
scroll-snap-align: center;
}
我该如何解决?
最后,您应该可以滚动浏览此时间线。活动元素应始终对齐中心并在视觉上突出显示。
我也遇到了同样的问题。我在这里用 JavaScript 解决了它:Implementation of CSS scroll snap event stop and element position detection
[].slice.call(container.children).forEach(function (ele, index) {
if (Math.abs(ele.getBoundingClientRect().left - container.getBoundingClientRect().left) < 10) {
// The 'ele' element at this moment is the element currently
// positioned. Add class .active for example!
} else {
// The 'ele' element at the moment is not
// the currently positioned element
}
});
将其放入事件滚动条中:
// Timer, used to detect whether horizontal scrolling is over
var timer = null;
// Scrolling event start
container.addEventListener('scroll', function () {
clearTimeout(timer);
// Renew timer
timer = setTimeout(function () {
// No scrolling event triggered. It is considered that
// scrolling has stopped do what you want to do, such
// as callback processing
}, 100);
});
对当前答案的改进是预先存储每个元素的边界,以防止每次触发滚动事件时回流。
let container = document.querySelector('.timeline-menu-dropdown-years-list-container')
let containerBounds = null
let currentItem = 0
// store items as an array of objects
const items = Array.from(document.querySelectorAll('.timeline-dropdown-year')).map(el => ({el}))
const storeBounds = () =>{
// store the bounds of the container
containerBounds = container.getBoundingClientRect() // triggers reflow
// store the bounds of each item
items.forEach((item, i)=>{
item.bounds = item.el.getBoundingClientRect() // triggers reflow
item.offsetY = item.bounds.top - containerBounds.top // store item offset distance from container
})
}
storeBounds() // store bounds on load
const detectCurrent = () => {
const scrollY = container.scrollTop // container scroll position
const goal = container.bounds.height / 2 // where we want the current item to be, 0 = top of the container
// find item closest to the goal
currentItem = items.reduce((prev, curr) => {
return (Math.abs(curr.offsetY - scrollY - goal) < Math.abs(prev.offsetY - scrollY - goal) ? curr : prev); // return the closest to the goal
});
// do stuff with currentItem
// here
}
detectCurrent() // detect current item on load
window.addEventListener('scroll', () => detectCurrent()) // detect current item on scroll
window.addEventListener('resize', () => storeBounds()) // update bounds on resize in case they have changed
进一步的改进是在滚动事件上有一个 debounce
函数,以减少计算的频率。我们真的不需要在每次触发滚动事件时都进行检查,并且可以在每次检查之间设置大约 200 毫秒的间隔。
根据布局是否更改,您可能还需要在调整页面大小时更新边界。
不熟悉浏览器重排工作原理的资源: