No, it's not essential to always use waitForSelector
. If the element is already on the page, then just select it with page.$
, page.$eval
, etc, otherwise wait for it. If you're not sure whether it'll be there or not, best to wait for it.
waitForSelector
is basically a common-case convenience wrapper on the more general waitForFunction
which either registers a requestAnimationFrame
loop or MutationObserver
to wait for the callback predicate to be true. The waitForSelector
docs claim "If at the moment of calling the method the selector already exists, the method will return immediately" but I don't see that it runs a preliminary page.$
-type query before attaching a requestAnimationFrame
from the present code.
On the other hand, page.$
and page.$eval
and company simply run a document.querySelector
for you with minimal overhead. Nonetheless, I wouldn't worry too much about performance; if you were to replace all page.$
calls with waitForSelector
calls, it'd probably not be super noticable.
On the other hand, I like the expressivity of using page.$
when it's appropriate. It communicates intent that you're sure the element exists, not as clear if you use waitForSelector
indiscriminately. page.$
won't throw, so the reporting is weaker, probably pushing the throw to the next line (or further) when you try to access a property on null
.
Note that waitForSelector
returns the first matching element, so the code can be written as
const selector = 'my-selector';
const ele = await page.waitForSelector(selector);
await ele.click();
Whatever you do, don't sleep. waitForTimeout
is being removed from the Puppeteer API, which is a good thing.
If you get tired of typing out page.waitForSelector
, you can always write a little wrapper for it:
const [page] = await browser.pages();
const $ = (...args) => page.waitForSelector(...args);
const foo = await $(".foo");
const bar = await $(".bar");
const baz = await $(".baz");