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?
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.
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')]");
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.
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()
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();
});
La solution est
(await page.$$eval(selector, a => a
.filter(a => a.textContent === 'target text')
))[0].click()
Voici ma solution:
let selector = 'a';
await page.$$eval(selector, anchors => {
anchors.map(anchor => {
if(anchor.textContent == 'target text') {
anchor.click();
return
}
})
});