纵向滚动与横向滚动
1 原理
先来看一下浏览器的滚动原理:当页面内容的高度超过视口高度的时候,会出现纵向滚动条;当页面内容的宽度超过视口宽度的时候,会出现横向滚动条。也就是当我们的视口展示不下内容的时候,会通过滚动条的方式让用户滚动屏幕看到剩余的内容。
同理当子元素内容高度或宽度超过父元素后,父元素也会产生滚动条。
所以我们需要准备以下 DOM 结构:
DOM 结构分为三层,wrapper => content => item
- wrapper: 可视区 or 滚动区,一般是有固定高度的容器
- content: 内容区域,他的高度是由子元素 item 撑开的
- item: 子元素,一般有多个
tips: 横向滚动 DOM 结构也是如此
2 非 flex 布局
2.1 纵向滚动
除了需要准备三层 DOM 结构外,还需要注意以下 2 点:
- 父层的 wrapper 固定高度
- 父层的 wrapper 设置
overflow: auto;
<div class="vertical_wrapper">
<div class="content">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
<div class="item">10</div>
</div>
</div>
.vertical_wrapper {
width: 300px;
height: 300px; /* wrapper中高固定 */
border: 1px solid #ccc;
box-sizing: border-box;
overflow: auto; /* 超出部分显示滚动条 */
}
.vertical_wrapper .item {
height: 50px;
}
2.2 横向滚动
除了需要准备三层 DOM 结构外,还需要注意以下 4 点:
- 父层的 wrapper 固定宽度
- 父层的 wrapper 设置
overflow: auto;
- 父层的 wrapper 设置
white-space: nowrap;
,否则会换行 - content 与 item 设置
display: inline-block;
<div class="horizontal_wrapper">
<div class="content">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
<div class="item">10</div>
</div>
</div>
.horizontal_wrapper {
width: 300px;
border: 1px solid #ccc;
box-sizing: border-box;
overflow: auto; /* 超出部分显示滚动条 */
white-space: nowrap; /* 不换行 */
}
.horizontal_wrapper .content,
.horizontal_wrapper .content .item {
display: inline-block; /* content与item需要设置为行内块元素 */
}
.horizontal_wrapper .content .item {
width: 50px;
height: 50px;
background-color: red;
}
2.3 横向滚动中的问题
以上布局由于 item 设置了display: inline-block;
,而且 item 元素之间是用回车进行换行,所以在浏览器渲染的时候回车符会解析成一个空格,如下图所示
可以使用以下 3 种解决方案之一解决:
-
item 元素之间不使用回车进行换行
<div class="horizontal_wrapper"> <div class="content"> <div class="item">1</div><div class="item">2</div><div class="item">3</div><div class="item">4</div><div class="item">5</div><div class="item">6</div><div class="item">7</div><div class="item">8</div><div class="item">9</div><div class="item">10</div> </div> </div>
-
content 元素的
font-size
设置为 0.horizontal_wrapper { width: 300px; border: 1px solid #ccc; box-sizing: border-box; overflow: auto; /* 超出部分显示滚动条 */ white-space: nowrap; /* 不换行 */ } .horizontal_wrapper .content { font-size: 0; /* 为了消除空格 */ } .horizontal_wrapper .content, .horizontal_wrapper .content .item { display: inline-block; /* content与item需要设置为行内块元素 */ } .horizontal_wrapper .content .item { width: 50px; height: 50px; background-color: red; font-size: 14px; /* 为了不让item继承父元素的font-size,需要单独设置字号 */ }
-
使用 flex 布局,下文会讲到
3 flex 布局
3.1 纵向滚动
不需要使用 flex 布局
3.2 横向滚动
除了需要准备三层 DOM 结构外,还需要注意以下 2 点:
- 父层的 wrapper 固定宽度
- 父层的 wrapper 设置
overflow: auto;
- content 设置
display: flex;
- item 设置
flex-shrink: 0;
,不压缩子元素
<div class="horizontal_wrapper">
<div class="content">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
<div class="item">10</div>
</div>
</div>
.horizontal_wrapper {
width: 300px;
border: 1px solid #ccc;
box-sizing: border-box;
overflow: auto; /* 超出部分显示滚动条 */
}
.horizontal_wrapper .content {
display: flex;
}
.horizontal_wrapper .content .item {
width: 50px;
height: 50px;
background-color: red;
flex-shrink: 0; /* 不压缩子元素 */
}
3.3 横向滚动中的问题
使用 flex 布局后,content 元素的宽度变为了 wrapper 的宽度,目前笔者暂未想到解决方案
4 完美解决方案
上述在不使用 flex 和使用 flex 布局后都会有小问题,解决方案是使用betterScroll 插件。该插件基于原生 JS 实现的,不依赖任何框架。完美运用于 Vue、React 等 MVVM 框架。
该插件的滚动原理与 1 描述一致,DOM 结构和 CSS 样式与 2.1、2.2 描述一致。
具体使用请参照betterScroll 文档
.io/docs/zh-CN/)。该插件基于原生 JS 实现的,不依赖任何框架。完美运用于 Vue、React 等 MVVM 框架。
该插件的滚动原理与 1 描述一致,DOM 结构和 CSS 样式与 2.1、2.2 描述一致。
具体使用请参照betterScroll 文档