欢迎来到我的博客
滚动该页面,观察右上角的导航菜单,会发现当前所在的章节会被高亮显示。
前言
本博客09年更新到现在,已经16年历史了,见证了自己的成长,以及前端行业的发展。
如果你觉得其中的内容还不错,欢迎分享,欢迎点赞。
第1章
第1章的内容
也欢迎关注我的抖音账号“张鑫旭本人”。
第2章
我平时还喜欢钓鱼,钓友可以关注我的抖音账号“最会钓鱼的程序员”。
2025-10-27 15:43:08
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11914
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。

好消息,Canvas也支持锥形渐变了,方法名称是createConicGradient()方法。
兼容性参见下图:

所有现代浏览器都已经支持,且已经支持了2年了。
最近遇到了个需求,必须使用Canvas实现锥形渐变,而不能是CSS。
那就是下图所示的饼图效果:

如果单论实现,无论是CSS还是Canvas都可以,CSS更佳,因为成本更低。
但是,注意看图片的左上角,还有一个下载为图片的功能。
html2canvas是不支持CSS锥形渐变的,保存的图片是空白。
所以需要Canvas实现,转换为图片,这样就能截图保存了。

也是就用到了这个createConicGradient()方法来绘制饼图。
语法如下:
createConicGradient(startAngle, x, y)
表示创建一个锥形渐变开始的位置。
其中startAngle是锥形的圆弧所在,需要注意的是,和CSS不同,在Canvas中,角度0表示的是三点钟方向,有就是水平朝右,但是CSS是12点钟方向,垂直朝上。
我们通过一个案例来了解下createConicGradient()方法是如何使用的。
<canvas></canvas>
绘制代码为:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
// 尺寸
const size = 300;
canvas.width = size;
canvas.height = size;
// 创建锥形渐变
const gradient = ctx.createConicGradient(-0.5 * Math.PI, size / 2, size / 2);
// 色带颜色
const arrColor = ['#10239E', '#2F54EB','#597EF7', '#85A5FF', '#D6E4FF'];
// 各自占据的百分比
const arrPercent = [0.07, 0.13, 0.2, 0.27, 0.33];
// 添加中断点
let sumPercent = 0;
arrPercent.forEach((percent, index) => {
gradient.addColorStop(sumPercent, arrColor[index]);
sumPercent += percent;
gradient.addColorStop(sumPercent, arrColor[index]);
});
// 设置渐变为填充样式
ctx.fillStyle = gradient;
// 绘制圆形
ctx.ellipse(size / 2, size / 2, size / 2 - 30, size / 2 - 30, 0, 0, 2 * Math.PI);
// 填充
ctx.fill();
在浏览器中的实时绘制效果如下:
完美!
在过去,本文所展示的饼图效果,我们可以使用arc()扇形绘制方法模拟,非本文重点,不做展开。
如果你让AI实现锥形渐变,大概率会使用CSS conic-gradient()锥形渐变,但是你让他解决html2canvas无法导出图片的问题,AI就会扯东扯西,不知道该如何是好了。
所以,新时代,我们学习前端,可能具体的技术细节不需要掌握,但是,结构、方向、策略、细节还是需要掌握的。
比方说本文的案例,你需要知道使用Canvas来绘制渐变,指导AI使用这个方法,他就能搞定,否则,AI就会迷路。
其中这样的例子很多。
例如,之前有同事想要在手机端实现划词交互效果,问AI,结果AI弄了一大堆代码,使用鼠标事件实现的,很啰嗦。于是同事就问我,我就说试试selectionchange事件,这么一点拨,AI立马就糊涂状态变成了大师。
还有案例,可编辑输入框类似于AT成组的交互,AI的实现也是,洋洋洒洒,代码多得不得了,感觉把整个编辑器都弄进来了。
实际,走DOM监控单向数据流,就百来行代码的事情。
所以说,学习新特性还是有用的。
我也不会担心自己被AI取代,只要持续学习,不排斥新事物,我只会比之前更加具有竞争力。
好了,又扯多了,最后,我家侍妾慕沛灵压阵,道友请转发!

