MoreRSS

site iconZhangXinXu | 张鑫旭修改

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

Inoreader Feedly Follow Feedbin Local Reader

ZhangXinXu | 张鑫旭的 RSS 预览

DOM新特性之caretPositionFromPoint API

2025-04-14 22:07:41

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11620
本文可全文转载,独立域名个人网站无需授权,但需要保留原作者、出处以及文中链接,任何网站均可摘要聚合,商用请联系授权。

封面图,小鹿看鸟

一、caretPositionFromPoint特性的语法和作用

caretPositionFromPoint是浏览器支持不久的一个新特性,可以基于当前的光标位置,返回光标所对应元素的位置信息,在之前,此特性使用的是非标准的document.caretRangeFromPoint()方法实现的。

caretPositionFromPoint()方法和elementsFromPoint()方法的区别在于,前者返回节点及其偏移、尺寸等信息,而后者返回元素。

比方说有一段<p>元素文字描述信息,点击这段描述的某个文字,caretPositionFromPoint()方法可以返回精确的文本节点以及点击位置的字符偏移值,而elementsFromPoint()方法只能返回当前<p>元素。

作用

caretPositionFromPoint()的作用场景比较小众,一般用在分词分句这种交互场景中。

例如制作视频封面的时候,书封标题的文字需要中断,此时,就可以直接点击实现。

语法

语法如下所示:

const range = document.caretPositionFromPoint(x, y, options);

其中:

  • x, y就是相对于浏览器窗口左上角的坐标值。
  • options是一个可选参数,支持一个名为shadowRoots的属性,其值为一个ShadowRoot对象数组。该方法可以返回在shadow DOM中定义的节点的插入符号位置。如果不设置,返回的会是shadow DOM根元素。

二、caretPositionFromPoint使用案例

这里通过一个书封书名中断简单示意下caretPositionFromPoint()方法的使用。

已知某书封HTML结构如下所示:

<figure>
    <img src="https://image.zhangxinxu.com/image/blog/202308/nonce-popover-cover.png">
    <figcaption>我是书名点我断句</figcaption>
</figure>

然后figcaption设置允许换行符换行效果:

figcaption {
    white-space: pre-wrap;
}

则下面的代码可以实现点击figcaption元素,自动中断:

// 中断方法
figcaption.addEventListener('click', function (event) {
    const { clientX, clientY } = event;
    let range;
    let textNode;
    let offset;

    if (document.caretPositionFromPoint) {
        range = document.caretPositionFromPoint(clientX, clientY);
        textNode = range.offsetNode;
        offset = range.offset;
    } else if (document.caretRangeFromPoint) {
        // 使用非标准的老的API代替
        range = document.caretRangeFromPoint(clientX, clientY);
        textNode = range.startContainer;
        offset = range.startOffset;
    } else {
        return;
    }
    
    // 文本节点在当前偏移位置分隔,返回的是后面的节点
    const replacement = textNode.splitText(offset);
    const brNode = document.createTextNode('\n');
    replacement.before(brNode);
});

眼见为实,您可以狠狠地点击这里:caretPositionFromPoint书名手动换行demo

最终实现的效果如下GIF所示,点击哪里的文字,文字就在哪里换行了:

点击文字断行GIF示意图

三、兼容性、点评与结语

caretPositionFromPoint的兼容性如下截图所示,Chrome 128版本开始支持的:

caretPositionFromPoint兼容性

Safari浏览器目前还不支持,我们可以使用caretRangeFromPoint代替,这个特性支持很久了:

caretRangeFromPoint兼容性

具体如何使用,可参见上面的演示页面源码。

点评

返回特定坐标的节点和选区偏移可以让我们精确知道当前坐标的文字内容是什么,又一定的实用价值。

但我们日常开发很少遇到相关的需求,因此,我建议大家了解下这个属性即可,知道Web有这么一个能力,当我们遇到相关场景的时候,知道有这么个方法可以解决即可。

OK,就说这么多。

感谢大家的阅读,我们下一篇文章再见,还是介绍新特性的。

👋🏻👋🏻👋🏻

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

(本篇完)

JS EditContext API 简介

2025-04-06 20:57:59

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11618
本文可全文转载,独立域名个人网站无需授权,但需要保留原作者、出处以及文中链接,任何网站均可摘要聚合,商用请联系授权。

