跳至主要内容

其他定位器

介绍

注意

查看主要定位器指南,了解最常见和推荐的定位器。

除了推荐的定位器,如 page.get_by_role()page.get_by_text(),Playwright 支持本指南中描述的各种其他定位器。

CSS 定位器

注意

我们建议优先使用用户可见定位器(如文本或可访问角色),而不是使用与实现绑定的 CSS,因为这些 CSS 可能会在页面更改时失效。

Playwright 可以通过 CSS 选择器定位元素。

page.locator("css=button").click()

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

  • CSS 选择器穿透打开的 Shadow DOM。
  • Playwright 添加了自定义伪类,如 :visible:has-text():has():is():nth-match() 等。

CSS:通过文本匹配

Playwright 包含许多 CSS 伪类,用于通过文本内容匹配元素。

  • article:has-text("Playwright") - :has-text() 匹配任何包含指定文本的元素(可能在子元素或后代元素中)。匹配不区分大小写,修剪空白并搜索子字符串。

    例如,article:has-text("Playwright") 匹配 <article><div>Playwright</div></article>

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

    # Wrong, will match many elements including <body>
    page.locator(':has-text("Playwright")').click()
    # Correct, only matches the <article> element
    page.locator('article:has-text("All products")').click()
  • #nav-bar :text("Home") - :text() 伪类匹配包含指定文本的最小元素。匹配不区分大小写,修剪空白并搜索子字符串。

    例如,这将找到 #nav-bar 元素内部包含文本“Home”的元素

    page.locator("#nav-bar :text('Home')").click()
  • #nav-bar :text-is("Home") - :text-is() 伪类匹配具有精确文本的最小元素。精确匹配区分大小写,修剪空白并搜索完整字符串。

    例如,:text-is("Log") 不匹配 <button>Log in</button>,因为 <button> 包含一个单独的文本节点 "Log in",它不等于 "Log"。但是,:text-is("Log") 匹配 <button> Log <span>in</span></button>,因为 <button> 包含一个文本节点 " Log "

    类似地,:text-is("Download") 不会匹配 <button>download</button>,因为它区分大小写。

  • #nav-bar :text-matches("reg?ex", "i") - :text-matches() 伪类匹配文本内容与类似 JavaScript 的正则表达式匹配的最小元素。

    例如,:text-matches("Log\s*in", "i") 匹配 <button>Login</button><button>log IN</button>

注意

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

注意

类型为 buttonsubmit 的输入元素通过它们的 value 而不是文本内容匹配。例如,:text("Log in") 匹配 <input type=button value="Log in">

CSS:仅匹配可见元素

Playwright 支持 CSS 选择器中的 :visible 伪类。例如,css=button 匹配页面上的所有按钮,而 css=button:visible 仅匹配可见按钮。这对于区分非常相似但可见性不同的元素很有用。

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

<button style='display: none'>Invisible</button>
<button>Visible</button>
  • 这将找到两个按钮,并抛出严格性违规错误

    page.locator("button").click()
  • 这将只找到第二个按钮,因为它可见,然后点击它。

    page.locator("button:visible").click()

CSS:包含其他元素的元素

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

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

page.locator("article:has(div.promo)").text_content()

CSS:匹配其中一个条件的元素

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

# Clicks a <button> that has either a "Log in" or "Sign in" text.
page.locator('button:has-text("Log in"), button:has-text("Sign in")').click()

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

CSS:根据布局匹配元素

注意

根据布局进行匹配可能会产生意外结果。例如,当布局更改一个像素时,可能会匹配不同的元素。

有时,当目标元素缺少独特特征时,很难想出一个好的选择器。在这种情况下,使用 Playwright 布局 CSS 伪类可能会有所帮助。这些可以与常规 CSS 结合使用,以确定多个选择中的一个。

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

请注意,布局伪类除了其他内容外还有用,例如 input。如果你只使用布局伪类,例如 :right-of(:text("Password")),你很可能不会找到你正在寻找的输入,而是文本和目标输入之间的某个空元素。

布局伪类使用边界客户端矩形来计算元素的距离和相对位置。

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

请注意,结果匹配按它们到锚元素的距离排序,因此你可以使用locator.first 选择最接近的元素。这只有在你有一个类似元素列表(其中最接近的显然是正确的元素)时才有用。但是,在其他情况下使用locator.first 很可能不会按预期工作 - 它不会定位你正在搜索的元素,而是定位某个恰好是最接近的元素,例如随机的空 <div>,或者一个已滚动出并且当前不可见的元素。

# Fill an input to the right of "Username".
page.locator("input:right-of(:text(\"Username\"))").fill("value")

# Click a button near the promo card.
page.locator("button:near(.promo-card)").click()

# Click the radio input in the list closest to the "Label 3".
page.locator("[type=radio]:left-of(:text(\"Label 3\"))").first.click()

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

CSS:从查询结果中选择第 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 开始。

# Click the third "Buy" button
page.locator(":nth-match(:text('Buy'), 3)").click()

:nth-match() 也可以用来等待指定数量的元素出现,使用locator.wait_for().

