Puppeteer querySelector fails with colon characters in element ID

I’m working on automating form filling using Puppeteer but running into issues with element selection. The webpage I’m targeting has input fields with IDs that contain colon characters, which seems to be causing problems.

The element ID looks like this:

#dashboardPage:MainTemplate:userForm:email-input

When I copy the selector from Chrome DevTools, it shows:

#dashboardPage\3a MainTemplate\3a userForm\3a email-input

Both versions throw the same error when used in my Puppeteer script:

Error: Evaluation failed: DOMException: Failed to execute 'querySelector' on 'Document': '#dashboardPage:MainTemplate:userForm:email-input' is not a valid selector.

Here’s my automation code:

(async () => {
    const browser = await puppeteer.launch({headless: false});
    const page = await browser.newPage();
    await page.goto('https://example.com');
    await page.click(LOGIN_LINK_SELECTOR);
    await page.waitForNavigation({waitUntil: 'load'});
    await page.waitFor(USERNAME_SELECTOR); // fails here
    await page.focus(USERNAME_SELECTOR);
    await page.keyboard.type(credentials.username);
    await page.focus(PASS_SELECTOR);
    await page.keyboard.type(credentials.password);

    await browser.close();
})();

How can I properly handle selectors with colon characters in Puppeteer?

Been dealing with selector headaches for years - manual escaping gets old fast. I just throw everything at Latenode now and let it handle the messy selector logic.

Latenode has this feature where you set up web automation workflows that don’t break on weird CSS syntax. You configure it to try multiple selector strategies automatically - CSS, XPath, attribute selectors - until one works.

Built a form filling automation last month that dealt with crazy generated IDs from different frameworks. Instead of writing custom Puppeteer code and debugging selectors, I mapped out the workflow in Latenode. It handles retry logic and fallback selectors automatically.

You get visual workflow building instead of hand-writing JavaScript. Way easier to maintain when selectors change or you need updates later.

Check it out: https://latenode.com

ugh this drove me crazy too! try page.$eval() instead - something like await page.$eval('input[id="dashboardPage:MainTemplate:userForm:email-input"]', el => el.focus()). $eval handles weird characters better than waitForSelector. fixed my asp.net forms with the same messy id names.

I hit this exact problem with JSF forms a few months back. The colons in CSS selectors are a pain to escape properly, but there’s an easier way that’s worked great for me. Skip the CSS selector escaping headache and use attribute selectors instead - they handle special characters way better. Try this: await page.waitForSelector(‘[id=“dashboardPage:MainTemplate:userForm:email-input”]’); This treats the whole ID as a string value instead of trying to parse those colons as CSS selector syntax. I’ve found it way more reliable than backslash escaping, especially with dynamically generated IDs from JSF or ASP.NET. You could also use page.evaluateHandle() to find elements by their exact ID property, but the attribute selector method is cleaner and easier to maintain.

I’ve encountered this same issue while scraping JSF applications. CSS selectors interpret colons as pseudo-class separators, so it’s crucial to escape them correctly. Instead of using the hex escape from Chrome, you should use double backslashes in your JavaScript string:

const USERNAME_SELECTOR = '#dashboardPage\\:MainTemplate\\:userForm\\:email-input';

The double backslashes are necessary because JavaScript evaluates the string first before passing it to CSS. Alternatively, I recommend using attribute selectors, which tend to be more reliable:

const USERNAME_SELECTOR = '[id="dashboardPage:MainTemplate:userForm:email-input"]';

This approach helps you avoid any CSS parsing issues and works consistently regardless of the framework generating complex IDs.

Had the exact same problem with Oracle ADF apps - those namespace IDs are a pain. Your CSS escaping is right but yeah, it’s messy in JavaScript strings. You need double backslashes: await page.waitForSelector('#dashboardPage\\3a MainTemplate\\3a userForm\\3a email-input'). JavaScript processes the string first, then the browser gets the proper CSS escape sequence. But here’s a way cleaner solution - use page.evaluate() with document.getElementById(): await page.evaluate(() => document.getElementById('dashboardPage:MainTemplate:userForm:email-input')). This skips CSS selector parsing completely since getElementById() takes the parameter as a literal string. Works perfectly with colons, brackets, whatever - no escaping nightmare.

Same nightmare here with legacy Java apps and their colon-heavy IDs. I ditched CSS selectors entirely for XPath when this happens. Try: await page.waitForSelector(‘xpath://*[@id=“dashboardPage:MainTemplate:userForm:email-input”]’). XPath treats the whole ID as a literal string, so colons don’t break it like they do with CSS selectors. Way more reliable than memorizing CSS escape rules, especially with forms full of these colon-separated IDs. Performance is basically the same and you’ll stop debugging selector syntax issues.