细说长谈文字超长展开收起功能

若是只是单纯的文字超长,多余文字...隐藏,那么问题仍是比较简单,几行css代码就搞定了,以下:css

overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
复制代码

当前要实现的是文字超长,页面在末尾显示“展开”和“收起”两中状态,点击能作响应动做,展开按钮状态出现的前提是要内容三行放不下了,才能出现展开按钮,不然不该该出现展开按钮。以下:
收起的样式:
html

展开

展开的样式:
web

收起

若是用上述的css来实现的,没办法捕捉到...的事件,没有办法再展开内容了。 接下来咱们用css和js来实现这两种方式。
以前咱们尝试过本身计算文字是否超长来作展开和隐藏,可是问题很多。dom

  • 头部有标签,得加入计算
  • 内容有换行标签
  • 单行可显示的字符数不是恰好对应上,如能够显示3个字符,那么“aa中",则中字换行。
  • ……

总而言之通过复杂的代码处理以后(计算标签宽度,处理换行等),也基本能达到要求,可是总差点意思,“展开”按钮不必定总在最右边,效果不是太好。接下来介绍下很稳的效果。ide

js的实现

咱们首先用js来实现展开收起功能。字体

getBoundingClientRect

返回元素的大小及其相对于视口的位置。 返回值是一个 DOMRect 对象,这个对象是由该元素的 getClientRects() 方法返回的一组矩形的集合, 即:是与该元素相关的CSS 边框集合 。 DOMRect 对象包含了一组用于描述边框的只读属性——left、top、right和bottom,单位为像素。除了 width 和 height 外的属性都是相对于视口的左上角位置而言的。 优化

alt

同上,咱们执行代码:ui

document.querySelector(".txt").getBoundingClientRect();
复制代码

alt

了解 getClientRects

返回一个指向客户端中每个盒子的边界矩形的矩形集合。 返回值是ClientRect对象集合,该对象是与该元素相关的CSS边框。每一个ClientRect对象包含一组描述该边框的只读属性——left、top、right和bottom,单位为像素,这些属性值是相对于视口的top-left的。即便当表格的标题在表格的边框外面,该标题仍会被计算在内。spa

实战 getClientRects

var rectCollection = Element.getClientRects();
复制代码

例子:3d

<div id="txt">我是一个小文本,<br>我是一个小文本</div>
复制代码

执行代码:

document.querySelector(".txt").getClientRects();
复制代码

返回结果以下:

getClientRects
若是咱们将html的div改为span

<span id="txt">我是一个小文本,<br>我是一个小文本</span>
复制代码

返回结果以下:

span

对于行内元素,元素内部的每一行都会有一个边框;对于块级元素,若是里面没有其余元素,一整块元素只有一个边框. 因此是div就返回了三个DOMRect,可是div只返回一个。

文字超长处理(一)

咱们采用文本遮住的方式来实现显示展开和收起。

在显示的文本外增长一个文本框

<div class="wrap">
  <span class="txt">我是一个小文本,<br>我是一个小文本</span>
  <span class="dot open">...展开</span>
</div>
复制代码

而后咱们设定字体大小和行高。好比字体是18,行高1.5。假如显示三行,则咱们设置外层最大为183+92 = 72px; 若是是要动态文字大小,则同比计算便可。

.wrap{
  width:100%;
  max-height:72px;
  position:relative;
  line-height:1.5;
  font-size:18px;
}
.dot{
    position: absolute;
    right: 0;
    bottom: 0;
    background: rgba(255,255,255,.9);
}

复制代码

这个时候若是是文字超长,咱们已经看到效果了。好比:

展开

接下来咱们须要把文字没有超长的时候,隐藏展开按钮便可。

第一步:判断外层的高度。

document.querySelector(".wrap").getClientRects();

bottom: 258.359375
height: 72
left: 55
right: 399
top: 186.359375
width: 344
x: 55
y: 186.359375
复制代码

若是height小于72,则直接隐藏,说明文字没有超长。

第二步:获取内部文字高度。

若是外层已经达到72px了,那么可能超长,也多是恰好三行。

document.querySelector(".txt").getClientRects();
复制代码

获得以下数据。

txt

  • 若是行数大于三行,则是须要显示展开按钮。
  • 若是小于等于三行,则不须要显示展开按钮

这里重点要看下,获取getClientRects的行数。要去掉换行等的空行,即Bottom值同样。

function getRectsLength(rects) {
    var line = 0, lastBottom=0;
    for(var i=0,len=list.length;i<len;i++){
        if(list[i].bottom ==lastBottom){
            return;
        }
        lastBottom = list[i].bottom;
    		line++; 
		}
    return line;
}
复制代码

