【问题标题】:How to trigger a JavaScript function only when the user scrolls the page如何仅在用户滚动页面时触发 JavaScript 函数
【发布时间】:2022-01-26 03:30:13
【问题描述】:

只有当用户滚动页面时,我才需要将 positon: fixed 添加到 .box

这里的重点是只有在用户滚动页面后才会生成具有类 .box 的元素。

这是我想出的:

window.addEventListener('scroll', () => {
const myDiv = document.querySelector('.box')
if (myDiv) {
    if (myDiv.style.position !== 'fixed') {
        myDiv.style.position = 'fixed'
    }
}
})

我的代码的问题是滚动事件现在将一直触发一百万个事件并杀死性能。

在不一次又一次触发滚动事件的情况下实现上述目标的正确方法是什么。

【问题讨论】:

标签: javascript sass


【解决方案1】:

盒子插入DOM后,可以查询盒子并为其添加固定位置样式。

如果您经常这样做,那么这里有一个更通用的函数,它将(给定一个选择器,例如 .box)等待一个元素被插入到 DOM 中:

function waitForElement(selector) {
  let element = null;
  let handled = false;
  let nextFrame;
  return (callback) => {
    function tick() {
      element = document.querySelector(selector);
      if (element === null) {
        nextFrame = requestAnimationFrame(tick);
      }
      if (element !== null && !handled) {
        handled = true;
        callback(element);
      }
    }
    cancelAnimationFrame(nextFrame);
    requestAnimationFrame(tick);
  };
}

用法:

const waitForBox = waitForElement(".box");

waitForBox((box) => {
  box.style.position = "fixed";
});

演示:

// Demo section, ignore this
const plane = document.querySelector(".plane");
const box = document.createElement("div");
box.className = "box";
box.style.width = "100px";
box.style.height = "100px";
box.style.background = "crimson";

function waitForElement(selector) {
  let element = null;
  let handled = false;
  let nextFrame;
  return (callback) => {
    function tick() {
      element = document.querySelector(selector);
      if (element === null) {
        nextFrame = requestAnimationFrame(tick);
      }
      if (element !== null && !handled) {
        handled = true;
        callback(element);
      }
    }
    cancelAnimationFrame(nextFrame);
    requestAnimationFrame(tick);
  };
}

// A function to call inside scroll callback that will
// wait for the .box element to be found in the DOM
const waitForBox = waitForElement(".box");

let boxAppended = false;
document.addEventListener("scroll", () => {
  // Appending the box to the DOM, ignore this
  if (!boxAppended) {
    boxAppended = true;
    plane.appendChild(box);
  }

  // Callback will be fired once when the .box
  // element is found in the DOM
  waitForBox((box) => {
    box.style.position = "fixed";
  });
});
body {
  padding: 0;
  height: 100vh;
  overflow: auto;
}

.plane {
  height: 200vh;
}
<div class="plane"></div>

【讨论】:

    【解决方案2】:

    例如,您可以使用requestAnimationFrame()setTimeout() 来防止滚动事件触发太多次。

    以下是如何使用来自Mozilla scroll event documentationrequestAnimationFrame() 执行此操作的示例:

    let lastKnownScrollPosition = 0;
    let ticking = false;
    
    function doSomething(scrollPos) {
      // Do something with the scroll position
    }
    
    document.addEventListener('scroll', function(e) {
      lastKnownScrollPosition = window.scrollY;
    
      if (!ticking) {
        window.requestAnimationFrame(function() {
          doSomething(lastKnownScrollPosition);
          ticking = false;
        });
    
        ticking = true;
      }
    });
    

    【讨论】:

    • 感谢@eli6,它运行良好。但我只是好奇他们为什么称第二个变量为 ticking?
    • @user1941537 滴答基本上是一个循环的迭代,在这种情况下是浏览器重绘循环。所以这里打勾是指“doSomething”更新计划在当前周期在浏览器中重绘,因此无需添加另一个更新。
    猜你喜欢
    • 1970-01-01
    • 2017-01-06
    • 2021-11-25
    • 2017-11-16
    • 1970-01-01
    • 2021-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多