网页界面接口规范 0.3.0
本包适用于提供带标识元素和事件队列的宿主。浏览器可用 DOM 实现;其他宿主只有在能提供相同语义时才声明此包。应用依赖本包后,全部函数都必须实现,缺少任何一个函数时不得装载。
0.2.0 在 0.1.0 的四个函数(读取页面语言、设置节点文字、设置节点属性、等待界面事件)之上增加:控件值读回与写入、显示/禁用/只读/类名、焦点、输入光标与选区、滚动、模态框、页面状态。旧函数的签名不变;设置节点属性 的属性白册扩充,等待界面事件 改为按类型从统一事件队列取事件(见[网页事件接口规范](../网页事件/规范.汉语.md))。
0.3.0 在 0.2.0 之上只增函数(旧函数签名与语义不变):读取节点属性、移除节点属性、读取节点文字(读回与移除)、点击节点(程序性点击)、设置节点位置、读取节点矩形(摆放浮层)。它们服务于云控制台与阅读页从 JavaScript 迁入豫言时遇到的缺口,见[前端补全计划](../../豫言操作系统/文档/前端补全计划.汉语.md)。
所有函数按元素 ID 定位页面中已经存在的元素,不产生句柄,不向应用暴露原生 DOM 对象。字符串均为豫言字符串。
失败语义
本包的所有失败都属于违规调用或平台故障,一律抛豫言事故(可被应用捕获),事故消息为中文并保留原因:元素不存在、元素类型不符、参数越界或格式不合规、属性或链接不在白册内、浏览器拒绝操作(如对已打开的 dialog 再次 showModal)。宿主不得把失败当作成功,也不得让宿主异常越过豫言边界而使应用无法捕获。没有“可恢复业务情形”,因此没有返回值形式的错误。
危险元素:script、style、iframe、object、embed、link、meta、base、frame、frameset、applet 不可作为读写目标(iframe 仅可设 class、title、hidden 及 data-*、aria-* 属性,其地址只能经网页文树包的 设置页面框架地址 修改)。
函数
页面语言与文字
读取页面语言 : 有 → 字符串:读取根元素的语言标签,未设置时为空字符串。设置节点文字 : 字符串 → 字符串 → 有:把元素的文字内容整体换成纯文本,不解析 HTML;文字至多 8 MiB(UTF-16 码元);br、input这类空元素只接受空文字。
属性
设置节点属性 : 字符串(ID) → 字符串(名称) → 字符串(值) → 有
名称须以 ASCII 小写字母开头,其后为小写字母、数字与短横线(至多 64 个字符),值不得含控制字符(制表、换行、回车除外),值至多 65536 个 UTF-16 码元。允许的属性(白册,与网页文树包共用同一张表):
| 属性 | 适用元素 | 值的规则 |
| --- | --- | --- |
| class | 任意 | 见 设置节点类名 |
| id | 任意 | 不含空白、引号、<、>、&、反引号、反斜杠与控制字符,1 至 128 个字符,且文档中不得已有别的元素使用;空串表示移除 |
| role | 任意 | [a-z][a-z-]*,至多 32 个字符 |
| title | 任意 | 至多 4096 个字符 |
| hidden disabled readonly checked selected open | 任意 | 布尔属性,值只能是 ""、"true"(存在)或 "false"(移除) |
| tabindex | 任意 | 只允许 -1 与 0 |
| type | button:button submit reset;input:button submit reset checkbox radio text search number email url tel password range date time datetime-local | 值在所列集合内 |
| placeholder | input、textarea | 至多 4096 个字符 |
| datetime | time | [0-9TZ:.+\- ],至多 64 个字符 |
| maxlength | input、textarea | 至多 6 位数字 |
| rows | textarea | 至多 6 位数字 |
| colspan rowspan | td、th | 至多 6 位数字 |
| for | label | 同 id 的字符规则 |
| value | input、option、button | 任意(≤ 65536 码元) |
| href | 仅 a | 见下 |
| target | 仅 a | 只允许 _blank、_self;设为 _blank 时宿主强制同时设置 rel="noopener noreferrer" |
| aria-*、data-* | 任意 | 名称 aria- 或 data- 后接 [a-z0-9-]+;值任意(受上述总限制) |
data-yy- 前缀保留给宿主:应用不得设置(data-yy 本身是操作键,应用可以设置)。style、src、srcdoc、rel、name、action、formaction、sandbox 以及所有 on* 事件处理属性一律拒绝。
a[href] 只允许:以 / 开头但不以 // 开头的站内路径、以 # 开头的锚、https:// 网址、http:// 网址(不区分大小写)。含空白、控制字符或反斜杠的地址,以及 javascript:、data:、vbscript:、mailto:、// 协议相对地址等一概拒绝。`http:` 仅对 `a[href]` 放行。
类名
设置节点类名 : 字符串(ID) → 字符串(类名,空格分隔) → 有:整体替换元素的 class。每个类名只允许 Unicode 字母、数字、下划线与短横线(因此中文类名可用),分隔符只能是 ASCII 空格(连续空格并为一个);至多 32 个类名,每个至多 64 个字符,总长至多 512;空串清除 class。
设置节点类标记 : 字符串(ID) → 字符串(单个类名) → 爻 → 有:设置元素上单个类名的有无(对应 classList.toggle(类名, 有)):阳加上、阴去掉,其余类名不受影响。类名只允许一个,字符规则同 设置节点类名(至多 64 个字符,不得含空格)。
控件值
读取节点值 : 字符串 → 字符串:读取 input、textarea、select 的当前值;值按 UTF-8 计超过 8 MiB 时抛事故(宿主与应用交换数据的单次上限是 16 MiB,不能让它变成无法捕获的宿主异常)。读取选区文字同此限制。设置节点值 : 字符串 → 字符串 → 有:设置上述控件的值,不触发 input 或 change 事件;值至多 8 MiB;文件输入框不可设值;select 若没有值相同的选项则抛事故。
显示、禁用、只读
设置节点显示 : 字符串 → 爻 → 有(阳显示,阴隐藏,对应 hidden 属性)、设置节点禁用(disabled)、设置节点只读(readonly)。
焦点
聚焦节点 : 字符串 → 有:使元素获得焦点;不可聚焦的元素不报错,焦点不变。读取聚焦标识 : 有 → 字符串:当前获得焦点的元素 ID;没有焦点或该元素没有 ID 时为空字符串。节点包含焦点 : 字符串 → 爻:元素自身或其后代拥有焦点时为阳。
输入光标与选区
设置输入光标 : 字符串 → 整数 → 整数 → 有:设置 input 或 textarea 的选区,参数为起、止位置。读取输入光标 : 字符串 → (整数 合 整数):读取选区的起、止位置。- 位置按豫言字符(Unicode 码点)计数,不是 JavaScript 的 UTF-16 码元;宿主负责换算(例如 😀 在豫言里占 1 个位置,在浏览器里占 2 个码元)。须 0 ≤ 起 ≤ 止 ≤ 文字长度,否则抛事故;不支持选区的输入类型(如 number、email)抛事故。
读取选区文字 : 有 → 字符串:读取当前选中的文字。当前聚焦的输入框内有选区时返回该选区,否则返回页面上的选中文字(getSelection()),没有选区时为空字符串。
滚动
读取滚动度量 : 字符串 → 字符串:返回 JSON 文字{"顶":整数,"总高":整数,"可视高":整数},分别是scrollTop、scrollHeight、clientHeight(四舍五入取整)。设置滚动顶 : 字符串 → 整数 → 有:设置scrollTop。
模态框
打开模态框 : 字符串 → 有、关闭模态框 : 字符串 → 有:对 dialog 元素调用 showModal() 与 close();元素不是 dialog、宿主不支持 dialog 或浏览器拒绝(如对已打开的 dialog 再次打开)时抛事故。dialog 的 cancel(Esc)与 close 事件走网页事件包:应用用 订阅界面事件 订阅并可在策略里同步阻止 cancel 的默认动作。浏览器只在页面已获用户激活时允许取消 `cancel`(未经用户交互时 cancel 不可取消,dialog 照常关闭),此时事件载荷带 可取消:false、已阻止默认:false。
页面状态与可见
读取页面状态 : 有 → 字符串:返回 JSON 文字{"可见":爻,"联机":爻,"窄屏":爻}。可见为!document.hidden,联机为navigator.onLine,窄屏由固定媒体查询(max-width: 700px)决定(宿主没有媒体查询能力时为阴)。读取节点可见 : 字符串 → 爻:元素已连入文档,且自身与所有祖先元素的计算样式display都不是none时为阳。
属性读回与移除、文字读回(0.3.0)
读取节点属性 : 字符串(ID) → 字符串(名称) → (爻 合 字符串):读取元素上某个属性的原样文字(getAttribute,不是 DOM 属性,也不是计算样式)。除元素 ID 外,标识还可以是保留字 `文档根`(<html>)与 `页体`(<body>),因此 ID 为“文档根”“页体”的元素只能由保留字指代其根元素。属性存在时返回(阳,值),布尔属性存在时值为空字符串(例如<details open>);不存在时返回(阴,空字符串)。名称须为[a-z][a-z0-9:_-]*(至多 64 个字符),不得以on开头,不得是style或srcdoc。元素是危险元素、iframe 上要读白册之外的属性、值超过 65536 个 UTF-16 码元时抛事故。type=password输入框的value属性视为不存在(与界面事件载荷同规)。典型用法:读根元素的data-lang,读<details>的open,读aria-expanded。移除节点属性 : 字符串(ID) → 字符串(名称) → 有:移除元素上一个白册内的属性(removeAttribute);属性本来不存在时什么也不做;名称不在“属性”一节的白册内(含data-yy-前缀、style、on*)时抛事故。移除class等同于设置节点类名传入空文字;移除id等同于用空串设置id;移除target不会自动移除此前被强制加上的rel。读取节点文字 : 字符串(ID) → 字符串:读取元素的textContent;文字按 UTF-8 计超过 8 MiB 时抛事故;危险元素与不存在的元素抛事故。
点击、位置与矩形(0.3.0)
点击节点 : 字符串(ID) → 有:对元素执行一次程序性点击(HTMLElement.click())。页面上对该元素的所有订阅(含网页事件包的委托订阅)照常收到click事件,事件不是“受信任事件”,浏览器不会因此打开弹窗或文件选择框。文件输入框、危险元素、href不满足上文链接规则的<a>、不存在的元素抛事故;被禁用的元素点击无效果且不抛事故。回放被自己拦截的点击时,须先取消拦截订阅,再点击,随后重订(否则宿主又会拦下这次点击)。设置节点位置 : 字符串(ID) → 整数 → 整数 → 有:把元素的style.left、style.top设为给定的像素整数(−100000 至 100000),用于摆放悬浮提示等浮层。元素的position(absolute或fixed)由页面样式表另行指定;本包不开放其他样式。读取节点矩形 : 字符串(ID) → 字符串:返回 JSON 文字{"左":整数,"上":整数,"右":整数,"下":整数},是元素相对视口的位置(getBoundingClientRect,四舍五入取整);display:none或未连入文档的元素四项均为 0。
事件
等待界面事件 : 有 → (字符串 合 字符串):按宿主收到的顺序取一个界面类型的事件,返回(事件名称,目标元素 ID);目标没有 ID 时第二项为空字符串。其他类型(消息、定时……)的事件留在统一队列里,不被吞掉。宿主关闭队列时返回(“关闭”,空字符串),之后的调用继续返回相同关闭标记。
界面事件的来源有两类:(1) 旧式隐式监听:宿主对文档里带 ID 元素的 click、input、change、submit 自动产生事件,行为与 0.1.0 相同;(2) 网页事件包的 订阅界面事件 订阅得到的事件(含委托:无 ID 的后代元素触发时第二项为空字符串)。某个同名事件已被 订阅界面事件 覆盖时,旧式隐式监听让路,同一次点击不会报两遍。需要事件载荷(键盘、值、操作键……)时,应用应改用 等待网页事件。
限额
| 项 | 限额 | | --- | --- | | 文字与控件值 | 至多 8 MiB | | 属性值 | 至多 65536 个 UTF-16 码元 | | 类名 | ≤ 32 个、每个 ≤ 64 个字符、总长 ≤ 512 | | 光标位置 | 0 至文字长度(码点) |
跨宿主一致性
- 所有元素定位按当前文档里的 ID;同一 ID 有多个元素时,取文档顺序里的第一个。
- 白册、限额与失败分类必须与本文一致;宿主可以更严,不可以更松。
- 属性与链接校验在宿主完成,应用不得依赖“先写后被浏览器拦截”。
一致性样例
设置节点文字("盒", "<img src=x onerror=alert(1)>"):元素里只有这串纯文字,没有子元素。设置节点属性("链", "href", "javascript:alert(1)"):抛事故;设置节点属性("链", "target", "_blank")之后元素的rel为noopener noreferrer。- 输入框值为“a😀bc”:
设置输入光标(ID, 1, 3)后读取选区文字为“😀b”,读取输入光标为(1,3)。 设置节点值("下拉", "不存在的值"):抛事故;读取节点值("盒")(盒是 div):抛事故。- (0.3.0)根元素为
<html data-lang="wen">:读取节点属性("文档根", "data-lang")为(阳,“wen”),读取节点属性("文档根", "data-无")为(阴,“”);<details id="项" open>:读取节点属性("项", "open")为(阳,“”);移除节点属性("项", "open")后为(阴,“”)。 - (0.3.0)
点击节点("钮")之后,订阅了该按钮click的应用收到事件;被拦截订阅的应用须先取消订阅界面事件才能让点击到达页面上别的监听者。点击节点("链")(href为javascript:…):抛事故。 - (0.3.0)
设置节点位置("提示", 12, 34)后元素style.left为12px、style.top为34px;读取节点矩形("提示")的左、上与之相符(在position:absolute且无偏移的容器中)。
边界
本包不规定:节点创建、选择器(见网页文树包 0.3.0 的查询)、事件的键盘与组字载荷(见网页事件包)、定时器、剪贴板、网络、任意样式(style)读写(仅 设置节点位置 一项例外)、getComputedStyle 的其他属性、拖放与文件选择。