元素选择器
元素选择器让脚本可以说清楚“我要找哪个界面元素”。它的写法接近 CSS:从元素类型开始,再按标题、状态或层级逐步缩小范围。
const buttons = root.queryElements('Button[title="Save"]');大多数情况下,可以先在元素菜单模式选中目标,在 复制 子菜单中选择 复制选择器。把结果粘贴进脚本后,再根据实际使用情况稍作简化。
从复制结果开始
Section titled “从复制结果开始”Cursor Crane 生成的选择器会尽量准确地指向当前元素,但界面中的标题、列表内容和网页文本常常会变化。复制后建议先运行一次,再删去不稳定的部分,只留下真正有区分度的条件。
例如,下面的选择器很精确:
Window[title="Project Alpha"] Group Button[title="Run"]如果项目名经常变化,而对应分组中的 Run 按钮已经足够明确,可以改成:
Group Button[title="Run"]| 目标 | 写法 | 示例 |
|---|---|---|
| 按元素类型找 | Button、TextField、List | Button |
| 按标题找 | [title="..."] | Button[title="Save"] |
| 按标识符找 | #id | Button#save-button |
| 不写 Kind,按辅助功能 role 找 | [role="AX..."] | [role="AXWebArea"] |
| 按状态找 | :enabled、:focused | TextField:focused |
| 找任意元素 | * | *[visible=true] |
支持的 Kind
Section titled “支持的 Kind”内置支持的 Kind 有:Application、Unknown、Popover、Group、Button、TextField、Cell、Image、Toolbar、ScrollArea、OverflowedWebContent、List、Link 和 Draggable。Kind 是 Cursor Crane 的内部分类,并不一定对应单个辅助功能 role。在选择器的类型位置,写出的 token 会匹配元素的 Kind 或辅助功能 role;辅助功能 role 可带或不带 AX 前缀,因此 Button 能匹配 AXButton。
并非每个辅助功能元素都有这些 Kind。你可以把任意原始辅助功能 role 写在类型位置,例如 WebArea 或 AXWebArea;也可以完全不写类型,仅用 role 属性筛选:
WebAreaAXWebArea[role="AXWebArea"]若要显式写通配符,请用 *,例如 *[role="AXWebArea"]。不确定 Kind 或 role 时,从复制结果开始最可靠。
空格表示“在里面任意位置”,> 表示“直接在里面”,逗号表示“匹配其中任意一个”:
Window ButtonWindow > ButtonButton[title="Save"], Button[title="Cancel"]Window Button会寻找窗口里任意层级的按钮。Window > Button只寻找窗口的直接子按钮。- 最后一行同时寻找保存和取消按钮。
验证已知元素
Section titled “验证已知元素”已经拿到元素时,可以调用 matchesSelector(selector) 验证它是否符合完整选择器,而不必再查询一次。父级和祖先条件也会参与匹配:
if (saveButton?.matchesSelector('Window > Toolbar Button[title="Save"]')) { await saveButton.performPress();}这个方法需要 accessibilityAPI 权限。选择器来自用户输入时请注意:无效选择器会抛出错误。
按文字和属性筛选
Section titled “按文字和属性筛选”属性条件支持完全匹配、开头匹配、结尾匹配和包含:
| 写法 | 含义 | 示例 |
|---|---|---|
= | 完全相同 | Button[title="Save"] |
^= | 以文字开头 | StaticText[value^="Error"] |
$= | 以文字结尾 | TextField[title$="name"] |
*= | 包含文字 | Button[label*="Continue"] |
| 属性 | 也可写作 | 匹配内容 |
|---|---|---|
id | identifier;#id 简写 | 辅助功能标识符;若没有,则匹配 DOM 标识符。 |
domId | domIdentifier | DOM 标识符。 |
role | — | 原始辅助功能 role,例如 AXButton。 |
subrole | — | 原始辅助功能 subrole。 |
title | — | 元素标题。 |
label | description | 辅助功能标签或描述。 |
value | — | 元素值。 |
type | — | 元素类型。 |
enabled | — | 元素是否可用。 |
visible | — | 元素是否可见。 |
hidden | — | 元素是否隐藏。 |
focused | — | 元素是否获得焦点。 |
domClass | — | DOM class token,见下文。 |
文字里有空格时,请用单引号或双引号包起来。
Button[enabled=true]TextField[focused=true]StaticText[value*="Completed"]匹配 DOM 标识符和 Class
Section titled “匹配 DOM 标识符和 Class”domId(也可写作 domIdentifier)和 domClass 是特殊的 DOM 属性,通常只会在浏览器、WebView 或 Electron 应用暴露的内容中可用;原生 macOS 控件一般不会提供它们。仅当复制出的选择器显示这些属性时再使用。
*[domId="compose-button"]Button[domIdentifier="compose-button"]domClass 用来提供 DOM class 列表。请将它写成显式属性;不支持 CSS 风格的 class 简写。
Button[domClass="primary active"]Button[domClass*="primary active"]=要求 DOM class token 的集合完全相同,顺序无关。*=要求元素包含列出的每一个 DOM class token。Button.primary和Button[class="primary"]都是无效写法;请改用Button[domClass="primary"]。
匹配前会按空白拆分 DOM class,因此 menu 不会匹配 menu-item。当选中的元素提供这些属性时,复制出的选择器会带上 domId 和 domClass。
选择第几个结果
Section titled “选择第几个结果”| 写法 | 含义 |
|---|---|
:first | 取第一个匹配结果。 |
:nth(n) | 取第 n 个匹配结果,从 1 开始。 |
:enabled / :disabled | 只保留可用或不可用的元素。 |
:visible / :hidden | 只保留可见或隐藏的元素。 |
:focused | 只保留当前获得焦点的元素。 |
List Row:nth(2) Button:first这会找到列表中的第二行,再取得那一行里的第一个按钮。
查询可见内容
Section titled “查询可见内容”元素查询的效率并不高:queryElements() 会遍历 children,元素层级很大时可能造成明显阻塞。请避免重复执行同一个查询。若多个目标元素有共同父节点,建议先查询一次父节点,再从父节点查询各个目标,而不是每次都从整个窗口开始搜索。对于长列表、表格和滚动区域,queryVisibleElement() 更适合“只看用户此刻看得到的内容”:
const visibleButtons = root.queryVisibleElement('Button:enabled', 50);
const toolbar = root.queryElements('Toolbar[title="Formatting"]', 1)[0];const boldButton = toolbar?.queryElements('Button[title="Bold"]', 1)[0];const italicButton = toolbar?.queryElements('Button[title="Italic"]', 1)[0];这样脚本就不会把注意力放在滚动到很远位置的项目上。两个查询方法都可以传入第二个参数,限制最多返回多少个结果。
在脚本中使用
Section titled “在脚本中使用”先找到窗口,再从窗口开始查询。内置 example.element-query.js 已经演示了完整流程:
const application = ctx.elementInspector.getElementByPid(window.application.pid);const root = application?.getWindows().find( element => element.windowId === window.windowId);
const matches = root?.queryVisibleElement('Button[enabled=true]', 50) ?? [];查询元素需要在 meta.permissions 中加入 "accessibilityAPI"。找到元素后,你可以高亮它、移动到它,或触发它支持的操作。详见脚本 API。