😉😊😇
🥰😍😘
本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=11914
(本篇完)
2025-10-20 17:56:39
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11889
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。
继续介绍前端前沿新特性,这次介绍的是两个改进键盘无障碍访问的CSS属性reading-flow和reading-order。
在Web开发中,DOM文档的属性和视觉表现顺序不一致是很正常的。
但是,此时,如果我们不进行干预,用户在使用Tab键进行索引访问的时候,顺序就会出现问题,导致困惑。
在过去,我们会使用tabindex属性进行交互处理。举个例子:
<div class="box" aria-owns="第一 第三 第二"> <a href="#" tabindex="1" id="one">第一</a> <a href="#" tabindex="3" id="two">第二</a> <a href="#" tabindex="2" id="three">第三</a> </div>
tabindex值为3的地2项就会被最后一个获取。
关于tabindex属性的具体规则,可以参见我之前的文章“HTML tabindex属性与web网页键盘无障碍访问”,或者购买《HTML并不简单》这本书,其中有详细介绍。
在比较简单的页面,我们使用tabindex属性是没问题的,但是页面比较复杂,有非常多的地方有类似的结构,那就会出现tabindex设置冲突的问题,因为tabindex的索引控制是全局的。
对于,对于这种局部布局的tabindex属性调整,需要有更加友好的特性,在这种背景下,reading-flow和reading-order属性应运而生。
在大多数场景下,我们都是使用reading-flow属性,且需要与Flex布局和Grid布局配合使用。
我们先看使用案例,HTML和CSS代码如下所示:
<div class="box"> <a href="##">张</a> <a href="##">鑫</a> <a href="##">旭</a> </div>
.box {
display: flex;
:nth-child(1) {
order: 2;
}
}
实时效果如下所示,您可以尝试使用 TAB 键查找下一个可聚焦元素,使用 TAB+SHIFT 键查找上一个可聚焦元素,观察焦点聚焦的顺序。
友情提示,可以先点击框框的空白处,再按下Tab键,下同。
结果会发现,第一个被聚焦的元素是视觉表现上排在随后的元素,如下截图所示:

这就会产生困惑,对吧,此时我们可以使用 reading-flow 属性优化此问题,一行代码的事情:
.box {
reading-flow: flex-visual;
}
我们再来看下实时渲染效果(Chrome 137+):
此时第一个聚焦的元素就是最前面的“鑫”字啦。

