在滚动中固定/修复Vanilla Javascript中的多个元素 [英] Pin / Fix multiple elements in Vanilla Javascript on scroll

查看:49
本文介绍了在滚动中固定/修复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()方法如下:


  1. 首先重置/修复所有内容

  2. 获取 projectsOffsetTop 值,该值比scrollY大,但最接近它(因此它将成为我们想要固定的元素)

  3. 从此开始,获取该值所属的 .project 元素的索引

  4. 如果索引为 -1 (即我们没有符合第2点条件的元素),返回并停止执行。

  5. 否则,我们将执行您原始方法中的逻辑,但将 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:

What you want to do in your pinElement() method is the following:

  1. Reset/unfix everything first
  2. 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)
  3. From that, get the index of the .project element that this value belongs to
  4. If the index is -1 (i.e. we do not have an element that fits the criteria in point 2), return and stop execution.
  5. 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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