统一等待网页事件、订阅界面事件(含选择器过滤与目标属性载荷)及读取队列丢弃数的公共接口。
源码浏览已生成;不能解析的文件以纯文本展示,不表示通过类型检查。
# 网页事件接口规范 0.2.0
本包为浏览器(及能提供同义事件队列的宿主)里的豫言应用提供**统一等待点**:界面事件、页面消息、定时、事件流、请求、编译、可见性、联机、历史和关闭都进入同一个先进先出队列,应用用一个阻塞调用 `等待网页事件` 取下一个事件,再自行分派。另外提供 `订阅界面事件`:由应用在订阅时预先声明“同步决定”(阻止默认动作、停止传播、按键规则),宿主在原生回调里当场执行,因为豫言只能事后处理事件,无法改变本次事件的默认行为。应用依赖本包后,全部函数都必须实现,缺少任何一个函数时不得装载。
0.2.0 相对 0.1.0 **只增不减**(六个函数的签名不变):`订阅界面事件` 的策略 JSON 新增三个字段——`选择器`(只对匹配某个 CSS 选择器的元素产生事件并执行阻止默认等同步动作)、`带属性`(事件载荷里带匹配元素的若干属性)、`带矩形`(载荷里带匹配元素的位置矩形);事件载荷相应新增 `目标标识`、`属性`、`矩形` 三个字段。同时把“定时”“事件流”“请求”“编译”四类事件明确指给各自的接口包:[网页定时](../网页定时/规范.汉语.md)、[网页事件源](../网页事件源/规范.汉语.md)、[网页请求](../网页请求/规范.汉语.md)、[网页编译](../网页编译/规范.汉语.md)。
## 事件队列
宿主为每个应用实例维护一个队列。每个事件有**类型**,队列内按到达顺序排列,宿主给每个事件一个单调递增的**序号**(从 1 起)。类型取下列固定集合:
| 类型 | 含义 | 来源 |
| --- | --- | --- |
| `界面` | 点击、输入、变更、提交、键盘、组字、焦点、滚动、对话框 cancel/close 等 DOM 事件 | 本包 `订阅界面事件`;网页界面包的旧式隐式监听 |
| `消息` | 当前文档里以“豫言”开头的应用消息 | 网页消息包 |
| `定时` | 一次或周期定时到期 | 网页定时包 |
| `事件流` | 服务器推送(SSE)事件:打开、成批的消息、错误 | 网页事件源包 |
| `请求` | 非阻塞网页请求的完成或失败 | 网页请求包 |
| `编译` | 本地编译的进度(成批)与结果 | 网页编译包 |
| `可见性` | 页面可见性变化(`visibilitychange`) | 本包 `订阅界面事件` |
| `联机` | 联机状态变化(`online`、`offline`) | 本包 `订阅界面事件` |
| `历史` | 历史遍历(`popstate`、`hashchange`) | 本包 `订阅界面事件` |
| `关闭` | 宿主关闭队列 | 宿主 |
| `宿主` | 低层宿主库的原始事件(动画帧、空闲回调、工作线程、广播频道、套接字、公开操作、文树变动等) | 宿主库;应用一般不使用 |
事件流、请求、编译、定时四类事件的字段与产生时机由各自的包规定,本包只规定它们共用的队列、序号、背压与取法。
### 背压与丢弃
- 每个类型有独立的队列上限,默认 1024,可用 `设置网页事件队列上限` 调整为 1 至 65536;宿主创建时也可传入初始上限。
- 某类型的队列满时,新事件到达会**丢弃该类型最旧的事件**,并计数;别的类型不受影响。`读取网页事件丢弃数` 返回自宿主启动以来的丢弃总数,`读取网页事件类型丢弃数` 返回某个类型的丢弃数。被丢事件留下的**序号缺口**可供应用发现丢失并重新同步。
- `宿主` 类型例外:为免丢失待答的公开操作调用,它满时拒绝新事件并报错,不丢旧事件(保持旧宿主库的行为)。
- **合并**:高频事件(`scroll`、`resize`、`mousemove`、`pointermove`、`touchmove`、`wheel`、`dragover`、`selectionchange`)默认合并——同一订阅、同一目标标识的事件若紧接在队列尾部,则新事件就地替换旧事件,不占新位置,也不算丢弃。订阅策略里的 `合并` 可覆盖默认。
- **批事件**:事件流与编译进度由宿主合成批事件(`详情` 里带 `批` 数组,见网页事件源、网页编译包),并配合背压保证不丢失;界面与消息事件不合批。
### 与旧函数的关系
网页界面包的 `等待界面事件` 和网页消息包的 `等待网页消息` 保留,但改为**按类型**从同一队列取事件:前者只取 `界面` 类型,后者只取 `消息` 类型,其余类型的事件原样留在队列里,不被吞掉。同一应用可以混用 `等待网页事件` 与这两个旧函数;混用时同一个事件只会被取走一次,先调用者先得。底层宿主库的 `等待浏览器事件`(原始 JSON)仍取任意类型的下一个事件,仅供旧应用继续运行,新应用不应与本包混用。
## 函数
### `等待网页事件 : 有 → 字符串`
阻塞直到队列里有事件,返回下一个事件(不分类型,按到达顺序)的 JSON 文字。宿主关闭后,每次调用都返回 `{"类型":"关闭","订阅号":0,"名称":"关闭"}`。调用可挂起,应用不得由此取得原生 DOM 对象。
事件 JSON 是对象,含下列字段;**各类事件只填相关字段,其余省略**:
| 字段 | 类型 | 含义 |
| --- | --- | --- |
| `类型` | 文字 | 上表之一,恒有 |
| `序` | 整数 | 宿主分配的序号,恒有(关闭事件除外) |
| `订阅号` | 整数 | 产生事件的订阅号;旧式隐式监听与宿主自身产生的事件为 0,定时事件为定时号 |
| `标识` | 文字 | 触发元素的 ID,无 ID 时为空文字(界面类型恒有) |
| `名称` | 文字 | 事件名(`click`、`keydown`、`visibilitychange`……;消息为消息名;定时为“定时”;关闭为“关闭”) |
| `操作键` | 文字 | 触发元素及其祖先(止于订阅目标,含目标自身)中最近的 `data-yy` 属性值;没有则省略 |
| `目标标识` | 文字 | 仅当订阅策略带 `选择器` 时出现:**原始事件目标**的 ID,无 ID 时为空文字(此时 `标识` 是匹配元素的 ID) |
| `属性` | 对象 | 仅当订阅策略带 `带属性` 时出现:匹配元素上存在的、被请求的属性(名称→文字);不存在的属性省略,值超过 4096 个 UTF-16 码元的省略 |
| `矩形` | 对象 | 仅当订阅策略带 `带矩形` 时出现:匹配元素的 `getBoundingClientRect()`,取整后的 `{"左":整数,"上":整数,"右":整数,"下":整数}`(视口坐标) |
| `值` | 文字 | 目标是 input、textarea、select 时的当前值;值长于 65536 个 UTF-16 码元时省略并带 `"值过长":true`(改用网页界面包的 `读取节点值`);`type=password` 的输入框、`scroll`、`mousemove` 等无值事件不带 |
| `选中` | 爻 | 目标是 checkbox 或 radio 时的 checked(旧式隐式监听的事件恒带 `值` 与 `选中`) |
| `键` `代码` | 文字 | 键盘事件的 `key` 与 `code` |
| `ctrl` `meta` `alt` `shift` | 爻 | 键盘与鼠标事件的修饰键 |
| `重复` | 爻 | 键盘事件的 `repeat` |
| `组字中` | 爻 | 键盘事件的 `isComposing`;`input` 事件的 `isComposing`;`compositionstart`、`compositionupdate` 为真,`compositionend` 为假 |
| `按钮` | 整数 | 鼠标事件的 `button` |
| `已阻止默认` | 爻 | 宿主在原生回调里是否已对该事件调用 `preventDefault`(界面类型恒有) |
| `可取消` | 爻 | 仅当订阅要求阻止默认而浏览器不允许取消该事件时出现,恒为假 |
| `详情` | 任意 JSON | 见下表 |
| `详情错误` | 文字 | 仅消息类型:详情超过 1 MiB 或不是有效 JSON 时,`详情` 为 null 并带此字段 |
`详情` 的形状:
| 事件 | 详情 |
| --- | --- |
| `input` | `{"输入类型":inputType,"数据":data 或 null}` |
| `compositionstart/update/end` | `{"数据":data}` |
| `focus` `blur` `focusin` `focusout` | `{"关联标识":relatedTarget 的 ID 或空文字}` |
| `scroll` | `{"顶":整数,"总高":整数,"可视高":整数}`(目标是文档时取滚动元素) |
| dialog 的 `cancel` `close` | `{"返回值":returnValue}` |
| 可见性 | `{"可见":爻}` |
| 联机 | `{"联机":爻}` |
| 历史 `popstate` | `{"网址":当前地址,"状态":JSON 化的 state 或 null}`(state 不能 JSON 化或超过 64 KiB 时为 null) |
| 历史 `hashchange` | `{"旧网址":文字,"新网址":文字}` |
| 消息 | 消息的 JSON 详情 |
| 定时 | `{"种类":"一次"或"重复","标记":文字,"时刻":整数}` |
| 宿主 | 原始事件对象 |
### `订阅界面事件 : 字符串 → 字符串 → 字符串 → 整数`
参数依次为**目标**、**事件名**、**策略 JSON**,返回订阅号(正整数)。
- **目标**:页面里已有元素的 ID,或保留字 `文档`、`窗口`(因此 ID 为“文档”或“窗口”的元素不能作为目标)。目标元素须在订阅时存在;元素以后被替换掉,订阅随之失效。
- **事件名**:ASCII,`[A-Za-z][A-Za-z0-9_.:-]*`,至多 64 个字符。`visibilitychange` 产生可见性类型事件,`online`、`offline` 产生联机类型,`popstate`、`hashchange` 产生历史类型,其余产生界面类型。以“豫言”开头的消息名不能在这里订阅,应使用网页消息包。
- **委托**:目标下**任意后代**触发的同名事件都会产生事件,`标识` 为触发元素的 ID(没有则为空文字),`操作键` 为最近带 `data-yy` 的祖先(含自身)的值。因此动态生成的按钮不必有 ID,只要带 `data-yy`。不冒泡的事件(focus、blur、scroll、close、cancel、load、error、toggle、invalid、mouseenter、mouseleave、pointerenter、pointerleave)宿主一律在捕获阶段监听,同样可以委托。
- 同一目标、同一事件名可以订阅多次,每次得到不同的订阅号,各自产生事件。
- 订阅总数至多 256。目标不存在、事件名或策略非法、数量超限时抛豫言事故(可被捕获);失败的订阅不留下任何痕迹。
**策略 JSON**是对象,字段全部可省略;出现未知字段或类型不对一律报错,避免笔误被默默忽略:
| 字段 | 类型 | 含义 |
| --- | --- | --- |
| `阻止默认` | 爻 | 在原生回调里调用 `preventDefault()`(事件可取消时);不能与 `被动` 同时为真 |
| `停止传播` | 爻 | 调用 `stopPropagation()` |
| `停止同处` | 爻 | 调用 `stopImmediatePropagation()`(同目标的后续监听器也收不到) |
| `捕获` | 爻 | 在捕获阶段监听(不冒泡事件总是捕获) |
| `被动` | 爻 | 被动监听;`scroll`、`wheel`、`touchstart`、`touchmove` 在没有上面三个动作时默认被动 |
| `合并` | 爻 | 是否合并紧邻的同源事件,默认仅高频事件为真 |
| `一次` | 爻 | 首个事件之后自动取消订阅(该事件仍会送达) |
| `仅命中` | 爻 | 有键规则时只把命中的事件送入队列 |
| `键规则` | 数组 | 至多 32 条,见下;仅适用于 `keydown`、`keyup`、`keypress` |
| `选择器` | 文字 | CSS 选择器(选择器列表亦可),至多 512 个字符,订阅时校验,非法则报错;见下文“选择器过滤” |
| `排除选择器` | 文字 | CSS 选择器,至多 512 个字符,订阅时校验;原始事件目标(含自身)落在匹配它的元素之内时,本次事件被忽略:既不投递,也不执行任何同步动作;见下文“选择器过滤” |
| `带属性` | 数组 | 至多 8 个属性名(`[a-z][a-z0-9:_-]*`,至多 64 字符,不得以 `on` 开头,不得是 `style`、`srcdoc`),不得重复;事件载荷带 `属性` |
| `带矩形` | 爻 | 为真时事件载荷带 `矩形` |
**没有键规则**时,`阻止默认`、`停止传播`、`停止同处` 对该订阅的每个事件生效。**有键规则**时,动作只在某条规则命中时才执行:若三个动作字段一个都没给,命中即 `阻止默认`(沿用宿主库“键盘阻止默认”规则的含义);若给了其中任何一个,则只执行所给的动作。
**键规则**是对象,同一条规则内各条件取“且”,多条规则取“或”;字段:
| 字段 | 含义 |
| --- | --- |
| `键` | 与 `event.key` 相同;单个字母不分大小写(`l` 也匹配 `L`)。`键` 与 `代码` 至少给一个 |
| `代码` | 与 `event.code` 相同 |
| `ctrl` `meta` `alt` `shift` | 布尔;省略表示不参与匹配,给出则必须与事件相同 |
| `含组字` | 布尔;默认假:输入法组字期间(`isComposing` 或 `keyCode` 为 229)的按键**不命中**,以免破坏输入法;为真则组字期间也匹配 |
| `仅选区为空` | 布尔;为真时,若输入框内或页面上有选中文字则不命中(例如只在没有选区时拦截 Ctrl+C) |
| `仅目标标识` | 文字;事件目标须是该 ID 的元素本身或其后代 |
| `仅当属性` | 对象 `{"标识":ID,"属性":名,"值":文字或 null}`:该元素的属性须等于给定值;`值` 为 null 表示该属性不存在;`hidden`、`disabled`、`readonly`、`open`、`checked`、`selected` 等布尔属性用 `"true"`(存在)与 `"false"`(不存在)表示 |
**规范强调**:所有同步条件都写在订阅时;键规则、阻止默认与停止传播在宿主的原生事件回调里当场判定和执行,豫言收到事件时本次事件的默认行为早已决定,**事后无法改变**。要按运行时状态改变拦截行为,应用应取消并重新订阅,或使用 `仅当属性`,让宿主读取页面里的属性现值。
**浏览器的实际约束**:dialog 的 `cancel`(Esc)只有在页面已获用户激活时才允许取消;否则事件不可取消,`preventDefault` 无效,此时事件载荷带 `可取消:false`、`已阻止默认:false`,dialog 照常关闭。
**选择器过滤**(0.2.0):策略带 `选择器` 时,宿主在原生回调里先做“委托匹配”,再谈其余:
1. 取事件的原始目标(若是文本节点则取其父元素);目标不是元素(例如 `scroll` 的目标是文档)则**不匹配**。
2. 取“最近匹配祖先”`m` = 从原始目标起向上(含自身)第一个匹配选择器的元素,且必须在订阅边界之内(订阅目标是元素时,`m` 须是该元素或其后代;订阅目标是 `文档` 或 `窗口` 时,不限)。
3. **没有 `m`:本次事件既不投递,也不执行阻止默认、停止传播、停止同处**——原生事件不受这次订阅的影响。
4. 有 `m`:照常检查键规则并执行动作;事件载荷里所有关于“目标元素”的字段——`标识`、`操作键`(从 `m` 向上找 `data-yy`,止于订阅边界)、`值`、`选中`、`属性`、`矩形`——都取 `m`(focus 类事件详情里的 `关联标识` 仍是 `relatedTarget` 的 ID,不受影响);原始目标的 ID 由 `目标标识` 报告。
典型用法:`订阅界面事件("文档", "click", {"捕获":true,"选择器":".豫菜单, .豫导航 [data-lang], a[href]","阻止默认":true,"停止同处":true,"带属性":["href","data-lang"]})`——只拦截导航链接与语言按钮的点击并读到被点链接的 `href`,页面上其他点击丝毫不受影响;`订阅界面事件("文档", "mouseover", {"选择器":".类型引用[title]","带属性":["title"],"带矩形":true})`——悬停在带标题的引用上才产生事件,并带回标题与位置,用于摆放浮层。选择器的语法错误在订阅时报错;`选择器` 与 `键规则` 可以并用(先选择器,后键规则)。
**排除选择器**(0.2.0):策略带 `排除选择器` 时,先检查原始目标(文本节点取其父元素):若它或它的某个祖先匹配该选择器(`closest`,不受订阅边界限制),本次事件被忽略,然后才做上面的选择器匹配。用来表达“点在某区域**之外**”——例如收起下拉面板:`订阅界面事件("文档", "click", {"排除选择器":".文件目录"})` 只在点击落在 `.文件目录` 之外时产生事件。(`选择器` 用的是“最近匹配祖先”,无法表达“之外”:`:not(...)` 会向上匹配到 `body`。)
没有 `选择器` 时,`带属性`、`带矩形` 作用于原始目标本身(目标是元素时)。`带属性` 读取的是**属性**(`getAttribute`,原样文字)而不是 DOM 属性;`type=password` 的输入框上名为 `value` 的属性不带出。
### `取消订阅界面事件 : 整数 → 有`
取消订阅并移除该订阅尚未取走的事件。订阅号无效或已取消时什么也不做。已经被 `等待网页事件` 取走的事件不受影响。
### `读取网页事件丢弃数 : 有 → 整数`、`读取网页事件类型丢弃数 : 字符串 → 整数`
见“背压与丢弃”。类型名不属于上表时,后者返回 0。
### `设置网页事件队列上限 : 字符串 → 整数 → 有`
设置某类型的队列上限,1 至 65536;类型名不在上表或上限越界时抛事故。调低上限时,多出的旧事件在该类型下一次有新事件到达时被丢弃并计数。
## 限额与失败分类
| 项 | 限额 |
| --- | --- |
| 队列上限(每类型) | 默认 1024,可配 1 至 65536 |
| 订阅数 | 界面订阅至多 256,消息订阅至多 128(见网页消息包) |
| 键规则条数 | 至多 32 |
| 事件名长度 | 至多 64 个字符 |
| 事件里的 `值` | 至多 65536 个 UTF-16 码元,超过则省略并标记 |
| 消息详情 | 至多 1 MiB |
失败分类:**违规调用**(目标不存在、事件名或策略非法、订阅超限、队列上限越界、类型名无效)抛豫言事故,消息为中文;**队列满**不是失败,按上文丢弃并计数;**宿主关闭**不是失败,等待返回“关闭”事件。
## 跨宿主一致性
- 事件按宿主收到的顺序排队,同一订阅号的事件保持先后。
- 序号在宿主实例内单调递增;丢弃只发生在队列头部(最旧)方向,不会打乱余下事件的顺序。
- 键规则匹配、`操作键` 搜索和委托规则必须与本文一致;字段缺省与省略规则必须一致,应用应把缺失字段视为“不适用”。
- 不能提供同步拦截的宿主不得声明本包。
## 一致性样例
1. 订阅 `容器` 的 `click`;点击容器内无 ID、带 `data-yy="打开:7"` 按钮里的 `span`;`等待网页事件` 返回 `类型` 为“界面”、`标识` 为空、`操作键` 为 `打开:7`。
2. 订阅 `文档` 的 `keydown`,策略 `{"键规则":[{"键":"Enter","shift":false}]}`;派发 Enter 后,`dispatchEvent` 一返回,事件的 `defaultPrevented` 即为真;派发 Shift+Enter 则为假;两者的事件都送达,`已阻止默认` 分别为真、假。
3. 把界面队列上限设为 5,连续点击 20 次而不取事件:队列里留下最新 5 个,`读取网页事件丢弃数` 为 15,留下事件的序号连续且大于 15。
4. 应用正阻塞在 `等待界面事件` 时到达一条页面消息:消息留在队列里,随后 `等待网页消息`(或 `等待网页事件`)取到,界面事件到达后 `等待界面事件` 返回。
5. 订阅 `文档` 的 `click`,策略 `{"捕获":true,"选择器":"a[href]","阻止默认":true,"带属性":["href"]}`;点击 `<a href="/x"><span>字</span></a>` 里的 `span`:事件到达,`标识` 为 `a` 的 ID(可为空)、`目标标识` 为 `span` 的 ID、`属性` 为 `{"href":"/x"}`,且 `dispatchEvent` 一返回,`defaultPrevented` 即为真;点击页面上的普通按钮:没有事件,`defaultPrevented` 为假。
6. 订阅策略 `{"选择器":"button["}`:订阅时抛事故,宿主不留下任何监听。
7. 订阅 `文档` 的 `click`,策略 `{"排除选择器":".文件目录"}`:点击 `.文件目录` 内的任意后代没有事件;点击页面上其他位置有事件(`标识` 等仍取原始目标)。
## 边界
本包不规定:网络请求、定时器的创建与取消、SSE 连接、本地编译(它们分属网页请求、网页定时、网页事件源、网页编译包,只借用本包的事件类型);不提供跨窗口、跨 Worker 事件;不提供“事后阻止默认”;不向应用暴露原生事件对象或 DOM 句柄;不保证高频事件(滚动、指针移动)逐个送达(默认合并);触屏、拖放、剪贴板 `paste` 的载荷字段留待后续版本。
# 网页事件之约 0.2.0
此包为浏览器(及能供同义事列之宿主)中之豫言客立**统一之候点**:界面事、页面消息、定时、事件流、请求、编译、可见性、联机、历史与关闭,皆入同一先进先出之列;客以一阻塞之术等待网页事件取下一事,而后自行分派。又立订阅界面事件:客于订时预定“同步之断”(止默认、止传播、按键之律),宿主于原生回调中当场行之,盖豫言惟能事后处事,不能改此次事之默认。客依此包,诸术皆须备,阙一则拒装。
零点二点零较零点一点零**惟增不减**(六术之签名不变):订阅界面事件之策略癸象增三字段——选择器(惟于合某 CSS 选择器之元素生事并行止默认等同步之作)、带属性(事之载带合者之若干属性)、带矩形(载带合者之位置矩形);事之载相应增目标标识、属性、矩形三字段。又明以定时、事件流、请求、编译四类之事,各归其包:[网页定时](../网页定时/规范.文言.md)、[网页事件源](../网页事件源/规范.文言.md)、[网页请求](../网页请求/规范.文言.md)、[网页编译](../网页编译/规范.文言.md)。
## 事列
宿主为每客器立一列。每事有**类**,列中依至之序而排,宿主予每事一单调递增之**序**(自一始)。类取下列定集:
| 类 | 义 | 源 |
| --- | --- | --- |
| 界面 | 点击、输入、变更、提交、键盘、组字、焦点、滚动、dialog 之 cancel 与 close 等 DOM 事 | 本包订阅界面事件;网页界面包之旧式隐听 |
| 消息 | 当前文档中“豫言”首之客消息 | 网页消息包 |
| 定时 | 一次或周期定时期至 | 网页定时包 |
| 事件流 | 服务端推送(SSE)之事:打开、成批之消息、错误 | 网页事件源包 |
| 请求 | 非阻塞网页请求之成或败 | 网页请求包 |
| 编译 | 本地编译之进(成批)与果 | 网页编译包 |
| 可见性 | 页面可见之变(visibilitychange) | 本包订阅界面事件 |
| 联机 | 联机之变(online、offline) | 本包订阅界面事件 |
| 历史 | 历史之遍(popstate、hashchange) | 本包订阅界面事件 |
| 关闭 | 宿主闭列 | 宿主 |
| 宿主 | 低层宿主库之原始事(动画帧、空闲回调、工作线程、广播频道、套接字、公开操作、文树变动等) | 宿主库;客通常不用 |
事件流、请求、编译、定时四类之事,其字段与生事之时,各由其包定之;此包惟定其共用之列、序、背压与取法。
### 背压与弃
- 每类各有独立之列限,默认一千零二十四,可以设置网页事件队列上限调为一至六万五千五百三十六;宿主创建时亦可传初限。
- 某类之列满,则新事至而**弃该类最旧之事**,并计其数;他类不受影响。读取网页事件丢弃数返自宿主启以来所弃之总数,读取网页事件类型丢弃数返某类所弃之数。所弃之事遗**序之缺口**,客可由此知失而复同步。
- 「宿主」类为例外:为免失待答之公开操作,其满则拒新事而报错,不弃旧事(守旧宿主库之行)。
- **合并**:高频之事(scroll、resize、mousemove、pointermove、touchmove、wheel、dragover、selectionchange)默认相并——同一订阅、同一目标标识之事若紧接列尾,则新事就地代旧事,不占新位,亦不计为弃。订阅策略之合并可易其默。
- **批事**:事件流与编译之进度,宿主合为批事(详情中带批数组,见网页事件源、网页编译二包),并配背压以保不失;界面与消息之事不合批。
### 与旧术之系
网页界面包之等待界面事件与网页消息包之等待网页消息仍在,而改为**依类**自同一列取事:前者惟取界面类,后者惟取消息类,他类之事原样留于列,不被吞。同一客器可混用等待网页事件与此二旧术;混用则同一事惟被取一次,先呼者先得。底层宿主库之等待浏览器事件(原始癸象)仍取任意类之下一事,惟供旧客续行,新客不当与此包混用。
## 诸术
### 等待网页事件(无参,返文)
阻塞至列中有事,返下一事(不分类,依至之序)之癸象文。宿主闭后,每呼皆返 `{"类型":"关闭","订阅号":0,"名称":"关闭"}`。此术可挂起,客不得由此得原生文树之物。
事之癸象为对象,含下列字段;**各类之事惟填相关之字段,余者省之**:
| 字段 | 类型 | 义 |
| --- | --- | --- |
| 类型 | 文 | 上表之一,恒有 |
| 序 | 整数 | 宿主所予之序,恒有(关闭之事除外) |
| 订阅号 | 整数 | 生事之订阅号;旧式隐听与宿主自生之事为零,定时之事为定时号 |
| 标识 | 文 | 触发元素之标识,无则空文(界面类恒有) |
| 名称 | 文 | 事名(click、keydown、visibilitychange 等;消息为消息名;定时为“定时”;关闭为“关闭”) |
| 操作键 | 文 | 触发元素及其祖先(止于订阅之目标,含目标自身)中最近之 data-yy 属性值;无则省 |
| 目标标识 | 文 | 惟订阅策略带选择器时现:**原始事目标**之标识,无则空文(是时“标识”乃合者之标识) |
| 属性 | 对象 | 惟订阅策略带带属性时现:合者所有之、所请之属性(名至文);无者省之,值逾四千零九十六 UTF-16 码元者省之 |
| 矩形 | 对象 | 惟订阅策略带带矩形时现:合者之 getBoundingClientRect(),取整后之 `{"左":整数,"上":整数,"右":整数,"下":整数}`(视口坐标) |
| 值 | 文 | 目标为 input、textarea、select 时之现值;值逾六万五千五百三十六 UTF-16 码元则省而带 `"值过长":true`(当改用网页界面之读取节点值);口令之框(type=password)与 scroll、mousemove 等无值之事不带 |
| 选中 | 爻 | 目标为 checkbox 或 radio 时之 checked(旧式隐听之事恒带值与选中) |
| 键 代码 | 文 | 键盘事之 key 与 code |
| ctrl meta alt shift | 爻 | 键盘与鼠标事之修饰键 |
| 重复 | 爻 | 键盘事之 repeat |
| 组字中 | 爻 | 键盘事之 isComposing;input 事之 isComposing;compositionstart、compositionupdate 为真,compositionend 为假 |
| 按钮 | 整数 | 鼠标事之 button |
| 已阻止默认 | 爻 | 宿主于原生回调中已否对此事施 preventDefault(界面类恒有) |
| 可取消 | 爻 | 仅当订阅欲阻止默认而事不可取消时出现,恒为假 |
| 详情 | 任意癸象 | 见下表 |
| 详情错误 | 文 | 惟消息类:详情逾一 MiB 或非有效癸象,则详情为 null 而带此字段 |
详情之形:
| 事 | 详情 |
| --- | --- |
| input | `{"输入类型":inputType,"数据":data 或 null}` |
| compositionstart、compositionupdate、compositionend | `{"数据":data}` |
| focus blur focusin focusout | `{"关联标识":relatedTarget 之标识或空文}` |
| scroll | `{"顶":整数,"总高":整数,"可视高":整数}`(目标为文档则取滚动元素) |
| dialog 之 cancel、close | `{"返回值":returnValue}` |
| 可见性 | `{"可见":爻}` |
| 联机 | `{"联机":爻}` |
| 历史之 popstate | `{"网址":现址,"状态":state 之癸象化或 null}`(state 不可癸象化或逾六十四 KiB 则 null) |
| 历史之 hashchange | `{"旧网址":文,"新网址":文}` |
| 消息 | 消息之癸象详情 |
| 定时 | `{"种类":"一次"或"重复","标记":文,"时刻":整数}` |
| 宿主 | 原始事之对象 |
### 订阅界面事件(目标、事名、策略,返订阅号整数)
参数依次为**目标**、**事名**、**策略之癸象文**,返订阅号(正整数)。
- **目标**:页中既有元素之标识,或保留字“文档”“窗口”(故标识为“文档”或“窗口”之元素不可为目标)。目标元素须于订时已在;元素后被替去,则订阅随之失效。
- **事名**:ASCII,`[A-Za-z][A-Za-z0-9_.:-]*`,至多六十四字。visibilitychange 生可见性类,online、offline 生联机类,popstate、hashchange 生历史类,余生界面类。“豫言”首之消息名不可于此订,当用网页消息包。
- **委托**:目标之下**任一后代**所触之同名事皆生事,标识为触发元素之标识(无则空文),操作键为最近带 data-yy 之祖先(含自身)之值。故动态所生之钮不必有标识,但带 data-yy 即可。不冒泡之事(focus、blur、scroll、close、cancel、load、error、toggle、invalid、mouseenter、mouseleave、pointerenter、pointerleave)宿主一律于捕获相听之,亦可委托。
- 同目标同事名可订多次,每次得异订阅号,各自生事。
- 订阅总数至多二百五十六。目标阙、事名或策略不合、数逾限,则发豫言事故(客可捕之);败之订阅不留痕迹。
**策略**为对象,诸字段皆可省;有未识之字段或类型不合,一律报错,免笔误默被忽略:
| 字段 | 类型 | 义 |
| --- | --- | --- |
| 阻止默认 | 爻 | 于原生回调中施 preventDefault()(事可取消时);不得与被动同为真 |
| 停止传播 | 爻 | 施 stopPropagation() |
| 停止同处 | 爻 | 施 stopImmediatePropagation()(同目标后续之听者亦不得见) |
| 捕获 | 爻 | 于捕获相听(不冒泡之事恒捕获) |
| 被动 | 爻 | 被动之听;scroll、wheel、touchstart、touchmove 无上三动作时默认被动 |
| 合并 | 爻 | 是否并紧邻之同源事,默认惟高频事为真 |
| 一次 | 爻 | 首事之后自撤订阅(该事仍送达) |
| 仅命中 | 爻 | 有键规则时,惟命中之事入列 |
| 键规则 | 数组 | 至多三十二条,见下;惟施于 keydown、keyup、keypress |
| 选择器 | 文 | CSS 选择器(选择器之列亦可),至多五百一十二字,订时验之,不合则报错;见下“选择器之滤” |
| 排除选择器 | 文 | CSS 选择器,至多五百一十二字,订时验之;原始事目标(含自身)落于合之之元素内,则此次事被忽略:既不投,亦不行任何同步之作;见下“选择器之滤” |
| 带属性 | 数组 | 至多八个属性名(`[a-z][a-z0-9:_-]*`,至多六十四字,不得以 on 首,不得为 style、srcdoc),不得重;事之载带属性 |
| 带矩形 | 爻 | 真则事之载带矩形 |
**无键规则**则阻止默认、停止传播、停止同处施于该订阅之每事。**有键规则**则动作惟于某规则命中时行:三动作字段一不给者,命中即阻止默认(承宿主库“键盘阻止默认”之义);给其中任一,则惟行所给之动作。
**键规则**为对象,同条诸件为“且”,诸条为“或”;字段:
| 字段 | 义 |
| --- | --- |
| 键 | 与 event.key 同;单字母不分大小写(l 亦合 L)。键与代码至少给一 |
| 代码 | 与 event.code 同 |
| ctrl meta alt shift | 布尔;省则不与匹配,给则须与事同 |
| 含组字 | 布尔;默认假:输入法组字之间(isComposing 或 keyCode 为 229)之键**不命中**,免坏输入法;真则组字之间亦合 |
| 仅选区为空 | 布尔;真则输入框内或页上有选中之文时不命中(如惟无选区时拦 Ctrl+C) |
| 仅目标标识 | 文;事之目标须为该标识之元素本身或其后代 |
| 仅当属性 | 对象 `{"标识":标识,"属性":名,"值":文或 null}`:该元素之属性须等于所给值;值为 null 表该属性不存在;hidden、disabled、readonly、open、checked、selected 等布尔属性以 "true"(在)与 "false"(不在)表之 |
**约中所重**:诸同步之件皆写于订时;键规则、止默认与止传播于宿主之原生事回调中当场判而行之,豫言得事时,此次事之默认早已定,**事后不能改**。欲依运行时之状而变拦截,客当撤订而重订,或用仅当属性,使宿主读页中属性之现值。
**浏览器之实**:dialog 之 cancel,浏览器惟于页已得用户激活时许取消;否则事不可取消,preventDefault 无效,是时事之载带可取消为假、已阻止默认为假。
**选择器之滤**(零点二点零):策略带选择器,则宿主于原生回调中先行“委托之合”,而后论余事:
1. 取事之原始目标(若为文本节点则取其父元素);目标非元素(如 scroll 之目标为文档)则**不合**。
2. 取“最近合者”m = 自原始目标向上(含自身)第一个合选择器之元素,且必在订阅之界内(订阅目标为元素,则 m 须为该元素或其后代;订阅目标为“文档”或“窗口”,则不限)。
3. **无 m:此次事既不投,亦不行止默认、止传播、止同处**——原生之事不受此订阅所扰。
4. 有 m:照常察键规则而行其作;事之载中凡涉“目标元素”之字段——标识、操作键(自 m 向上求 data-yy,止于订阅之界)、值、选中、属性、矩形——皆取 m(focus 类事详情中之关联标识,仍为 relatedTarget 之标识,不受影响);原始目标之标识,由目标标识报之。
典型之用:订阅界面事件(“文档”,“click”,`{"捕获":true,"选择器":".豫菜单, .豫导航 [data-lang], a[href]","阻止默认":true,"停止同处":true,"带属性":["href","data-lang"]}`)——惟拦导航之链与语言之钮,得所点之链之 href,页上他点击丝毫不受扰;订阅界面事件(“文档”,“mouseover”,`{"选择器":".类型引用[title]","带属性":["title"],"带矩形":true}`)——惟悬于带标题之引用乃生事,并带回标题与位置,以置浮层。选择器之语法误,订时即报;选择器与键规则可并用(先选择器,后键规则)。
**排除选择器**(零点二点零):策略带排除选择器,则先察原始目标(文本节点取其父元素):其自身或某祖先合此选择器(closest,不限订阅之界),则此次事被忽略,而后方行上文选择器之合。用以表“点于某域**之外**”——如收起下拉之面板:订阅界面事件(“文档”,“click”,`{"排除选择器":".文件目录"}`),惟点落于 `.文件目录` 之外乃生事。(选择器所用者为“最近合者”,不能表“之外”:`:not(...)` 会上溯而合 body。)
无选择器,则带属性、带矩形施于原始目标本身(目标为元素时)。带属性所读乃**属性**(getAttribute,原样之文)而非 DOM 之属性;type=password 之输入框上名 value 之属性不带出。
### 取消订阅界面事件(订阅号)
撤订阅,并去其未取之事。订阅号无效或已撤则无事。已被等待网页事件取去之事不受影响。
### 读取网页事件丢弃数、读取网页事件类型丢弃数
见“背压与弃”。类名不在上表则后者返零。
### 设置网页事件队列上限(类、上限)
定某类之列限,一至六万五千五百三十六;类名不在上表或限越界则发事故。调低其限,则多出之旧事于该类下一新事至时被弃而计之。
## 限额与败之类
| 项 | 限 |
| --- | --- |
| 列限(每类) | 默认一千零二十四,可配一至六万五千五百三十六 |
| 订阅 | 界面订阅至多二百五十六,消息订阅至多一百二十八(见网页消息之约) |
| 键规则之条数 | 至多三十二 |
| 事名之长 | 至多六十四字 |
| 事中之值 | 至多六万五千五百三十六 UTF-16 码元,逾则省而标之 |
| 消息详情 | 至多一 MiB |
败之类:**违约之调用**(目标阙、事名或策略不合、订阅逾限、列限越界、类名无效)发豫言事故,辞为华言;**列满**非败,依上文弃而计之;**宿主闭**非败,等待返“关闭”之事。
## 跨宿主之一致
- 事依宿主所收之序而排,同一订阅号之事保其先后。
- 序于宿主实例内单调而增;弃惟发于列首(最旧)之向,不乱余事之序。
- 键规则之匹配、操作键之搜与委托之规须合此文;字段之缺省与省略须一致,客当视缺失之字段为“不适用”。
- 不能供同步拦截之宿主不得自称具此包。
## 一致性之例
1. 订“容器”之 click;点击容器内无标识、带 `data-yy="打开:7"` 之钮中之 span;等待网页事件返:类型为“界面”、标识为空、操作键为 `打开:7`。
2. 订“文档”之 keydown,策略 `{"键规则":[{"键":"Enter","shift":false}]}`;派发 Enter,则 dispatchEvent 一返,事之 defaultPrevented 即真;派发 Shift+Enter 则假;二者之事皆送达,已阻止默认依次为真、假。
3. 界面列限设为五,连点二十次而不取事:列中留最新五事,读取网页事件丢弃数为十五,所留事之序相连而大于十五。
4. 客正阻塞于等待界面事件时,一页面消息至:消息留于列,其后等待网页消息(或等待网页事件)取之;界面事至,则等待界面事件返之。
5. 订“文档”之 click,策略 `{"捕获":true,"选择器":"a[href]","阻止默认":true,"带属性":["href"]}`;点击 `<a href="/x"><span>字</span></a>` 中之 span:事至,标识为 a 之标识(可空)、目标标识为 span 之标识、属性为 `{"href":"/x"}`,且 dispatchEvent 一返,defaultPrevented 即真;点页上寻常之钮:无事,defaultPrevented 为假。
6. 订阅策略 `{"选择器":"button["}`:订时发事故,宿主不留任何监听。
7. 订“文档”之 click,策略 `{"排除选择器":".文件目录"}`:点 `.文件目录` 内之任一后代无事;点页上他处有事(标识等仍取原始目标)。
## 界
此包不定:网络请求、定时器之造与撤、SSE 之联、本地编译(分属网页请求、网页定时、网页事件源、网页编译诸包,惟借此包之事类);不供跨窗、跨工之事;不供“事后止默认”;不向客露原生事对象或文树之柄;不保高频之事(滚动、指针移动)逐一送达(默认并之);触屏、拖放、剪贴之 paste 之载字段,留待后版。