IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    尝试借助CSS @container实现多行文本展开收起

    XboxYan发表于 2023-12-18 10:00:00
    love 0
    欢迎关注我的公众号:前端侦探

    之前写过这样一篇文章:CSS 实现多行文本“展开收起”,介绍了一些纯 CSS 实现多行文本展开收起的小技巧,非常巧妙,有兴趣的可以回顾一下。

    Kapture 2023-03-25 at 13.56.18.gif

    不过展开收起按钮的隐藏和显示采用了“障眼法”,也就是通过一个伪元素设置和背景相同的颜色覆盖实现的,如下

    image-20231214165523741

    时代在进步,CSS也在不断发展。 CSS 容器查询出来也有一段时间了,能够动态判断容器尺寸,赶紧拿来用一下,发现并没有想象中的那么顺利,甚至还有些难用,一起看看吧

    一、简单介绍一下容器查询

    CSS 容器查询,顾名思义,就是可以动态查询到容器的尺寸,然后设置不同的样式。

    比如有这样一个容器

    <div class="card">
      <h2>欢迎关注前端侦探</h2>
    </div>

    简单美化一下

    .card{
      display: grid;
      place-content: center;
      width: 350px;
      height: 200px;
      background-color: #FFE8A3;
      border-radius: 8px;
      border: 1px dashed #9747FF;
    }

    效果如下

    image-20231214171221855

    现在这个容器宽度是 350px,现在希望在宽度小于 250px时文字颜色变为绿色,要怎么做呢?

    非常简单,只需要规定一下容器的类型,然后写一个查询语句就行了,关键实现如下

    .card{
      /**/
      container-type: size;
    }
    @container (width < 250px){
      .card h2{
        color: #14AE5C;
      }
    }

    这样在动态改变元素尺寸时就会自动改变颜色了,效果如下

    Kapture 2023-12-14 at 17.18.14

    是不是非常简单?

    可事实是这样吗,其实还有很多局限

    二、容器查询的局限

    主要是有两点局限

    第一点,容器查询不可更改容器本身样式,比如像这样,直接改颜色是不生效的

    .card{
      /**/
      container-type: size;
    }
    @container (width < 250px){
      .card{
        color: #14AE5C;
      }
    }

    白白浪费了一层标签。

    也无法通过:has去匹配父级

    .card{
      /**/
      container-type: size;
    }
    @container (width < 250px){
      body:has(.card h2){
        color: #14AE5C;
      }
    }

    还有一点问题更大,容器必须手动指明尺寸,不可以由内容撑开,也就是自适应内容尺寸,比如我们将上面的宽高去除

    image-20231214175008788

    可以看到,在设置成容器查询类型后,容器的宽高都变成了 0,必须手动设置宽高

    所以,在实际应用中,必须要想办法规避这两个问题。

    三、多行文本展开收起中的应用

    现在回头看多行文本的例子,通过之前的文章,我们可以很“轻松”的实现这样一个布局,如果不太清楚的可以回顾一下,这里就不多描述了

    <div class="text-wrap">
      <div class="text-content">
        <label class="expand"><input type="checkbox" hidden></label>
        欢迎关注前端侦探,这里有一些有趣的、你可能不知道的HTML、CSS、JS小技巧技巧。
      </div>
    </div>

    相关样式如下

    .text-wrap{
      display: flex;
      position: relative;
      width: 300px;
      padding: 8px;
      outline: 1px dashed #9747FF;
      border-radius: 4px;
      line-height: 1.5;
      text-align: justify;
      font-family: cursive;
    }
    .expand{
      font-size: 80%;
      padding: .2em .5em;
      background-color: #9747FF;
      color: #fff;
      border-radius: 4px;
      cursor: pointer;
    }
    .expand::after{
      content: '展开';
    }
    .text-content{
      display: -webkit-box;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: 3;
      overflow: hidden;
    }
    .text-content::before{
      content: '';
      float: right;
      height: calc(100% - 24px);
    }
    .expand{
      float: right;
      clear: both;
      position: relative;
    }
    .text-wrap:has(:checked) .text-content{
      -webkit-line-clamp: 999;
    }
    .text-wrap:has(:checked) .expand::after{
      content: '收起';
    }

    这样就得到了一个“右下角”可以展开收起的布局,不过目前按钮是始终可见的

    image-20231214190408837

    我们尝试用容器查询来判断一下

    .text-wrap{
      /**/
      container-type: size;
    }

    结果...高度都变成了 0

    image-20231214191156770

    所以直接添加是不行的

    有什么办法可以让容器查询可以自适应内容高度呢?我这里想到的办法是,外层用一个自适应内容高度的容器,然后容器查询盒子用绝对定位的方式,高度跟随外层,原理如下

    image-20231214191930783

    因此,我们需要添加两层,一层作为自适应内容的容器,一层作为容器查询盒子,自适应内容的文本可以用伪元素来代替,和真实内容保持一致就行了

    <div class="text-wrap">
        <div class="text" title="欢迎关注前端侦探,这里有一些有趣的、你可能不知道的HTML、CSS、JS小技巧技巧。">
          <div class="text-size">
            <div class="text-flex">
              <div class="text-content">
                <label for="check" class="expand"><input type="checkbox" id="check" hidden></label>
                欢迎关注前端侦探,这里有一些有趣的、你可能不知道的HTML、CSS、JS小技巧技巧。
              </div>
            </div>
          </div>
        </div>
      </div>

    然后把.text-size座位容器查询盒子

    .text-size{
      position: absolute;
      inset: 0;
      container-type: size;
    }
    @container (height <= 4.5em) {
      .text-size .expand{
        display: none;
      }
    }

    虽然现在有点乱,但容器查询已经生效了,在小于等于4.5em(3行)的时候,右下角按钮已经消失了

    image-20231214192858528

    如果隐藏占位伪元素,其实是这样的

    image-20231214195018639

    空出一大段空白确实不雅,由于我们需要查询的高度是最大高度,所以外层自适应高度不能再变了,相当于 JS 中的 scrollHeight,因此,这层容器需要设置绝对定位,从而不影响最外层容器

    .text{
      position: absolute
    }

    同时将占位伪元素隐藏后,效果如下

    image-20231214194933552

    现在高度都回到了0,因此我们需要额外一份文本来自适应最外层容器,而且也能展开收起

    <div class="text-wrap">
      <div class="text" title="欢迎关注前端侦探,这里有一些有趣的、你可能不知道的HTML、CSS、JS小技巧技巧。">
        <div class="text-size">
          <div class="text-flex">
            <div class="text-content">
              <label class="expand"><input type="checkbox" hidden></label>
              欢迎关注前端侦探,这里有一些有趣的、你可能不知道的HTML、CSS、JS小技巧技巧。
            </div>
          </div>
        </div>
      </div>
      <div class="text-content text-place">
        欢迎关注前端侦探,这里有一些有趣的、你可能不知道的HTML、CSS、JS小技巧技巧。
      </div>
    </div>

    我们只需要它的高度,所以可以设置为不可见

    .text-place{
      visibility: hidden;
    }

    这样容器的高度其实是由text-place这一层撑开的,效果如下

    image-20231214195312024

    总算实现了动态查询自适应文本容器高度,效果如下

    Kapture 2023-12-14 at 19.55.36

    还有很多细节,可以查看以下 demo

    • CSS @container clamp (codepen.io)')

    四、总结:容器查询还是不太适合

    总的来说,容器查询并没有想象中那么“好用”,甚至有些难用,也有可能使用过场景并不在这里,虽然最终勉强实现了,但是代价太大了,多了两份相同的文本内容,HTML结构也复杂了好多。下面总结一下

    1. 容器查询可以根据容器的尺寸匹配不同的样式
    2. 容器查询并没有那么“好用”,有两个局限性
    3. 一个是容器查询不可更改容器本身样式,导致白白浪费一层标签
    4. 还有一个是容器必须手动指明尺寸,不可以由内容撑开,也就是自适应内容尺寸,否则容器尺寸就是 0
    5. 为了规避容器查询的局限性,使用一层额外的文本充当容器查询
    6. 使用另一层额外的文本来撑开最外层容器

    仅仅作为尝试,实际并不推荐,最终结构还是过于复杂,3份相同的内容有些过于冗余,其实HTML结构实现到右下角按钮那里就可以了,动态高度还是交给 JS去判断吧。最后,如果觉得还不错,对你有帮助的话,欢迎点赞、收藏、转发 ❤❤❤

    欢迎关注我的公众号:前端侦探


沪ICP备19023445号-2号
友情链接