# Wait until all three buttons are visible
page.locator(":nth-match(:text('Buy'), 3)").wait_for()
注意

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

第 n 个元素定位器

可以使用 nth= 定位器传递基于零的索引来将查询缩小到第 n 个匹配项。

# Click first button
page.locator("button").locator("nth=0").click()

# Click last button
page.locator("button").locator("nth=-1").click()

父元素定位器

当你需要定位其他元素的父元素时,大多数情况下你应该使用locator.filter() 过滤子元素定位器。例如,考虑以下 DOM 结构

<li><label>Hello</label></li>
<li><label>World</label></li>

如果你想定位文本为 "Hello" 的标签的父 <li>,使用locator.filter() 是最佳选择

child = page.get_by_text("Hello")
parent = page.get_by_role("listitem").filter(has=child)

或者,如果您无法找到父元素的合适定位器,请使用xpath=..。请注意,这种方法并不十分可靠,因为 DOM 结构的任何更改都会破坏您的测试。如果可能,请优先使用locator.filter()

parent = page.get_by_text("Hello").locator('xpath=..')

React 定位器

注意

React 定位器是实验性的,并以_为前缀。其功能可能会在未来发生变化。

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

在 React 定位器中,组件名称以 **驼峰式大小写** 形式编写。

page.locator("_react=BookItem").click()

更多示例

  • 按 **组件** 匹配:_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"]
  • 按组件和 **键** 匹配:_react=BookItem[key = '2']
  • 按属性值 **正则表达式** 匹配:_react=[author = /Steven(\\s+King)?/i]

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

注意

React 定位器支持 React 15 及更高版本。

注意

React 定位器以及React DevTools仅适用于 **未压缩** 的应用程序构建。

Vue 定位器

注意

Vue 定位器是实验性的,并以_为前缀。其功能可能会在未来发生变化。

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

在 Vue 定位器中,组件名称以 **短横线分隔式小写** 形式编写。

page.locator("_vue=book-item").click()

更多示例

  • 按 **组件** 匹配:_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

注意

Vue 定位器支持 Vue2 及更高版本。

注意

Vue 定位器以及Vue DevTools仅适用于 **未压缩** 的应用程序构建。

XPath 定位器

警告

我们建议优先使用用户可见的定位器,例如文本或可访问角色,而不是使用与实现绑定的 XPath,这些 XPath 在页面更改时很容易失效。

XPath 定位器等效于调用Document.evaluate

page.locator("xpath=//button").click()
注意

任何以//..开头的选择器字符串都被视为 xpath 选择器。例如,Playwright 将'//html/body'转换为'xpath=//html/body'

注意

XPath 不会穿透影子根。

XPath 联合

可以使用管道运算符 (|) 在 XPath 中指定多个选择器。它将匹配所有可以通过该列表中的某个选择器进行选择的元素。

# Waits for either confirmation dialog or load spinner.
page.locator("//span[contains(@class, 'spinner__loading')]|//div[@id='confirmation']").wait_for()

标签到表单控件的重定向

警告

我们建议通过标签文本进行定位,而不是依赖标签到控件的重定向。

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

例如,考虑以下 DOM 结构:<label for="password">Password:</label><input id="password" type="password">。您可以使用page.get_by_text()通过其“Password”文本定位该标签。但是,以下操作将在输入字段上执行,而不是在标签上执行

# Fill the input by targeting the label.
page.get_by_text("Password").fill("secret")

但是,其他方法将定位标签本身,例如expect(locator).to_have_text() 将断言标签的文本内容,而不是输入字段。

# Fill the input by targeting the label.
expect(page.locator("label")).to_have_text("Password")

旧版文本定位器

警告

我们建议使用现代文本定位器

旧版文本定位器匹配包含传递文本的元素。

page.locator("text=Log in").click()

旧版文本定位器有几个变体

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

    page.locator("text=Log in").click()
  • text="Log in" - 文本主体可以使用单引号或双引号进行转义,以在修剪空格后搜索具有精确内容的文本节点。

    例如,text="Log" 不会匹配 <button>Log in</button>,因为 <button> 包含一个文本节点 "Log in",它不等于 "Log"。但是,text="Log" 匹配 <button> Log <span>in</span></button>,因为 <button> 包含一个文本节点 " Log "。此精确模式暗示区分大小写匹配,因此 text="Download" 不会匹配 <button>download</button>

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

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

    page.locator("text=/Log\s*in/i").click()
注意

以引号 ("') 开头和结尾的字符串选择器被视为旧版文本定位器。例如,"Log in" 在内部被转换为 text="Log in"

注意

匹配始终规范化空格。例如,它将多个空格转换为一个空格,将换行符转换为空格,并忽略开头和结尾的空格。

注意

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

id、data-testid、data-test-id、data-test 选择器

警告

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

  • id
  • data-testid
  • data-test-id
  • data-test
# Fill an input with the id "username"
page.locator('id=username').fill('value')

# Click an element with data-test-id "submit"
page.locator('data-test-id=submit').click()
注意

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

链接选择器

警告

我们建议链接定位器

定义为 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 元素。