当前位置: 首页 > Web前端 > CSS

CSS实现多行文本“展开和折叠”

时间:2023-03-31 02:14:09 CSS

多行文本展开和折叠是一种很常见的交互,如下图演示此类布局和交互难点主要有以下几点位于多行文本右下角”“展开和折叠”按钮在“展开和折叠”之间切换当文本不超过指定行数时,“展开和折叠”按钮不显示说实话,看着之前单独在这个布局下,即使借助JavaScript也是一件容易的事情(需要计算文本的宽度来动态截取文本,vue-clamp就是干这个的),更不用说下面的交互和判断逻辑了,不过琢磨了一下,其实纯CSS也可以完美实现,下面我们一步步来看看是如何实现的吧~1.位于右下角的“展开收起”按钮很多设计同学喜欢这个设计。把按钮放在右下角,和文字混在一起,而不是单行,视觉上可能会更舒适美观。我们先来看看多行文本截断。这是比较简单的多行文本截断。假设有这样一个html结构浮动元素是如何定位的呢?正如我们前面提到的,当一个元素浮动时,它会被移出正常的文档流,然后向左或向右平移,直到它碰到它所在容器的边框,或者另一个浮动元素。

多行文字超出省略。你应该对此很熟悉。主要使用line-clamp,关键样式如下。text{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:垂直;overflow:hidden;}右下角环绕效果说到文字环绕效果,一般可以想到floatingfloat。没错,不要以为浮动已经成为过去式,在特定场景下还是很有用的。比如下面放一个按钮,然后设置浮动Expand浮动元素怎么定位?正如我们前面提到的,当一个元素浮动时,它会被移出正常的文档流,然后向左或向右平移,直到它碰到它所在容器的边框,或者另一个浮动元素。
.btn{浮动:左;/*其他装饰风格*/}如果设置了浮动右边。右上角的按钮怎么移到右下角?试试margin.btn{float:right;顶部边距:50px;/*其他装饰风格*/}可以看到虽然按钮到达了右下角,但是文字并没有把按钮上方的空间包围起来,留了一大片空间Cut,难道就没有办法了吗?虽然margin不能解决问题,但是整个文字还是会受到浮动按钮的影响。如果有多个浮动元素怎么办?这里伪元素用于替换::before.text::before{content:'';浮动:对;宽度:10px;height:50px;/*先随便设置一个高度*/background:red}现在按钮已经到了左边的伪元素,如何移动到底部呢?很简单,只需要清除floatingclear:both;就是这样。btn{浮动:对;明确:两者;/*其他装饰样式*/}可以看到现在文字完全被右侧两个浮动元素包围了,只要将红色背景的伪元素宽度设置为0(或者不设置宽度,默认为0),会实现右下角环绕的效果。text::before{content:'';浮动:对;宽度:0;/*设置为0,或者不设置宽度*/height:50px;/*先随便设置一个高度*/}动态高度虽然完成了右下和环绕,但是高度是固定的,怎么设置动态地?这里可以使用calc计算,只需将整个容器的高度减去按钮的高度即可,如下。text::before{内容:'';浮动:对;宽度:0;height:calc(100%-24px);}不幸的是,它似乎没有任何效果。打开控制台发现calc(100%-24px)的计算高度为0,原因其实很容易想到。就是高度100%失效的问题。网上有一些对这类问题的分析。很多,通常的解决办法是给parent指定一个高度,但是这里的高度是动态变化的,还有展开状态,高度就更不可预测了,所以设置height除此之外,其实还有一种方式,就是使用flex布局。大致的方法是在flex布局的子项中按百分比计算变化高度。具体可以参考w3.org中css-flexbox的说明。如果flexitem有align-self:stretch,重做其内容的布局,将这个使用过的大小作为它的确定的交叉大小,以便百分比大小的孩子可以被解析。所以这里需要给.text包裹一层,然后设置显示:flexExpand如何浮动元素定位正如我们前面提到的,当一个元素浮动时,它会被移出正常的文档流,然后向左或向右平移,直到它碰到它所在容器的边界,或者碰到另一个浮动元素。
.wrap{display:flex;}实际上,display:grid和display:-webkit-box同样有效。原理与此类似。刚刚计算出来的高度生效,改变了文本的行数,也位于右下角~另外,动态高度也可以用负margin来实现(性能会比calc稍微好一点).text::before{内容:'';浮动:对;宽度:0;/*height:calc(100%-24px);*/height:100%;margin-bottom:-24px;}至此,右下角环绕效果基本完成,省略号也位于展开按钮之前。完整代码可以查看codepen右下角的多行展开环绕效果4.其他浏览器兼容处理以上实现是最完美的处理方式。我认为兼容性不是大问题。毕竟只用了文字截断和浮动。虽然-webkit-line-clamp以-webkit-为前缀,但是firefox也支持。打开一看,傻眼了,safari和firefox全乱了。向上!这有点不舒服。难道之前的努力都白费了吗?忽略这两个是不可能的,否则只能是demo,不能用于生产环境。赶紧打开控制台看看是什么原因。一番查找,原来是display:-webkit-box!设置该属性后,原本的文字好像变成了一整块,浮动元素无法产生环绕效果。去掉之后,浮动元素就正常了。那么问题来了:如何实现不显示多行截断:-webkit-box?其实上面的努力已经达到了将右下角包裹起来的效果。如果在知道行数的情况下设置一个最大高度,是不是也完成了多行的截断?为了方便设置高度,可以加一个行高line-height。如果需要设置为3行,则设置高度为line-height*3.text{/*display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:垂直;*/行高:1.5;最大高度:4.5em;overflow:hidden;}为了方便和更好的控制行数,这里可以通过属性选择常用的行数controller是独立的(一般不会太多),如下[line-clamp="1"]{max-height:1.5em;}[line-clamp="2"]{max-height:3em;}[line-clamp="3"]{max-height:4.5em;}.........可以看到基本正常,只是没有省略号,现在加上省略号,跟在按钮展开之前,可以用伪元素实现。btn::before{内容:'...';位置:绝对;左:-10px;颜色:#333;transform:translateX(-100%)}这样,Safari和Firefox的兼容布局就基本完成了,完整代码可以在codepe中查看n右下角多行展开环绕效果(完全兼容)2、“展开”和“折叠”两种状态指的是CSS状态切换。大家可以想到inputtype="checkbox"。这里我们也需要用到这个特性,先添加一个input,然后把之前的按钮换成label,通过for属性Expand浮动元素是如何定位的正如我们前面提到的,当一个元素被浮动时,它被移出正常文档流向左或向右平移,然后平移直到它触及它所在容器的边框,或触及另一个浮动元素。这样,当你点击标签时,实际上是点击了输入元素。现在添加两个状态,只显示3行,不限制行数。exp:checked+.text{-webkit-line-clamp:999;/*设置足够大的行数*/}兼容版本可以直接设置最大高度max-height为更大的值,或者直接设置为none.exp:checked+.text{max-height:none;}有这里还是个小问题,点击后“Expand”按钮应该变成“Collapse”,如何修改呢?有一个技巧。任何需要动态修改内容的人都可以使用伪类内容生成技术。具体方法是去除或隐藏按钮中的文字,使用伪元素生成.btn::after{content:'expand'/*使用内容生成*/}add:checkedstate.exp:checked+.text.btn::after{content:'Collapse'}兼容版本由于模拟了前面的省略号,所以不能自动隐藏,所以需要额外处理。exp:checked+.text.btn::before{可见性:隐藏;/*隐藏展开状态下的省略号*/}与本文开头的效果基本一致。完整代码可以查看codepen多行展开折叠交互。兼容版本可以查看codepen多行展开折叠交互(完全兼容)。还有一点,如果你设置了一个max-height合适的值,注意是一个合适的值,具体原理可以参考csstricks:dynamicheighttransitionanimation,也可以加上transitionanimation.text{transition:.3s最大高度;}.exp:checked+.text{最大高度:200px;/*超过最大行高也没关系}3.判断文本行数上面的交互基本达到了要求,但是还是有问题。比如当文字比较少的时候,此时没有截断,也就是没有省略号,但是“展开”按钮仍然位于右下角。如何隐藏它?通常js的解决方法很简单,只需要比较元素的scrollHeight和clientHeight,然后加上对应的类名即可。下面是伪代码if(el.scrollHeight>el.clientHeight){//textexceedsel.classList.add('trunk')}那么,CSS是如何实现这种判断的呢?可以肯定的是,CSS不具备这种逻辑判断,我们大多数人需要通过其他角度的“蒙眼”来实现。比如在这个场景中,当没有出现截断的时候,就说明文字是完全可见的。这时候如果在文本末尾添加一个元素(红色方块),为了不影响原来的布局,这里设置绝对定位。文本::在{内容:''之后;宽度:10px;高度:10px;位置:绝对;background:red;}如你所见,这里的红色小方块完全跟在省略号后面。当省略号出现时,红色小方块肯定会消失,因为它已经被压下去了。这里暂时隐藏parentoverflow:hidden看看原理是什么。然后,可以把刚才那个红色的小方块设置成足够大的Size,降低透明度,比如100%*100%.text::after{content:'';宽度:100%;高度:100%;位置:绝对;background:red;}可以看到,红色块覆盖了所有的右下角,现在把背景改成白色(和父级一样的背景色),给父级加上overflow:hidden。text::after{内容:'';宽度:100%;高度:100%;位置:绝对;background:#fff;}现在看看点击展开的效果。现在展开后,发现按钮不见了(刚才被伪元素遮住了,无法点击)。如果我希望它在点击后仍然可见怎么办?只需添加:checked状态,展开时隐藏overlay.exp:checked+.text::after{visibility:hidden;}这样就实现了隐藏文字较少的展开按钮的功能。最后,完整代码可以查看codepen多行展开折叠自动隐藏,或者CSS多行展开折叠自动隐藏(runjs.work)兼容版可以查看codepen多行展开折叠自动隐藏(完全兼容)codepen访问打不开,可以访问这里codepen多行自动展开收起隐藏(完全兼容)(runjs.work)RunJS,前端代码创作分享在线。需要注意的是,兼容版本可以支持IE10+(这个过分了,居然支持IE),但是由于IE不支持codepen,所以测试IE可以复制到本地测试。4.总结和描述总的来说,重点是布局,交互其实比较容易,整体实现成本其实很低,也没有比较生僻的属性,除了布局——webkit-box好像是有点bug(毕竟是-webkit-kernel,Firefox只是借用了它,难免有些问题),好在可以用另一种方式来实现多行文本截断效果,兼容性还是不错的,基本完全兼容(IE10+),这里梳理一下实现重点关注文字换行效果首先考虑浮动floatflex布局子元素可以按百分比计算高度多行文字截断也可以结合文字换行效果使用max-height模拟实现状态切换可以用checkboxCSS改变文字可以用伪元素生成更多可以用CSS来屏蔽“醒目法”展开和收起多行文字的效果可以说是长篇大论了行业长期存在的问题。js的解决方案有很多,但都不是完美的。希望这个全新的CSS解决方案能给你带来不一样的灵感。感谢阅读,欢迎点赞收藏转发~