如何在 Web 应用程序中实现无限滚动

与传统的点击加载分页方式不同,无限滚动技术允许用户在向下滚动页面时,内容能够持续加载。这种方式能够提供更为流畅的用户体验,特别是在移动设备上浏览时,感受更为明显。

本文将详细介绍如何利用纯粹的 HTML、CSS 和 JavaScript 技术来实现无限滚动功能。

前端基础搭建

首先,我们需要创建一个基本的 HTML 结构来展示内容。以下是一个示例:

  <link rel="stylesheet" href="https://wilku.top/how-to-implement-infinite-scroll-in-a-web-application/style.css" />
  <h2>无限滚动页面</h2>
  <div class="products__list">
    <img src="https://fakestoreapi.com/img/71li-ujtlUL._AC_UX679_.jpg" />
    <img src="https://fakestoreapi.com/img/71li-ujtlUL._AC_UX679_.jpg" />
    <img src="https://fakestoreapi.com/img/71li-ujtlUL._AC_UX679_.jpg" />
    <img src="https://fakestoreapi.com/img/71li-ujtlUL._AC_UX679_.jpg" />
    <img src="https://fakestoreapi.com/img/71li-ujtlUL._AC_UX679_.jpg" />
    <img src="https://fakestoreapi.com/img/71li-ujtlUL._AC_UX679_.jpg" />
  </div>
  <noscript><p>您的浏览器不支持 JavaScript。</p></noscript>
  

这个页面包含了若干占位符图片,并引用了两个外部资源:一个 CSS 文件和一个 JavaScript 文件。

可滚动内容的 CSS 样式设计

为了在网格中显示这些占位符图片,请将以下 CSS 代码添加到名为 `style.css` 的文件中:

  * {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  }

  html { font-size: 62.5%; }

  body {
    font-family: Cambria, Times, "Times New Roman", serif;
  }

  h2 {
    text-align: center;
    font-size: 5rem;
    padding: 2rem;
  }

  img {
    width: 100%;
    display: block;
  }

  .products__list {
    display: flex;
    flex-wrap: wrap;
    gap: 2rem;
    justify-content: center;
  }

  .products__list > * {
    width: calc(33% - 2rem);
  }

  .loading-indicator {
    display: none;
    position: absolute;
    bottom: 30px;
    left: 50%;
    background: #333;
    padding: 1rem 2rem;
    color: #fff;
    border-radius: 10px;
    transform: translateX(-50%);
  }
  

应用上述样式后,你的页面应该呈现如下效果:

JavaScript 核心实现

接下来,编辑 `script.js` 文件。为了实现无限滚动,您需要检测用户何时滚动到内容容器或页面底部附近。

    "use strict";

    window.addEventListener("scroll", () => {
      if (
        window.scrollY + window.innerHeight >=
          document.documentElement.scrollHeight - 100
      ) {
        fetchMoreContent();
      }
    });
  

接下来,创建一个函数来获取更多的占位数据。

    async function fetchMoreContent() {
      try {
        let response = await fetch("https://fakestoreapi.com/products?limit=3");

        if (!response.ok) {
          throw new Error("网络请求失败");
        }

        let data = await response.json();
        console.log(data);
      } catch (error) {
        console.error("获取新内容时出现问题:", error);
      } finally {
        console.log("Fetch 函数已触发");
      }
    }
  

在这个示例项目中,我们使用了Fake Store API

为了验证数据是否正确获取,请查看浏览器控制台:

你可能会注意到,随着滚动操作的进行,数据会被多次获取,这可能会对设备性能产生不利影响。为了避免这种情况,我们需要定义一个变量来记录当前数据是否正在获取:

 let isFetching = false; 

然后,修改获取函数,使其仅在上一次获取完成后才开始新的数据获取。

    async function fetchMoreContent() {
      if (isFetching) return;

      isFetching = true;

      try {
        let response = await fetch("https://fakestoreapi.com/products?limit=3");

        if (!response.ok) {
          throw new Error("网络请求失败");
        }

        let data = await response.json();
      } catch (error) {
        console.error("获取新内容时出现问题:", error);
      } finally {
        console.log("Fetch 函数已触发");
        isFetching = false;
      }
    }
  

显示新内容

为了在用户向下滚动时显示新内容,我们需要创建一个函数,将新的图片附加到父容器。

首先,选择父元素:

 const productsList = document.querySelector(".products__list"); 

然后,创建一个函数来添加内容。

    function displayNewContent(data) {
      data.forEach((item) => {
        const imgElement = document.createElement("img");
        imgElement.src = item.image;
        imgElement.alt = item.title;
        productsList.appendChild(imgElement);
      });
    }
  

最后,修改数据获取函数,并将获取到的数据传递给内容追加函数。

    async function fetchMoreContent() {
      if (isFetching) return;

      isFetching = true;

      try {
        let response = await fetch("https://fakestoreapi.com/products?limit=3");

        if (!response.ok) {
          throw new Error("网络请求失败");
        }

        let data = await response.json();
        displayNewContent(data);
      } catch (error) {
        console.error("获取新内容时出现问题:", error);
      } finally {
        console.log("Fetch 函数已触发");
        isFetching = false;
      }
    }
  

至此,无限滚动功能已经初步实现。

为了增强用户体验,可以在获取新内容时显示加载指示器。首先,添加以下 HTML 代码:

 <h2 class="loading-indicator">加载中...</h2> 

接下来,选择加载元素:

 const loadingIndicator = document.querySelector(".loading-indicator"); 

最后,创建两个函数来切换加载指示器的显示与隐藏:

    function showLoadingIndicator() {
      loadingIndicator.style.display = "block";
      console.log("正在加载...");
    }

    function hideLoadingIndicator() {
      loadingIndicator.style.display = "none";
      console.log("加载完成。");
    }
  

然后,将这两个函数添加到数据获取函数中。

    async function fetchMoreContent() {
      if (isFetching) return;

      isFetching = true;
      showLoadingIndicator();

      try {
        let response = await fetch("https://fakestoreapi.com/products?limit=3");

        if (!response.ok) {
          throw new Error("网络请求失败");
        }

        let data = await response.json();
        displayNewContent(data);
      } catch (error) {
        console.error("获取新内容时出现问题:", error);
      } finally {
        console.log("Fetch 函数已触发");
        hideLoadingIndicator();
        isFetching = false;
      }
    }
  

这样,加载指示器也会随之显示和隐藏,优化了用户体验。

以下是一些值得遵循的最佳实践:

  • 避免一次性加载过多数据,以免造成浏览器负担,影响性能。
  • 不要在滚动事件触发后立即发起数据请求,可以利用节流或防抖函数来延迟请求,避免过多网络请求。
  • 并非所有用户都喜欢无限滚动,可以提供分页组件作为替代方案。
  • 当没有更多内容可加载时,应通知用户,而不是持续尝试加载。

精通无缝内容加载

无限滚动技术能够为用户提供更流畅的内容浏览体验,这对于移动设备用户尤为重要。通过应用本文提供的建议和技巧,您也可以轻松地将此功能集成到您的网站中。

记住要始终从用户体验的角度出发,诸如显示进度指示器和错误提示等细节,能够确保用户明确了解发生了什么。