toxic-johann / toxic-johann.github.io

my blog
6 stars 0 forks source link

CSS 文字截断技巧 #46

Open toxic-johann opened 6 years ago

toxic-johann commented 6 years ago

在业务中我们经常遇到文字过多无法完整展示的情况。一般情况下,我们会采用截断的方式。这种方式有如下几个需求。

  1. 文字未超出指定长度或高度时,不应该有影响
  2. 文字超出指定长度或高度时,使用省略号代替
  3. 文字超出指定长度或高度时,省略号可进行交互(可选)

假如我们希望使用 css 实现以上功能的话,有什么方法吗?

下面我将举例几个常见的需求场景来阐述相关技巧。

使用特定 CSS 属性对文本溢出进行截断

CSS 自身就已经提供了文本溢出截断的相关属性。我们可以利用它们完成我们的需求。

单行文本溢出截断

单行文本溢出截断相信大家都是顺手拈来了。关键点是使用:text-overflow: ellipsis;。以及在某些元素下还要注意换行问题。

示例代码如下:

div {
  width: 100px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

点击此处观看 demo

单行最多展示九个字

这是设计师给我某个同学提的需求,关键点如下:

  1. 若字数小于等于 9,则展示九个字
  2. 若字数大于 9,则展示九个字加省略号。

这个需求和浏览器的实现不太一样。因为浏览器会将最后一个字符占据的空位用于放置省略号。

假设我们每个字符大小都为 15 px。

如果空位长度为 150px(能放下十个字符),则内容是十个字符的时候,会完全显示十个字符。

如果空位长度为 145px (能放下九个字符),则内容是十个字符的时候,会展示八个字符 + 省略号。

为了实现我们的需求,我们可以利用伪元素,在后面添加多一个字体。

代码如下:

.demo3 {
  width: 150px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.demo3:after {
  content: '我';
  opacity: 0;
}

点击此处观看 demo

多行文本溢出截断

如果我们要展示的文字是多行文字,并且不能超出固定行的话。我们就需要一些特别的属性进行辅助。

这个属性就是 -webkit-line-clamp

在此样式中我们展示如何控制文字不超过四行。

示例代码如下:

p {
  width: 400px;
  text-overflow: ellipsis; 
  display: -webkit-box;
  overflow: hidden;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  text-align: justify;
}

点击此处观看 demo

要注意概属性需要与以下属性配合:

它需要和 display、-webkit-box-orient 和 overflow 结合使用:

因为这个属性是浏览器提供的原生功能,所以展示的效果会比较好。

但是这个方案的缺点也比较明显:

1. 支持的浏览器有限制

因为该属性是 webkit 的私有属性,所以只有 webkit 内核的浏览器才支持这个属性,像 Firefox,IE 等浏览器就无法支持该特性了。

2. 不能很方便的开发“点击展开”的交互

因为是否超过范围完全由浏览器判断,并没有向我们报告。所以我们很难去判断是否有超出。

因此这种模式多建议使用在移动端上,而且无需要判断展开的界面内。

使用 float 模拟实现超出截断

之前参阅前端客栈的《纯 CSS 实现多行文字截断》了解到的一种思想。就是利用 float 属性进行模拟。

大家有兴趣可以阅读以下原文。

关键点在下图:

img

第三个模块会因为文本的长度而处于不同位置,这就是我们解题的关键。

我们也根据这个思想举几个例子进行分析:

使用 float 实现单行文本只展示九个字符

我们利用 float ,将省略号和文本放在一个容器内。并且利用 translate 将省略号上移。

当文本未超出规定长度时,省略号在容器外,被遮盖无法看见。

当文本超出规定长度时,省略号被挤向下层,因为偏移,所以出现在目标视野内。

我们所需效果如下:

假设我们的 HTML 代码如下:

<section>
  <div>一二三四五六七八九十</div>
  <div class="ellipsis">…</div>
</section>

CSS 代码如下:

section {
  font-size: 15px;
  width: 150px;
  overflow: hidden;
}

div {
  float: left;
}

.ellipsis {
  transform: translate(900%, -100%);
  background-color: white;
}

点击此处查看 demo

这样编写的好处是, 省略号是一个 HTML 节点。我们可以监听该节点确认用户是否要展开图片。

这样子有利于我们实现相关交互。

使用 float 实现多行文本截断

多行文本的实现原理也是如上。但是有一个关键点是我们怎么让高度也是可控的。

以上图为例进行分析,我们知道父容器的高度设为 auto 的时候,其高度等于 1 的高度。但我们需要的高度是 2 的高度。因为我们采取了这种模式,所以高度会受到影响。

比较理想的方法,是将这个区域仅仅作展示用,而实际高度由另外一个元素进行控制。我们直接观看代码。

最终效果如下:

HTML 代码如下:

<section>
  <div class="display-area">
    <div class="placeholder"></div>
    <p class="content">President Donald Trump has warned Syria's government the US is "locked and loaded" to strike again if it carries out chemical attacks.</p>
    <div class="ellipsis">…</div>
  </div>
  <p class="content-placeholder">President Donald Trump has warned Syria's government the US is "locked and loaded" to strike again if it carries out chemical attacks.</p>
</section>

CSS 代码如下:

section {
  font-size: 15px;
  overflow: hidden;
  width: 500px;
  position: relative;
  border: 1px solid black;
  margin: 10px;
}

.content-placeholder {
  opacity: 0;
  z-index: -1;
  pointer-events: none;
  max-height: 120px;
}

.display-area {
    position: absolute;
    height: 100%;
}

.display-area .placeholder {
    float: left;
    width: 1.5em;
    content: '';
    height: 100%;
    max-height: 120px;
}

.display-area .content {
    float: right;
    margin-left: -1.5em;
    width: 100%;
}

.display-area .ellipsis {
    float: right;
    height: 25px;
    width: 1.5em;
    position: relative;
    left: 100%;
    transform: translate(-100%, -100%);
    text-align: right;
    background: linear-gradient(90deg, rgba(255, 255, 255, 0), #fff 20%, #fff);
}

点击此处查看 demo

同样的,因为省略号也是 DOM 节点,所以我们可以直接监听他的点击来进行交互。