EditContext API 简介

一、EditContext API的作用和设计背景

JS EditContext API可以用来实现比较高级的Web输入编辑功能,包括控制更精细,反应更灵敏,和输入法配合更完美等。

目前我们实现内容输入与编辑多是借用<input><textarea>元素,如果有富文本编辑内容(常见的文章发布编辑器),则是借助contenteditable属性,或者使用CSS -webkit-user-modify属性。

但是,以contenteditable属性举例,其自带的很多内置行为在高级开发的时候是不需要的。

举几个例子,有序或无需列表在起始位置按下Backspace删除,默认行为是当前列表删除,同时内容和上一个列表合并,但往往我们希望的是取消列表状态。

Tab键之后二级缩进而不是创建Tab空格。

链接文本后面换行或输入空格,自动超链接。

换行使用<p>而不是div。

内容inline-blockinline-flex之后需要作为整体,光标可以定位在后面,但是不能在里面。

这些内置行为均无法满足。

以及,一些开发场景需要使用Canvas做图形编辑器,例如,B站或者抖音视频上传时候封面的DIY,其中就有canvas中实现文本输入。

又比如需要协调开发的在线文档开发等。

总而言之,现有的Web编辑能力只能实现一些低层级的编辑功能,想要高级定制,需要很大的实现成本,基于这个背景,就设计了EditContext API,这个API是Microsoft Edge 团队推动支持的,可以将文本输入与 HTML DOM 视图分离,从而可以提供更好的开发体验。

二、EditContext的语法和使用

EditContext对象支持4个属性,7个方法和5种事件,具体参见MDN文档,我这里不做介绍,我自己也懒得去学习了,估计以后也用不到,因为Safari还没有看到支持的迹象。

EditContext属性和方法

这里,通过一个案例展示EditContext的使用,给定一个canvas元素:

<canvas id="editor"></canvas>

则下面这段JavaScript代码可以监听canvas画布上输入的内容(点击canvas元素,浏览器会自动):

const editContext = new EditContext();
const editorElement = document.getElementById("editor");
editorElement.editContext = editContext;

editContext.addEventListener('textupdate', (event) => {
    const newText = event.text;
    // 处理新输入的文本
    console.log('输入的文本是:', newText);
});

下图是我开启输入法之后的console输出效果截图:

输入法与提示

此时,我们就可以识别输入的内容,在canvas上进行绘制,实现canvas画布上的文字输入效果。

兼容性

EditContext API目前兼容性不算好,所以文本的标题才是简介:

EditContext API的兼容性

大家目前了解下就好了。

三、就说这么多

好吧,关于这个API就说这么多,我也懒得深入,因为深入了也是会被AI也剽窃了去,并不会给我带来任何好处,自然就没有研究这类我自己以后也不会使用的特性的动力了。

举例来说,今日有尝试询问AI对EditContext的理解,结果发现很多都是一本正经地胡说八道。

胡说八道示意

因为啥呢,因为这个特性特别的新的,业内,尤其中文圈子内,还没有相关的学习资料,于是就有很多错误的输出。

但是,等哪天抓取了我或者其他一些人的文章输出,自己再“学习”一下,输出会更准确些。

但,搜索相关信息的用户并不知道这个信息源是我提供了,也不会跳转到我这篇文章,对我而言,知识输出就是个吃力不讨好的事情,从某种意义上讲,AI的出现会抑制个体的原创动力。

ok,不再扯淡了,至此,本文over,赶在清明假期结束前写完了。

🧑‍🧑‍🧒🧑‍🧑‍🧒‍🧒🧑‍🧒

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

(本篇完)

光速了解script style link元素新增的blocking属性

2025-03-27 21:32:23

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11568
本文可全文转载,独立域名个人网站无需授权,但需要保留原作者、出处以及文中链接,任何网站均可摘要聚合,商用请联系授权。

HTML blocking属性

一、前言

继续介绍Web前端前言新特性,这次要介绍的是一个HTML属性,名为blocking 属性它主要用于控制资源加载时对渲染的阻塞行为。

blocking 属性允许开发者对资源加载的优先级和时机进行精细控制,从而影响页面的渲染流程。浏览器在解析 HTML 文档时,会根据 blocking 属性的值来决定是否等待资源加载完成后再继续渲染页面,这对于优化页面性能和提升用户体验至关重要。

