太妙了!!使用CSS+JS实现多行文本的展开收起效果!

4,896 阅读7分钟

要求

话不用多说了直接上要求吧!

三行的时候省略(内容字数是不限的,内容文字是随机的),在内容末尾自动加上省略号,最后面增加展开按钮,并且希望展开和省略号之前有一定的间距。

效果

展开收起.gif

动手试试

最开始的思路

为了不影响大家的思路,我把实现代码放到最后面,当然想看代码的可以直接跳过这一部分。

先分享一下最开始我的思路吧,一开始看见这个效果的时候,我的内心是这样的。

我心想这不挺简单的

微信截图_20231022162739.png

结果。。。还是我太菜了。。。

微信截图_20231022162052.png

一开始看到我的首要想法是使用定位 ,我想着使用定位,定到右下角,完了再给文字背景改成白色,再增加一定的宽度盖住原来的文字不就实现了吗?确实是可以的,但是这样有一定的缺点,就是我们无法精准的盖住之前的文字,可能会有只盖住一半的情况,比如这样:

微信截图_20231022175424.png

文字相同的情况下对比一下正确的实现效果:

微信截图_20231022175456.png

相信聪明的童鞋已经看出哪不对劲了,确实使用定位是有一些不精准的,那有的同学会说我再加宽一下正好盖住不就行了,我想说这样的话总有盖不住的时候。。。

切换思路

那经过上面的测试我们已经知道定位实现不了,那应该怎么办呢?

在 flex 如此流行的今天,相信大家已经忘记了还有一种布局方式,那就是:float

什么是 float

float - CSS:层叠样式表 | MDN (mozilla.org)

微信截图_20231022165453.png

看完这张图大家应该恍然大悟了吧,我们只需要给元素增加 float即可。

    .float {
        float: right;
        clear: both;
        margin-left: 7px;
        font-size: 15px;
        color: #2a3aff;
      }

那如果只使用浮动,初级目标已经实现了,我们如何放到第三行右下角呢?

我们知道如果我们同时写了两个浮动元素,并且给他们分别清除浮动之后的效果是这样的:

效果图:

微信截图_20231024160943.png

这样基本已经实现了,但是我们并不想看到上面的浮动,只想看到展开。

这里有两种方法:

一种是给上面的浮动元素的宽度设置为0;

另一种是删掉上面的浮动元素标签,给外层元素添加伪元素,宽度同样为0;

这两个效果一致,只不过后者不会在页面中多写一个标签。

效果图:

微信截图_20231024161048.png

//第一种前者      
    .floatVerticalLine {
        clear: both;
        float: right;
        height: calc(100% - 22px);
      }
//第二种后者   
    .outerElement::before {
            content: '';
            background-color: yellow;
            float: right;
            height: calc(100% - 28px);
          }

展开收起切换效果

上面我们已经实现了展开文字的效果,但是并没有和收起进行结合实现,接下来进行结合实现!

目前我们是通过 -webkit-line-clamp: 3这个属性来限制在一个块元素显示的文本的行数。

那如果我们点击展开的时候把这个属性改到足够大的是不是就可以展开全部内容了?我们改成下面这样!

      /*用来限制在一个块元素显示的文本的行数。*/
      .line-clamp-three {
        -webkit-line-clamp: 3;
      }
      //改动后的
      .line-clamp-countless {
        -webkit-line-clamp: 666;
      }

现在只需要使用 js 点击展开的时候切换这个class类即可!

  const floatDom = document.querySelector('.float')
  floatDom.addEventListener('click', () => {
    if (floatDom.innerText === '展开') {
      floatDom.innerText = '收起'
      innerElementDom.classList.remove('line-clamp-three')
      innerElementDom.classList.add('line-clamp-countless')
    } else {
      floatDom.innerText = '展开'
      innerElementDom.classList.remove('line-clamp-countless')
      innerElementDom.classList.add('line-clamp-three')
    }
  })

效果如下:

展开收起2.gif

什么时候显示展示?

目前我们都是在已有展示的情况下做的测试,但是如果我们只有两行或者三行不满的话怎么办?当然是不能显示展示了,那这个怎么判断呢?

我们知道,只有超过三行的时候我们才会显示展示更多按钮,所以我们在页面加载的时候直接判断是否是三行不就行了!

那怎么判断三行?不太好判断,那判断三行的高度总可以吧!

我设置line-height:1.5,那每行的高度就是font-size * 1.5; 16*1.5=24;三行就是72!只需要判断是否大于72即可!

默认情况下,设置浮动元素和展开为display:none,条件成立时进行设置为 block;

  const innerElementDom = document.querySelector('.innerElement')
  if (innerElementDom.offsetHeight > 72) {
    floatDom.style.display = 'block'
    floatDomLineHeight.style.display = 'block'
    innerElementDom.classList.remove('line-clamp-countless')
    innerElementDom.classList.add('line-clamp-three')
  }

