Pitfall Preferences jobs-apply

Linkedin Shadow Dom Locator Fix

pitfalllinkedinshadow-domplaywright

LinkedIn Easy Apply Modal in Shadow DOM : querySelectorAll Finds Nothing

What Happened

LinkedIn’s Easy Apply modal is rendered inside a shadow DOM within a full-viewport preload iframe (https://www.linkedin.com/preload/). We called page.evaluate(document.querySelectorAll('[role="dialog"]')) to locate the modal and got 0 elements back : even though the modal was visually open and clearly visible in screenshots. The apply flow was completely broken for any job using the Easy Apply flow.

Root Cause

See definitions/root-cause-analysis for the analytical framework. Specific cause: page.evaluate() runs in the main page context and cannot pierce closed shadow DOM boundaries. LinkedIn’s Easy Apply modal lives inside a shadow root within a preload iframe : two layers of isolation. The modal elements simply do not exist from the perspective of querySelectorAll called in the page context. The DOM query succeeds (returns an empty NodeList) with no error, so the failure looks like the modal is not open yet rather than a selector scoping problem.

How to Avoid

Use Playwright locator() API : it pierces open shadow DOM automatically:

  • page.locator('[role="dialog"]').count() finds 2 elements where querySelectorAll found 0
  • Convert all page.$() / page.$$() calls to page.locator().all() + .elementHandle()
  • Add a _modalScope() helper for iframe-aware button scoping
  • Dismiss dialog methods always search the main frame (_realPage)

When working on the LinkedIn adapter: NEVER use page.evaluate(querySelectorAll) for modal elements. Always use page.locator() / frame.locator().