2020 年 8 月 27 日

捲動

scroll 事件允許對頁面或元素捲動做出反應。我們可以在這裡執行許多不錯的操作。

例如

  • 根據使用者在文件中的位置顯示/隱藏其他控制項或資訊。
  • 當使用者向下捲動到頁面底部時載入更多資料。

以下是一個顯示目前捲動的小函式

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

動作

目前捲動 = 捲動視窗

scroll 事件同時在 window 和可捲動元素上運作。

防止捲動

我們如何讓某個東西無法捲動?

我們無法使用 onscroll 監聽器中的 event.preventDefault() 來防止捲動,因為它會在捲動發生之後觸發。

但我們可以在導致捲動的事件中使用 event.preventDefault() 來防止捲動,例如 pageUppageDownkeydown 事件。

如果我們為這些事件新增一個事件處理常式,並在其中使用 event.preventDefault(),則捲動不會開始。

有許多方法可以啟動捲動,因此使用 CSS 的 overflow 屬性更為可靠。

以下是一些你可以解決或瀏覽以查看 onscroll 應用程式的任務。

任務

重要性:5

建立一個無盡頁面。當訪客捲動到結尾時,它會自動將目前日期時間附加到文字中(以便訪客可以捲動更多內容)。

像這樣

請注意捲動的兩個重要特點

  1. 捲動是「彈性的」。我們可以在某些瀏覽器/裝置中稍微捲動超過文件開頭或結尾(下方會顯示空白,然後文件會自動「彈回」到正常狀態)。
  2. 捲動是不精確的。當我們捲動到頁尾時,我們實際上可能距離真實的文件底部 0-50 像素。

因此,「捲動到結尾」應表示訪客距離文件結尾不超過 100 像素。

附註:在現實生活中,我們可能想要顯示「更多訊息」或「更多商品」。

為任務開啟沙盒。

解決方案的核心是一個函式,當我們在頁面結尾時,會將更多日期新增到頁面(或在現實生活中載入更多內容)。

我們可以立即呼叫它,並將其新增為 window.onscroll 處理常式。

最重要的問題是:「我們如何偵測頁面是否已捲動到底部?」

讓我們使用視窗相對座標。

文件表示(並包含)在 <html> 標籤中,即 document.documentElement

我們可以取得整個文件的視窗相對座標為 document.documentElement.getBoundingClientRect()bottom 屬性將是文件底部的視窗相對座標。

例如,如果整個 HTML 文件的高度為 2000px,則

// when we're on the top of the page
// window-relative top = 0
document.documentElement.getBoundingClientRect().top = 0

// window-relative bottom = 2000
// the document is long, so that is probably far beyond the window bottom
document.documentElement.getBoundingClientRect().bottom = 2000

如果我們向下捲動 500px,則

// document top is above the window 500px
document.documentElement.getBoundingClientRect().top = -500
// document bottom is 500px closer
document.documentElement.getBoundingClientRect().bottom = 1500

當我們捲動到最後,假設視窗高度為 600px

// document top is above the window 1400px
document.documentElement.getBoundingClientRect().top = -1400
// document bottom is below the window 600px
document.documentElement.getBoundingClientRect().bottom = 600

請注意,bottom 不能為 0,因為它永遠不會到達視窗頂部。bottom 座標的最低限制是視窗高度(我們假設為 600),我們無法再向上捲動它。

我們可以將視窗高度取得為 document.documentElement.clientHeight

對於我們的任務,我們需要知道文件底部距離它不超過 100px(即:如果高度為 600,則為 600-700px)。

所以這裡有這個函式

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

    // if the user hasn't scrolled far enough (>100px to the end)
    if (windowRelativeBottom > document.documentElement.clientHeight + 100) break;

    // let's add more data
    document.body.insertAdjacentHTML("beforeend", `<p>Date: ${new Date()}</p>`);
  }
}

在沙盒中開啟解決方案。

重要性:5

建立一個「回到頂部」按鈕,以協助頁面捲動。

它應該像這樣運作

  • 當頁面未向下捲動至少視窗高度時,它會隱藏。
  • 當頁面向下捲動超過視窗高度時,會在左上角出現一個「向上」箭頭。如果頁面捲回,它就會消失。
  • 當按一下箭頭時,頁面會捲動到頂部。

像這樣(左上角,捲動以查看)

為任務開啟沙盒。

重要性:4

假設我們有一個低速客戶端,並且想要節省他們的行動裝置流量。

為此,我們決定不立即顯示影像,而是用佔位符取代它們,如下所示

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

因此,最初所有影像都是 placeholder.svg。當頁面捲動到使用者可以看到影像的位置時,我們將 src 變更為 data-src 中的影像,然後影像就會載入。

以下是在 iframe 中的範例

捲動它以查看「依需求」載入影像。

需求

  • 當頁面載入時,那些在螢幕上的影像應該在任何捲動之前立即載入。
  • 有些影像可能是常規的,沒有 data-src。程式碼不應該觸及它們。
  • 一旦影像載入,就不應該在捲入/捲出時重新載入。

附註:如果你可以,請建立一個更進階的解決方案,它會「預載入」在目前位置下方/之後一頁的影像。

附註:只處理垂直捲動,不處理水平捲動。

為任務開啟沙盒。

onscroll 處理常式應該檢查哪些影像可見並顯示它們。

我們也希望在頁面載入時執行它,以立即偵測可見影像並載入它們。

程式碼應該在文件載入時執行,以便它可以存取其內容。

或將它放在 <body> 底部

// ...the page content is above...

function isVisible(elem) {

  let coords = elem.getBoundingClientRect();

  let windowHeight = document.documentElement.clientHeight;

  // top elem edge is visible?
  let topVisible = coords.top > 0 && coords.top < windowHeight;

  // bottom elem edge is visible?
  let bottomVisible = coords.bottom < windowHeight && coords.bottom > 0;

  return topVisible || bottomVisible;
}

showVisible() 函數使用由 isVisible() 實作的可見度檢查來載入可見的影像

function showVisible() {
  for (let img of document.querySelectorAll('img')) {
    let realSrc = img.dataset.src;
    if (!realSrc) continue;

    if (isVisible(img)) {
      img.src = realSrc;
      img.dataset.src = '';
    }
  }
}

showVisible();
window.onscroll = showVisible;

附註:此解決方案也有 isVisible 的變體,它會「預先載入」在目前文件捲動範圍上下 1 頁內的影像。

在沙盒中開啟解決方案。

教學課程地圖

留言

留言前請先閱讀…
  • 如果您有改進建議,請 提交 GitHub 議題 或提交拉取請求,而不是留言。
  • 如果您看不懂文章中的某些內容,請詳細說明。
  • 若要插入少數幾個字元的程式碼,請使用 <code> 標籤;若要插入多行程式碼,請將它們包在 <pre> 標籤中;若要插入超過 10 行的程式碼,請使用沙盒 (plnkrjsbincodepen…)