JavaScript 无限滚动

摘要:在本教程中,您将学习如何实现 JavaScript 无限滚动功能。

您将要构建的内容

下图展示了您将要构建的 web 应用程序

JavaScript Infinite Scroll Application

该页面将显示来自 API 的引号列表。默认情况下,它将显示 10 个引号。

如果您向下滚动到页面底部,web 应用程序将显示一个加载指示器。此外,它将调用 API 获取更多引号并将其追加到当前列表中。

您将使用的 API 的 URL 如下

https://api.javascripttutorial.net/v1/quotes/?page=1&limit=10Code language: JavaScript (javascript)

API 接受两个查询字符串:pagelimit。这些查询字符串允许您从服务器对引号进行分页。

引号被划分为由 page 查询字符串确定的页面。每个页面都有由 limit 参数指定数量的引号。

单击此处查看使用 JavaScript 无限滚动功能的最终 web 应用程序。

创建项目结构

首先,创建一个名为 infinite-scroll 的新文件夹。在该文件夹内,创建两个子文件夹 cssjs

其次,在 css 文件夹中创建 style.css,在 js 文件夹中创建 app.js

第三,在 infinite-scroll 文件夹中创建一个新的 HTML 文件 index.html。

最终的项目文件夹结构如下所示

向 index.html 文件添加代码

打开 index.html 并向其中添加以下代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript Infinite Scroll - Quotes</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>

    <div class="container">
        <h1>Programming Quotes</h1>

        <div class="quotes">
        </div>

        <div class="loader">
            <div></div>
            <div></div>
            <div></div>
        </div>
    </div>
    <script src="js/app.js"></script>
</body>
</html>Code language: HTML, XML (xml)

index.html 文件中,将 style.css 放置在头部部分,将 app.js 放置在主体部分。

主体部分包含一个类名为 containerdiv。容器元素有四个子元素

  • 一个标题一(h1),显示页面标题。
  • 一个类名为 quotesdiv,它将成为所有引号的父元素。
  • 一个加载器,显示加载指示器。默认情况下,加载指示器不可见。

制作 app.js

以下代码使用 querySelector() 选择类名为 quotesdivloader

const quotesEl = document.querySelector('.quotes');
const loader = document.querySelector('.loader');Code language: JavaScript (javascript)

getQuotes() 函数

以下 getQuotes() 函数调用 API 并返回引号

const getQuotes = async (page, limit) => {
    const API_URL = `https://api.javascripttutorial.net/v1/quotes/?page=${page}&limit=${limit}`;
    const response = await fetch(API_URL);
    // handle 404
    if (!response.ok) {
        throw new Error(`An error occurred: ${response.status}`);
    }
    return await response.json();
}Code language: JavaScript (javascript)

getQuotes() 函数接受两个参数:pagelimit。它使用 Fetch API 从 API 获取数据。

由于 fetch() 返回一个 promise,您可以使用 await 语法获取响应。您可以调用响应对象的 json() 方法来获取 json 数据。

getQuotes() 返回一个 promise,它将解析为 JSON 数据。

由于 getQuotes() 函数使用了 await 关键字,因此它必须是一个 async 函数。

showQuotes() 函数

以下定义了 showQuotes() 函数,该函数从 quotes 数组生成 <blockquote> 元素并将它们追加到 quotes 元素

// show the quotes
const showQuotes = (quotes) => {
    quotes.forEach(quote => {
        const quoteEl = document.createElement('blockquote');
        quoteEl.classList.add('quote');

        quoteEl.innerHTML = `
            <span>${quote.id})</span>
            ${quote.quote}
            <footer>${quote.author}</footer>
        `;

        quotesEl.appendChild(quoteEl);
    });
};Code language: JavaScript (javascript)

工作原理

showQuotes() 使用 forEach() 方法遍历 quotes 数组。

对于每个引号对象,它创建一个类名为 quote<blockquote> 元素

<blockquote class="quote">
</blockquote>Code language: HTML, XML (xml)

它使用 模板字面量 语法生成引号对象的 HTML 表示形式。它将 HTML 添加到 <blockquote> 元素中。

以下是生成的 <blockquote> 元素的示例

<blockquote class="quote">
   <span>1)</span>
      Talk is cheap. Show me the code.
    <footer>Linus Torvalds</footer>
</blockquote>Code language: HTML, XML (xml)

在每次迭代结束时,该函数使用 appendChild() 方法将 <blockquote> 元素追加到 quotesEl 元素的子元素中。

显示/隐藏加载指示器函数

以下定义了两个显示和隐藏加载指示器元素的函数

const hideLoader = () => {
    loader.classList.remove('show');
};

const showLoader = () => {
    loader.classList.add('show');
};Code language: JavaScript (javascript)

加载指示器的透明度为 0,默认情况下不可见。.show 类将加载指示器的透明度设置为 1,这将使其可见。

要隐藏加载指示器,您需要从加载指示器元素中删除 show 类。类似地,要显示加载指示器,您需要将其 show 类添加到其类列表中。

定义控制变量

以下声明了 currentPage 变量并将其初始化为 1

 let currentPage = 1;Code language: JavaScript (javascript)

当您向下滚动到页面底部时,应用程序将发出 API 请求以获取下一个引号。在此之前,您需要将 currentPage 变量增加 1。

要指定您一次想要获取的引号数量,您可以使用类似这样的常量

const limit = 10;Code language: JavaScript (javascript)

以下 total 变量存储从 API 返回的引号总数

let total = 0;Code language: JavaScript (javascript)