如果Flex容器设置设置reading-flow:flex-flow,那么,浏览器读取内容的方向就会和flex-flow属性保持一致。
PS:flex-flow属性是flex-direction和flex-wrap属性的缩写属性。
例如flex容器新增CSS样式:
.box {
flex-direction: row-reverse;
reading-flow: flex-flow;
}
那么焦点聚焦的顺序就会是“鑫-旭-张”,从后往前依次聚焦,实时渲染效果如下:
如果我们取消第一项所设置的order:2,焦点方向还是从后往前,也就是按照Flex方向的聚焦顺序,不受DOM在文档中的顺序影响。
reading-flow属性由于Grid是二维布局的,因此,在Grid布局中,reading-flow属性支持的值要多一些,示意:
reading-flow: grid-columns; reading-flow: grid-rows; reading-flow: grid-order;
还是通过案例进行学习。
HTML基本结构和CSS如下:
.wrapper { display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: 100px; } .wrapper a:nth-child(2) { grid-column: 3; grid-row: 2 / 4; } .wrapper a:nth-child(5) { grid-column: 1 / 3; grid-row: 1 / 3; }此时的布局效果渲染如下,Tab索引顺序则按照DOM的顺序执行,也就是甲乙丙丁戊,但是,这并不是布局上的视觉顺序。
如果我们希望Tab顺序按照视觉顺序水平聚焦,则就可以设置:
.wrapper { reading-flow: grid-rows; }实时渲染效果如下:
此时,焦点聚焦的顺序就是“戊-甲-乙-丙-丁”,如下示意图所示:
再看看
grid-columns的效果:.wrapper { reading-flow: grid-columns; }可以键盘访问下面的布局亲自感受焦点顺序(Chrome 137+):
此时,焦点聚焦的顺序就是“戊-丙-丁-甲-乙”,如下示意图所示:
关于grid-order
这个用在使用order属性改变网格顺序的场景下,此时,焦点属性会优先按照order属性切换,例如:
.wrapper { reading-flow: grid-order; } .wrapper a:nth-child(4) { order: -1; }此时,优先被聚焦的是“丁”这个设置了
order:-1的网格。实时效果:
四、什么时候使用reading-order属性
reading-order属性通常和reading-flow:source-order声明配合使用,可以强制任意布局中,子项的焦点获取顺序,值为数值,可以是负值,例如:<div class="follow"> <a href="##">欢迎</a> <a href="##">抖音</a> <a href="##">关注</a> <a href="##" class="me">最会钓鱼的程序员</a> </div>.follow { display: flow-root; reading-flow: source-order; } .me { reading-order: -1; }实时效果如下,可以看到,我的抖音钓鱼账号的名称“最会钓鱼的程序员”被第一个聚焦了!
截图示意:
四、兼容性、结语
目前
reading-flow和reading-order属性仅Chrome浏览器,极其使用其内核的浏览器支持:
不过此特性是渐进增强特性,可以放心使用,基本上,我建议,只要是Flex布局,都可以加上
reading-flow:flex-visual这么一句话,浏览器不支持,或者Flex布局本身顺序正常,那就还和现在一样,但是如果浏览器支持,用户的无障碍访问体验就会UP,这种只有好处,没有坏处的事情,没有理由不去做他!结语碎碎念
这些边边角角的CSS新特性实在是太多了,我每周介绍一个,感觉都来不及。
但是,怎么讲呢?大多都是锦上添花的东西,现有的技术也能解决,但是由于兼容性等原因,学了,也不能立即使用,这就导致这些新特性啊,大多都会湮没在时代的洪流中。
以后这种学习的事情啊,我觉得都会交给AI了。
来,我的仆人AI酱,帮我把这段代码的无障碍访问体验提高下。
然后AI酱:“好滴,我最美丽的主人,让我看看这段代码,哦,这样子,我明白了,确实可以提升,我搜到了一个名叫钓鱼佬的博客,里面有介绍到
reading-flow和reading-order属性,可以一用……”瞧瞧没,需要学习吗?不需要。
那我写这些东西还有什么意义呢?值得深思的问题。
好了,就这样吧,感谢阅读,欢迎转发,在新特性的学习这方面,人类比AI还是有个半年到一年的优势的。
有请落云宗柳玉掌门压阵:
😉😊😇
🥰😍😘本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=11889(本篇完)
2025-10-13 11:59:37
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11880
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。

CSS field-sizing是一个CSS新特性,专门给输入型表单元素使用的,例如<input>、<textarea>元素等。
是这样的,在过去,无论是单行输入框,还是多行文本域,其宽度在内容输入的时候,都是固定的,如果希望尺寸跟着内容的宽度走,需要使用JavaScript代码帮忙处理,现在,无需这么麻烦,使用field-sizing设置下就好了,例如:
input { field-sizing: content }
<input placeholder="输入内容">
此时,你在输入框内键入内容,可以看到,输入框的宽度基于你输入的内容多少自动撑大或缩小了。
实时效果如下所示(Chrome 123+,或者最新的Safari),大家可以再下面的输入框随便写点什么内容:
是不是可以看到,输入框的宽度紧跟着文字内容多少变化了?
语法很简单,就下面这几个属性值:
field-sizing: content; field-sizing: fixed;
其中:
fixed是默认值,表示尺寸固定。content表示尺寸根据内容多少进行变化。没什么好讲的。
一些补充说明:
field-sizing:content声明的时候,原本会影响尺寸的size属性就会变得无效。rows和cols属性也会无效。field-sizing属性也可以用在<select>元素上。min-width和max-width属性限制输入框的最小尺寸和最大尺寸。避免内容过多的时候,影响排版布局。好了,其实就这么点内容,我觉得这个特性还是有点用处的。
最后,兼容性:

