- 首页
- 前端开发
- 在滚动中固定/修复Vanilla Javascript中的多个元素
在滚动中固定/修复Vanilla Javascript中的多个元素
[英] Pin / Fix multiple elements in Vanilla Javascript on scroll
本文介绍了在滚动中固定/修复Vanilla Javascript中的多个元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我是javascript的初学者,我正在尝试使用es6规范构建一些东西。
我想从ScrollMagic和pin重新创建图钉效果
所以我有一个带有页眉和页脚和3个部分的简单html标记:
< header class = forewords>
< h1>某些文本< / h1>
< / header>
< div class = wrapper>
< section class = project id = item1>这是第1部分< / section>
< section class = project id = item2>这是第2部分< / section>
< section class = project id = item3>这是第3部分< / section>
< / div>
< footer class = endings>
< h1>某些文本< / h1>
< / footer>
我附加了一些样式来模拟现实情况。
以下是JavaScript逻辑:
获取所有项目:
常量项目= Array.from(document.querySelectorAll('。project'));
获取所有从顶部偏移的项目和所有项目的高度:
let projectsOffsetTop = projects.map(project => project.offsetTop);
let projectsHeight = projects.map(project => project.offsetHeight);
创建一个函数以在有人调整窗口大小时更新值:
函数updateProjectsOffsetTop(){
projectsOffsetTop = projects.map(project => project.offsetTop);
projectsHeight = projects.map(project => project.offsetHeight);
};
window.addEventListener('resize',updateProjectsOffsetTop);
如果滚动大于其偏移量,则最终固定该元素。
函数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小提琴。
谢谢
const projects = Array.from(document.querySelectorAll ('。项目'));让projectsOffsetTop = projects.map(project => project.offsetTop);让projectsHeight = projects.map(project => project.offsetHeight);函数updateProjectsOffsetTop(){projectsOffsetTop = projects.map(project => project.offsetTop); projectsHeight = projects.map(project => project.offsetHeight); };函数pinElement(){如果(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;边距:0;填充:0; }页眉,页脚{宽度:100%;填充:10%;背景颜色:灰色;职位:相对} .project {宽度:100%;高度:100vh;职位:相对显示:flex;证明内容:中心; align-items:居中;最高:0; }#item1 {background-color:yellow;}#item2 {background-color:blue;}#item3 {background-color:red;} .fixed {位置:fixed; }
< header class = forewords>< ; h1> Lorem ipsum dolor坐着,安全地上流精英。 Harum soluta ipsam quaerat cupiditate neque,必需品nihil perferendis sunt减!无效锻炼,比阿特人大树,totam等减去hic。 < / header> < div class = wrapper> < section class = project id = item1>这是第1部分< / section> < section class = project id = item2>这是第2部分< / section> < section class = project id = item3>这是第3部分< / section> < / div> < footer class = endings>< h1> Lorem ipsum dolor atmet,安全地帮助精英人士。 Repudiandae vel,perferendis ullam totam recusandae sed repellendus暨! molestiae,自成一体的quidem nam est,临时清单发明。 < / footer>
$ p $ b
解决方案
您提供了出色的MCVE进行工作,因此非常感谢您花费如此多的精力和时间提出一个很好的问题。好消息是,您几乎在那里。您的逻辑是正确的,一切都说得通。您真正缺少的是:
- 重置样式的逻辑的正确位置(主体顶部填充并删除
fixed
类)
- 获取最接近的
.project
元素的索引,但是滚动高度
您想在 pinElement()$ c $中执行的操作c>方法如下:
- 首先重置/修复所有内容
- 获取
projectsOffsetTop
值,该值比scrollY大,但最接近它(因此它将成为我们想要固定的元素)
- 从此开始,获取该值所属的
.project
元素的索引
- 如果索引为
-1
(即我们没有符合第2点条件的元素),返回
并停止执行。
- 否则,我们将执行您原始方法中的逻辑,但将
1
替换为我们在步骤3中确定的索引。
请记住,这是您稍微重构的 pinElement()
方法:
function pinElement(){
//全部重置样式
projects.forEach((project)=> {
document.body.style.paddingTop = 0;
project.classList.remove('fixed');
});
//获取最接近顶部
常量的项目的索引const valueClosestToScrollY = Math.max.apply(Math,projectsOffsetTop.filter((offsetTop)=> offsetTop< = window.scrollY));
const idx = projectsOffsetTop.indexOf(valueClosestToScrollY);
//如果找不到索引,则如果(idx === -1)
返回,我们将不执行任何操作。
//否则,我们设置适当的样式和类
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'));让projectsOffsetTop = projects.map(project => project.offsetTop);让projectsHeight = projects.map(project => project.offsetHeight);函数updateProjectsOffsetTop(){projectsOffsetTop = projects.map(project => project.offsetTop); projectsHeight = projects.map(project => project.offsetHeight);};函数pinElement(){//重置所有样式projects.forEach((project)=> {document.body.style.paddingTop = 0;项目。 classList.remove('fixed');}); //获取最接近顶部const值的项目的索引ClosestToScrollY = Math.max.apply(Math,projectsOffsetTop.filter((offsetTop)=> offsetTop< = window.scrollY)); const idx = projectsOffsetTop.indexOf(valueClosestToScrollY); //如果未找到索引,则如果(idx === -1)返回,我们将不执行任何操作; // //否则,如果(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 {框大小:border-box;} *,* ::之前,* ::之后{框大小:继承;边距:0;填充:0;}页眉,页脚{宽度:100%;填充:10%;背景颜色:灰色;位置:相对;}。project {宽度:100%;高度:100vh;职位:相对显示:flex;证明内容:中心; align-items:居中; top:0;}#item1 {background-color:yellow;}#item2 {background-color:blue;}#item3 {background-color:red;}。fixed {位置:fixed;}
< header class = forewords> < h1> Lorem ipsum dolor坐着,安全地服从精英。 Harum soluta ipsam quaerat cupiditate neque,必需品nihil perferendis sunt减! / h1< / header> div class = wrapper> < section class = project id = item1>这是第1部分< / section> < section class = project id = item2>这是第2部分< / section> < section class = project id = item3>这是第3部分< / section>< / div>< footer class = endings> < h1> Lorem ipsum dolor坐着,安全地服从精英。 Repudiandae vel,perferendis ullam totam recusandae sed repellendus暨! < / h1>< / footer>
p>
从侧面讲,出于性能原因,您可能希望对滚动事件进行节流/去抖动,因此 pinElement()
不会被过度调用。
I'm a beginner in javascript and i'm trying to build some stuff with es6 spec.
I would like to recreate the pin effects from ScrollMagic and pin different section while i scroll down my page.
So I have this simple html markup with an header a footer and 3 section:
<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>
And I've attached some styles to simulate a realistic situation.
Here comes the javascript logic:
Get all the projects:
const projects = Array.from(document.querySelectorAll('.project'));
Get all the projects offset from top and all the projects height:
let projectsOffsetTop = projects.map(project => project.offsetTop);
let projectsHeight = projects.map(project => project.offsetHeight);
Create a function to update the value if somebody resize the window:
function updateProjectsOffsetTop() {
projectsOffsetTop = projects.map(project => project.offsetTop);
projectsHeight = projects.map(project => project.offsetHeight);
};
window.addEventListener('resize', updateProjectsOffsetTop);
finaly pin the element if the scroll is greater than its offset.
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);
But i cant make it work with all the projects element. Even with for loop. What is the best practice? I want to solve this in Vanilla ES6 if it's possible.
Find attached the complete js fiddle.
Thanks
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>
解决方案
You've provided an amazing MCVE to work on, so thank you so much for taking so much effort and time to ask a great question. The good news is that you are almost there! Your logic is sound, and everything makes sense. What you are really missing is this:
- The correct placement of the logic to reset the styles (body top padding and removing the
fixed
class)
- Getting the index of the
.project
element that is closest, but more than, the scroll height
What you want to do in your pinElement()
method is the following:
- Reset/unfix everything first
- Get the
projectsOffsetTop
value that is more than scrollY, but the closest to it (so that it will be element that we want to pin)
- From that, get the index of the
.project
element that this value belongs to
- If the index is
-1
(i.e. we do not have an element that fits the criteria in point 2), return
and stop execution.
- Otherwise, we perform the logic you have in your original method, but substitute
1
with the index we have identified in step 3.
With that in mind, here is your slightly refactored pinElement()
method:
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');
}
};
Fun tip: you can use template literals to do this:
document.body.style.paddingTop = `${projectsHeight[idx]}px`;
…instead of this:
document.body.style.paddingTop = ${projectsHeight[idx] + 'px';
Here is a proof-of-concept example:
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>
On a side note, for performance reasons, you might want to look into throttling/debouncing your scroll event, so that pinElement()
is not called excessively.
这篇关于在滚动中固定/修复Vanilla Javascript中的多个元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文