多行文本溢出的一个解决方案
有下面这样的一个 HTML 元素:
html
<div class="content">
插件一般是可独立完成某个或一系列功能的模块。
一个插件是否引入一定不会影响到系统本身的正常运行(除非它和另一个插件存在依赖关系)。
插件在何时被引入,何时被调用都是由系统来调度的。
一个系统可以存在多个插件,这些插件可以通过系统预定的方式进行组合。
</div>
我们给他加上一点样式:
css
.content {
width: 300px;
background-color: lightblue;
}
页面上可以看到 400px 宽度的亮蓝色矩形:
通常情况下,我们都不会一下子就展示这么多数据,一般情况都不会超过 3 行(我乱说的)。
而单行文本溢出已经有了很成熟的解决方案了。
css
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
多行文本溢出
这里就不讨论使用 JavaScript 对内容截取的方案了,我们看看纯 css 方案如何解决多行文本溢出。
-webkit-line-clamp
-webkit-line-clamp
可以把块容器中的内容限制为指定的行数。
它只有在 display
属性被设置为 -webkit-box
或者 -webkit-inline-box
并且 box-orient
属性设置成 vertical
时才有效果。
这个属性最初是在 Webkit 中实现的,但存在一些问题。由于需要支持旧版本的浏览器, CSS Overflow Module Level 4 中被标准化。
css
.webkit-line-clamp {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
看看效果:
可以说是非常完美了,但这个方案需要考虑一下兼容性。
另一种方案
在写样式之前,需要把 DOM 结构做一下修改,给内容多加一层嵌套:
html
<div class="content float-ellipsis">
<div class="inner">
插件一般是可独立完成某个或一系列功能的模块。
一个插件是否引入一定不会影响到系统本身的正常运行(除非它和另一个插件存在依赖关系)。
插件在何时被引入,何时被调用都是由系统来调度的。
一个系统可以存在多个插件,这些插件可以通过系统预定的方式进行组合。
</div>
</div>
然后加入以下的样式片段:
css
.float-ellipsis {
height: 60px;
line-height: 20px;
overflow: hidden;
}
.float-ellipsis::before {
content: '';
float: left;
width: 40px;
height: 60px;
}
.float-ellipsis::after {
content: '...';
float: right;
position: relative;
left: 100%;
width: 40px;
height: 20px;
text-align: right;
background-image: linear-gradient(to right, transparent, lightblue);
transform: translate(-100%, -100%);
}
.float-ellipsis .inner {
float: right;
width: 100%;
margin-left: -40px;
word-break: break-all;
}
效果与 -webkit-line-clamp
差不多,甚至省略号还多了个渐变的效果:
从 CSS 上面,似乎我们只需要给容器加个 ::after
伪类,直接放置 ...
省略号到右下角也可以实现相应的效果。
html
<div class="content only-after">
<div class="inner">
插件一般是可独立完成某个或一系列功能的模块。
一个插件是否引入一定不会影响到系统本身的正常运行(除非它和另一个插件存在依赖关系)。
插件在何时被引入,何时被调用都是由系统来调度的。
一个系统可以存在多个插件,这些插件可以通过系统预定的方式进行组合。
</div>
</div>
css
.only-after {
position: relative;
margin: 20px auto;
height: 60px;
line-height: 20px;
overflow: hidden;
}
.only-after::after {
content: '...';
position: absolute;
right: 0;
bottom: 0;
width: 40px;
height: 20px;
text-align: right;
background-image: linear-gradient(to right, transparent, lightblue);
}
在这个案例看来,两者好像没什么区别。但是看一下另一组案例:
html
<div class="content float-ellipsis">
<div class="inner">
插件一般是可独立完成某个或一系列功能的模块。
</div>
</div>
<div class="content only-after">
<div class="inner">
插件一般是可独立完成某个或一系列功能的模块。
</div>
</div>
显然,仅用 ::after
在内容没有发生溢出时,界面上依然会展示 ...
,这与我们预期是不符合的(仅在在文本溢出时才显示 ...
)。所以我们加上了 ::before
元素,再与 .inner
、::after
元素结合,采用浮动布局的形式来实现这个多行文本溢出的处理。
布局的奇妙现象
float 布局
先看一下一个很普通的浮动布局(float)的例子:
html
<div class="container">
<div class="box box-1">1</div>
<div class="box box-2">2</div>
<div class="box box-3">3</div>
</div>
如上面的 DOM 结构,如果不加任何 CSS 的话,三个 box 应该是从上往下排列放置。但如果我们想排在同一行,则需要一点 CSS 来控制了:
css
.container {
width: 400px;
height: 100px;
border: 1px solid #333;
}
.box {
width: 100px;
height: 100px;
text-align: center;
line-height: 100px;
}
.box-1 {
float: left;
background-color: green;
}
.box-2 {
float: right;
width: 200px;
background-color: skyblue;
opacity: .5;
}
.box-3 {
float: right;
background-color: orange;
}
这是一个比较常见的浮动布局。需要注意的时,box-2
和 box-3
都是右浮动 float: right;
的,在浮动布局中,放置元素会根据他的 DOM 所在的顺序依次放置,所以我们能看到的是 1 3 2
这样的盒子方式。
在设置宽度的时候,也是刚刚好给到了容器可以容纳的宽度,所以我们可以看到所有盒子都会并排在同一行。如果我们给 box-1
或 box-2
增加了宽度的话,box-3
就会被挤到下一行重新放置:
diff
.box-2 {
float: right;
- width: 200px;
+ width: 250px;
background-color: skyblue;
opacity: .5;
}
在上面的代码中,我们给 box-2
增加了 50px 的宽度,当前行剩余宽度就剩下:400 - 100 - 250 = 50
像素的宽度,而 box-3
宽度为 100px,当前行空间不足,所以会被放置到下一行。
结合一下 margin
我们先看一下 margin
带来的效果:
从上面的图示可以看出:当 margin
设置成负值时,其它元素是可以放置到该元素原本占据的空间上的(蓝色元素有一部分与橙色元素重叠了)。
基于这个原理,我们把 CSS 作一些修改:
diff
.box-2 {
float: right;
width: 250px;
+ margin-left: -50px;
background-color: skyblue;
opacity: .5;
}
可以看到,原本在另外一行的 box-3
它回到了第一行,并且部分内容与 box-2
重叠了。如果我们把 margin-left
设置成 box-3
的宽度(100px)时,那么 box-3
会完全重叠在 box-2
的空间中:
接下来我们把 box-2
的宽度设置成与容器宽度一致。
css
.box-2 {
float: right;
width: 100%;
margin-left: -100px;
background-color: skyblue;
opacity: .5;
}
貌似与我们想象中的不太一样。width: 100%;
时,box-2
应该独自占据一行才对。就像下面这种子:
但偏偏 box-1
与 box-2
在同一行是正确的,因为 margin-left: -100px
使得 box-2
在实际布局中占据的空间宽度应该为 100% - 100px = 300px
。
上面讨论的布局与位置都是基于等高度的情况,下面将高度进行一下调整:
diff
.box-2 {
float: right;
width: 100%;
+ height: 150px;
margin-left: -100px;
background-color: skyblue;
opacity: .5;
}
可能你会觉得它应该是这样子的:
但实际上,box-3
会被放置在 box-1
的正下方,并与 box-2
有一半区域发生了重叠:
为什么会这样子呢,我们只需要改动一下 box-1
的高度,就能更清晰地看清楚了:
css
.box-1 {
float: left;
height: 40px;
line-height: 40px;
background-color: green;
}
为什么会这样子呢?事实上,虽然 box-2
的宽度是 100%(400px),但是它在布局上占据的宽度空间却只有 width - marginLeft
也就是 300px。
而 box-1
的宽度为 100px,高度为 50px,只占据了 box-2
的 margin
空间中的一小部分,把 box-1
放置完毕后,剩余的部分空间足够把 box-3
放置进去(甚至还有剩余)。所以就会呈现出图上面的效果。
结合 position 和 translate
在开始之前,我们先把样式恢复一下:
css
.box-1 {
float: left;
background-color: green;
}
.box-2 {
float: right;
width: 100%;
height: 150px;
margin-left: -100px;
background-color: skyblue;
opacity: .5;
}
.box-3 {
float: right;
background-color: orange;
}
我们现在想要做的是,把原本位于左下角的 box-3
挪到右上角:
diff
.box-3 {
float: right;
+ position: relative;
+ left: 100%;
background-color: orange;
+ transform: translate(-100%, -100%);
}
为什么不直接使用 translate
呢,因为在案例中我们的宽度位置是固定的,但实际上我们开发时宽度可能是一个未知的值,所以使用了 left: 100%
,来让 box-3
定位到最后侧(它是基于容器宽度的百分比值),然后再使用 translate(-100%, -100%)
使其放置到对应的位置(相对于自身的百分比值)。
多行文本方案所使用到的 css 相关知识已经完毕,接下来我们看看动图效果来理解一下为什么要这样子做:
当 box-2
的高度不超过容器高度时,box-3
会在容器之外;而当 box-2
的高度超出容器的高度时,box-3
则在容器的右侧。
以上,就是本文的所有内容了。