# 元素拾取器

[← 返回 README](../README.md) · [文档索引](./index.md)

不想对着 DevTools 抄类名时用它：**点选界面里的任意元素**，得到一条尽量活得久的选择器，
外加它**实际解析到**的 DSH 令牌。

---

## 选择器是「挑」出来的，不是「算」出来的

设计系统用 CSS Modules：`_card_1fywu_26` 这类类名里的哈希**随构建变化**，
写进样式表下次升级就失效。所以候选按「什么能活下来」排序：

| 优先级 | 类型 | 例 | 为什么 |
| --- | --- | --- | --- |
| 1 | `[data-*]` 钩子 | `[data-dockkit-strip]` | DSH 自己也在用它查询，跨版本最稳 |
| 2 | `[aria-label]` | `div[aria-label="QQ 音乐"]` | 语义层，不会随便改 |
| 3 | 手写语义类名 | `div.dsh-music-qq-head` | 插件作者自己维护 |
| 4 | 哈希容错 | `div[class*="_card_"]` | 不接哈希，但依赖命名习惯 |
| 5 | 结构路径 | `div:nth-child(2) > div` | 兜底，界面改版会失效 |

### 命中多个元素不是隐藏，是标注

候选旁边会写「N 个命中」：**一次性给一族元素写样式是正当需求**，而结构路径注定会在升级时断。

- **唯一性只决定默认选项**（默认选中的是第一个命中的），证据层级用来打破平局。
- 探针查不出来时写「命中数未知」，并且**按非唯一处理** ——
  「没查出来」不等于「只有一个」。

### 结构路径为什么脆

`body > div:nth-child(36) > … > code` 这种路径在界面上是**脆的**：实测同一条路径
**换个序号同样命中 0**，因为 DSH 的对话框渲染在 body 上的 portal 里，
`nth-child` 的序号取决于 portal 的排布。命中数为 0 就是在说这件事。

---

## 交互

- **悬停只预览，点击才锁定**：高亮框一直跟着指针（上面标着 `宽×高`），
  点击把它固定成实心框，面板同时换成这个元素的候选与令牌。
  **`Enter` 或面板上的「插入规则」才提交**，`Esc` 或「取消拾取」退出。
- **不接管页面**：不遮罩、不拦点击、不改属性 —— 拾取期间设置页照常能用
  （可以关掉它去别的页面拾取，**会话挂在模块级**而不是这一行上）。
- **层级导航**：

  | 键 | 动作 |
  | --- | --- |
  | `↑` / `W` | 上一级（父元素） |
  | `↓` / `S` | 下一级（子元素） |
  | `←` / `A` | 上一个同级 |
  | `→` / `D` | 下一个同级 |

  也有滑杆（**0 一定是「点到的那个元素」**）。移动鼠标只改悬停预览，
  **不会**悄悄换掉已锁定的元素 —— 要换选就再点一下。

- 面板是**浮动**的（挂在 `body` 上），带候选选择器、令牌 chips（可关掉不想写的）、
  层级滑杆与快捷键提示。

## 「插入规则」做什么

- 表里**已有同名规则就打开它**（绝不重复追加）。
- 否则在表尾追加一条空规则、把勾选的令牌写成声明，
  并把**光标停在新块内**、编辑器**滚到新规则**。
- **复用已有规则时只开面板、不动光标** —— 那条规则不是这次手势创建的，
  把光标丢进去是惊吓而不是帮忙。

## 令牌反查

令牌表**从 CSSOM 现读**（文档里各样式表的 `--*` 定义），所以新增令牌、主题改值会自动跟上：
插入的是 `var(--dsw-alias-bg-layer-1)`，**不是它解析出的 `#101010`**。

两条约束是刻意的：

- **作用域**：只有**定义规则能匹配该元素或其祖先**的令牌才算数 ——
  别的组件卡片内部的定义，对它外头的元素不成立。
- **种类**：只有**名字读起来像该属性**的令牌才会被建议
  （`bg` / `label`·`text`·`fg` / `border` / `corner`·`radius`）。
  **宁可一个都不给**，也不给一个解析不出来的 ——
  否则一个 22px 的圆角会被建议成 `line-height` 的令牌。

> 所以「一个令牌都没建议」是正常的，不是没读到。见 [已知限制](./known-issues.md)。

## 排除名单

拾取器不碰**排除名单**里的元素（默认 `:root, html, body`，见「壁纸设置 → 排除名单」；只匹配元素自己，不看祖先）：
爬到这一层时，面板的读数会说明原因，并**不给候选** —— 静默不响应会让人以为拾取器坏了。
名单是逐条求值的，写好的一条选择器抛错只跳过那一条，不会让整个拾取会话失效。

## 相关

- [控件](./controls.md) —— 拾取按钮在表头的位置
- [规则面板](./rule-panel.md) —— 插入之后改这条规则的地方
- [已知限制](./known-issues.md) —— 跨域 iframe 里拾取不到
