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

类型为 buttonsubmit 的输入元素通过其 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:visibleinput >> 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 中选择元素

我们的 csstext 引擎默认穿透 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 的可用值为 truefalse"mixed"。示例:

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

    了解更多关于 aria-checked

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

    • 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 - 通常存在于角色 headinglistitemrowtreeitem 中的数字属性,<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 的可用值为 truefalse"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

通常可以通过某些属性或文本内容区分元素。在这种情况下,优先使用 textcss 选择器而不是 :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 结构更改的影响。

以下示例使用内置的 textcss 选择器引擎。

// 查询 "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-* 属性受 cssid 选择器 支持。

<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();

避免与实现绑定的选择器

xpathcss 可能与 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();