web-dev-qa-db-fra.com

Marionnettiste: Cliquez sur l'élément avec le texte

Existe-t-il une méthode (introuvable dans l'API) ou une solution permettant de cliquer sur un élément avec du texte?

Par exemple, j'ai HTML:

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

Et je veux cliquer sur l'élément dans lequel le texte est placé (Cliquez sur le bouton à l'intérieur de .elements), comme:

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

Toute solution?

50

Vous pouvez utiliser un sélecteur XPath avec 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");
}

Découvrez clickByText dans ce Gist pour un exemple complet. Il évite les guillemets, ce qui est un peu délicat avec les expressions XPath.

62
tokland

Le réponse principale actuelle by tokland ne fonctionne que sur les nœuds de texte et non sur les nœuds contenant d'autres éléments.

Réponse courte

Cette expression XPath interrogera un bouton contenant le texte "Texte du bouton":

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

Pour respecter également le <div class="elements"> entourant les boutons, utilisez le code suivant:

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

Explication

Pour expliquer pourquoi l’utilisation du nœud de texte (text()) est erronée dans certains cas, prenons un exemple:

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

Premièrement, vérifions les résultats lorsque vous utilisez contains(text(), 'Text'):

  • //button[contains(text(), 'Start')] retournera les deux deux nœuds (comme prévu)
  • //button[contains(text(), 'End')] ne renverra que un nœud (le premier) sous le nom text() renvoie une liste avec deux textes (Start et End), mais contains ne vérifie que le premier
  • //button[contains(text(), 'Middle')] renverra no les résultats sous forme de text() n'inclut pas le texte des nœuds enfants.

Voici les expressions XPath pour contains(., 'Text'), qui fonctionne sur l'élément lui-même, y compris ses nœuds enfants:

  • //button[contains(., 'Start')] retournera les deux deux boutons
  • //button[contains(., 'End')] retournera à nouveau les deux deux boutons
  • //button[contains(., 'Middle')] retournera un (le dernier bouton)

Donc, dans la plupart des cas, il est plus logique d'utiliser le . au lieu de text() dans une expression XPath.

13
Thomas Dondorf

solution rapide pour pouvoir utiliser les sélecteurs CSS avancés tels que ": contient (texte)"

donc en utilisant ceci bibliothèque vous pouvez simplement

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

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

Vous pouvez également utiliser page.evaluate() pour cliquer sur des éléments obtenus à partir de document.querySelectorAll() qui ont été filtrés par contenu textuel:

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

Vous pouvez également utiliser page.evaluate() pour cliquer sur un élément en fonction de son contenu textuel à l'aide de document.evaluate() et d'une expression XPath correspondante:

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();
});
5
Grant Miller

La solution est

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

Voici ma solution:

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