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

移动端横向滚动脚本失效问题求助

移动端横向页面切换滚动问题修复方案

问题还原

你开发的双页面横向切换网站,首页显示时侧边页以0宽度隐藏;点击按钮激活侧边页,脚本触发横向滚动切换,同时隐藏首页,返回流程同理。但在Chrome移动端模式下出现两个问题:

  • 脚本无法触发横向滚动,只能手动拖动页面
  • 滚动到最右侧时,首页不会自动隐藏
  • 临时把.certOpenLeft类宽度改成10000px能启用滚动,但会彻底破坏页面布局

以下是你提供的简化示例代码:

原HTML代码

<html>
<head>
  <title>HTML Scrolling example</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
  <div id="horizontalPagesWrapper">
    <div class="frontpage pageOpen">
      <div class="contentContainer">
        <h1>HTML Scrolling example</h1>
        <button class="infoButton" onclick="ScrollUpAndActivateCertificates()">SIDE PAGE</button>
      </div>
    </div>

    <div class="certificates pageClosed">
      <div class="contentContainer">
        <h1>Welcome to the side page</h1>
        <button class="infoButton" onclick="ScrollUpAndActivateFrontpage()">FRONT PAGE</button>
      </div>
    </div>
  </div>
</body>
</html>

原CSS代码

body {
  background-color: white;
  font-family: 'Lato';
  margin: 0;
  padding: 0;
  backface-visibility: hidden;
  overflow-x: hidden;
  position: relative;
  -webkit-overflow-scrolling: touch;
  overflow-y: scroll;
}

/* Styles for moving the pages */
#horizontalPagesWrapper {
  display: flex;
  flex-direction: row;
}

.pageOpen {
  width: 100%;
  max-height: 100%;
}

.pageClosed {
  max-width: 0px;
  max-height: 0px;
  overflow: hidden;
}

.certOpenLeft {
  position: absolute;
  width: 100%;
  left: 100%;
}

.certMoveToFront {
  width: 100%;
  left: 0;
}

.certMoveToSide {
  position: absolute;
  max-width: 100%;
  left: 100%;
}

/* Page styles*/
@media only screen and (min-width: 769px) {
  .contentContainer {
    display: flex;
    align-items: center;
    flex-direction: column;
    width: 100%;
  }
}

@media only screen and (max-width: 768px) {
  .contentContainer {
    display: flex;
    align-items: center;
    flex-direction: column;
    width: 100%;
  }
}

原JS代码

function ScrollUpAndActivateCertificates() {
  // Check if page is at the top
  if (window.scrollY === 0) {
    document.getElementsByClassName("certificates")[0].classList.remove("pageClosed");
    document.getElementsByClassName("certificates")[0].classList.add("certOpenLeft");
    scrollRightAndRemoveFrontpage();
  }

  // Scroll to top and restart function
  else {
    window.scrollTo({
      top: 0,
      behavior: "smooth"
    });
    setTimeout(ScrollUpAndActivateCertificates, 1000);
  }
}

function scrollRightAndRemoveFrontpage() {
  window.scrollTo({
    left: window.innerWidth,
    behavior: "smooth"
  });

  if (window.scrollX >= window.innerWidth - 100) // Needs leeway for windows scrollbar?
  {
    // Disable front page
    document.getElementsByClassName("frontpage")[0].classList.remove("pageOpen");
    document.getElementsByClassName("frontpage")[0].classList.add("pageClosed");
    // Set cert as the only page
    document.getElementsByClassName("certificates")[0].classList.add("certMoveToFront");
    document.getElementsByClassName("certificates")[0].classList.remove("certOpenLeft");
  } else {
    setTimeout(scrollRightAndRemoveFrontpage, 1000);
  }
}

function ScrollUpAndActivateFrontpage() {
  if (window.scrollY === 0) {
    document.getElementsByClassName("frontpage")[0].classList.remove("pageClosed");
    document.getElementsByClassName("frontpage")[0].classList.add("pageOpen");

    document.getElementsByClassName("certificates")[0].classList.remove("certMoveToFront");
    document.getElementsByClassName("certificates")[0].classList.add("certOpenLeft");

    // Disabling active styles
    document.getElementsByClassName("certificates")[0].classList.remove("certMoveToFront");

    // Making sure the page is scrolled to the right when other page is activated
    window.scrollTo({
      left: window.innerWidth,
      behavior: "auto"
    });

    ScrollLeftAndRemoveCertificates();
  } else {
    window.scrollTo({
      top: 0,
      behavior: "smooth"
    });
    setTimeout(ScrollUpAndActivateFrontpage, 1000);
  }
}

