深入解析 text-overflow: ellipsis 与 mask-image 的遮挡问题

310 阅读3分钟

深入解析 text-overflow: ellipsis 与 mask-image 的遮挡问题

当我们在使用 text-overflow: ellipsis 属性处理文本溢出时,可能会遇到省略号(...)与 mask-image 发生遮挡的问题。这个问题的根本原因涉及到浏览器渲染机制和CSS属性的优先级处理。

问题现象

当文本溢出容器并启用 text-overflow: ellipsis 时,浏览器会在文本末尾显示省略号。但如果同时使用了 mask-image,省略号可能会被 mask-image 遮挡或产生意外的视觉效果。

如下图,我有一个 inline-flex 的标签元素,在未与省略号重合时一切正常 image.png

当这个元素被 ... hide 时,发现 icon 并没有被 hide

image.png

技术原理

  • 这个问题涉及到几个关键的渲染层面:

    • 渲染层叠顺序(Stacking Context)
    • mask-image 的应用机制
    • 文本溢出处理的渲染流程

问题原因剖析

text-overflow: ellipsis 的省略号是在文本渲染层生成的,而 mask-image 是在合成层(Compositing Layer)中处理的。当两者同时存在时,mask-image 会作用于整个元素区域,包括省略号部分。

这个问题的根本原因在于 CSS 的文本溢出处理机制与复合内联元素的渲染方式之间的交互冲突:

CSS 文本溢出处理机制:

  • text-overflow: ellipsis 主要设计用于处理纯文本内容
  • 当应用于包含内联元素的容器时,省略号会在文本溢出点插入
  • CSS 规范中并未明确定义省略号与内联非文本元素的互动行为

内联复合元素的特殊性:

  • 带有 mask-image 的复合内联元素拥有自己的渲染层
  • 当这类元素被 inline-flex 或 inline-block 包裹并包含背景色时,它们的视觉渲染处理不同于纯文本
  • 由于内联元素被视为单一对象,省略号无法"穿透"内部结构或完全隐藏它们

如何解决

我们在解决这个问题时,需要考虑如下几个关键点:

  • 只有在文本真的溢出时,展示省略号并截断内容
  • 展示省略号后,要确保省略号区域遮挡住文本尾部的内容
  • 要注意若文本区域本身存在背景色,甚至交互后产生新的背景色, 遮罩层需要跟这个背景色时刻保持统一
  • 要注意文本区域的背景色有可能是带透明度的颜色,要注意遮罩层遮罩后,颜色应跟透明后的颜色一致

判断是否真的溢出

  useLayoutEffect(() => {
    if (containerRef.current) {
      const content = containerRef.current
        if (content.scrollWidth > content.clientWidth) {
          content.classList.add('overflowed')
        }
    }
  }, [])    

增加双重遮罩,保证颜色统一(解决带透明度背景的问题)

    .content.overflowed::after {
      position: absolute;
      top: 0;
      right: 0;
      z-index: 2;
      width: 16px;
      height: 100%;
      text-align: right;
      background: var(--bg-color); // 继承父容器的背景色
      transition: background 0.1s;
      content: '...';
    }

    .content.overflowed::before {
      position: absolute;
      top: 0;
      right: 0;
      z-index: 1;
      width: 16px;
      height: 100%;
      text-align: right;
      background: #fff; // 模拟外层的背景色,防止因为 父容器背景色带透明度导致颜色不统一
      content: '';
    }

对于带交互的父容器,不同交互对应不同背景,可以用 css 变量来传递这个信息

    .container {
        --bg-color: xxx;
        &:hover {
            --bg-color: yyy;
        }
        &:active {
            --bg-color: zzz;
        }
    }