blocking 属性目前支持的HTML元素包括<script>元素、<style>元素和<link>元素。

使用示意:

<link rel="stylesheet" href="styles.css" blocking="render">
<style blocking="render">
  p {
    color: blue;
  }
</style>
<script src="script.js" async blocking="render"></script>

二、属性值和含义

目前关于blocking 属性的资料非常少,根据我从某个不太权威的地方看到的资料,blocking 属性有以下几个可选值:

render
当资源的 blocking 属性设置为 render 时,资源的加载会阻塞页面的渲染。
load
当设置为 blocking=”load” 时,资源的加载会阻塞 load 事件的触发。load 事件在页面的所有资源(包括图片、脚本等)加载完成后触发。
none
资源的加载不会阻塞页面的渲染或 load 事件的触发。这意味着浏览器会在后台异步加载该资源,而不会等待它加载完成就继续渲染页面。这种方式适用于那些对页面渲染不是至关重要的资源,如一些统计脚本或非关键的图片。

上面的属性值解释似乎很合理。

但是我查阅MDN文档还有规范,似乎目前仅支持render属性值。

这让我很是不解,为什么呢?

比方说<link>元素天然阻止后面的资源渲染,设置blocking="render"是多余的,<style>元素也是类似。

唯一有点应用场景的就是<script>元素中和async这个异步属性同时设置的时候,也就是资源还是异步加载,但是页面的渲染执行需要这个JS加载完毕。

哦,不对,我想了下,如果<link>元素在页面的末尾,而不是<head>中,blocking="render"还是有作用的,虽然这个场景很罕见。

总之,blocking属性是个不知所谓,看不懂的CSS属性。

兼容性

目前block属性除了Firefox浏览器之外,其余的现代浏览器都已经支持了,如下截图所示:

blocking属性的兼容性

三、DOMTokenList特性

虽然blocking属性长得浓眉大眼的,但特么居然还是个DOMTokenList特性的属性。

在HTML这门语言中,具有DOMTokenList的属性并不多,一个是class属性,比方说我们经常使用的classList.add()/remove()方法,还有一个是rel属性,对应的是relList,还有就是for属性吧,而且是<output>元素上的for属性。

最后一个就是本文介绍的blocking属性了。

同样的,我们可以使用add/remove/toggle/contains等方法对相关属性进行编辑处理。

例如,假设有HTML如下:

<script id="el" async blocking="render"></script>

则我们就可以使用如下所示的JavaScript代码增加blocking另外的值,此值可以任意设置,虽然大概率没什么实际的用处。

el.blocking.add('zhangxinxu');

此时,HTML信息就是这样的:

<script id="el" async blocking="render zhangxinxu"></script>

截图示意:

add方法执行结果示意

完整属性和方法参见:

{
  add: function add() { [native code] },
  contains: function contains() { [native code] },
  entries: function entries() { [native code] },
  forEach: function forEach() { [native code] },
  item: function item() { [native code] },
  keys: function keys() { [native code] },
  length: 2,
  remove: function remove() { [native code] },
  replace: function replace() { [native code] },
  supports: function supports() { [native code] },
  toggle: function toggle() { [native code] },
  toString: function toString() { [native code] },
  value: "render zhangxinxu",
  values: function values() { [native code] }
}

四、点评一下总结一下

花里胡哨不知所谓的HTML属性,到现在我都没搞清楚这个属性到底是做什么用的。

鸡肋,蛋疼,与现有能力重复,很难找到与设计初衷相匹配的场景。

我认为唯一的作用,反而是其DOMTokenList特性,终于<script><style><link>元素有了个可以存储任意字符串数据的属性了。

我们可以利用blocking属性标记任意的数据,方便我们进行数据的交互。

好了,就这样吧,以后要是此属性有更新迭代,我再来更新吧。

网红图片吉卜力风格

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

(本篇完)

研究下attachInternals方法,可让普通元素有表单特性

2025-03-23 23:34:55

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11570
本文可全文转载,独立域名个人网站无需授权,但需要保留原作者、出处以及文中链接,任何网站均可摘要聚合,商用请联系授权。

封面占位图

本文又是独家内容,因为持续学习的人本就不多,持续关注前沿技术的人就更少,而关注自定义元素和原生表单特性的就更少,所以,也就我这样的老人家会研究并撰写相关的知识了。

