1. 一档:单行省略
如果某个区域文字太多而显示区域有限,我们就会选择只显示部分文字,然后最后显示3个点的省略号。实现的方案很简单,前端新同学都可以轻松实现,就是用到了text-overflow: ellipsis;
这个属性。
参考:
2. 二档:多行省略
当然,业务中还有多行省略的需求,
比如商品卡片中商品名的显示,要求最多显示两行,超过的内容在第二行结束时截断,显示省略号。
多行省略的方案就很多了,最简单的使用css trick:用旧版的flex布局-webkit-box
,配合css属性-webkit-line-clamp
,可以方便实现多行的省略。demo:
p {
width: 300px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
还有很多其它方案,比如把省略符做为单独一层盖在内容上,模拟省略效果的;配合js获得字符位置的,甚至使用canvas来渲染的。
参考:
3. 三档:多行省略且省略符不在最后
如图,多行省略,且右下角不是省略符,而是一个链接,链接左边才是截断后显示的省略号。
本人不才,目前还没找到纯css的方案可以优雅的解决这个问题。我使用了Range这个对象,配合js,暂时为相对优雅的方式解决。dom简单,需要的css不多,js量也少,容易封装。
首先dom部分:
<div class="ellipsis">
<span class="text">豌豆公主是目前唯一由日本供应商直接进驻的跨境电商平台,独家代理日本高端品牌。</span>
<div class="link">
<span>... </span>
<a>查看详情></a>
</div>
</div>
其中.text中就是我们要显示和截断的内容,.link中是右下角的链接。注意
在链接前有一个省略符,一会儿会讲它的用处。
然后css来实现简单布局
.ellipsis{
position: relative;
width: 200px;
height: 40px;
border: 1px solid #000;
line-height: 20px;
overflow: hidden;
font-size: 14px;
}
.link{
position: absolute;
right: 0;
bottom: 0;
color: red;
}
.link span{
opacity: 0;
}
目前的效果如下
链接已经放到了右下角,并且超过两行的内容也被截掉了。不过链接和第二行文本有重叠,第二行文本还需要向左继续截断。
注意,从css上可以看出,和链接放在一起的省略号,透明度为0,是显示不出来的
接下来我的思路时:
- 获得.link的位置,可以使用
element.getBoundingClientRect()
轻松获得,记为linkRect - 然后检测.text中的每一个文字的位置,记为letterRect。如果letterRect和linkRect有交集,如果有交集,就说明要在此文字前截断;然后在截图后的文本后补上省略号就行。因为在link中考虑到了省略号占用的空间,所以最终结果上可以自信的补上省略号。
那如何获得每个字符的位置呢?这就可以用到Range这个对象了。按MDN定义:The Range
interface represents a fragment of a document that can contain nodes and parts of text nodes。它表示包含节点或文本节点的文档片断,在web富文本编辑器中,经常会用到它。
<div id="desc">这里是一段demo文本</div>
<script>
var textNode = document.querySelector('#desc').childNodes[0];
var range = document.createRange();
range.setStart(textNode, 0);
range.setEnd(textNode, 3);
</script>
上面的range就是.desc中这里是
三个词的文本节点片断。
同时,range有一个很有意思的特性:它可以折叠。如上面例子,如果设置结束和开始一样,那它就折叠了。
range.setStart(textNode, 1);
range.setEnd(textNode, 1);
如上,此时range折叠,它表示这
和里
两个字符中间的位置。
另外,Range本身也有getBoundingClientRect方法,获得此文档片断的位置。结果Range的折叠功能和位置能力,我们就可以实现上面的思路了。
// content为要截断的文本元素
const content = document.querySelector('.ellipsis span');
// 它的第一个节点就是文本节点,可以传给range对象的
const textNode = content.childNodes[0];
const range = document.createRange();
// 获得链接元素的位置。注意它里面是包含了省略号的空间了。
const linkRect = document.querySelector('.link').getBoundingClientRect();
const text = content.innerText;
let max = 0;
for(let i=0;i<text.length;i++){
// 把range折叠到某个文字前面
range.setStart(textNode, i);
range.setEnd(textNode, i);
// 获得折叠的位置,即这个字符前这个位置
const rect = range.getBoundingClientRect();
// 如果和link的空间有交叉,说明需要从上一个字符前截断
if(rect.bottom > linkRect.top && rect.left>linkRect.left) {
max = i-1;
break;
}
}
if(max>0) {
textNode.textContent = text.substring(0, max)+'...';
}
上面方案的优点:
- dom的代码和结构很简单,不需要特殊的dom结构和辅助的元素;
- 样式简单,只需要简单控制超出内容隐藏就行了,不需要很奇巧淫技的css属性;
- js最后只对dom做了一些内容调整,并且未改变区域高度,不会引起页面大面积重绘,性能很好 最终实现demo,查看>>
参考