Issue: I cannot select the calendar widget on hotel website (https://www.ihg.com/hotels/us/en/reservation).
Error: Node is either not clickable or not an Element
What I tried (below is my code):
const puppeteer = require('puppeteer')
const fs = require('fs/promises')
async function start(){
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto("https://www.ihg.com/hotels/us/en/reservation", {waitUntil: "domcontentloaded"});
//! This is not working - Fill Number of NIGHTS
await page.click('#datePicker div[class="input-adjacent-icon fa fa-calendar"]');
// waitForSelector
await page.waitForSelector('span[class="datepicker-field"] span[class="input-and-icon brand-outline"]');
await browser.close()
}
start()
2
Answers
If you were to run
document.querySelectorAll('#datePicker div[class="input-adjacent-icon fa fa-calendar"]')
you would see that there are 6 elements that match your selector. However only the last 2 are viewable.Here’s what the new code would look like:
Preamble: this website is a mess of what looks like AngularJS. It has duplicate ID attributes on elements, and plenty of async behavior and general weirdness, so it won’t be easy to automate.
This may be an xy problem that happens so often in web scraping: there’s a tendency to assume you need to act like a user might ("I need to open the date picker"). But rather than getting too fixated on proposed solution Y, consider a different solution to the real problem X ("How can I populate the date inputs using any technique that the site accepts?") and use
.type()
and the keyboard to manipulate the input boxes, without worrying about automating the date picker.Keeping in mind that the last id seems to be the correct input for each element, you can select the elements, then use the keyboard to erase the current date and type in a new one. Pressing Enter helps ensure the proper events are triggered so the site understands the change. There’s a bit of async fussiness as the site validates and manipulates the dates, so I’m entering the check in date, then the destination, then the check out date.
Using ‘domcontentloaded’ is generally good, but I noticed that the inputs weren’t quite ready in some cases, so I reverted to ‘load’, which is slower. There may be a predicate you can check for readiness that’s more precise than ‘load’, letting you get back to ‘domcontentloaded’, but I suggest focusing on correctness first, then speed later (left as an exercise to keep this post in scope).
You may need to play with this a bit, but hopefully it communicates one possible general strategy. Proceed carefully.