MoreRSS

site iconZhangXinXu | 张鑫旭修改

出版《CSS选择器世界》,喜欢钓鱼、写作。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

ZhangXinXu | 张鑫旭的 RSS 预览

使用Intl.Segmenter返回更准确的字符长度

2025-09-30 10:57:06

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11883
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。

分词与字符长度

一、Emoji字符长度问题

我们平常获取字符串的长度都是直接使用length属性,但对于某些字符,返回的结果会在预期之外,例如:

"鑫空间".length

"🌝".length

"🇮🇳".length

"👨‍👩‍👧‍👦".length

"दरबार".length

结果如下:

"鑫空间".length // 3

"🌝".length    // 2

"🇮🇳".length    // 4

"👨‍👩‍👧‍👦".length    // 11

"दरबार".length  // 5

看到没,看起来Emoji表情字符的长度是1,但是,实际返回的Emoji长度并不是1,不仅不是1,不同的Emoji返回的长度还不一样,这就会给我们精确统计字符长度带来困扰。

怎么办呢?

二、返回字符的真实长度

我们可以使用Intl.Segmenter()返回字符串的真实长度,例如,我们不妨给字符串扩展一个名为 realLength 的方法,完整代码如下所示:

Object.defineProperty(String.prototype, 'realLength', {
  get: function() {
    return Array.from(
      new Intl.Segmenter("en", {
        // 颗粒度:字位,默认值,可省略
        granularity: "grapheme" 
      }).segment(this)
	).length;
  }
});

此时,我们就可以返回字符串真实的长度了。

"鑫空间".realLength // 3

"🌝".realLength    // 1

"🇮🇳".realLength    // 1

"👨‍👩‍👧‍👦".realLength    // 1

"दरबार".realLength  // 4

是不是很美丽?

紫灵

三、Emoji字符长度不是1的原因

JavaScript使用UTF-16编码,这种编码方式最多支持 2x 16 共 65,536 个字符,6万多个字符看起来多,但是世界上那么多语言,已经不够用了,尤其Emoji表情字符,都在65,536 个字符开外。

JS想要表示,只能组合多个特殊的 2 字节代码单元以生成人类可读的字符。

例如:

Array.from({ length: "🌝".length }, (_, i) => "🌝"[i])
// [ '\ud83c', '\udf1d' ]

于是,JS代码直接使用length属性访问的时候,返回值就是2,但是人类理解肯定是1个字符。

这就是Emoji字符长度不是1的原因。

四、关于Intl.Segmenter

Intl.Segmenter 是 JavaScript 中一个专门用于文本分段的强大工具,它能够根据不同的语言和文本规则,将字符串智能地分割成有意义的单位,如字符、单词或句子。

核心概念与语法

Intl.Segmenter 的核心作用在于进行语言敏感的分段。

传统的字符串分割方法(如 split(‘ ‘))对于英语这类使用空格分隔单词的语言勉强适用,但对于中文、日文等不依赖空格的语言就无能为力了,甚至处理包含复杂表情符号(Emoji)的文本时也会出错。

Intl.Segmenter 则依据 Unicode 标准,能够理解不同语言的语法习惯,进行精准的边界检测。

使用示意:

const segmenter = new Intl.Segmenter([locales[, options]]);

其中:

locales
表示语言,使用一个 BCP 47 语言标签的字符串,如 “zh-CN”、”en-US”、”ja”等。它指定了分段所依据的语言规则。如果省略,则会使用运行时环境的默认语言设置。
options
可选参数,包括下面这些:

granularity

表示分段的粒度,是最重要的选项。可取值为:

  • “grapheme”(默认值):在用户感知的字符(字形簇)边界分割。例如,一个基础字符加上修饰符(如 “a” + “́” 组成 “á”)会被视为一个整体,复杂的 Emoji 序列(如家庭表情 👨‍👩‍👧‍👦)也不会被拆散。
  • “word”:在单词边界分割。它能正确识别中文的词汇单元(如将“我真的很强”分割为“我”、“真的”、“很”、“强”),并能区分标点符号和空格(isWordLike 属性为 false)。
  • “sentence”:在句子边界分割。它能智能区分句号的不同用途,例如不会将数字 “100.000” 中的点号误判为句子结束。

