javascript - 在滚动时固定/修复 Vanilla Javascript 中的多个元素
问题描述
我是 javascript 的初学者,我正在尝试使用 es6 规范构建一些东西。
我想在向下滚动页面时从 ScrollMagic 重新创建 pin 效果并固定不同的部分。
所以我有这个简单的 html 标记,带有页眉、页脚和 3 个部分:
<header class="forewords">
<h1>Some text</h1>
</header>
<div class="wrapper">
<section class="project" id="item1">this is section 1</section>
<section class="project" id="item2">this is section 2</section>
<section class="project" id="item3">this is section 3</section>
</div>
<footer class="endings">
<h1>some text</h1>
</footer>
我附加了一些样式来模拟现实情况。
这里是javascript逻辑:
获取所有项目:
const projects = Array.from(document.querySelectorAll('.project'));
获取从顶部偏移的所有项目和所有项目高度:
let projectsOffsetTop = projects.map(project => project.offsetTop);
let projectsHeight = projects.map(project => project.offsetHeight);
如果有人调整窗口大小,则创建一个函数来更新值:
function updateProjectsOffsetTop() {
projectsOffsetTop = projects.map(project => project.offsetTop);
projectsHeight = projects.map(project => project.offsetHeight);
};
window.addEventListener('resize', updateProjectsOffsetTop);
如果滚动大于其偏移量,则最终固定元素。
function pinElement() {
if (window.scrollY >= projectsOffsetTop[1]) {
document.body.style.paddingTop = projectsHeight[1] +'px';
projects[1].classList.add('fixed');
} else {
document.body.style.paddingTop = 0;
projects[1].classList.remove('fixed');
}
};
window.addEventListener('scroll', pinElement);
但我不能让它与所有项目元素一起工作。即使使用 for 循环。最佳做法是什么?如果可能的话,我想在 Vanilla ES6 中解决这个问题。
找到附件完整的 js fiddle。
谢谢
const projects = Array.from(document.querySelectorAll('.project'));
let projectsOffsetTop = projects.map(project => project.offsetTop);
let projectsHeight = projects.map(project => project.offsetHeight);
function updateProjectsOffsetTop() {
projectsOffsetTop = projects.map(project => project.offsetTop);
projectsHeight = projects.map(project => project.offsetHeight);
};
function pinElement() {
if (window.scrollY >= projectsOffsetTop[1]) {
document.body.style.paddingTop = projectsHeight[1] +'px';
projects[1].classList.add('fixed');
} else {
document.body.style.paddingTop = 0;
projects[1].classList.remove('fixed');
}
};
window.addEventListener('resize', updateProjectsOffsetTop);
window.addEventListener('scroll', pinElement);
html {
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
margin: 0;
padding: 0;
}
header, footer {
width: 100%;
padding: 10%;
background-color: grey;
position: relative;
}
.project {
width: 100%;
height: 100vh;
position: relative;
display: flex;
justify-content: center;
align-items: center;
top: 0;
}
#item1 {background-color: yellow;}
#item2 {background-color: blue;}
#item3 {background-color: red;}
.fixed {
position: fixed;
}
<header class="forewords"><h1>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Harum soluta ipsam quaerat cupiditate neque, necessitatibus amet nihil perferendis sunt minus! Exercitationem nulla inventore, aut beatae magnam, totam et minus hic.</h1>
</header>
<div class="wrapper">
<section class="project" id="item1">this is section 1</section>
<section class="project" id="item2">this is section 2</section>
<section class="project" id="item3">this is section 3</section>
</div>
<footer class="endings"><h1>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repudiandae vel, perferendis ullam totam recusandae sed repellendus cum! Molestiae, aut ut sequi eos quidem nam quo est, ad tempora inventore odit.</h1>
</footer>
解决方案
你提供了一个令人惊叹的 MCVE 来工作,所以非常感谢你花这么多精力和时间来提出一个很好的问题。好消息是你快到了!你的逻辑是合理的,一切都是有道理的。您真正缺少的是:
- 重置样式的逻辑的正确位置(主体顶部填充和删除
fixed
类) .project
获取最接近但大于滚动高度的元素的索引
您想要在您的pinElement()
方法中执行以下操作:
- 首先重置/取消修复所有内容
- 获取
projectsOffsetTop
大于scrollY但最接近它的值(这样它将成为我们要固定的元素) - 从中获取
.project
该值所属元素的索引 - 如果索引是
-1
(即我们没有符合第 2 点标准的元素),则return
停止执行。 - 否则,我们将执行您在原始方法中的逻辑,但替换
1
为我们在步骤 3 中确定的索引。
考虑到这一点,这是您稍微重构的pinElement()
方法:
function pinElement() {
// Reset all styles
projects.forEach((project) => {
document.body.style.paddingTop = 0;
project.classList.remove('fixed');
});
// Get the index of the project that is closest to top
const valueClosestToScrollY = Math.max.apply(Math, projectsOffsetTop.filter((offsetTop) => offsetTop <= window.scrollY));
const idx = projectsOffsetTop.indexOf(valueClosestToScrollY);
// If index is not found, we don't do anything
if (idx === -1)
return;
// Otherwise, we set the appropriate styles and classes
if (window.scrollY >= projectsOffsetTop[idx]) {
document.body.style.paddingTop = `${projectsHeight[idx]}px`;
projects[idx].classList.add('fixed');
}
};
有趣的提示:您可以使用模板文字来执行此操作:
document.body.style.paddingTop = `${projectsHeight[idx]}px`;
…而不是这个:
document.body.style.paddingTop = ${projectsHeight[idx] + 'px';
这是一个概念验证示例:
const projects = Array.from(document.querySelectorAll('.project'));
let projectsOffsetTop = projects.map(project => project.offsetTop);
let projectsHeight = projects.map(project => project.offsetHeight);
function updateProjectsOffsetTop() {
projectsOffsetTop = projects.map(project => project.offsetTop);
projectsHeight = projects.map(project => project.offsetHeight);
};
function pinElement() {
// Reset all styles
projects.forEach((project) => {
document.body.style.paddingTop = 0;
project.classList.remove('fixed');
});
// Get the index of the project that is closest to top
const valueClosestToScrollY = Math.max.apply(Math, projectsOffsetTop.filter((offsetTop) => offsetTop <= window.scrollY));
const idx = projectsOffsetTop.indexOf(valueClosestToScrollY);
// If index is not found, we don't do anything
if (idx === -1)
return;
// Otherwise, we set the appropriate styles and classes
if (window.scrollY >= projectsOffsetTop[idx]) {
document.body.style.paddingTop = `${projectsHeight[idx]}px`;
projects[idx].classList.add('fixed');
}
};
window.addEventListener('resize', updateProjectsOffsetTop);
window.addEventListener('scroll', pinElement);
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
margin: 0;
padding: 0;
}
header,
footer {
width: 100%;
padding: 10%;
background-color: grey;
position: relative;
}
.project {
width: 100%;
height: 100vh;
position: relative;
display: flex;
justify-content: center;
align-items: center;
top: 0;
}
#item1 {
background-color: yellow;
}
#item2 {
background-color: blue;
}
#item3 {
background-color: red;
}
.fixed {
position: fixed;
}
<header class="forewords">
<h1>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Harum soluta ipsam quaerat cupiditate neque, necessitatibus amet nihil perferendis sunt minus! Exercitationem nulla inventore, aut beatae magnam, totam et minus hic.</h1>
</header>
<div class="wrapper">
<section class="project" id="item1">this is section 1</section>
<section class="project" id="item2">this is section 2</section>
<section class="project" id="item3">this is section 3</section>
</div>
<footer class="endings">
<h1>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repudiandae vel, perferendis ullam totam recusandae sed repellendus cum! Molestiae, aut ut sequi eos quidem nam quo est, ad tempora inventore odit.</h1>
</footer>
附带说明一下,出于性能原因,您可能需要考虑限制/消除滚动事件,以免pinElement()
过度调用。
推荐阅读
- python - 第 1 行第 1 列的意外字符
- sql - 查询以确定是否存在跨行具有特定列值的 ID
- react-native - 任何适用于世博会的同步存储?
- javascript - 如何使用 reactjs、formik 和 axios 创建方法编辑数据
- java - JAVA中的番石榴速率限制器
- swift - SpriteKit - 未找到字体
- python - 是否有一种可编程的方法来计算幂和的指数值
- optimization - 为什么打开 gfortran 编译器优化后 matmul 会变慢?
- svm - 如何判断一个多类数据集是否线性可分?
- kotlin - IntelliJ 错误 - 无法为初始化脚本打开初始化通用类缓存