Skip to main content

选择器

选择器是用于创建 Locator 的字符串。Locators 用于通过 locator.click([options])、locator.fill(value[, options]) 等方法对元素执行操作。有关调试选择器的信息,请参阅此处。

编写好的选择器既是一门艺术,也是一门科学,因此请务必查看最佳实践部分。

快速指南​

  • 文本选择器

    await page.locator('text=Log in').click();

    了解更多关于 文本选择器。

  • CSS 选择器

    await page.locator('button').click();
    await page.locator('#nav-bar .contact-us-item').click();

    了解更多关于 CSS 选择器。

  • 通过属性选择,使用 CSS 选择器

    await page.locator('[data-test=login-button]').click();
    await page.locator('[aria-label="Sign in"]').click();

    了解更多关于 CSS 选择器。

  • 结合 CSS 和文本选择器

    await page.locator('article:has-text("Playwright")').click();
    await page.locator('#nav-bar >> text=Contact Us').click();

    了解更多关于 :has-text() 和 :text() 伪类。

  • 包含另一个元素的元素,使用 CSS 选择器

    await page.locator('.item-description:has(.item-promo-banner)').click();

    了解更多关于 :has() 伪类。

  • 基于布局选择,使用 CSS 选择器

    await page.locator('input:right-of(:text("Username"))').click();

    了解更多关于 布局选择器。

  • 仅可见元素,使用 CSS 选择器

    await page.locator('.login-button:visible').click();

    了解更多关于 选择可见元素。

  • 选择第 n 个匹配项

    await page.locator(':nth-match(:text("Buy"), 3)').click();

    了解更多关于 :nth-match() 伪类。

  • XPath 选择器

    await page.locator('xpath=//button').click();

    了解更多关于 XPath 选择器。

  • React 选择器 (实验性)

    await page.locator('_react=ListItem[text *= "milk" i]').click();

    了解更多关于 React 选择器。

  • Vue 选择器 (实验性)

    await page.locator('_vue=list-item[text *= "milk" i]').click();

    了解更多关于 Vue 选择器。

文本选择器​

文本选择器定位包含传递文本的元素。

await page.locator('text=Log in').click();