Safari已经明确支持了。
好,就这么多!感谢阅读,欢迎点赞,转发。
😉😊😇
🥰😍😘
本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=11880
(本篇完)
2025-09-30 10:57:06
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11883
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。

我们平常获取字符串的长度都是直接使用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
是不是很美丽?

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 是 JavaScript 中一个专门用于文本分段的强大工具,它能够根据不同的语言和文本规则,将字符串智能地分割成有意义的单位,如字符、单词或句子。
Intl.Segmenter 的核心作用在于进行语言敏感的分段。
传统的字符串分割方法(如 split(‘ ‘))对于英语这类使用空格分隔单词的语言勉强适用,但对于中文、日文等不依赖空格的语言就无能为力了,甚至处理包含复杂表情符号(Emoji)的文本时也会出错。
Intl.Segmenter 则依据 Unicode 标准,能够理解不同语言的语法习惯,进行精准的边界检测。
使用示意:
const segmenter = new Intl.Segmenter([locales[, options]]);
其中:
granularity
表示分段的粒度,是最重要的选项。可取值为:
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命名对象还有很多其他方法,多是与语言、金钱、数字、字符处理相关的。
详见我之前的这篇文章:“JS Intl对象完整简介及在中文中的应用”
还是非常强大与实用的。
就是语法复杂了点,学习成本比较高,真正使用的时候,需要去搜索查找资料。
不过现在都有AI了,只需要知道有这么个东西就好了,AI会帮你搞定。
行吧,就说这么多。
明天就是国庆节了,祝大家国庆节快乐!
不说了,我要准备出发去钓鱼了!
欢迎大家关注我的钓鱼账号:“最会钓鱼的程序员”。
随时了解我国庆节的战况。

😉😊😇
🥰😍😘
本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=11883
(本篇完)
2025-09-19 20:12:32
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11837
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。

本文要介绍的 CSS scroll-target-group属性以及:target-current伪类,可以实现滚动的时候,基于位置不同,对应的菜单项自动高亮的效果。
关于这种效果的实现,我之前还专门写文章介绍过:“尝试使用JS IntersectionObserver让标题和导航联动”。
没想到,短短几年之后,纯CSS就能实现这样的效果了。
眼见为实,滚动下面内嵌的局部滚动条(需要Chrome 140+),大家就可以看到对应的效果了。
滚动该页面,观察右上角的导航菜单,会发现当前所在的章节会被高亮显示。
本博客09年更新到现在,已经16年历史了,见证了自己的成长,以及前端行业的发展。
如果你觉得其中的内容还不错,欢迎分享,欢迎点赞。
第1章的内容
也欢迎关注我的抖音账号“张鑫旭本人”。
我平时还喜欢钓鱼,钓友可以关注我的抖音账号“最会钓鱼的程序员”。
或者参见下图所示的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-marker伪元素的特性( CSS Carousel API中的一个特性),关于::scroll-marker伪元素,可以参见我前几个月刚写的文章:“CSS ::scroll-button ::scroll-marker伪元素又是干嘛用的”。
需要使用的时候,直接设置值为auto就可以了。
非常新的一个特性,MDN连文档都没有,Chrome 140浏览器支持。

:target-current伪类相对支持早一些,和::scroll-marker伪元素等特性同一时间支持的,基于滚动位置匹配scroll-marker-group或者scroll-target-group里面的锚点或标记元素。
使用示意:
::scroll-marker {
background-color: white;
}
::scroll-marker:target-current {
background-color: black;
}
Chrome 135+浏览器支持。

很好很强,静待浏览器全面支持。
我发现CSS的野心挺大的,看架势,要覆盖几乎Web上常见的交互,JS退让为逻辑处理。
不过一来学习成本高,二来以后有AI,三来有成熟替代方案,未来会有几个人使用呢?我看悬。
所以前端没啥搞头了,不如来钓鱼吧,欢迎大家关注我的钓鱼账号:“最会钓鱼的程序员”。
周周更新!

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