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

修复移动端(触摸设备)看板垂直滚动异常问题

问题描述

我有一个看板,响应式模式下每次仅显示一列。已在父容器添加列吸附属性scroll-snap-type: x mandatory;,并给每个子元素设置scroll-snap-align: center;。水平滚动运行正常,但在真实移动端尝试垂直滚动单列内容时,内容会出现错乱跳转。

  • 问题仅出现在真实移动端,桌面端无此现象
  • 期望实现类似原生Trello应用的效果
  • 寻求纯CSS解决方案,或CSS+JS组合方案

相关代码

CSS

.app-board-card {
    position: relative;
    box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px;;
    border-radius: 5px;
    padding: 10px 10px 0;
    cursor: pointer;
    background: #fff;
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-gap: 7px;
    visibility: visible;
    transform: translateZ(0);
    margin-top: 7px;
}

.app-board-columns {
    list-style-type: none;
    padding: 0;
    display: flex;
    flex-flow: row;
    align-items: flex-start;
    margin: 0;
}
@media only screen and (max-width: 767px) .app-board-columns {
   display: grid;
        grid-auto-flow: column;
        gap: 1rem;
        overflow-y: auto;
        overscroll-behavior-x: contain;
        scroll-snap-type: x mandatory;
        -webkit-overflow-scrolling: touch;
}

.app-board-column {
    background: #fff;
    border-radius: 5px;
    padding: 5px 0 0;
    width: 330px;
    max-width: 330px;
    margin: 0 10px 5px 0;
    position: relative;
    transform: translateZ(0);
    flex: 1 0 auto;
    height: 100%;
    display: flex;
    flex-flow: column;
}
@media only screen and (max-width: 767px) .app-board-column {
   width: 260px;
   max-width: 260px;
   scroll-snap-align: center;
}

.app-board-column-inner-scroll {
    display: flex;
    flex-flow: column;
    overflow-x: hidden;
    overflow-y: scroll; 
}

.app-board-column-inner {
    display: flex;
    flex-flow: column;
    padding: 20px 0 60px 15px;
    min-height: 150px;
    flex: 1 0 auto;
}
@media only screen and (max-width: 767px) .app-board-column-inner {
   padding-right: 15px;
}

.app-board-column.col1 .app-board-card {
   background-color: #fdc6c4;
}
.app-board-column.col2 .app-board-card {
   background-color: #d8edff;
}
.app-board-column.col3 .app-board-card {
   background-color: #eee;
}

/* For demo only */

.app-board-column:after {
  color: gray;
  content: ".app-board-column";
}
.app-board-column {
  border: thin dashed blue;
  min-height: 10rem;
}
.app-board-card {
   min-height: 70px;
}

HTML

<ul class="app-board-columns">
   <li class="app-board-column col1">
        <div class="app-board-column-inner-scroll">
            <div class="app-board-column-inner">
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
            </div>
        </div>
   </li>
    <li class="app-board-column col2">
        <div class="app-board-column-inner-scroll">
            <div class="app-board-column-inner">
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                </div>
            </div>
        </div>
    </li>
    <li class="app-board-column col3">
        <div class="app-board-column-inner-scroll">
            <div class="app-board-column-inner">
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                <article class="app-board-card"></article>
                </div>
            </div>
        </div>
    </li>
</ul>
解决方案

1. 修复CSS语法错误

原代码中的媒体查询写法存在语法错误(缺少包裹样式的大括号),会导致移动端样式无法正确解析,先修正:

/* 修正后的媒体查询结构 */
@media only screen and (max-width: 767px) {
  .app-board-columns {
    display: grid;
    grid-auto-flow: column;
    gap: 1rem;
    overflow-y: hidden; /* 后续会解释修改这个属性的原因 */
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
  }
  .app-board-column {
    width: 260px;
    max-width: 260px;
    scroll-snap-align: center;
  }
  .app-board-column-inner {
    padding-right: 15px;
  }
}

2. 解决滚动冲突核心问题

移动端触摸滚动冲突是错乱跳转的根源:父容器(.app-board-columns)原本设置了overflow-y: auto,加上-webkit-overflow-scrolling: touch的触摸滚动优化,会导致垂直滚动时触摸事件被父容器干扰,结合scroll-snap的强制吸附就会出现跳转。

关键CSS调整:

/* 父容器:仅保留水平滚动能力 */
@media only screen and (max-width: 767px) {
  .app-board-columns {
    overflow-y: hidden; /* 移除垂直滚动,避免干扰子容器 */
    overscroll-behavior-y: contain; /* 隔离垂直滚动事件,防止冒泡到页面 */
  }
}

/* 子列滚动容器:确保高度正确并添加触摸优化 */
.app-board-column {
  height: 100vh; /* 根据页面布局设置高度,比如减去头部高度可以用calc(100vh - 60px) */
}
.app-board-column-inner-scroll {
  flex: 1; /* 占满列的剩余空间,确保滚动容器高度正确 */
  -webkit-overflow-scrolling: touch; /* 给子容器添加触摸滚动流畅度优化 */
}

3. 可选JS增强方案(若CSS方案仍有轻微冲突)

如果移动端仍存在偶尔的滚动干扰,可以添加少量JS来临时禁用父容器的scroll-snap:

// 给每个子滚动容器绑定触摸事件
document.querySelectorAll('.app-board-column-inner-scroll').forEach(container => {
  container.addEventListener('touchstart', () => {
    // 触摸子容器时,临时关闭父容器的scroll-snap
    document.querySelector('.app-board-columns').style.scrollSnapType = 'none';
  });
  container.addEventListener('touchend', () => {
    // 触摸结束后恢复scroll-snap
    setTimeout(() => {
      document.querySelector('.app-board-columns').style.scrollSnapType = 'x mandatory';
    }, 100);
  });
});

效果说明

修改后,移动端垂直滚动单列内容时,触摸事件会被子滚动容器完全捕获,父容器的水平scroll-snap不会干扰垂直滚动,同时保持水平切换列时的吸附效果,和原生Trello应用的体验一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 18:34:54