Puppeteer:单击带有文本的元素

时间:2017-11-21 07:46:23

标签: puppeteer

是否有任何方法(在API中找不到)或解决方案点击带文字的元素?

例如我有html:

<div class="elements">
    <button>Button text</button>
    <a href=#>Href text</a>
    <div>Div text</div>
</div>

我想点击文字被包裹的元素(点击.elements里面的按钮),如:

Page.click('Button text', '.elements')

任何解决方案?

9 个答案:

答案 0 :(得分:54)

您可以将XPath选择器与page.$x(expression)

一起使用
const linkHandlers = await page.$x("//a[contains(text(), 'Some text')]");

if (linkHandlers.length > 0) {
  await linkHandlers[0].click();
} else {
  throw new Error("Link not found");
}

查看此gist中的clickByText以获取完整示例。它负责转义引号,这对于XPath表达式来说有点棘手。

答案 1 :(得分:5)

提出了一种快速解决方案,可以使用高级的CSS选择器,例如“:contains(text)”

因此,使用此library您可以

const select = require ('puppeteer-select');

const element = await select(page).getElement('button:contains(Button text)');
await element.click()

答案 2 :(得分:4)

您还可以使用page.evaluate()单击从document.querySelectorAll()获得的已按文本内容过滤的元素:

await page.evaluate(() => {
  [...document.querySelectorAll('.elements button')].find(element => element.textContent === 'Button text').click();
});

或者,您可以使用page.evaluate()和相应的XPath表达式,使用document.evaluate()根据元素的文本内容单击元素。

await page.evaluate(() => {
  const xpath = '//*[@class="elements"]//button[contains(text(), "Button text")]';
  const result = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null);

  result.iterateNext().click();
});

答案 3 :(得分:4)

tokland的current top answer仅适用于文本节点,不适用于内部具有其他元素的节点。

简短答案

此XPath表达式将查询包含文本“按钮文本”的按钮:

const [button] = await page.$x("//button[contains(., 'Button text')]");
if (button) {
    await button.click();
}

要同时遵守按钮周围的<div class="elements">,请使用以下代码:

const [button] = await page.$x("//div[@class='elements']/button[contains(., 'Button text')]");

说明

要解释为什么在某些情况下使用文本节点(text())是错误的,我们来看一个示例:

<div>
    <button>Start End</button>
    <button>Start <em>Middle</em> End</button>
</div>

首先,让我们在使用contains(text(), 'Text')时检查结果:

  • //button[contains(text(), 'Start')]将同时返回两个两个节点
  • //button[contains(text(), 'End')]仅返回一个节点(第一个),因为text()返回带有两个文本(StartEnd)的列表,但contains仅会检查第一个
  • //button[contains(text(), 'Middle')]将返回结果,因为text()不包含子节点的文本

以下是contains(., 'Text')的XPath表达式,它可用于元素本身(包括其子节点):

  • //button[contains(., 'Start')]将同时返回两个两个按钮
  • //button[contains(., 'End')]将再次返回两个两个按钮
  • //button[contains(., 'Middle')]将返回一个(最后一个按钮)

因此,在大多数情况下,在XPath表达式中使用.代替text()更有意义。

答案 4 :(得分:2)

这是我的解决方案:

let selector = 'a';
    await page.$$eval(selector, anchors => {
        anchors.map(anchor => {
            if(anchor.textContent == 'target text') {
                anchor.click();
                return
            }
        })
    });

答案 5 :(得分:2)

解决方案是

(await page.$$eval(selector, a => a
            .filter(a => a.textContent === 'target text')
))[0].click()

答案 6 :(得分:1)

有很多答案提示contains,但鉴于OP的使用情况(从所有方面看来都是与目标字符串"Button text"和目标字符串完全匹配),我认为这种不精确性没有任何动机。元素<button>Button text</button>

我更喜欢使用更精确的[text()="Button text"],这样可以避免在按钮为<button>Button text and more stuff</button>时产生误报:

const [el] = await page.$x('//*[@class="elements"]//a[text()="Button text"]');
el && (await el.click());

这预见了this answer的相反情况,它试图尽可能地宽松。

许多答案还没有达到.elements父类的要求。

答案 7 :(得分:0)

文本选择器或组合器选项没有受支持的CSS选择器语法,我的解决方法是:

await page.$$eval('selector', selectorMatched => {
    for(i in selectorMatched)
      if(selectorMatched[i].textContent === 'text string'){
          selectorMatched[i].click();
          break;//Remove this line (break statement) if you want to click on all matched elements otherwise the first element only is clicked  
        }
    });

答案 8 :(得分:-1)

我必须:

await this.page.$eval(this.menuSelector, elem => elem.click());