function ScrollLeftAndRemoveCertificates() {
  window.scrollTo({
    left: 0,
    behavior: "smooth"
  });

  if (pageXOffset == 0) {
    document.getElementsByClassName("certificates")[0].classList.add("pageClosed");
    document.getElementsByClassName("certificates")[0].classList.remove("certOpenLeft");
  } else {
    setTimeout(ScrollLeftAndRemoveCertificates, 1000);
  }
}

问题根源

  1. body的overflow-x: hidden限制:直接截断了横向滚动空间,脚本无法触发滚动,因为没有可滚动的区域
  2. 绝对定位脱离布局流:.certOpenLeft用绝对定位脱离了flex容器,导致#horizontalPagesWrapper宽度无法被撑开,没有产生横向滚动的基础
  3. 滚动判断逻辑不可靠:固定1秒的定时器无法适配移动端平滑滚动的延迟,scrollX值可能还没达到目标就执行了判断
  4. 宽度单位不匹配:用100%作为页面宽度,在flex布局下容易出现计算偏差,尤其是移动端视口变化时

修复方案

1. 调整CSS核心样式

body {
  background-color: white;
  font-family: 'Lato';
  margin: 0;
  padding: 0;
  backface-visibility: hidden;
  /* 移除overflow-x: hidden,把横向滚动交给容器处理 */
  position: relative;
  -webkit-overflow-scrolling: touch;
  overflow-y: scroll;
}

#horizontalPagesWrapper {
  display: flex;
  flex-direction: row;
  width: fit-content; /* 让容器宽度自动适配子元素总宽度 */
  overflow-x: auto; /* 开启容器横向滚动 */
  -webkit-overflow-scrolling: touch; /* 移动端平滑滚动支持 */
}

.pageOpen {
  width: 100vw; /* 用视口宽度确保页面占满屏幕 */
  min-height: 100vh; /* 占满视口高度,避免滚动区域异常 */
}

.pageClosed {
  width: 0; /* 用width替代max-width,确保完全隐藏 */
  min-height: 0;
  overflow: hidden;
  flex-shrink: 0; /* 防止flex容器压缩元素 */
}

.certOpenLeft {
  /* 移除绝对定位,让页面留在flex容器内 */
  width: 100vw;
  min-height: 100vh;
  flex-shrink: 0; /* 固定宽度,不被压缩 */
}

.certMoveToFront {
  width: 100vw;
  min-height: 100vh;
}

2. 优化JS逻辑

// 提前缓存DOM元素,减少重复查询
const frontpage = document.querySelector('.frontpage');
const certificates = document.querySelector('.certificates');
const wrapper = document.getElementById('horizontalPagesWrapper');

function ScrollUpAndActivateCertificates() {
  if (window.scrollY === 0) {
    certificates.classList.remove('pageClosed');
    certificates.classList.add('certOpenLeft');
    
    // 等待DOM更新后执行滚动,用scrollIntoView更可靠
    requestAnimationFrame(() => {
      certificates.scrollIntoView({ behavior: 'smooth', inline: 'start' });
      // 根据平滑滚动时长设置延迟,确保滚动完成后再切换状态
      setTimeout(() => {
        frontpage.classList.remove('pageOpen');
        frontpage.classList.add('pageClosed');
        certificates.classList.add('certMoveToFront');
        certificates.classList.remove('certOpenLeft');
      }, 1000);
    });
  } else {
    window.scrollTo({ top: 0, behavior: 'smooth' });
    setTimeout(ScrollUpAndActivateCertificates, 1000);
  }
}

function ScrollUpAndActivateFrontpage() {
  if (window.scrollY === 0) {
    frontpage.classList.remove('pageClosed');
    frontpage.classList.add('pageOpen');
    
    certificates.classList.remove('certMoveToFront');
    certificates.classList.add('certOpenLeft');
    
    requestAnimationFrame(() => {
      frontpage.scrollIntoView({ behavior: 'smooth', inline: 'start' });
      setTimeout(() => {
        certificates.classList.add('pageClosed');
        certificates.classList.remove('certOpenLeft');
      }, 1000);
    });
  } else {
    window.scrollTo({ top: 0, behavior: 'smooth' });
    setTimeout(ScrollUpAndActivateFrontpage, 1000);
  }
}

关键优化点

  • 把横向滚动的控制权交给#horizontalPagesWrapper,避免和body的垂直滚动冲突
  • 用100vw代替100%,确保页面宽度始终匹配视口,解决移动端布局偏差
  • 使用scrollIntoView替代手动设置scrollX,自动适配容器滚动位置,更适配移动端
  • 缓存DOM元素,减少重复查询提升性能
  • 调整页面高度为100vh,确保滚动区域完整

内容的提问来源于stack exchange,提问作者mico vihavainen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 20:57:16