可惜,现在做这类文字输出的收益极低,因为大家只能通过我的社交媒体账户获取,随着AI的日益普及,传统搜索引擎注定没落,而AI把我辛苦写的内容一抓,然后自己“思考”一番输出给用户,我什么好处都得不到。

某种意义上,AI智能是限制了原创内容输出的动力的,尤其是独立平台的个体创作者。

好了,不吐槽了,回到正题,讲下HTML元素的全局方法attachInternals()方法,此方法用在自定义元素中,也就是我们常说的Web Components组件中,可以让自定义元素具有原生表单元素一样的特性。

此方法的使用还是挺简单的,关键是如何理解其运作原理,想了下,没有通过一个案例来介绍更直观了。

一、自定义复选框组件案例

此案例描述如下:

有一个自定义的复选框元素,通过attachInternals()方法让其行为类似于原生的checkbox类型输入框,支持验证和表单提交。

下面这个演示页面就是最终实现的效果。

您可以狠狠地点击这里:HTML attachInternals自定义元素原生表单特性demo

相关HTML如下所示:

<form id="form" action="" novalidate>
    <ui-checkbox value="1" name="agree" required tabindex="-1"></ui-checkbox> 同意协议
    <p>
        <button type="submit">提交</button>
    </p>
</form>

默认渲染效果如下图所示:

表单渲染效果

非常朴实无华的样式效果,下面关键来了,如果我们点击提交按钮,就会发现,表单内置的提示效果出现了:

提示效果

如果我们点击复选框,使其选中,然后再点击提交按钮,可以看到自定义的表单数据就像原生的输入框一样,查询字符串在URL地址上呈现出来了。

原生表单提交一样的行为

是不是还挺神奇的,那具体如何实现的呢?

二、attachInternals方法的用法

单看语法,attachInternals方法使用还是挺简单的,如下:

const someInternals = someCustomElement.attachInternals() 

然后这个someInternals对象就包含多种原生表单元素所拥有的若干属性和方法,包括:

  • setFormValue(value: string|FormData|File, state?: any),表单赋值用的。
  • form — 获取所在的表单元素。
  • setValidity(flags: Partial<ValidityState>, message?: string, anchor?: HTMLElement) — 设置验证提示类型和文字信息,可以指定锚定元素。
  • willValidate — 表单提交的时候如果会触发验证,则返回值是true.
  • validity — 验证状态,是个对象。
  • validationMessage — 错误提示验证信息。
  • checkValidity — 检查是否合法,
  • reportValidity — 出现提示信息,如果不合法。
  • labels — label[for]属性关联元素
  • 用于在元素上设置aria信息的许多其他属性或方法。

上面这些属性和方法的详细介绍,可以参见我多年前撰写的这篇文章:“checkValidity等form原生JS验证方法和属性详细介绍

然而,事情并没有上面展示的那么简单。

首先第一点,attachInternals方法只适用于自定义元素,如果是常规元素,会报错,就像下面的错误截图:

表单只能自定义元素

其次,在构建自定义元素类的时候,需要让formAssociated这个静态属性的返回值是true。

使用示意:

class UiCheckbox extends HTMLElement {
  static get formAssociated() {
    return true;
  }

  constructor() {
    super();
    this.internals = this.attachInternals();
    // ...
  }
}

customElements.define('ui-checkbox', UiCheckbox);

最后,虽说上面的代码已经可以让<ui-checkbox>元素通过自定义的internals属性访问表单元素的原生属性和方法。

但是,类似表单提交字段传值,支持原生FormData这些,并不是默认就有的,根据我的尝试,需要使用上面提到的setFormValue()方法赋值。

包括验证提示的信息也需要自己进行自定义处理。

这里,不做进一步展开,如何使用的,访问demo演示页面,页面上有完整的源代码展示。

三、评价综述

attachInternals()确实有其使用场景,但是,我对此方法的普及与应用并不看好,两个原因:

其一,学习和使用门槛略高,适合喜欢走完全自定义路线的组件开发者。
其二,当下Web流行数据提交流,而不是form行为提交流,市场不广泛。

我细想了下,要是我,会使用此方法吗?

我是个原生开发推崇之人,以LuLu UI组件举例,里面所有的控件都是基于原生表单元素实现的,包括无法轻松样式自定义的下拉框,所以,想不到需要用到attachInternals()的场景。