上面的处理方式,使用外层的wrap,须要是一个块状元素。若是外层不是块状元素,好比以下,签名须要插入一个标签,文字挨着标签显示。则只能用第二种方式实现。

alt

文字超长处理(二)

使用getClientRects方法来处理文字超长,因为这个方法是实时返回页面元素的边界盒子,因此采用的是过后处理机制。

html页面内容,在每个item里面,存储showTxt和hideTxt,开始内容都在showTxt里面。

<span class="quan_feed_desc">
<span class="show_txt"></span>
<span class="dot">...</span>
<span class="hide_txt" style="display:none"></span>
<span class="quan_feed_more dot">展开</span>
</span>
复制代码

开始渲染到页面的时候是整个内容都渲染上去,即渲染到上面的show_txt,而后再针对页面dom节点处理。当一页数据渲染完成以后,再执行handleText。
获取没有处理过的列表项。

function handleText() {
    const dom = document.querySelectorAll(".quan_feed_desc:not([data-loaded]");
    if(dom.length==0) return;
    Array.from(dom).map((d)=>{
        handleDomLength(d);
    }); 
}
复制代码

获取getClientRects的行数。要去掉换行等的空行,即Bottom值同样。

function getRectsLength(rects) {
    var line = 0, lastBottom=0;
    for(var i=0,len=list.length;i<len;i++){
        if(list[i].bottom ==lastBottom){
            return;
        }
        lastBottom = list[i].bottom;
    		line++; 
		}
    return line;
}
复制代码

单独处理某一项的行数。其中feedList是渲染的页面数据。循环处理字符,若是行数超过3行,则一直减小显示的文本数据,知道只有三行为止。这里有个优化点,加速回退的进程,减小回退次数,好比粗略计算文字的长度是否大幅高于3行,而后大幅回退文字。

function handleDomLength(dom) {
    const item = feedList[dom.dataset.index];
    const rects = dom.getClientRects();
    var h = getRectsLength(rects);
    if(h<3||!item.showTxt){
        dom.dataset.loaded = 1;
        continue;
    }
    
    dom.querySelectorAll(".dot").forEach(function (el) {
        el.style.display="inline-block";//把点点和展开放开,为后面计算更真实
    });
    let t = item.showTxt;
    let showtxt = dom.querySelector(".show_txt");
    while(h>3&&t){
        let step=1,m;
        if(t.indexOf("<br/>")==0){//回退的时候,若是碰到换行要总体替换
            step = 5;
        }else if(t.indexOf("<br>")==0){
            step = 4;
        }else if(m = t.match(/\[[^\]]+\]/)){// [大哭]是表情,统一回退
            step = m[0].length;
        }
        t = t.slice(0,-step);
        showtxt.innerHTML = t;
        h = getLength(dom.getRectsLength());
    }
    item.hideTxt =item.showTxt.slice(0,t.length);
    item.showTxt = t;
    dom.dataset.loaded = 1;
}
复制代码

这个处理方式,最大的好处就是很精准,基本上完美达到产品的需求,不太足的地方就是须要回退字符串,频繁操做dom文本。

css的实现

css使用float来实现,以下:

alt

有个三个盒子 div,粉色盒子左浮动,浅蓝色盒子和黄色盒子右浮动。
当浅蓝色盒子的高度低于粉色盒子,黄色盒子仍会处于浅蓝色盒子右下方。
若是浅蓝色盒子文本过多,高度超过了粉色盒子,则黄色盒子不会停留在右下方,而是掉到了粉色盒子下。
使用float浮动原理的表现形式巧妙的展示这个功能。

<div class="wrap">
    <div class="text">
        我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人我是中国人
    </div>
</div>
复制代码

这里只有一个文本div,而后使用了before和after来实现,动态的出现“...更多”。

.wrap{
    height: 40px;
    line-height: 20px;
    overflow: hidden;
}
.wrap .text{
    float:right;
    margin-left: -5px;
    width: 100%;
    word-break: break-all;
}
.wrap::before{
    float:left;
    width: 5px;
    content:'';
    height: 40px;;
}
.wrap::after{
    float: right;
    height: 20px;
    line-height: 20px;
    width:4em;
    content:'...更多';
    margin-left:-4em;
    position: relative;
    text-align: right;
    left:100%;
    top:-20px;
    padding-right: 5px;
    background:-webkit-gradient(linear,left top,right top,from(rgba(255,255,255,.7)),to(white))
}
复制代码

注意这里须要指定line-height,代码中而后指定after的top为向上移动一行。

相关文章
相关标签/搜索