可以看到当文字不足三行时,下面的展开并没有出现!

微信截图_20231024153729.png

写在最后

之所以分享这个效果,一是觉得挺有意思的,二是也是感慨目前随着前端技术的发展,一些新技术慢慢已经取代了老技术,就比如 flex 和 float,我们在工作中可能更多的是使用 flex 进行布局(也有使用float的),而慢慢的忽视了 float,而真正需要 float 登场的时候,又想不起来什么时候要去使用它了。

源码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      html,
      body {
        height: 100%;
      }
      .outerElement {
        display: flex;
        width: 600px;
      }
      .innerElement {
        line-height: 1.5;
        overflow: hidden;
        position: relative;
        overflow: hidden; /*必须结合的属性,当内容溢出元素框时发生的事情*/
        text-overflow: ellipsis; /*可以用来多行文本的情况下,用省略号“…”隐藏超出范围的文本 。*/
        display: -webkit-box; /*必须结合的属性 ,将对象作为弹性伸缩盒子模型显示 。*/
        -webkit-box-orient: vertical; /*必须结合的属性 ,设置或检索伸缩盒对象的子元素的排列方式 。*/
      }
      .line-clamp-three {
        -webkit-line-clamp: 3; /*用来限制在一个块元素显示的文本的行数。*/
      }
      .line-clamp-countless {
        -webkit-line-clamp: 666; /*用来限制在一个块元素显示的文本的行数。*/
      }
      .floatVerticalLine {
        display: none;
        clear: both;
        float: right;
        height: calc(100% - 22px);
      }
      .float {
        float: right;
        clear: both;
        display: none;
        margin-left: 7px;
        font-size: 15px;
        color: #2a3aff;
      }
      .float:hover {
        cursor: pointer;
      }
    </style>
  </head>
  <body>
    <h1>超过三行展示收起</h1>
    <div class="outerElement">
      <div class="innerElement">
        <div class="floatVerticalLine"></div>
        <div class="float">展开</div>
        对目前的知识星球来说,一个个星球是创作者和用户们的领地,有时候,在地球解决不了的问题,可以抛向星空,或许来自火星或者月亮的人看见了,就解决了。
        如果星球和星空能够互补,后续陆续会做上网页版,也会结合知识星球的App进行迭代在星空回答过问题,用户能访问到你的星球。觉得开星球太重,可以先到星空感受感受气氛。
        对目前的知识星球来说,一个个星球是创作者和用户们的领地,有时候,在地球解决不了的问题,可以抛向星空,或许来自火星或者月亮的人看见了,就解决了。
        如果星球和星空能够互补,后续陆续会做上网页版,也会结合知识星球的App进行迭代在星空回答过问题,用户能访问到你的星球。觉得开星球太重,可以先到星空感受感受气氛。
        对目前的知识星球来说,一个个星球是创作者和用户们的领地,有时候,在地球解决不了的问题,可以抛向星空,或许来自火星或者月亮的人看见了,就解决了。
        如果星球和星空能够互补,后续陆续会做上网页版,也会结合知识星球的App进行迭代在星空回答过问题,用户能访问到你的星球。觉得开星球太重,可以先到星空感受感受气氛。
        对目前的知识星球来说,一个个星球是创作者和用户们的领地,有时候,在地球解决不了的问题,可以抛向星空,或许来自火星或者月亮的人看见了,就解决了。
        如果星球和星空能够互补,后续陆续会做上网页版,也会结合知识星球的App进行迭代在星空回答过问题,用户能访问到你的星球。觉得开星球太重,可以先到星空感受感受气氛。
      </div>
    </div>
  </body>
</html>
<script>
  const floatDom = document.querySelector('.float') //展开收起元素
  const floatDomLineHeight = document.querySelector('.floatVerticalLine') //浮动元素撑高度
  const innerElementDom = document.querySelector('.innerElement') //内容容器
  if (innerElementDom.offsetHeight > 72) {
    floatDom.style.display = 'block'
    floatDomLineHeight.style.display = 'block'
    innerElementDom.classList.remove('line-clamp-countless')
    innerElementDom.classList.add('line-clamp-three')
  }

  floatDom.addEventListener('click', () => {
    if (floatDom.innerText === '展开') {
      floatDom.innerText = '收起'
      innerElementDom.classList.remove('line-clamp-three')
      innerElementDom.classList.add('line-clamp-countless')
    } else {
      floatDom.innerText = '展开'
      innerElementDom.classList.remove('line-clamp-countless')
      innerElementDom.classList.add('line-clamp-three')
    }
  })
</script>