[Bug]: Elements can not be clicked when using emulation
Version
1.46.0
Steps to reproduce
scripts:
from playwright.sync_api import Playwright, sync_playwright
def run(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
context = browser.new_context(**playwright.devices["iPhone 14 Pro"])
page = context.new_page()
page.goto("https://www.simplytoimpress.com/baptism-invitations/design-105133")
# ---------------------
context.close()
browser.close()
with sync_playwright() as playwright:
run(playwright)
Expected behavior
Can operate normally
Actual behavior
None of the elements in the red box can be operated
The script can not navigate to elements, nor can it click to manually manipulate them
Additional context
In the script-initiated browser window, manually switch to developer mode, and then simulate the mobile page is operational. I do not know whether can be configured to solve with the new_context arguments?
Environment
- Operating System: [windows11]
- CPU: [Inter]
- Browser: [All, Chromium, Firefox, WebKit]
- Python Version: [3.12]
- Other info:
Moving upstream since I can repro with npx playwright open --device="Galaxy S8" https://www.simplytoimpress.com/baptism-invitations/design-105133.
Thank you for your support.
Investigation:
- on desktop, they do
onclick="die_cut_changed('2', false, 0); displayLightningIcon();"inside the DOM - on mobile, they attach
on('touchend', function() {click event listeners via JavaScript - based on the User-Agent the site returns a different version, desktop or mobile
- on the mobile version, they don't attach
'click'event handlers - inside our Codegen, we pretend to be a mobile device but never emit touch events, this is why the clicks are noops.
- Its blocked by https://github.com/microsoft/playwright/issues/2903
Workaround: We recommend switching to pointerdown/pointerup since touch events are deprecated as per here: https://w3c.github.io/touch-events/#:~:text=license%20rules%20apply.-,Status%20of%20This%20Document,-This%20document%20is
The Community Group considers Touch Events a legacy API – authors are strongly encouraged to adopt Pointer Events instead.
Workaround: We recommend switching to pointerdown/pointerup since touch events are deprecated as per here: https://w3c.github.io/touch-events/#:~:text=license%20rules%20apply.-,Status%20of%20This%20Document,-This%20document%20is
The Community Group considers Touch Events a legacy API – authors are strongly encouraged to adopt Pointer Events instead.
I tried the following method but it didn't work. Is it the code?
from playwright.sync_api import Playwright, sync_playwright
def run(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
context = browser.new_context(**playwright.devices["iPhone 14 Pro"])
page = context.new_page()
page.goto("https://www.simplytoimpress.com/baptism-invitations/design-105133")
selector = 'div[data-value="13"]'
locator = page.locator(selector)
print(locator.is_visible())
js_code = """
(element) => {
const eventInit = {
pointerId: 1,
bubbles: true,
cancelable: true,
composed: true,
pointerType: 'mouse', # nor touch
isPrimary: true,
};
console.log('Creating pointerdown event');
const pointerDownEvent = new PointerEvent('pointerdown', eventInit);
element.dispatchEvent(pointerDownEvent);
console.log('pointerdown event dispatched');
console.log('Creating pointerup event');
const pointerUpEvent = new PointerEvent('pointerup', eventInit);
element.dispatchEvent(pointerUpEvent);
console.log('pointerup event dispatched');
}
"""
page.evaluate(js_code, page.query_selector(selector))
page.wait_for_timeout(100_000)
# ---------------------
context.close()
browser.close()
with sync_playwright() as playwright:
run(playwright)
You can do the following:
import { chromium, devices } from '@playwright/test';
(async () => {
// Make sure to run headed.
const browser = await chromium.launch({ headless: false });
// Setup context however you like.
const context = await browser.newContext(devices['iPhone 15']);
await context.addInitScript(() => {
document.addEventListener('click', clickEvent => {
// Get the target element
const target = clickEvent.target;
// Function to create and dispatch a touch event
function dispatchTouchEvent(eventType) {
const touch = new Touch({
identifier: Date.now(),
target: target,
clientX: clickEvent.clientX,
clientY: clickEvent.clientY,
pageX: clickEvent.pageX,
pageY: clickEvent.pageY
});
const touchEvent = new TouchEvent(eventType, {
bubbles: true,
cancelable: true,
view: window,
touches: [touch],
targetTouches: [touch],
changedTouches: [touch]
});
target.dispatchEvent(touchEvent);
}
// Dispatch touchstart and touchend events
dispatchTouchEvent('touchstart');
dispatchTouchEvent('touchend');
}, true); // Use capture phase to ensure this runs before other click handlers
});
// Pause the page, and start recording manually.
const page = await context.newPage();
await page.goto('https://www.simplytoimpress.com/baptism-invitations/design-105133');
await page.pause();
})();
Make sure to attach the addInitScript only for mobile tho. Its in JavaScript, but the same works in Python as well (make sure to use """ when passing the script over to the add_init_script function.
Folding into https://github.com/microsoft/playwright/issues/2903
Thank you so much!