滚动事件允许在页面或元素上滚动时作出反应。我们可以在这里做一些有用的事情。

比如:

  • 根据用户在文档中的位置显示/隐藏其他控件或信息。
  • 当用户滚动到页面末尾时加载更多的数据。

下面是一个显示当前滚动的小函数:

window.addEventListener('scroll', function() {
  document.getElementById('showScroll').innerHTML = pageYOffset + 'px';
});

动作:

Current scroll = scroll the window

scroll 事件在 window 和可滚动元素上都可以运行。

防止滚动

我们怎样让某些东西不可滚动呢?我们不能在 onscroll 监听者中通过使用 event.preventDefault() 来阻止滚动,因为它在滚动发生之后才触发。

但我们可以在导致滚动的事件上使用 event.preventDefault() 来阻止滚动。

例如:

  • wheel 事件 —— 鼠标滚轮(“滚动”触控板也会生成它)。
  • pageUppageDownkeydown 事件。

有时可能会有帮助,但还有很多滚动方式,所以很难处理它们。因此,使用 CSS 让一些东西不可滚动更为可靠,比如 overflow 属性。

这里有几个你可以完成的练习,你也可以看它们关于 onscroll 的应用。

任务

重要程度: 5

新建一个无限页面。当访问者滚动到底部时,它会自动将当期日期时间附加到文本中(以便访问者不停地滚动)。

就像这样:

请注意滚动的两个重要特性:

  1. **滚动是“弹性的(elastic)。**在一些浏览器/设备中,我们可以稍微超出文档的开始或结束(超出部分显示的是空白区域,然后文档将自动“反弹”到正常状态)。
  2. 滚动并不精确。 当我们滚动到页面结束时,实际上我们可能与真正的文档底部相差 0-50px。

因此“滚动到结束”就意味着访问者离文档结束的距离不超过 100px。

P.S. 在现实生活中,我们可能希望显示“更多的信息”或者“更多的商品”。

打开一个任务沙箱。

解决方案的核心是在页面结束时向页面添加更多日期(或者在实际中加载更多的内容)。

我们可以立即调用它,然后将其添加为 window.onscroll 处理器。

最重要的问题是:“如何检测到页面被滚动到了底部?”

我们使用窗口相对坐标。

<html> 标签表示(并包含)文档,即 document.documentElement

我们可以通过 document.documentElement.getBoundingClientRect() 获取整个文档相对于窗口的坐标。bottom 属性将是文档结束的窗口相对坐标。

例如,如果整个 HTML 文档的高度是 2000px,那么:

// 当我们在页面顶部时
// 相对于窗口的 top = 0
document.documentElement.getBoundingClientRect().top = 0

// 窗口相对底部 = 2000
// 文档很长,所以可能会远远超出窗口底部
document.documentElement.getBoundingClientRect().bottom = 2000

如果我们向下滚动 500px,那么:

// 文档顶部在窗口上方 500px
document.documentElement.getBoundingClientRect().top = -500
// 文档底部距离窗口近了 500px
document.documentElement.getBoundingClientRect().bottom = 1500

当我们滚动到末尾时,假设窗口高度为 600px

// 文档顶部在窗口 -1400 px 之上
document.documentElement.getBoundingClientRect().top = -1400
// 文档底部为 600 px
document.documentElement.getBoundingClientRect().bottom = 600

请注意,底部不是 0,因为它永远不会到达窗口顶部。底部坐标的最低限度是窗口高度,我们不能再滚动了。

窗口的高度是 document.documentElement.clientHeight

我们希望文档底部在窗口高度加上 100px 以内。

这是函数:

function populate() {
  while(true) {
    // 文档底部
    let windowRelativeBottom = document.documentElement.getBoundingClientRect().bottom;

    // 如果它比窗口高度还大 100px,那么我们就不是在页面的底部
    // (查看上述示例,大的底部意味着我们需要进行更多滚动)
    if (windowRelativeBottom > document.documentElement.clientHeight + 100) break;

    // 否则,我们会添加更多数据
    document.body.insertAdjacentHTML("beforeend", `<p>Date: ${new Date()}</p>`);
  }
}

使用沙箱打开解决方案。

重要程度: 5

创建一个 “to the top” 按钮来帮助页面滚动。

它应该像这样运行:

  • 页面向下滚动的距离没有超过窗口高度时 —— 按钮不可见。
  • 当页面滚动超过窗口高度时 —— 在左上角出现一个“向上”箭头。如果页面回滚,就会消失。
  • 单击箭头时,页面将滚动到顶部。

就像这样:

打开一个任务沙箱。

重要程度: 4

假设我们有一个速度较慢并希望节省自己移动流量客户。

为此,我们决定不立即显示图像,而是将其替换为占位符,如下所示:

<img src="placeholder.svg" width="128" height="128" data-src="real.jpg">

因此,起初的所有图像都是 placeholder.svg。当页面滚动到用户可以看到图像位置时 —— 我们就会将 src 更改为 data-srcsrc,然后加载图像。

这是 iframe 中的一个示例:

滚动它可以看到图像的“按需”加载。

要求:

  • 当页面被加载时,屏幕上的图像应该在滚动之前立即加载。
  • 某些图像可能很常规,没有 data-src 属性。代码不能改动它们。
  • 一旦图像被加载,它就不应该在滚动时重新加载。

P.S. 如果你有能力,可以创建一个更高级的解决方案,来“预加载”位于当前(之后)位置的图像。

P.P.S. 只有垂直滚动会被处理,水平滚动则不会。

打开一个任务沙箱。

onscroll 处理器应该检查哪些图像是可见的,然后显示它们。

我们还希望在页面加载时运行它,以便在任何滚动之前立即检测图像可见性并加载它们。

如果我们把它放在 <body> 底部,那么它会在页面内容被加载时运行。

// ...页面内容如上所述...

function isVisible(elem) {

  let coords = elem.getBoundingClientRect();

  let windowHeight = document.documentElement.clientHeight;

  // 顶部可见或底部可见
  let topVisible = coords.top > 0 && coords.top < windowHeight;
  let bottomVisible = coords.bottom < windowHeight && coords.bottom > 0;

  return topVisible || bottomVisible;
}

showVisible();
window.onscroll = showVisible;

对于可视化图像,我们可以使用 img.dataset.src 并将其赋值 img.src(如果还没有这样做)。

P.S. 解决方案还有一个 isVisible 的变体,即位于 1 个页面上方/下方的“预加载”图像(页面高度是 document.documentElement.clientHeight)。

使用沙箱打开解决方案。

教程路线图

评论

在评论之前先阅读本内容…
  • 欢迎你在文章下添加补充内容、提出你的问题或回答提出的问题。
  • 使用 <code> 标签插入几行代码,对于多行代码 — 可以使用 <pre>,对于超过十行的代码 — 建议使用沙箱(plnkrJSBincodepen 等)。
  • 如果你无法理解文章中的内容 — 请详细说明。