美文网首页技术程序员让前端飞
多行文本省略近完美方案

多行文本省略近完美方案

作者: Yieiy | 来源:发表于2018-02-05 16:22 被阅读190次

    原创文章,转载请注明出处。

    背景

    在项目需求开发时,由于文本内容长度的不确定性,经常会碰到文本内容超出的问题,通常这种情况,UI/UE都会要求我们将超出部分用省略号(...)来显示,这种做法基本上已经是一个业界共识了。然而,这么一个理所当然的效果,在前端却一直是一个难题。

    希望

    对于文本省略,由于CSS3的出现,出现了单行文本省略和多行文本省略的划分了(看完Can I Use,你会觉得这句话不合理,可,那又如何,哈哈(ಡωಡ)hiahiahia)。之所以这么说,那是因为CSS3定义了一个文本省略方案:

    text-overflow: ellipsis | clip;

    这在前端界可谓是相当腻害的进步,前端大拿、二拿、小拿们纷纷举起键盘YY:前端的巨轮势不可挡!

    永远在那绿油油的田野上

    不得不说,text-overflow的出现 ,确实让前端为之动容,多么美好,哪里还需要什么JS算字数啊。然而,真正用上text-overflow才发现,理想很美好,现实很骨感,这哥们只管单行文本溢出的省略,对于多行文本,却视而不见。

    希望,依旧在那绿油油的田野上……

    于是,文本省略从没有变成了两种:单行文本省略和多行文本省略。

    单行文本省略

    有,总比没有好

    这是一条永恒的道理,好歹,解决单行文本省略时候,我们可以开开心心用CSS解决,而不用再去搞有的没的JS了。

    用之前先看兼容

    这是一条CSS规则。查Can I Use,这哥们的兼容性简直震憾人心啊:

    text-overflow兼容性
    牛不牛,服不服,连让你们诟病的IE都兼容到不可思议的地步了。(看到这里,我就想问,这哥们怎么到CSS3才被定义哈)

    当然,并不是一条text-overflow: ellipsis;就可以实现文本省略。

    无规矩,不成方圆

    同样,text-overflow的使用,需要满足一些规矩

    1. 块级元素
    2. overflow: 计算值非visible;
    3. 元素宽度:超出时,有一个确切的计算值
    4. white-space: nowrap | pre;

    估计你们会发现,上述的规矩和你们认识的,或者在网上找到的教程不太一样。嗯嗯,的确如此,搜索各类教程基本给的说法是这样的:

    1. 块级元素
    2. overflow: hidden; // 也有说非visible
    3. width: 具体值,非auto
    4. white-space: nowrap;

    其实,基本差不多,解释一下不一样的地方:

    1. overflow确实是非visible,但是,是计算值,并不是设定值。因为CSS里有个叫inherit的关键字。
    2. 元素宽度,而不是width,比如,max-width,以及厉害的flex布局也是可以的。
    3. white-space: pre; 也是可以的,这个属性的设定主要是为了不折行pre也是可以达到目的。

    每个规则我就不一一给例子验证了,如果有问题,欢迎评论留言哈。这里就简单

    举一个粟子
    P.S. 图片来源于网络,但是没找到源头,若你们知道,麻烦告诉我,万分感谢!若是作者看到,觉得不可使用,告知于我,必及时删除之!
    width规则里,我们用flex布局:
    <div style="display: flex;">
      <div id="ellipsis-item" style="flex: 1;
        white-space: nowrap;
        overflow: hidden;
        text-overflow:ellipsis;
      ">这是一串很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长的文字</div>
      <div style="flex: 1;">这是一串很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长的文字</div>
    </div>
    

    把这坨下代码放到你的页面里,如果看不到...,缩小一下窗口,你会看到类似这样的情况:

    flex布局无需width也可以实现...省略
    很显然,代码里,我们并没有为#ellipsis-item元素声明width或是max-width属性,但是,省略依然见效。原因就是,设定flex属性后,元素就会被分配宽度计算值,从而使省略生效。在这个例子,宽度值是50%父元素宽度。

    单行文本省略虽然不是本文的重点,但是也简单回顾一下,并完善些(可能不是完美)text-overflow的使用规则。

    多行文本省略

    也许有人会说,多行文本也有实现方法啊。确实,在Webkit系浏览器里还是有的:

    line-clamp

    OK,再回顾一下。

    Webkit系解决方案

    text-overflow一样,我们先看一下兼容性:

    line-clamp兼容性
    兼容性和text-overflow比起来,简单是惨不能睹,真是清一色Webkit,而且,还是-webkit-line-clamp,连正经的line-clamp都不是。为什么是-webkit-line-clamp呢?因为
    1. W3C没规范
    2. Webkit系自己YY的:所以带-webkit-
    3. 规则上,这哥们要转正,以后也得改哈

    那就说下规则吧:

    1. display: -webkit-box | -webkit-inline-box;
    2. -webkit-box-orient: vertical | block-axis
    3. overflow: 计算值非visible
    4. -webkit-line-clamp: N; // 行数

    可以看出来,-webkit-line-clamp依赖于box布局,而这家伙是最老的Flex布局方案,已经由新版的flex系替代,所以说,这哥们即使进W3C规范,估计也得改改了。

    换句说,现在的写法,很可能未来不能再使用,结局估计是这两种:

    1. W3C收录规范:规则要改,因为box迟早会被废弃
    2. W3C自己定义一个新的属性:整个废弃

    所以,如果不是别无选择,还是不太建议使用。

    好残酷,需求还要做,怎么办。。。JS搞起啊,算字数啊,加...

    哈哈……

    假装很完美的方案

    哇靠,讲了那么多,总算回到标题了。。扯了这么多,感觉成了标题党,会不会被打哈。。
    那些用JS算宽度算字数的方案我就不说了哈,你要是喜欢就自行到网上搜索呗。由于本人一贯的原则是:

    能用CSS的,就不要用JS

    所以,接下来这个方案,也必然是纯CSS方案了。另外,给你们点继续看下去的勇气:

    1. 纯CSS
    2. 高兼容性
    3. 适用于N行:不区分单行还是多行

    首先,看个效果图:

    多行文本截断效果图
    是不是看起来超厉害,是不是以为是用的-webkit-line-clamp。。。然而,并不是哦。

    其次,来理一下原理。

    不管是单行截断还是多行截断,事实上,浏览器并没有把超长的文字删除,而是渲染时候在块尾渲染个...覆盖在上面,由于规则里overflow: 计算值非visible,所以,视觉上是看不到的。

    要验证我的说法是正确的,很简单,把你们经常用overflow: hidden;的写法改成overflow: auto;overflow: scroll;即可。

    所以,我们的原理也是这样的,

    拿个...盖到上面去

    简单搞个图看下:


    ...盖示意图

    从上图可以看到,实际上,我们要做的就是把粉色那个...,放在框的右下角,以隐藏右下角的最后那个字/内容,当我们把粉色改成白色,就会得到我们希望的...省略效果了:


    ...的最终效果

    说到这里,...的方案很简单,可以使用一个特定的元素,也可以用::after伪元素。最后的代码类似这样的:

    <div style="
        margin: 50px;
        width: 200px;
        height: 3em;
        line-height: 1.5;
        overflow: hidden;
        position:relative;outline:1px solid #ff9900
    ">这是一串很长的文字,现在开始哦。第二行文字在这里开始了
        <i style="
            position: absolute;
            bottom: 0;
            right: 0;
            width: 1.5em;
            padding-left: 2px;
            background-color: white;
        ">...</i>
    </div>
    

    到目前为止,一切似乎很顺利,网上随便一搜,都差不多是这个样子的。或许,你们有人已经在YY:你丫是在逗我吗,这谁不知道哈。

    诚然,如果我说的方案就到此结束,还真是在逗你们了,但是,请认真继续看哈。

    这个方案有一个很严重的问题,那就是:

    理所当然地认为内容一定会溢出

    是的,网上有些认真的人在写这个方案时候,还是会来个温馨提醒的,有的也会告诉你计算一下内容会不会溢出,如果是这样,干嘛不一开始就直接计算得了,还整了一个CSS+JS计算的方案哈。

    当然,如果你可以百分百确定你的内容会溢出,到目前为止的方案已经足够。相反,你应该继续看下去。回到我刚才的截图里:


    多行文本截断效果图

    截图里可以看到,我们最终的方案是要保证:

    当且仅当内容溢出时,显示...省略

    究竟如何实现这个功能呢?回想一下我们这个...的方案原理,我们是拿...盖住右下角的内容来实现省略,其实,同样的道理,我们可以

    拿个东西盖住...

    是的,原理还是那个原理,只是换了谁盖谁。所以,前面那句话,我们就可以换成:

    当且仅当内容溢出时,不盖住...

    如何达到这个效果呢?要解释这个问题,我们来具象一下overflow: hidden;是什么样的一个效果。

    假设我们的内容是用一个数组存储的,我们的内容框只能承载20个字,也就是说,我们的数组length一旦大于20,index >= 20的元素就不能再显示了。

    现在,我们的数组是空的,我们给它丢个字(假设为:略)进去,使用栈方法,从头往里一直丢字(unshift('X'),X为某个字),那第一次丢进去的“略”字,就会被一直往后推,直到它被推到20的位置,“略”字就“不再显示”了。

    同样,对...的覆盖,我们也用“推”的方法,想象一下,我们找个东西,把它盖到...上,这就是第一个字“略”,然后,我们一直在这个东西前面添加我们的文本内容,当我们把这个东西推到框外时,就意味着我们的内容溢出了,...需要显示。而在此之前,由于东西一直处在index < 20,所以,它就一直存在,一直盖住...,这就是:内容未溢出,不显示...

    至此,原理简单地抽象了一下。接下来,就是实现了,实现有很多方式,这里给出其中一种,具体怎么实现,看个人喜好吧,毕竟

    思想是统一的,实现都是看人的

    给出以下某个实现:

    <style type="text/css">
        .ellipsis {
          margin: 20px;
        }
    
        .ellipsis {
          position: relative;
          width: 200px;
          max-height: 3em;
          line-height: 1.5;
          overflow: hidden;
          outline: 1px solid #ff9900;
        }
        .ellipsis::before {
          content: '...';
          position: absolute;
          z-index: 1;
          bottom: 0;
          right: 0;
          width: 1.5em;
          padding-left: 3px;
          box-sizing: border-box;
          background-color: white;
        }
        .ellipsis::after {
          content: '';
          display: inline-block;
          position: absolute;
          z-index: 2;
          width: 100%;
          height: 100%;
          background-color: white;
        }
      </style>
      <div class="ellipsis">这是一串很长的文字</div>
      <div class="ellipsis">这是一串很长的文字,现在开始哦。第二行文字在这</div>
      <div class="ellipsis">这是一串很长的文字,现在开始哦。第二行文字在这里开始了,从“里”字开始,就应该被...省略了。</div>
    

    效果图这样的:


    最终效果图

    原理已经讲了,我就不再划实现的要点了,多动动脑,不会有害处的。

    只是比较完美

    虽然,最后的实现效果图上看,好像很完美的样子,但是,这个方案,同样有比较麻烦的地方,简单举例一下

    1. 中英文:纯英文或纯中文,比较简单,但是,如果右下角的字不能确定是中文还是英文时候,...的size和position的设定基本不会有一个完美值,有兴趣的可以自行测试
    2. 多选文本省略时,内容框的高度,要和N行高度保持一致,这是没有办法像浏览器处理那样,可以处理行上的省略的(overflow: hidden;只会截断)

    其他的,我就不再举例了。对于1,可以考虑用渐变混合一下,弱化问题,对于2,就乖乖设定高度吧。

    总之,这是一个还算比较完美的方案,但是,还是有其缺陷。如果有更好的方案,再做补充吧,也欢迎评论提供。

    总结

    本文总结了文本省略的方案,既有对浏览器支持方案的一些新见解,也分享了自己的解决方案,来划一下重点:

    text-overflow使用规矩:

    1. 块级元素
    2. overflow: 计算值非visible;
    3. 元素宽度:超出时,有一个确切的计算值
    4. white-space: nowrap | pre;

    Webkit系列的多行文本省略:

    1. display: -webkit-box | -webkit-inline-box;
    2. -webkit-box-orient: vertical | block-axis
    3. overflow: 计算值非visible
    4. -webkit-line-clamp: N; // 行数

    本人的方案:

    1. 在框右下角放个...,盖住右下角的内容
    2. 在内容尾,跟随一个mask,盖住上述的...,内容超出时,mask被推出,显示...

    P.S. 原创文章,转载请注明出处。

    相关文章

      网友评论

        本文标题:多行文本省略近完美方案

        本文链接:https://www.haomeiwen.com/subject/xlgczxtx.html