You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过JS事件检测用户滚动至电影滑块末尾?

滚动触发加载:解决横向flexbox列表的“到达末尾”检测问题

我正在做一个大学电影票务网站项目,首页需要实现滚动加载功能:

  • 用户先看到10部电影
  • 滚动到电影列表末尾时,通过fetch请求额外5部电影
  • 加载完成后展示15部,支持继续滚动

现在卡在第二步:不知道怎么给横向滚动的flexbox(#movie-slider)添加“到达末尾”的触发事件,求解决办法。


现有代码示例

HTML

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Movie Tickets</title>
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Teko:wght@300&display=swap" rel="stylesheet">
  <link rel="stylesheet" href="/css/index.css">
  <link rel="stylesheet" href="/css/general.css">
</head>

<body>
  <article class="content">
    <section id="movie-slider">
    </section>
  </article>
  <script src="/js/index.js" type="module"></script>
  <script src="/js/general.js" type="module"></script>
</body>

</html>

CSS

body {
  margin: 0;
  font-family: 'Teko', sans-serif;
}

.float-right {
  float: right;
}

.content {
  transition: margin-top 0.4s;
}

.content {
  background: fixed;
  background-image: url("/images/homepage_background.jpg");
  background-position: center;
  background-size: cover;
  min-height: 100vh;
}

#movie-slider {
  display: flex;
  flex-direction: row;
  align-items: end;
  overflow: auto;
  gap: 10px;
  padding: 10vh 10%;
}

.movie-banner {
  margin-top: 50px;
  margin-bottom: 150px;
  flex-basis: 250px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
}

.movie-banner__image {
  object-fit: cover;
  height: 400px;
}

.movie-banner__title {
  color: wheat;
  text-align: center;
  font-family: 'Bebas Neue', cursive;
  font-size: 1.5em;
  padding: 10px
}

.movie-banner__desc {
  color: wheat;
  font-size: 1.3em;
  text-align: center;
}

JavaScript

class Movie {
  constructor(title, image, description, duration, release, age, schedule, seats) {
    this.title = title;
    this.image = image;
    this.description = description;
    this.duration = duration;
    this.release = release;
    this.age = age;
    this.schedule = schedule; //dictionary met alle tijden per dag.
    this.seats = seats;
  }
}

const blankMovie = new Movie(
  "Blank",
  "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Grey_background.jpg/636px-Grey_background.jpg?20140113173544",
  "blank",
  "0 minutes",
  100
);

var allMovies = [];

for (let i = 0; i < 10; i++) {
  allMovies.push(blankMovie)
}

function populatewith(movies) {
  const homePage = document.getElementById("movie-slider");

  for (let i = 0; i < allMovies.length; i++) {
    const movieDiv = document.createElement("div");
    movieDiv.className = "movie-banner";

    const movieTitle = document.createElement("span");
    movieTitle.className = "movie-banner__title";
    movieTitle.textContent = allMovies[i].title;

    const movieImage = document.createElement("img");
    movieImage.className = "movie-banner__image";
    movieImage.src = allMovies[i].image;

    const movieDur = document.createElement("span");
    movieDur.className = "movie-banner__desc";
    movieDur.textContent = allMovies[i].duration;

    homePage.appendChild(movieDiv);
    movieDiv.appendChild(movieTitle);
    movieDiv.appendChild(movieImage);
    movieDiv.appendChild(movieDur);
  }
}

populatewith(allMovies);

解决方案

核心思路

要检测横向滚动容器是否到达末尾,需监听容器的scroll事件,通过三个属性的数值对比判断:

  • scrollLeft:容器已滚动的水平距离
  • clientWidth:容器的可视宽度
  • scrollWidth:容器内容的总宽度(含不可见部分)

当scrollLeft + clientWidth >= scrollWidth - 阈值时,即可判定到达列表末尾,触发加载逻辑。

具体实现

1. 添加滚动监听与末尾检测

给#movie-slider容器添加scroll事件监听,同时加入加载锁避免重复请求:

const movieSlider = document.getElementById('movie-slider');
let isLoading = false; // 加载锁,防止同一时间多次发起请求

movieSlider.addEventListener('scroll', () => {
  // 预留20px提前触发阈值,提升用户体验
  const threshold = 20;
  const isAtEnd = movieSlider.scrollLeft + movieSlider.clientWidth >= movieSlider.scrollWidth - threshold;
  
  if (isAtEnd && !isLoading) {
    isLoading = true;
    loadMoreMovies();
  }
});
2. 实现加载更多逻辑

编写异步加载函数,请求新数据并增量渲染到列表:

async function loadMoreMovies() {
  try {
    // 替换为你的实际API地址,用allMovies.length作为偏移量
    const response = await fetch(`/api/movies?offset=${allMovies.length}`);
    const newMovies = await response.json();
    
    // 将新电影添加到全局数组
    allMovies.push(...newMovies);
    // 只渲染新增的电影,避免全量重绘
    renderNewMovies(newMovies);
    
    isLoading = false; // 加载完成,解锁
  } catch (error) {
    console.error('加载电影失败:', error);
    isLoading = false; // 出错后也要解锁,保证后续能触发加载
  }
}

// 增量渲染函数,仅处理新电影
function renderNewMovies(movies) {
  const homePage = document.getElementById("movie-slider");
  
  movies.forEach(movie => {
    const movieDiv = document.createElement("div");
    movieDiv.className = "movie-banner";

    const movieTitle = document.createElement("span");
    movieTitle.className = "movie-banner__title";
    movieTitle.textContent = movie.title;

    const movieImage = document.createElement("img");
    movieImage.className = "movie-banner__image";
    movieImage.src = movie.image;

    const movieDur = document.createElement("span");
    movieDur.className = "movie-banner__desc";
    movieDur.textContent = movie.duration;

    homePage.appendChild(movieDiv);
    movieDiv.appendChild(movieTitle);
    movieDiv.appendChild(movieImage);
    movieDiv.appendChild(movieDur);
  });
}
3. 优化初始渲染函数

修改原populatewith函数,避免重复渲染全部内容:

function populatewith(movies) {
  const homePage = document.getElementById("movie-slider");
  homePage.innerHTML = ''; // 清空容器,防止重复初始化
  
  movies.forEach(movie => {
    const movieDiv = document.createElement("div");
    movieDiv.className = "movie-banner";

    const movieTitle = document.createElement("span");
    movieTitle.className = "movie-banner__title";
    movieTitle.textContent = movie.title;

    const movieImage = document.createElement("img");
    movieImage.className = "movie-banner__image";
    movieImage.src = movie.image;

    const movieDur = document.createElement("span");
    movieDur.className = "movie-banner__desc";
    movieDur.textContent = movie.duration;

    homePage.appendChild(movieDiv);
    movieDiv.appendChild(movieTitle);
    movieDiv.appendChild(movieImage);
    movieDiv.appendChild(movieDur);
  });
}

内容的提问来源于stack exchange,提问作者Jord van Eldik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 08:24:53