现代 CSS

图解CSS:CSS溢出(Part2)

特别声明:如果您喜欢小站的内容,可以点击申请会员进行全站阅读。如果您对付费阅读有任何建议或想法,欢迎发送邮件至: airenliao@gmail.com!或添加QQ:874472854(^_^)

第一部分主要和大家聊了CSS溢出的概念和理论相关的,在第一部分主要以实例,问题排查和常见溢出问题三个部分展开。

溢出实例

在构建 Web 页面或应用的时候,总是避免不了给容器设置一个具有约束性的尺寸,有的时候,因为容器没有足够的空间来容纳内容,从而造成内容溢出容器,严重的会打破 Web 布局,干扰页面的美观。为了让这些现象能不干扰 Web 布局,所以需要CSS溢出特性。除此之外,我们在构建一些Web组件和实现一些UI效果,也需要依赖CSS溢出特性。那么接下来,我们来看一些溢出相关的案例。

水平滚动的容器

在 Web 布局中,水平滚动是一种常见的布局做法,因为它有助于减少屏幕较小的设备的垂直空间。比如在 H5 的页面中,像Swiper这样的幻灯片组件或类似这种水平滑动的UI效果随处可见:

比如上面视频中的卡片和导航菜单都具有水平滚动的效果,我们可以通过水平剪切内容并允许其滚动来创建一个水平滚动容器。在 CSS 中要实现该效果,需要具备两个条件:

  • 滚动容器的宽度 width(或 inline-size)具有一定的约束,它有一个固定的值,哪怕 width的值是根据其父元素(或祖先元素)的width计算得到的
  • 滚动容器显式设置 overflow-x 的值为 scrollauto,建议将其设置为 auto,因为 scroll 值会让容器的滚动条一直呈现

我们来看一个卡片在水平滚动容器中滑动的效果:

<!-- HTML -->
<ul class="cards">
    <li class="card"></li>
    <!-- ... -->
    <li class="card"></li>
</ul>

/* CSS */
.cards {
    overflow-x: auto;

    /* 滚动捕捉*/
    scroll-snap-type: x mandatory;

    /* 不显示滚动条 */
    scrollbar-width: 0;
}

.card {
    scroll-snap-align: center;
}

/* Flexbox Layout*/
.cards--flex {
    display: flex;
    gap: calc(var(--gutter) / 2);
    flex-wrap: nowrap;
}

.cards--flex .card {
    flex: 1 0 calc(50% - var(--gutter) * 2);
}

/* Grid Layout */
.cards--grid {
    display: grid;
    gap: calc(var(--gutter) / 2);
    grid-template-columns: 10px;
    grid-auto-flow: column;
    grid-auto-columns: calc(50% - var(--gutter) * 2);
    align-items: stretch;
}

最终效果如下:

就这个示例而言,滚动容器.cards的宽度和父容器的宽度相同(width: 100%),不管我们使用的是Flexbox还是Grid布局,都让所有卡片.card保持在一行排列,所有卡片宽度加上卡片之间的间距就大于容器宽度,卡片就会溢出容器:

再来看另一个有关于水平滚动容器的示例,即水平滚动导航栏:

我们可以使用同样的方法来实现,具体代码请查看下面的示例:

水平滚动容器不管是Flexbox还是Grid布局,如果显式设置了justify-content的值为flex-endend,水平滚动将会失效。具体的解决方案将放到后面来阐述!

模态框(弹窗)

模态框(Modal)是Web中很常见的一个 Web 组件,大多数情况之下,模态框具有一个固定的宽高尺寸,至少会有一个最大高度(max-height)的限制避免模态框顶部和底部被裁剪(模态框一般会使用绝对定位,让其位于页面的中间位置)。对于一个具有尺寸约束的模态框而言,当模态框内容太长时,我们可以很容易地使该区域可滚动:

我们可以使用 HTML5 的 <dialog> 元素来构建模态框:

<!-- HTML -->
<dialog>
    <div class="dialog__content">
        <div class="dialog__heading"></div>
        <div class="dialog__body"></div>
        <div class="dialog__footer"></div>
    </div>
</dialog>

.dialog__content 设置一个max-widthmax-height值,用来约束模态框的尺寸。另外,使用CSS Grid来布局,让.dialog__body 的高度为 1fr