hasMoreQuotes() 函数

以下 hasMoreQuotes() 函数在以下情况下返回 true

  • 这是第一次获取 (total === 0)
  • 或者还有更多引号要从 API 获取 (startIndex < total)
const hasMoreQuotes = (page, limit, total) => {
    const startIndex = (page - 1) * limit + 1;
    return total === 0 || startIndex < total;
};Code language: JavaScript (javascript)

loadQuotes() 函数

以下定义了一个执行四个操作的函数

  • 显示加载指示器。
  • 如果还有更多引号要获取,则通过调用 getQuotes() 函数从 API 获取引号。
  • 在页面上显示引号。
  • 隐藏加载指示器。
// load quotes
const loadQuotes = async (page, limit) => {
    // show the loader
    showLoader();
    try {
        // if having more quotes to fetch
        if (hasMoreQuotes(page, limit, total)) {
            // call the API to get quotes
            const response = await getQuotes(page, limit);
            // show quotes
            showQuotes(response.data);
            // update the total
            total = response.total;
        }
    } catch (error) {
        console.log(error.message);
    } finally {
        hideLoader();
    }
};Code language: JavaScript (javascript)

如果 getQuotes() 函数执行速度很快,您将看不到加载指示器。

为了确保加载指示器始终显示,您可以使用 setTimeout() 函数

// load quotes
const loadQuotes = async (page, limit) => {

    // show the loader
    showLoader();

    // 0.5 second later
    setTimeout(async () => {
        try {
            // if having more quotes to fetch
            if (hasMoreQuotes(page, limit, total)) {
                // call the API to get quotes
                const response = await getQuotes(page, limit);
                // show quotes
                showQuotes(response.data);
                // update the total
                total = response.total;
            }
        } catch (error) {
            console.log(error.message);
        } finally {
            hideLoader();
        }
    }, 500);

};Code language: JavaScript (javascript)

通过添加 setTimeout() 函数,加载指示器将至少显示半秒钟。您可以通过更改 setTimeout() 函数的第二个参数来调整延迟时间。

附加滚动事件

要使用户滚动到页面底部时加载更多引号,您需要附加一个 滚动事件处理程序

如果满足以下条件,滚动事件处理程序将调用 loadQuotes() 函数

  • 首先,滚动位置在页面底部。
  • 其次,还有更多引号要获取。

滚动事件处理程序还将在加载下一个引号之前增加 currentPage 变量。

 window.addEventListener('scroll', () => {
    const {
        scrollTop,
        scrollHeight,
        clientHeight
    } = document.documentElement;

    if (scrollTop + clientHeight >= scrollHeight - 5 &&
        hasMoreQuotes(currentPage, limit, total)) {
        currentPage++;
        loadQuotes(currentPage, limit);
    }
}, {
    passive: true
});Code language: JavaScript (javascript)

初始化页面

当页面第一次加载时,您需要调用 loadQuotes() 函数来加载第一批引号

loadQuotes(currentPage, limit);

将 app.js 代码包装在 IIFE 中

为了避免您定义的变量和函数发生冲突,您可以将 app.js 文件中的所有代码包装在一个 IIFE 中。

最终的 app.js 将如下所示

(function () {

    const quotesEl = document.querySelector('.quotes');
    const loaderEl = document.querySelector('.loader');

    // get the quotes from API
    const getQuotes = async (page, limit) => {
        const API_URL = `https://api.javascripttutorial.net/v1/quotes/?page=${page}&limit=${limit}`;
        const response = await fetch(API_URL);
        // handle 404
        if (!response.ok) {
            throw new Error(`An error occurred: ${response.status}`);
        }
        return await response.json();
    }

    // show the quotes
    const showQuotes = (quotes) => {
        quotes.forEach(quote => {
            const quoteEl = document.createElement('blockquote');
            quoteEl.classList.add('quote');

            quoteEl.innerHTML = `
            <span>${quote.id})</span>
            ${quote.quote}
            <footer>${quote.author}</footer>
        `;

            quotesEl.appendChild(quoteEl);
        });
    };

    const hideLoader = () => {
        loaderEl.classList.remove('show');
    };

    const showLoader = () => {
        loaderEl.classList.add('show');
    };

    const hasMoreQuotes = (page, limit, total) => {
        const startIndex = (page - 1) * limit + 1;
        return total === 0 || startIndex < total;
    };

    // load quotes
    const loadQuotes = async (page, limit) => {

        // show the loader
        showLoader();

        // 0.5 second later
        setTimeout(async () => {
            try {
                // if having more quotes to fetch
                if (hasMoreQuotes(page, limit, total)) {
                    // call the API to get quotes
                    const response = await getQuotes(page, limit);
                    // show quotes
                    showQuotes(response.data);
                    // update the total
                    total = response.total;
                }
            } catch (error) {
                console.log(error.message);
            } finally {
                hideLoader();
            }
        }, 500);

    };

    // control variables
    let currentPage = 1;
    const limit = 10;
    let total = 0;


    window.addEventListener('scroll', () => {
        const {
            scrollTop,
            scrollHeight,
            clientHeight
        } = document.documentElement;

        if (scrollTop + clientHeight >= scrollHeight - 5 &&
            hasMoreQuotes(currentPage, limit, total)) {
            currentPage++;
            loadQuotes(currentPage, limit);
        }
    }, {
        passive: true
    });

    // initialize
    loadQuotes(currentPage, limit);

})();Code language: JavaScript (javascript)

这是最终版本 的 web 应用程序

本教程对您有帮助吗?