在卷轴上固定/修复Vanilla Javascript中的多个元素

时间:2018-05-02 14:29:33

标签: javascript ecmascript-6

我是javascript的初学者,我正在尝试使用es6规范构建一些东西。

我想从ScrollMagic重新创建引脚效果,并在向下滚动页面时固定不同的部分。

所以我有这个简单的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小提琴。

由于

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>

1 个答案:

答案 0 :(得分:2)

你提供了一个令人惊叹的MCVE,所以非常感谢你花了这么多精力和时间来提出一个很好的问题。好消息是你几乎那里!你的逻辑是合理的,一切都是有道理的。你真正缺少的是:

  • 正确放置逻辑以重置样式(正文顶部填充并删除fixed类)
  • 获取最接近但超过滚动高度的.project元素的索引

您希望在pinElement()方法中执行以下操作:

  1. 首先重置/解锁所有内容
  2. 获取超过scrollY的projectsOffsetTop值,但最接近它(以便它将成为我们想要固定的元素)
  3. 从中获取此值所属的.project元素的索引
  4. 如果索引为-1(即我们没有符合第2点条件的元素),return并停止执行。
  5. 否则,我们会执行原始方法中的逻辑,但将1替换为我们在步骤3中确定的索引。
  6. 考虑到这一点,这是您稍微重构的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';
    

    这是一个概念验证示例:

    &#13;
    &#13;
    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);
    &#13;
    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;
    }
    &#13;
    <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>
    &#13;
    &#13;
    &#13;

    在旁注中,出于性能原因,您可能希望调查滚动事件的限制/去抖动,以免过度调用pinElement()