javascriptpuppeteer

Is it possible to get a CSS Selector from an existing node in Puppeteer?


I have a pretty simple use case here, I need to go through a table of elements and click on each one, and enter a value.

In the browser environment, you can call the QuerySelector on document, and any node. So you can do something easily like :

const list = document.querySelectorAll('.rows')

And then query elements from each row with

list.map(el => { 
    el.querySelector('.thing_1').click();
    el.querySelector('.thing_2').click();
    el.querySelector('.thing_3').click();
})

I'm using the list element el as the root of the QuerySelector to get the 2nd level elements.

So now I'm in a situation where the Javascript Click function does not work correctly, and I need to mouse click. In puppeteer I believe you need to do this by calling the page.click() method, which Page is not available in the Browser level context (ie: within page.evaluate() or $$eval() )

I know I can get the elements of the nodes the same way as QuerySelectorAll with the const list = page.$$('.rows') function, But once I have that, How do I iterate through them to query the 2nd level elements FROM the node as the root element of the next query? Is it even possible in the puppeteer context?

As you might guess, you can't just go

page.click('.thing_1'); 

Because that query will just give you the thing_1 of the First Row, and there's not really a good way to query select the current row besides using the existing element node. Ideally you'd like to css query in the same way to do something like

list.map((el,page) => { 
    page.click(el.querySelector('.thing_1'));
    ...
},page)

Is this even possible? If so what's the correct syntax for it? Every answer I've seen on this type of question effectively uses the Javascript Click function and not the Puppeteer click function.


Solution

  • I figured this out thanks to another answer while I was testing. In case anyone else struggles with this :

    JAVASCRIPT BROWSER CONTEXT

    const rows = document.QuerySelectorAll(".row") 
    const data = rows.map(r=>r.querySelector(".item_in_row").innerText); 
    

    PUPPETEER CONTEXT:

    const rows = await page.$$(".row"); 
    let data = []; 
    for(let r of rows) { 
        const item = await r.$(".item_in.row"); 
        const item_in_row_text = await item.evaluate(x => x.innerText);
        data.push(item_in_row_text); 
    }
    

    JS document.QuerySelectorAll() == Puppeteer page.$$() JS document.QuerySelector() == Puppeteer page.$() You can run the same .$() and .$$() functions (as well as evaluate and other page functions) on Array of CdpElementHandle objects returned from the page.$$() function like you can on NodeList of elements returned by document.querySelectorAll().

    Also, I believe calling the .click() function in the puppeteer context on a CdpElementHandle object will perform a Puppeteer click much like page.click(selector) , as opposed to the javascript click function. My issue was getting the 2nd level handle of the table objects in the puppeteer context in the first place.