文本选择器有一些变体:

  • text=Log in - 默认匹配不区分大小写,修剪空白并搜索子字符串。例如,text=Log 匹配 <button>Log in</button>。

    await page.locator('text=Log in').click();
  • text="Log in" - 文本主体可以用单引号或双引号转义,以搜索修剪空白后具有确切内容的文本节点。例如,text="Log" 不匹配 <button>Log in</button>,因为 <button> 包含一个不等于 "Log" 的单个文本节点 "Log in"。但是,text="Log" 匹配 <button> Log <span>in</span></button>,因为 <button> 包含一个文本节点 " Log "。这种精确模式意味着区分大小写的匹配,因此 text="Download" 将不匹配 <button>download</button>。

    引用主体遵循通常的转义规则,例如使用 \" 在双引号字符串中转义双引号:text="foo\"bar"。

    await page.locator('text="Log in"').click();
  • "Log in" - 以引号(" 或 ')开头和结尾的选择器被假定为文本选择器。例如,"Log in" 在内部转换为 text="Log in"。

    await page.locator('"Log in"').click();
  • /Log\s*in/i - 主体可以是包裹在 / 符号中的 类似 JavaScript 的正则表达式。例如,text=/Log\s*in/i 匹配 <button>Login</button> 和 <button>log IN</button>。

    await page.locator('text=/Log\\s*in/i').click();
  • article:has-text("Playwright") - :has-text() 伪类可以在 css 选择器内部使用。它匹配内部某处包含指定文本的任何元素,可能在子元素或后代元素中。匹配不区分大小写,修剪空白并搜索子字符串。例如,article:has-text("Playwright") 匹配 <article><div>Playwright</div></article>。

    请注意,:has-text() 应与其他 css 说明符一起使用,否则它将匹配包含指定文本的所有元素,包括 <body>。

    // 错误,将匹配包括 <body> 在内的许多元素
    await page.locator(':has-text("Playwright")').click();
    // 正确,仅匹配 <article> 元素
    await page.locator('article:has-text("Playwright")').click();
  • #nav-bar :text("Home") - :text() 伪类可以在 css 选择器内部使用。它匹配包含指定文本的最小元素。此示例等效于 text=Home,但在 #nav-bar 元素内部。

    await page.locator('#nav-bar :text("Home")').click();
  • #nav-bar :text-is("Home") - :text-is() 伪类可以在 css 选择器内部使用,用于严格的文本节点匹配。此示例等效于 text="Home"(注意引号),但在 #nav-bar 元素内部。

  • #nav-bar :text-matches("reg?ex", "i") - :text-matches() 伪类可以在 css 选择器内部使用,用于基于正则表达式的匹配。此示例等效于 text=/reg?ex/i,但在 #nav-bar 元素内部。
note

匹配总是规范化空白。例如,它将多个空格转换为一个,将换行符转换为空格,并忽略前导和尾随空白。

note

类型为 button 和 submit 的输入元素通过其 value 而不是文本内容进行匹配。例如,text=Log in 匹配 <input type=button value="Log in">。

CSS 选择器​

Playwright 以两种方式增强了标准 CSS 选择器:

  • css 引擎默认穿透打开的 shadow DOM。
  • Playwright 添加了自定义伪类,如 :visible、:text 等。
await page.locator('button').click();

选择可见元素​

使用 Playwright 有两种仅选择 可见 元素的方法:

  • CSS 选择器中的 :visible 伪类
  • visible= 选择器引擎

如果您希望选择器是 CSS 并且不想依赖 链式选择器,请像这样使用 :visible 伪类:input:visible。如果您更喜欢组合选择器引擎,请使用 input >> visible=true。后者允许您将 text=、xpath= 和其他选择器引擎与可见性过滤器结合使用。

例如,input 匹配页面上的所有输入,而 input:visible 和 input >> visible=true 仅匹配可见输入。这对于区分非常相似但在可见性方面不同的元素很有用。

note

通常最好遵循 最佳实践 并找到更可靠的方法来唯一标识元素。

考虑一个带有两个按钮的页面,第一个不可见,第二个可见。

<button style='display: none'>Invisible</button>
<button>Visible</button>
  • 这将找到第一个按钮,因为它是 DOM 顺序中的第一个元素。然后它将等待按钮变为可见再单击,或者在等待时超时:

    await page.locator('button').click();
  • 这些将找到第二个按钮,因为它是可见的,然后单击它。

    await page.locator('button:visible').click();
    await page.locator('button >> visible=true').click();

选择包含其他元素的元素​

按文本过滤​

Locators 支持一个选项,仅选择内部某处具有某些文本的元素,可能在后代元素中。匹配不区分大小写并搜索子字符串。

await page.locator('button', { hasText: 'Click me' }).click();

您也可以传递正则表达式。

按另一个定位器过滤​

Locators 支持一个选项,仅选择具有匹配另一个定位器的后代的元素。

page.locator('article', { has: page.locator('button.subscribe') })

请注意,内部定位器是从外部定位器开始匹配的,而不是从文档根开始。

在 CSS 选择器内部​

:has() 伪类是一个 实验性 CSS 伪类。如果作为参数传递的任何选择器相对于给定元素的 :scope 匹配至少一个元素,它将返回该元素。

以下代码段返回内部具有 <div class=promo> 的 <article> 元素的文本内容。

await page.locator('article:has(div.promo)').textContent();

增强现有定位器​

您可以通过将 :scope 选择器传递给 locator.locator(selector[, options]) 并指定所需的选项来向任何定位器添加过滤。例如,给定选择表中某些行的定位器 row,您可以过滤为仅包含文本 "Hello" 的行。

const row = page.locator('.row');
// ... 稍后 ...
await row.locator(':scope', { hasText: 'Hello' }).click();

选择匹配条件之一的元素​

CSS 选择器列表​

逗号分隔的 CSS 选择器列表将匹配列表中任何一个选择器可以选择的所有元素。

// 单击具有 "Log in" 或 "Sign in" 文本的 <button>。
await page.locator('button:has-text("Log in"), button:has-text("Sign in")').click();

:is() 伪类是一个 实验性 CSS 伪类,对于指定元素的一系列额外条件可能很有用。

XPath 联合​

管道运算符 (|) 可用于在 XPath 中指定多个选择器。它将匹配列表中任何一个选择器可以选择的所有元素。

// 等待确认对话框或加载微调器。
await page.locator(`//span[contains(@class, 'spinner__loading')]|//div[@id='confirmation']`).waitFor();

在 Shadow DOM 中选择元素​

我们的 css 和 text 引擎默认穿透 Shadow DOM:

  • 首先,它们按迭代顺序搜索 light DOM 中的元素,并且
  • 然后,它们按迭代顺序递归搜索打开的 shadow roots 内部。

特别是,在 css 引擎中,任何 后代组合器 或 子组合器 穿透任意数量的打开 shadow roots,包括选择器开头的隐式后代组合器。它不搜索关闭的 shadow roots 或 iframe 内部。

如果您想退出此行为,可以使用 :light CSS 扩展或 text:light 选择器引擎。它们不穿透 shadow roots。

await page.locator(':light(.article > .header)').click();

更高级的 Shadow DOM 用例:

<article>
<div>In the light dom</div>
<div slot='myslot'>In the light dom, but goes into the shadow slot</div>
#shadow-root
<div class='in-the-shadow'>
<span class='content'>
In the shadow dom
#shadow-root
<li id='target'>Deep in the shadow</li>
</span>
</div>
<slot name='myslot'></slot>
</article>
  • "article div" 和 ":light(article div)" 都匹配第一个 <div>In the light dom</div>。
  • "article > div" 和 ":light(article > div)" 都匹配作为 article 直接子元素的两个 div 元素。
  • "article .in-the-shadow" 匹配 <div class='in-the-shadow'>,穿透 shadow root,而 ":light(article .in-the-shadow)" 不匹配任何内容。
  • ":light(article div > span)" 不匹配任何内容,因为两个 light-dom div 元素都不包含 span。
  • "article div > span" 匹配 <span class='content'>,穿透 shadow root。
  • "article > .in-the-shadow" 不匹配任何内容,因为 <div class='in-the-shadow'> 不是 article 的直接子元素。
  • ":light(article > .in-the-shadow)" 不匹配任何内容。
  • "article li#target" 匹配 <li id='target'>Deep in the shadow</li>,穿透两个 shadow roots。

基于布局选择元素​

有时,当目标元素缺乏明显的特征时,很难为其想出一个好的选择器。在这种情况下,使用 Playwright 布局选择器可能会有所帮助。这些可以与常规 CSS 结合使用,以精确定位多个选择之一。

例如,input:right-of(:text("Password")) 匹配位于文本 "Password" 右侧的输入字段 - 当页面有多个难以区分的输入时很有用。

请注意,布局选择器作为其他内容的补充很有用,例如 input。如果您单独使用布局选择器,例如 :right-of(:text("Password")),您很可能得到的不是您正在寻找的输入,而是文本和目标输入之间的一些空元素。

note

布局选择器取决于页面布局,可能会产生意想不到的结果。例如,当布局改变一个像素时,可能会匹配不同的元素。

布局选择器使用 bounding client rect 来计算元素的距离和相对位置。

  • :right-of(inner > selector) - 匹配位于匹配内部选择器的任何元素右侧的元素,处于任何垂直位置。
  • :left-of(inner > selector) - 匹配位于匹配内部选择器的任何元素左侧的元素,处于任何垂直位置。
  • :above(inner > selector) - 匹配位于匹配内部选择器的任何元素上方的元素,处于任何水平位置。
  • :below(inner > selector) - 匹配位于匹配内部选择器的任何元素下方的元素,处于任何水平位置。
  • :near(inner > selector) - 匹配靠近(在 50 CSS 像素内)匹配内部选择器的任何元素的元素。

请注意,结果匹配按其到锚点元素的距离排序,因此您可以使用 locator.first() 来选择最近的一个。这仅在您有类似元素列表的情况下有用,其中最近的一个显然是正确的。但是,在其他情况下使用 locator.first() 很可能无法按预期工作 - 它不会针对您正在搜索的元素,而是碰巧最近的其他元素,如随机空 <div>,或滚动出且当前不可见的元素。

// 填写 "Username" 右侧的输入。
await page.locator('input:right-of(:text("Username"))').fill('value');

// 单击 promo card 附近的按钮。
await page.locator('button:near(.promo-card)').click();

// 单击列表中最接近 "Label 3" 的单选输入。
await page.locator('[type=radio]:left-of(:text("Label 3"))').first().click();

所有布局选择器都支持可选的最大像素距离作为最后一个参数。例如,button:near(:text("Username"), 120) 匹配距离文本为 "Username" 的元素最多 120 像素的按钮。

通过标签文本选择元素​

Playwright 中的目标输入操作会自动区分标签和控件,因此您可以定位标签以对关联控件执行操作。

例如,考虑以下 DOM 结构:<label for="password">Password:</label><input id="password" type="password">。您可以使用类似 text=Password 的内容定位标签,并对输入执行以下操作:

  • click 将单击标签并自动聚焦输入字段;
  • fill 将填写输入字段;
  • inputValue 将返回输入字段的值;
  • selectText 将选择输入字段中的文本;
  • setInputFiles 将为 type=file 的输入字段设置文件;
  • selectOption 将从选择框中选择一个选项。
// 通过定位标签填写输入。
await page.locator('text=Password').fill('secret');

但是,其他方法将针对标签本身,例如 textContent 将返回标签的文本内容,而不是输入字段。

XPath 选择器​

XPath 选择器等效于调用 Document.evaluate。示例:xpath=//html/body。

以 // 或 .. 开头的选择器被假定为 xpath 选择器。例如,Playwright 将 '//html/body' 转换为 'xpath=//html/body'。

note

xpath 不穿透 shadow roots

第 N 个元素选择器​

您可以使用 nth= 选择器将查询缩小到第 n 个匹配项。与 CSS 的 nth-match 不同,提供的索引是基于 0 的。

// 单击第一个按钮
await page.locator('button >> nth=0').click();

// 单击最后一个按钮
await page.locator('button >> nth=-1').click();

React 选择器​

note

React 选择器是实验性的,前缀为 _。功能可能会在未来发生变化。

React 选择器允许通过组件名称和属性值选择元素。语法与 属性选择器 非常相似,并支持所有属性选择器运算符。

在 React 选择器中,组件名称使用 CamelCase 转录。

选择器示例:

  • 按 组件 匹配:_react=BookItem
  • 按组件和 精确属性值 匹配,区分大小写:_react=BookItem[author = "Steven King"]
  • 仅按属性值匹配,不区分大小写:_react=[author = "steven king" i]
  • 按组件和 真值属性值 匹配:_react=MyButton[enabled]
  • 按组件和 布尔值 匹配:_react=MyButton[enabled = false]
  • 按属性 值子字符串 匹配:_react=[author *= "King"]
  • 按组件和 多个属性 匹配:_react=BookItem[author *= "king" i][year = 1990]
  • 按 嵌套 属性值 匹配:_react=[some.nested.value = 12]
  • 按组件和属性值 前缀 匹配:_react=BookItem[author ^= "Steven"]
  • 按组件和属性值 后缀 匹配:_react=BookItem[author $= "Steven"]
  • 按组件和 key 匹配:_react=BookItem[key = '2']
  • 按属性值 正则表达式 匹配:_react=[author = /Steven(\\s+King)?/i]

要在树中查找 React 元素名称,请使用 React DevTools。

note

React 选择器支持 React 15 及更高版本。

note

React 选择器以及 React DevTools 仅适用于 未压缩 的应用程序构建。

Vue 选择器​

note

Vue 选择器是实验性的,前缀为 _。功能可能会在未来发生变化。

Vue 选择器允许通过组件名称和属性值选择元素。语法与 属性选择器 非常相似,并支持所有属性选择器运算符。

在 Vue 选择器中,组件名称使用 kebab-case 转录。

选择器示例:

  • 按 组件 匹配:_vue=book-item
  • 按组件和 精确属性值 匹配,区分大小写:_vue=book-item[author = "Steven King"]
  • 仅按属性值匹配,不区分大小写:_vue=[author = "steven king" i]
  • 按组件和 真值属性值 匹配:_vue=my-button[enabled]
  • 按组件和 布尔值 匹配:_vue=my-button[enabled = false]
  • 按属性 值子字符串 匹配:_vue=[author *= "King"]
  • 按组件和 多个属性 匹配:_vue=book-item[author *= "king" i][year = 1990]
  • 按 嵌套 属性值 匹配:_vue=[some.nested.value = 12]
  • 按组件和属性值 前缀 匹配:_vue=book-item[author ^= "Steven"]
  • 按组件和属性值 后缀 匹配:_vue=book-item[author $= "Steven"]
  • 按属性值 正则表达式 匹配:_vue=[author = /Steven(\\s+King)?/i]

要在树中查找 Vue 元素名称,请使用 Vue DevTools。

note

Vue 选择器支持 Vue2 及更高版本。

note

Vue 选择器以及 Vue DevTools 仅适用于 未压缩 的应用程序构建。

角色选择器​

角色选择器允许通过其 ARIA 角色、ARIA 属性 和 可访问名称 选择元素。请注意,角色选择器 不能替代 可访问性审计和一致性测试,而是提供有关 ARIA 准则的早期反馈。

语法与 CSS 属性选择器 非常相似。例如,role=button[name="Click me"][pressed] 选择具有可访问名称 "Click me" 的已按下按钮。

请注意,许多 html 元素具有被角色选择器识别的隐式 定义角色。您可以在此处找到所有 支持的角色。ARIA 准则 不建议 通过将 role 和/或 aria-* 属性设置为默认值来重复隐式角色和属性。

角色选择器支持的属性:

  • checked - 通常由 aria-checked 或原生 <input type=checkbox> 控件设置的属性。checked 的可用值为 true、false 和 "mixed"。示例:

    • role=checkbox[checked=true],等同于 role=checkbox[checked]
    • role=checkbox[checked=false]
    • role=checkbox[checked="mixed"]

    了解更多关于 aria-checked。

  • disabled - 通常由 aria-disabled 或 disabled 设置的布尔属性。示例:

    • role=button[disabled=true],等同于 role=button[disabled]
    • role=button[disabled=false]

    请注意,与大多数其他属性不同,disabled 通过 DOM 层次结构继承。了解更多关于 aria-disabled。

  • expanded - 通常由 aria-expanded 设置的布尔属性。示例:

    • role=button[expanded=true],等同于 role=button[expanded]
    • role=button[expanded=false]

    了解更多关于 aria-expanded。

  • include-hidden - 控制是否匹配隐藏元素的布尔属性。默认情况下,角色选择器仅匹配 ARIA 定义 的非隐藏元素。使用 [include-hidden],将匹配隐藏和非隐藏元素。示例:

    • role=button[include-hidden=true],等同于 role=button[include-hidden]
    • role=button[include-hidden=false]

    了解更多关于 aria-hidden。

  • level - 通常存在于角色 heading、listitem、row、treeitem 中的数字属性,<h1>-<h6> 元素的默认值。示例:

    • role=heading[level=1]

    了解更多关于 aria-level。

  • name - 匹配 可访问名称 的字符串属性。支持属性运算符,如 = 和 *=,以及正则表达式。

    • role=button[name="Click me"]
    • role=button[name*="Click"]
    • role=button[name=/Click( me)?/]

    了解更多关于 可访问名称。

  • pressed - 通常由 aria-pressed 设置的属性。pressed 的可用值为 true、false 和 "mixed"。示例:

    • role=button[pressed=true],等同于 role=button[pressed]
    • role=button[pressed=false]
    • role=button[pressed="mixed"]

    了解更多关于 aria-pressed。

  • selected - 通常由 aria-selected 设置的布尔属性。示例:

    • role=option[selected=true],等同于 role=option[selected]
    • role=option[selected=false]

    了解更多关于 aria-selected。

示例:

  • role=button 匹配所有按钮;
  • role=button[name="Click me"] 匹配具有 "Click me" 可访问名称的按钮;
  • role=checkbox[checked][include-hidden] 匹配已选中的复选框,包括当前隐藏的复选框。

id, data-testid, data-test-id, data-test 选择器​

Playwright 支持使用某些属性选择元素的简写。目前,仅支持以下属性:

  • id
  • data-testid
  • data-test-id
  • data-test
// 填写 id 为 "username" 的输入
await page.locator('id=username').fill('value');

// 单击具有 data-test-id "submit" 的元素
await page.locator('data-test-id=submit').click();
note

属性选择器不是 CSS 选择器,因此不支持任何特定于 CSS 的内容,如 :enabled。有关更多功能,请使用适当的 css 选择器,例如 css=[data-test="login"]:enabled。

note

属性选择器穿透 shadow DOM。要退出此行为,请在属性后使用 :light 后缀,例如 page.locator('data-test-id:light=submit').click()

从查询结果中选择第 n 个匹配项​

有时页面包含许多相似的元素,很难选择特定的一个。例如:

<section> <button>Buy</button> </section>
<article><div> <button>Buy</button> </div></article>
<div><div> <button>Buy</button> </div></div>

在这种情况下,:nth-match(:text("Buy"), 3) 将选择上面代码段中的第三个按钮。请注意,索引是基于 1 的。

// 单击第三个 "Buy" 按钮
await page.locator(':nth-match(:text("Buy"), 3)').click();

:nth-match() 对于等待指定数量的元素出现也很有用,使用 locator.waitFor([options]).

// 等待直到所有三个按钮都可见
await page.locator(':nth-match(:text("Buy"), 3)').waitFor();
note

与 :nth-child() 不同,元素不必是兄弟元素,它们可以在页面上的任何位置。在上面的代码段中,所有三个按钮都匹配 :text("Buy") 选择器,:nth-match() 选择第三个按钮。

note

通常可以通过某些属性或文本内容区分元素。在这种情况下,优先使用 text 或 css 选择器而不是 :nth-match()。

父选择器​

父级可以使用 .. 选择,这是 xpath=.. 的缩写形式。

例如:

const parentLocator = elementLocator.locator('..');

链式选择器​

定义为 engine=body 或简写形式的选择器可以与 >> 标记组合,例如 selector1 >> selector2 >> selectors3。当选择器被链接时,下一个选择器是相对于上一个选择器的结果进行查询的。

例如,

css=article >> css=.bar > .baz >> css=span[attr=value]

等同于

document
.querySelector('article')
.querySelector('.bar > .baz')
.querySelector('span[attr=value]')

如果选择器需要在主体中包含 >>,则应将其转义在字符串中,以免与链式分隔符混淆,例如 text="some >> text"。

中间匹配​

默认情况下,链式选择器解析为最后一个选择器查询的元素。选择器可以以 * 为前缀,以捕获中间选择器查询的元素。

例如,css=article >> text=Hello 捕获具有文本 Hello 的元素,而 *css=article >> text=Hello(注意 *)捕获包含具有文本 Hello 的元素的 article 元素。

最佳实践​

选择器的选择决定了自动化脚本的弹性。为了减少维护负担,我们建议优先考虑面向用户的属性和显式契约。

优先考虑面向用户的属性​

文本内容、输入占位符、可访问性角色和标签等属性是面向用户的属性,很少更改。这些属性不受 DOM 结构更改的影响。

以下示例使用内置的 text 和 css 选择器引擎。

// 查询 "Login" 文本选择器
await page.locator('text="Login"').click();
await page.locator('"Login"').click(); // 简写形式

// 查询 "Search GitHub" 占位符属性
await page.locator('css=[placeholder="Search GitHub"]').fill('query');
await page.locator('[placeholder="Search GitHub"]').fill('query'); // 简写形式

// 查询 "Close" 可访问性标签
await page.locator('css=[aria-label="Close"]').click();
await page.locator('[aria-label="Close"]').click(); // 简写形式

// 结合角色和文本查询
await page.locator('css=nav >> text=Login').click();

定义显式契约​

当面向用户的属性频繁更改时,建议使用显式测试 ID,如 data-test-id。这些 data-* 属性受 css 和 id 选择器 支持。

<button data-test-id="directions">Itinéraire</button>
// 使用 css 查询 data-test-id 属性
await page.locator('css=[data-test-id=directions]').click();
await page.locator('[data-test-id=directions]').click(); // 简写形式

// 使用 id 查询 data-test-id
await page.locator('data-test-id=directions').click();

避免与实现绑定的选择器​

xpath 和 css 可能与 DOM 结构或实现绑定。当 DOM 结构更改时,这些选择器可能会中断。同样,locator.nth(index)、locator.first() 和 locator.last() 与实现和 DOM 结构绑定,如果 DOM 更改,将针对不正确的元素。

// 避免长的 css 或 xpath 链
await page.locator('#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input').click();
await page.locator('//*[@id="tsf"]/div[2]/div[1]/div[1]/div/div[2]/input').click();