.dialog__content {
    display: grid;
    grid-template-columns: auto;
    grid-template-rows: auto 1fr auto;
    grid-template-areas: 
        "heading"
        "body"
        "footer";
    max-width: 50vw;
    max-height: 50vh;
}

.dialog__heading {
    grid-area: heading;
}

.dialog__body {
    grid-area: body;
}

.dialog__footer {
    grid-area: footer;
}

在这种情况之下,我们并不知道放置在 .dialog__body 中的内容是什么,所占空间是多少,有可能整个容器无法容纳所放置的内容。在这种情况之下,我们需要在.dialog__body 容器上设置overflow-y的值为auto。这样一来,当内容高度超过容器高度时,就会出现滚动条:

如果.dialog__body出现滚动条时,就会产生多个容器出现滚动条(比如.dialog__bodybody两个容器元素),即 z轴不同层的滚动容器。此时,模态框向下滚动到底部时,如果继续往下滚动则会引起弹框下面的内容,比如body元素会继续滚动。这也是滚动链默认的表现:

为了避免滚动扩散到其他滚动容器,我们可以在顶部的滚动容器中(即 .dialog__body)设置overscroll-behavior属性设置为 contain。这样一来,模态框中的滚动容器滚动到底部之后也不会影响模态框底部的滚动容器(body)的滚动:

关键代码:

.dialog__body {
    overflow-y: auto; 
    overscroll-behavior: contain;
    min-height: 0
}

最终效果如下:

注意,模态框的布局了可以使用 CSS Flexbox 来布局,.dialog__body可以显式设置flex:1,让其占用Flexbox容器的剩余空间,只不过这种布局方案会在个别系统中(比如iOS系统)中让overflow-y失效。这是触发了Flex项目的边缘情况所产生的Bug,具体解决方案稍后在“溢出常见问题与排查”一节中介绍。

带有圆角的卡片

带圆角的卡片是常见的一种UI设计,不少开发者还原带有圆角的卡片UI时,更倾向于给卡片容器的子元素设置border-radius,比如:

<!-- HTML -->
<div class="card">
    <img class="card__thumb" src="" alt="" />
    <div class="card__content"></div>
</div>

/* CSS */
.card__thumb {
    border-radius: 10px 10px 0 0;
}

.card__content {
    border-radius: 0 0 10px 10px;
}

但我们面对的卡片UI可能是多样化的,比如:

面对上图这样的UI设计,如果按上面代码来处理卡片圆角效果的话,工作量相对而言是更多的。在这种情况之下,更好的处理方式是在卡片容器.card设置border-radius,并且在该容器上显式设置 overflow 的值为 hidden。这样一来,不管卡片是如何布局,圆角的设置相对而言都会更灵活一些:

.card {
    border-radius: 10px;
    overflow: hidden;
}

在容器显式设置 overflow: hidden 可以避免卡片子元素与容器上下顶角处溢出的内容在视觉上看上去被裁切掉:

文本截断

前面提到过,文本截断主要有“单行文本截断” 和 “多行文本截断” 两种类型。需要注意的是,这里所说的文本截断并不是单纯的将溢出的文本裁剪掉,而是截剪后的文本末尾添加省略号指示符(...),以表示还有更多的文本内容(如上图所示)。

在 CSS 中实现单行文本截断和多行文本截断技术手段各有差异:

单行文本截断常用 text-overflow: ellipsis 结合 white-space: nowrapoverflow: hidden

.single-line-truncation {
    text-overflow: ellipsis;
    overflow: hidden;
    white-space: nowrap;
}

多行文本截断常用 line-clamp,但该属性必须使用 display: -webkit-box-webkit-box-orient: vertical 才能生效:

.multiple-line-truncation {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2; /* 截断的最大行数 */
    overflow: hidden;
}

如果将line-clamp的值设置为 1 的时候,那么它的效果有点类似于 text-overflow: ellipsiswhite-space: nowrapoverflow: hidden 效果等同。不过,使用 line-clamp 来实现文本截断效果时,不能和 white-space: nowrap 一起使用,而且不能显式在容器上设置 padding-top(或padding-bottom),也不能显式设置具体的height值。

剩余80%内容付费后可查看
返回顶部