Cómo hacer clic en un elemento con texto en Titiritero

5 minutos de lectura

avatar de usuario
Aleksandr Golubovskij

¿Hay algún método (no encontrado en la API) o solución para hacer clic en el elemento con texto?

Por ejemplo, tengo html:

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

Y quiero hacer clic en el elemento en el que se ajusta el texto (haga clic en el botón dentro de .elements), como:

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

  • Compartió la respuesta aquí: stackoverflow.com/a/47829000/6161265

    – Dr. Abu Taher

    8 de enero de 2018 a las 0:30

  • ¿Encontraste Respuesta?

    – Shubham Batra

    15 de enero de 2018 a las 5:55

  • Si ayuda, la página en la que quería hacer clic tiene jQuery cargado, así que pude y usé el método de evaluación para ejecutar el código jQuery.

    – brandito

    26 de febrero de 2018 a las 4:16

avatar de usuario
Tomas Dondorf

Respuesta corta

Esta expresión XPath consultará un botón que contiene el texto “Texto del botón”:

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

Respetar también la <div class="elements"> alrededor de los botones, use el siguiente código:

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

Explicación

Para explicar por qué usar el nodo de texto (text()) está mal en algunos casos, veamos un ejemplo:

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

Primero, verifiquemos los resultados al usar contains(text(), 'Text'):

  • //button[contains(text(), 'Start')] devolverá los dos dos nodos (como se esperaba)
  • //button[contains(text(), 'End')] solo regresará una nodos (el primero) como text() devuelve una lista con dos textos (Start y End), pero contains solo revisare el primero
  • //button[contains(text(), 'Middle')] regresará no resultados como text() no incluye el texto de los nodos secundarios

Aquí están las expresiones XPath para contains(., 'Text')que funciona en el propio elemento, incluidos sus nodos secundarios:

  • //button[contains(., 'Start')] devolverá los dos dos botones
  • //button[contains(., 'End')] volverá de nuevo los dos dos botones
  • //button[contains(., 'Middle')] regresará una (el último botón)

Entonces, en la mayoría de los casos, tiene más sentido usar el . en vez de text() en una expresión XPath.

  • algo que funcione con todo tipo de elemento? no puedo saber si el texto está dentro de un botón, ap, un div, un lapso, etc.

    – Andrea Bisello

    15 de noviembre de 2019 a las 9:30

  • @AndreaBisello Puedes usar //*[...] en cambio.

    – Thomas Dondorf

    15 de noviembre de 2019 a las 17:21

  • ¿Podría funcionar si el ‘Texto del botón’ está en una matriz?

    – usuario303749

    8 abr a las 14:41

avatar de usuario
tierra de tokland

Puede usar un selector XPath con página.$x(expresión):

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");
}

Verificar clickByText en esto esencia para un ejemplo completo. Se encarga de escapar de las comillas, lo cual es un poco complicado con las expresiones XPath.

  • Impresionante: traté de hacerlo para otras etiquetas, pero no puedo hacerlo funcionar. (li, h1,…) ¿Cómo harías eso?

    – Runa Jeppesen

    9 de julio de 2018 a las 15:23

  • @RuneJeppesen Reemplazar //a[contains with //*[contains to select any element, not just an anchor (a) element.

    – Unixmonkey

    Sep 16, 2020 at 15:39

user avatar
Grant Miller

You can also use page.evaluate() to click elements obtained from document.querySelectorAll() that have been filtered by text content:

await page.evaluate(() => {
  [...document.querySelectorAll('.elements button')].find(elemento => elemento.textContent === 'Texto del botón').click();  });

Como alternativa, puede utilizar page.evaluate() para hacer clic en un elemento basado en su contenido de texto usando document.evaluate() y una expresión XPath correspondiente:

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

hizo una solución rápida para poder usar selectores css avanzados como “: contiene (texto)”

entonces usando esto biblioteca tu puedes sólo

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

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

La solucion es

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

  • Considere reemplazar filter(...)[0] con find(...).

    – ggorlen

    19 de marzo a las 22:27


avatar de usuario
Kamen

Aquí está mi solución:

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

  • Considere reemplazar filter(...)[0] con find(...).

    – ggorlen

    19 de marzo a las 22:27


Dado que el caso de uso de OP parece ser una coincidencia exacta en la cadena de destino "Button text", <button>Button text</button>, text() parece el método correcto en lugar del menos preciso contains().

Aunque Thomas hace un buen argumento para contains cuando hay subelementos, evitando falsos negativos, utilizando text() evita un falso positivo cuando el botón está, digamos, <button>Button text and more stuff</button>, que parece un escenario igualmente probable. Es útil tener ambas herramientas a mano para que pueda elegir la más adecuada según cada caso.

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

Tenga en cuenta que muchas otras respuestas perdieron el .elements requisito de la clase principal.

Otra función XPath es [normalize-space()="Button text"] que “elimina los espacios en blanco iniciales y finales de una cadena, reemplaza secuencias de caracteres de espacios en blanco por un solo espacio” y puede ser útil para ciertos casos.

Además, a menudo es útil usar waitForXPath que espera, luego devuelve, el elemento que coincide con XPath o lanza si no se encuentra dentro del tiempo de espera especificado:

const xp = '//*[@class="elements"]//button[text()="Button text"]';
const el = await page.waitForXPath(xp);
await el?.click();

¿Ha sido útil esta solución?