除非是浏览器没有的组件,而这个组件同时有表单提交或者验证的需求,例如,使用div模拟的富文本编辑器。

嗯……再看吧,说不定哪天我就遇到时候使用attachInternals()方法的场景,到时候,我再文中补充好了。

以上就是本文的全部内容,感谢阅读!

也欢迎分享,让更多人了解,我家侍女先谢过诸位了。

侍女

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

(本篇完)

一种更好的文字隐藏的方法-::first-line伪元素

2025-03-17 01:04:41

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11559
本文可全文转载,独立域名个人网站无需授权,但需要保留原作者、出处以及文中链接,任何网站均可摘要聚合,商用请联系授权。

first-line封面占位图

一、前言

去年年中开始开启了《CSS世界》三部曲精讲视频系列,然后前段时间正好讲到::first-line伪元素,突然意识到,很长一段时间内,我居然把这个东西给忘记了。

::first-line伪元素本身的作用是个鸡肋,就是控制第一行文字的样式。

但是其衍生的作用倒是有个妙用,那就是隐藏按钮或者图标内的文字的同时不改变color属性的上下文。

例如,LuLu UI中的按钮控件,其按钮的loading效果是隐藏文字,显示旋转图标🌼,按钮尺寸不变,如下GIF动图所示。

loading效果截图示意

其中有个要求,那就是loading旋转图标的颜色要和文字保持一致,我们很自然会想到currentColor关键字,或者颜色不指定,直接继承。

这就会产生一个矛盾的点,那就是如果我们使用类似下面的语句隐藏文字:

button {
  color: transparent;
}

那么图标的颜色也会变成transparent,因为图标颜色和文字颜色是一致的。

那么有没有什么办法可以让文字隐藏,又不影响color属性的上下文呢?

二、一直使用的是-webkit-text-fill-color

我这些年一直使用的是-webkit-text-fill-color属性来设置文字隐藏的,此属性虽然是-webkit-私有前缀,但是Firefox浏览器也是支持的,加上现在IE浏览器已经名存实亡,因此,还用得不错。

text-fill兼容性

相关代码如下所示,只展示关键部分:

button {
    /* 略... */
}
button.loading::before {
    content: '';
    width: 20px; height: 20px;
    box-sizing: border-box;
    border: 2px solid;
    border-top-color: transparent;
    border-radius: 50%;
    position: absolute;
    inset: 0;
    margin: auto;
    animation: spin 1s linear infinite;
}
button.loading {
    -webkit-text-fill-color: transparent;
}

此时,给按钮元素增加类名loading,文字就会隐藏,但是又不会影响loading图形的颜色。

三、::first-line似乎更合适

实际上,这里可以还可以使用::first-line伪元素,兼容性更好,记忆成本也更低。

相关代码实现示意:

button {
    /* 略... */
}
button.loading::before {
    /* 同上... */
}
button.loading::first-line {
    color: transparent;
}

非常巧妙的一种实现。

下图所示的就是上面两种方法实现的效果GIF示意图:

点击我loading效果示意

完整代码

实际演示效果和完整代码,您可以狠狠地点击:

不影响color上下文按钮文字透明demo

☝️🖕☝️🖕

三、铁树开花,思路延伸

除了保留color上下文,::first-line伪元素,包括上面提到的-webkit-text-fill-color属性还有一个有用的特性,就是有更高的文字颜色重置优先级。

我举个例子,大家就知道什么意思了,比方说有这样一段HTML代码:

<button id="button" class="button">按钮</button>

然后按钮以很高的选择器优先级设置了一个颜色,例如:

#button {
   color: blue !important;
}

然而,按钮禁用的时候,文字颜色要是灰色,得,优先级如此高,怎么重置?再使用一个!important吗?

无需!

下面两段CSS都可以重置上面的蓝色设置:

button:disabled {
  -webkit-text-fill-color: gray;
}
button:disabled::first-line {
  color: gray;
}

如果两者同时使用,根据我的测试,text-fill-color属性要更生猛一些,会覆盖::first-line的颜色设置。

好,本文就这么多内容。

哎呀,好久没写东西写到快1点,就不唠嗑了,直接结尾。

我们下篇文章再见!

👋👋👋👋

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

(本篇完)