localeMatcher

指定所使用的语言匹配算法,可以是 “best fit”(默认值,使用环境提供的可能最匹配的算法)或 “lookup”(使用特定的 BCP 47 算法)。

基本使用方法

创建分段器实例后,通过调用其 segment(inputString) 方法对文本进行分段。该方法返回一个 Segments 可迭代对象。

例如:

// 创建一个中文词段器
const segmenter = new Intl.Segmenter('zh-CN', { granularity: 'word' });
const text = "《HTML并不简单》是国内唯一一本精讲HTML的技术书籍";
const segments = segmenter.segment(text); // 返回一个 Segments 对象

// 遍历
for (const segment of segments) {
  console.log(`片段: "${segment.segment}", 起始索引: ${segment.index}, 是否像词: ${segment.isWordLike}`);
}

在我的Chrome浏览器下,输出的结果是:

// 片段: "《", 起始索引: 0, 是否像词: false
// 片段: "HTML", 起始索引: 1, 是否像词: true
// 片段: "并不", 起始索引: 5, 是否像词: true
// 片段: "简单", 起始索引: 7, 是否像词: true
// 片段: "》", 起始索引: 9, 是否像词: false
// 片段: "是", 起始索引: 10, 是否像词: true
// 片段: "国内", 起始索引: 11, 是否像词: true
// 片段: "唯一", 起始索引: 13, 是否像词: true
// 片段: "一本", 起始索引: 15, 是否像词: true
// 片段: "精", 起始索引: 17, 是否像词: true
// 片段: "讲", 起始索引: 18, 是否像词: true
// 片段: "HTML", 起始索引: 19, 是否像词: true
// 片段: "的", 起始索引: 23, 是否像词: true
// 片段: "技术", 起始索引: 24, 是否像词: true
// 片段: "书籍", 起始索引: 26, 是否像词: true

可以看到浏览器自带对中文句子进行分词了。

兼容性

目前Intl.Segmenter所有现代浏览器均已支持,前端也能自动分词分句了。

Intl.Segmenter兼容性

五、结语

Intl命名对象还有很多其他方法,多是与语言、金钱、数字、字符处理相关的。

详见我之前的这篇文章:“JS Intl对象完整简介及在中文中的应用

还是非常强大与实用的。

就是语法复杂了点,学习成本比较高,真正使用的时候,需要去搜索查找资料。

不过现在都有AI了,只需要知道有这么个东西就好了,AI会帮你搞定。

行吧,就说这么多。

明天就是国庆节了,祝大家国庆节快乐!

不说了,我要准备出发去钓鱼了!

欢迎大家关注我的钓鱼账号:“最会钓鱼的程序员”。

随时了解我国庆节的战况。

张鑫旭本人

😉😊😇
🥰😍😘

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=11883

(本篇完)

CSS scroll-target-group加:target-current滚动菜单自动高亮

2025-09-19 20:12:32

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11837
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。

一、又一个JS交互被干掉了

本文要介绍的 CSS scroll-target-group属性以及:target-current伪类,可以实现滚动的时候,基于位置不同,对应的菜单项自动高亮的效果。

关于这种效果的实现,我之前还专门写文章介绍过:“尝试使用JS IntersectionObserver让标题和导航联动”。

没想到,短短几年之后,纯CSS就能实现这样的效果了。

眼见为实,滚动下面内嵌的局部滚动条(需要Chrome 140+),大家就可以看到对应的效果了。

  • 前言
  • 第1章
  • 第2章
  • 欢迎来到我的博客

    滚动该页面,观察右上角的导航菜单,会发现当前所在的章节会被高亮显示。

    前言

    本博客09年更新到现在,已经16年历史了,见证了自己的成长,以及前端行业的发展。

    如果你觉得其中的内容还不错,欢迎分享,欢迎点赞。

    第1章

    第1章的内容

    也欢迎关注我的抖音账号“张鑫旭本人”。

    第2章

    我平时还喜欢钓鱼,钓友可以关注我的抖音账号“最会钓鱼的程序员”。

    或者参见下图所示的GIF效果。

    滚动导航自动高亮

    是不是很赞?

    二、如何实现导航菜单匹配?

    实现代码非常简单,HTML代码就下面这些:

    <menu>
        <li><a href="#intro">前言</a></li>
        <li><a href="#ch1">第1章</a></li>
        <li><a href="#ch2">第2章</a></li>
    </menu>
    
    <article>
        <h1>欢迎来到我的博客</h1>
        <section id="intro">...</section>
        <section id="ch1">...</section>
        <section id="ch2">...</section>
    </article>

    CSS代码也非常简单,就这么点内容:

    menu {
        position: fixed;
        scroll-target-group: auto;
    }
    
    a:target-current {
        color: red;
    }

    结束了,就结束了,对吧,给菜单容器设置scroll-target-group:auto,然后菜单里面的链接元素使用:target-current设置匹配样式就可以了。

    此时,链接元素对应的href锚点元素进入区域的时候,链接元素就会高亮啦!

    牛逼!

    牛逼

    三、关于scroll-target-group属性

    下面快速介绍下scroll-target-group属性。

    该属性可以让锚点元素有类似于::scroll-marker伪元素的特性( CSS Carousel API中的一个特性),关于::scroll-marker伪元素,可以参见我前几个月刚写的文章:“CSS ::scroll-button ::scroll-marker伪元素又是干嘛用的”。

    需要使用的时候,直接设置值为auto就可以了。

    兼容性

    非常新的一个特性,MDN连文档都没有,Chrome 140浏览器支持。

    scroll-target-group的兼容性

    四、关于:target-current伪类

    :target-current伪类相对支持早一些,和::scroll-marker伪元素等特性同一时间支持的,基于滚动位置匹配scroll-marker-group或者scroll-target-group里面的锚点或标记元素。

    使用示意:

    ::scroll-marker {
      background-color: white;
    }
    
    ::scroll-marker:target-current {
      background-color: black;
    }

    兼容性

    Chrome 135+浏览器支持。

    :target-current兼容性

    五、结语说明

    很好很强,静待浏览器全面支持。

    我发现CSS的野心挺大的,看架势,要覆盖几乎Web上常见的交互,JS退让为逻辑处理。

    不过一来学习成本高,二来以后有AI,三来有成熟替代方案,未来会有几个人使用呢?我看悬。

    所以前端没啥搞头了,不如来钓鱼吧,欢迎大家关注我的钓鱼账号:“最会钓鱼的程序员”。

    周周更新!

    张鑫旭本人

    😉😊😇
    🥰😍😘

    本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
    本文地址:https://www.zhangxinxu.com/wordpress/?p=11837

    (本篇完)

    使用CSS linear()函数实现更逼真的物理动画效果

    2025-09-15 20:12:36

    by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11841
    本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。

    一、被低估的linear()函数

    CSS动画曲线函数新增了一个名为linear()的函数,目前所有现代浏览器都已经支持:

    linear()函数

    一开始,我以为又是什么缝缝补补的新特性,细细一眼就,发现这东西还是挺强的。

    之前的动画缓动函数我们都是使用cubic-bezier()函数,贝塞尔曲线实现的,但是这个函数有个问题,最多就4个属性值,只能实现先快后慢,先慢后快,或者一次性bounce缓动。

    但是真实的物理动画往往是复杂的,例如一个皮球落地,他不可能只弹一下,往往要蹦很多下,cubic-bezier()函数就无法模拟。

    在这种背景下,就设计了linear()函数,虽然是线性函数,但是参数是无限的,于是我们可以通过枚举的方式,可以模拟任何的运动曲线。

    举个例子,小球落地的动画效果。

    点击下面的篮球,可以查看对应的动画效果:

    🏀

    相关的HTML和CSS代码示意:

    <div class="ballX">
      <span>🏀</span>
    </div>
    :root {
      --bounce-easing: linear(0, 0.004, 0.016, 0.035, 0.063, 0.098, 0.141 13.6%, 0.25, 0.391, 0.563, 0.765, 1, 0.891 40.9%, 0.848, 0.813, 0.785, 0.766, 0.754, 0.75, 0.754, 0.766, 0.785, 0.813, 0.848, 0.891 68.2%, 1 72.7%, 0.973, 0.953, 0.941, 0.938, 0.941, 0.953, 0.973, 1, 0.988, 0.984, 0.988, 1);
    }
    .ballX {
      height: 200px;
      border-bottom: solid;
    }
    .ballX span {
      animation: fallDown 1s var(--bounce-easing);
    }
    @keyframes fallDown {
      from { translate: 0 -150px; }
      to { translate: 0 0; }
    }

    如果希望弹动更加逼真,我们可以提高函数参数的精度(切分更细致),就像这样:

    :root {
      --bounce-easing: --linear(0 0%, 0.0009 0.7117%, 0.0034 1.3049%, 0.0144 2.6097%, 0.0586 5.2195%, 0.1313 7.8292%, 0.2356 10.5575%, 0.5338 16.2515%, 1 23.1317%, 0.8675 25.86%, 0.7715 28.707%, 0.7383 30.1305%, 0.7129 31.6726%, 0.6991 33.0961%, 0.6952 33.9265%, 0.6944 34.6382%, 0.6957 35.3499%, 0.6994 36.0617%, 0.7133 37.4852%, 0.7361 38.9087%, 0.7705 40.4508%, 0.8638 43.4164%, 1 46.6192%, 0.9404 48.5172%, 0.8981 50.4152%, 0.8725 52.3132%, 0.8657 53.2622%, 0.8629 54.3298%, 0.8647 55.2788%, 0.8705 56.2278%, 0.8959 58.2444%, 0.9357 60.1423%, 1 62.3962%, 0.9487 65.1246%, 0.9362 66.4294%, 0.9326 67.1412%, 0.9312 67.8529%, 0.9321 68.5647%, 0.9352 69.2764%, 0.9482 70.6999%, 1 73.5469%, 0.9742 75.4448%, 0.9675 76.3938%, 0.9646 77.4614%, 0.9663 78.4104%, 0.973 79.4781%, 1 81.6133%, 0.986 83.0368%, 0.9824 83.7485%, 0.9811 84.4603%, 0.982 85.172%, 0.9852 85.8837%, 0.9997 87.4259%, 0.9918 88.7307%, 0.9898 89.7983%, 0.9923 90.7473%, 0.9998 91.8149%, 0.9954 92.8826%, 0.9945 93.5943%, 0.9959 94.306%, 1 95.1364%, 0.9981 95.6109%, 0.9972 96.3227%, 1 97.3903%, 0.9988 98.102%, 0.9998 98.8138%, 1 100%);
    }

    二、linear()函数如何使用?

    大家无需关心linear()的语法,因为实际使用的时候,都是通过工具生成的。

    这里介绍两个工具:

    • linear-easing-generator
    • Nordcraft css-linear

      这个工具在上面链接文章的中间部分,如下截图所示,选择对应的动画类型,可以看到对应的运动曲线、效果预览和对应的代码:

      liner()效果示意

    这里,重点介绍下上面的工具(仓库地址:linear-easing-generator),因为其支持自定义实现。

    缓动函数转linear()代码

    网上有很多动画函数的,例如比较经典的Bounce弹动函数:

    1 - Math.abs(Math.cos(t * 3.5 * Math.PI) * (1 - t))

    使用的时候,我们直接将linear-easing-generator这个页面左侧的函数替换成下图所示的这样:

    缓动函数示意

    此时,此工具就会帮我买生成对应的linear()函数代码(如下图所示),其中的CSS变量名称就是self.xxxx后面的属性值决定的,我们还能调整动画的精度。

    生成代码示意

    更多的缓动函数

    不得不提一下我的这个Tween.js项目( https://github.com/zhangxinxu/Tween ),目前Star已经破千了。

    其中展示了十几个经典的各种缓动函数算法,访问见这里

    不过这些函数都是4个参数(如下图所示),我们需要将部分参数常量化才能使用:

    代码参数示意

    以Bounce.easeOut方法举例,这是其原本的函数:

    Bounce.easeOut = function(t, b, c, d) {
        if ((t /= d) < (1 / 2.75)) {
            return c * (7.5625 * t * t) + b;
        } else if (t < (2 / 2.75)) {
            return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
        } else if (t < (2.5 / 2.75)) {
            return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
        } else {
            return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
        }
    }

    我们只需要将b, c, d参数设置为常量值就可以了,如下所示:

    self.bounceEaseOut = function(t) {
      const d = 1.333;
      const b = 0;
      const c = 1;
      if ((t /= d) 
    

    此时,右下角就可以看到如下所示的linear()函数代码了,学到了木有?

    :root {
      --bounce-ease-out-easing: linear(0, 0.004, 0.016, 0.035, 0.062, 0.098, 0.141, 0.191, 0.25 24.2%, 0.39, 0.562, 0.765, 1 48.5%, 0.943, 0.893, 0.851, 0.816, 0.788, 0.768, 0.755, 0.75, 0.753, 0.763, 0.782, 0.809, 0.844, 0.888, 0.94, 1, 0.972);
    }

    生成曲线和动画效果示意

    三、了解下linear()函数的原理

    linear() 函数的核心在于让动画进度在指定的多个关键点之间以恒定速率变化,但不同区段可以设置不同的进度变化速度,从而形成一种“分段线性”的缓动效果。

    • 接受参数:它接受两个或更多的数值参数,这些参数代表动画的进度(通常介于 0 到 1 之间,0% 表示动画开始,100% 表示完成)。
    • 分段线性:浏览器会根据你提供的这些点,计算出多个线性的速度段。在每个区段内,动画的速度是恒定的,但不同区段之间速度可能会发生突变(直接转折)。
    • 可选百分比:你还可以为每个进度点指定一个时间百分比,用以控制该进度点出现在动画持续时间的哪个时刻。如果不指定,浏览器会自动在时间上均匀分布这些进度点。

    语法和参数

    linear() 函数可以用于 animation-timing-functiontransition-timing-function 属性。

    /* 语法 */
    animation-timing-function: linear(<点列表>);
    transition-timing-function: linear(<点列表>);
    

    参数示例与解释

    • linear(0, 1):这表示从开始 (0) 到结束 (1) 的速度是恒定的。它的效果与关键字 linear 完全相同。
    • linear(0, 0.9, 1):这表示动画的前 50% 的时间播放了整体进度的前 90%,而后 50% 的时间仅播放剩下的 10% 的动画。这意味着前半段动画“很快”,后半段动画“很慢”。
    • linear(0, 0.5 20%, 0.8 60%, 1):这是一个更复杂的例子,它通过百分比精确指定了进度点出现的时间:
      • 前 20% 的时间:动画从 0% 进展到 50%。
      • 中间 40% 的时间(从 20% 到 60%):动画从 50% 进展到 80%。
      • 最后 40% 的时间(从 60% 到 100%):动画从 80% 进展到 100%。

    与其他缓动函数对比

    缓动函数 特点 适用场景
    linear() 分段恒定速度,速度变化直接转折 需要精确控制不同阶段进度变化速率的动画
    linear (关键字) 从开始到结束完全恒定的速度 进度条、机械感强的动画
    ease (默认) 慢速开始,加速,然后慢速结束 大多数UI交互,如按钮悬停、菜单展开
    ease-in 慢速开始,逐渐加速 元素从页面中离开
    ease-out 快速开始,逐渐减速 元素进入页面
    ease-in-out 慢速开始和结束,中间较快 具有明确开始和结束状态的动画
    cubic-bezier(n,n,n,n) 通过贝塞尔曲线自定义速度曲线,可创建非常丰富和自然的加速度变化 当预定义函数无法满足需求,需要特定加速度模型时

    四、最后的小结

    linear() 函数为你提供了另一种控制动画节奏的工具。它不是通过平滑的加速度曲线,而是通过定义多个关键进度点及其(可选的)出现时间,来创建一种分段恒速、速度变化直接转折的动画效果。当你需要实现一些非传统、有特定节奏或阶梯式进度变化的动画时,linear() 会非常有用。

    最后,渔获展示时间,周六钓的,钓费120元,两条鱼加起来10斤出头一点,切线两个大的:

    渔获

    😉😊😇
    🥰😍😘

    本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
    本文地址:https://www.zhangxinxu.com/wordpress/?p=11841

    (本篇完)

    @function自定义函数让CSS支持编程啦

    2025-09-05 11:08:44

    by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11847
    本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。

    @function封面图

    一、@function规则来啦

    Chrome 139浏览器除了新支持了 CSS if()函数(参见“CSS倒反天罡居然支持if()函数了”一文),还支持了期待已久的@function规则。

    @function函数的兼容性

    @function规则可以让我们自定义运算逻辑,比之前单纯的CSS var()变量要更加强大。

    下面我们通过几个案例带大家快速了解下@function规则的用法和作用。

    二、@function规则精彩案例

    先看下面这段代码,是我实际项目中代码的截图,大家注意看红框框部分:

    源码示意截图

    字符版如下:

    .board-bar-x {
      /* 略... */
      --bottom: calc(var(--offset-bottom, 0px) + 15px + 0.25rem);
      --bottom2: calc(var(--offset-bottom2, 0px) + 15px + 0.25rem);
      --size: calc(20px + calc((var(--char-length) + 1) * 1ch));
      --size2: calc(20px + calc((var(--char-length2) + 1) * 1ch));
    }

    实现功能是使用::before::after伪元素绘制两条线。

    在没有@function规则之前,当一个复杂计算需要多处使用的时候,我会定义专门的CSS变量进行简化,例如上面的--bottom--bottom2,可即便如此,后面的计算逻辑我还是要一个一个罗列。

    上面这句话有些人估计不明白,那我重新处理下,如果不使用CSS变量进行二次变量处理,那么实际的CSS代码会是这样(仅展示关键部分):

    .board-bar-x::before {
      position: absolute;
      bottom: calc(var(--offset-bottom, 0px) + 15px + 0.25rem);
      padding-bottom: calc(var(--offset-bottom, 0px) + 15px + 0.25rem);
    }
    .board-bar-x::after {
      position: absolute;
      bottom: calc(var(--offset-bottom2, 0px) + 15px + 0.25rem);
      padding-bottom: calc(var(--offset-bottom2, 0px) + 15px + 0.25rem);
    }

    会出现4个冗长的CSS属性值,使用CSS变量简化后,就会是这样:

    .board-bar-x::before {
      position: absolute;
      bottom: var(--bottom);
      padding-bottom: var(--bottom);
    }
    .board-bar-x::after {
      position: absolute;
      bottom: var(--bottom2);
      padding-bottom: var(--bottom2);
    }

    但是,定义变量的那个地方,还是需要一一罗列:

    .board-bar-x {
      --bottom: calc(var(--offset-bottom, 0px) + 15px + 0.25rem);
      --bottom2: calc(var(--offset-bottom2, 0px) + 15px + 0.25rem);
    }

    CSS @function就可以将这种罗列,也一并处理掉。代码示意:

    @function --fn(--offset) {
      result: calc(var(--offset, 0px) + 15px + 0.25rem)
    }
    .board-bar-x {
      --bottom: --fn(var(--offset-bottom));
      --bottom2: --fn(var(--offset-bottom2));
    }
    .board-bar-x::before {
      position: absolute;
      bottom: var(--bottom);
      padding-bottom: var(--bottom);
    }
    .board-bar-x::after {
      position: absolute;
      bottom: var(--bottom2);
      padding-bottom: var(--bottom2);
    }

    或者不再使用CSS变量过渡,直接一步到位。

    @function --fn (--offset) {
      result: calc(var(--offset, 0px) + 15px + 0.25rem)
    }
    .board-bar-x::before {
      position: absolute;
      bottom: --fn(var(--offset-bottom));
      padding-bottom: --fn(var(--offset-bottom));
    }
    .board-bar-x::after {
      position: absolute;
      bottom: --fn(var(--offset-bottom2));
      padding-bottom: --fn(var(--offset-bottom2));
    }

    是不是就容易理解多了?

    三、更多自定义函数案例

    1. 适合学习案例-取负值

    CSS代码如下所示:

    /* 返回当前值的负值 */
    @function --negate(--value) {
      result: calc(-1 * var(--value));
    }
    aside {
      --size: 999em;
      padding-bottom: var(--size);
      margin-bottom: --negate(var(--size));
    }

    2. 高级案例 – media查询

    /* 在小于640px的屏幕上为侧边栏占用1fr的空间,在较大的屏幕上使用相对具体具体宽度值 */
    @function --layout-sidebar(--sidebar-width: max(20ch, 20vw)) {
      result: 1fr;
      
      @media (width > 640px) {
        result: var(--sidebar-width) auto;
      }
    }
    
    .layout {
      display: grid;
      /*侧边栏宽度兜底20ch和20vw的较大计算值 */
      grid-template-columns: --layout-sidebar();
    }

    实时效果如下所示(拖拽右下角的小按钮,改变宽度,看看布局有没有变化):

    3. 高级案例 – 和 if() 函数一起

    示意代码:

    @function --light-dark(--light, --dark) {
      result: if(
        style(--scheme: dark): var(--dark);
        else: var(--light)
      );
    }

    只要在body或者HTML元素上设置 style="--scheme: dark",就会自动使用黑夜模式的变量。

    四、结语及碎碎念

    CSS 的 @function 规则为样式表带来了真正的编程能力,允许你定义可重用的计算逻辑,让 CSS 代码更灵活、模块化且易于维护。

    基本语法

    使用 @function 定义,result 返回结果,示意:

    @function --fn-name(arg) { 
      result: ... 
    }

    支持传递参数,并可设置默认值。

    例如:

    @function --border(--color: red) { ... }

    其他特性

    支持声明参数类型(如 length, color, number, angle),增强健壮性。

    函数内部可以调用其他函数,实现逻辑复用,或者使用嵌套其他规则,上面有案例。

    总结一下 CSS @function 通过引入参数、返回值、类型检查和组合能力,让 CSS 拥有了更强大的抽象和复用能力。它是实现 “CSS 即设计系统” 理念的重要工具,能有效提升大型或动态项目的样式代码质量。

    OK,以上就是本文的全部内容,如果你觉得还不错,欢迎转发,点赞,分享。

    😉😊😇
    🥰😍😘

    本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
    本文地址:https://www.zhangxinxu.com/wordpress/?p=11847

    (本篇完)

    HTML之快速了解hidden=until-found的作用

    2025-08-29 16:20:18

    by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11844
    本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。

    一、hidden属性新增新值

    任意HTML元素,设置hidden属性后,会隐藏自身(样式没有被重置的前提下)。

    可能有些人并不知道,hidden属性还支持一个名为until-found值,他可以让内容隐藏,但是在特殊场景下也会显示。

    这个特殊场景包括锚点锚定,或者浏览器层面的文字搜索匹配。

    我们开看例子。

    如下HTML代码:

    <div class="container">
      <a href="#zhangxinxu">显示作者名字</a>
      <h5 class="target">
        <p id="zhangxinxu" hidden="until-found">名字是张鑫旭</p>
      </h5>
    </div>

    我们不妨扔一点样式美化下,至于什么样式不重要,总之,就有会如下所示的实时渲染效果:

    显示作者名字

    此时,我们点击上面的链接文字,触发锚点跳转,就会发现,原本隐藏的<p>元素显示了。

    就像下图这样:

    姓名显示后的效果

    大家再试试搜索

    如果是桌面端浏览器,试试按下Ctrl+F搜索,例如:

    搜索示意

    此时,当我们的搜索选项与hidden="until-found"里面的内容匹配的时候,这段内容就突然显示了,且高亮的颜色(橙色背景)和其他区域匹配的文字颜色(黄色背景)不同,如下截图所示:

    搜索高亮

    二、beforematch 事件

    until-found隐藏的元素还支持一个名为beforematch的事件,在该元素从隐藏变成显示之前的一瞬间执行。

    还是上面的案例,我们给元素加个beforematch事件,让显示的时候,外面的容器边框颜色高亮,则可以这么处理:

    const untilFound = document.getElementById("zhangxinxu");
    untilFound.addEventListener('beforematch', () => {
      untilFound.closest('div').style.borderColor = 'red';
    });

    实时测试效果如下(大胆点击链接,看看边框红了没有):

    显示作者名字

    三、content-visibility: hidden

    hidden="until-found"隐藏和传统的hidden隐藏不同,后者是display:none隐藏,但是,前者使用的是content-visibility:hidden隐藏,这个隐藏在我的《CSS新世界》这本书中有介绍。

    content-visibility:hidden只会隐藏内容,元素的边框、布局、背景都是保留的,想必visibility: hidden,其不会除非内容渲染,在频繁显隐交互的场景下有更高的性能。

    content-visibility示意

    所以,hidden="until-found"直接设置在内容元素上是无效的,例如保持内联水平的<span>元素上,等。

    四、until-found的应用场景

    基于其“隐藏但可被发现”的特性,hidden=”until-found” 非常适合以下场景:

    • 可折叠/可展开内容(如手风琴菜单):这是比较典型的应用场景。你可以将折叠起来的内容设置为 hidden=”until-found”。这样,即使用户没有展开这些内容,他们仍然可以通过页面查找功能搜索到里面的信息。当用户通过查找或片段导航找到这些隐藏内容时,浏览器会自动移除 hidden 属性并将其显示出来。
    • 初始隐藏的提示或帮助信息:页面上有些辅助信息或详细说明可能默认是折叠或隐藏的,以保持界面简洁。使用 hidden=”until-found” 可以确保用户需要通过搜索特定关键词来获取这些帮助信息时,能够轻松找到它们。
    • 标签页(Tabs)中未激活的内容:在一些标签页界面中,非当前激活的标签页内容通常是隐藏的。如果你希望用户能通过搜索找到所有标签页内的内容,而不仅仅是当前显示的那个,hidden=”until-found” 可以提供帮助。

    至于提高页面内容显隐的渲染性能,这个大家就不用考虑了,没有任何意义,1ms和2ms的差异有区别吗?没有区别,对吧。

    五、兼容性、结语、渔获

    Safari浏览器已经确定支持该特性,Chrome浏览器已经支持好几年了。

    until-found兼容性截图

    结语

    懒得结语

    渔获展示

    见上一篇文章。

    本周钓货回头补上。

    好了,就这样吧。

    😉😊😇
    🥰😍😘

    本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
    本文地址:https://www.zhangxinxu.com/wordpress/?p=11844

    (本篇完)