ScreenshotNeo

BlogHow-to

How to End a Touchscreen Gesture in Puppeteer

End a Puppeteer touch with its TouchHandle, use touchEnd() for the first active touch, or call tap() for a complete tap. See runnable examples and troubleshooting.

By the ScreenshotNeo team4 October 20266 min read

To finish a touch gesture you started with Puppeteer, keep the TouchHandle returned by page.touchscreen.touchStart(x, y) and call await touch.end(). For a simple tap, use await page.touchscreen.tap(x, y), which dispatches both touchstart and touchend. You can also call await page.touchscreen.touchEnd() to end the first active touch.

Choose the right touch method

What you need Use Behavior
Complete a tap at a viewport coordinate page.touchscreen.tap(x, y) Dispatches touchstart and touchend.
Start, optionally move, and finish a specific touch touch.end() on its handle Ends the touch represented by that handle.
End the first active touch from the page API page.touchscreen.touchEnd() Targets the first touch that is active.
Tap an element by its center elementHandle.tap() Scrolls it into view if needed, then taps; throws if the element detached.

When your code has the handle, prefer touch.end() to finish that particular touch. Do not assume that touchEnd() means “end the most recent touch”: its documented behavior is to end the first active touch. The documentation does not establish broader ordering guarantees for overlapping touches.

Complete runnable example

This Node.js example launches Chromium, opens a page, starts a touch, moves it, ends it, and closes the browser. The optional movement illustrates a gesture that lasts longer than a tap.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.setViewport({ width: 800, height: 600, isMobile: true, hasTouch: true });
    await page.goto('https://example.com');

    const touch = await page.touchscreen.touchStart(100, 200);
    await touch.move(140, 220); // Optional. Browser throttling may affect touchmove events.
    await touch.end();
  } finally {
    await browser.close();
  }
})();

Install Puppeteer in the project with npm install puppeteer, save the snippet as a JavaScript file, and run it with node filename.js. The coordinates are viewport coordinates in CSS pixels; choose a point within the page’s current viewport.

End a gesture or perform a tap

Explicitly end the touch you started

const touch = await page.touchscreen.touchStart(100, 200);
await touch.move(140, 220); // Optional
await touch.end();

touchStart() emits touchstart and returns the handle. Its end() method dispatches touchend for that touch. Keep the handle in the scope where you will finish the interaction; do not discard it if you need to end that specific touch later.

Use the page-level end method

await page.touchscreen.touchStart(100, 200);
// Perform any intermediate work here.
await page.touchscreen.touchEnd();

This is useful when you deliberately want the page touchscreen API to end the first active touch. If you need to associate the end with the exact touch you began, retain and use its handle instead.

Send a tap at coordinates

await page.touchscreen.tap(100, 200);

Use tap() when there is no separate hold or movement phase. It dispatches the start and end events as one method call.

Tap an element

const button = await page.$('button.submit');
if (!button) {
  throw new Error('Submit button was not found');
}
await button.tap();

ElementHandle.tap() scrolls the element into view if needed and taps its center. It throws if the handle’s element has detached from the DOM. For new code, Puppeteer’s interactions guide recommends locators for selecting and interacting with elements; the coordinate-based touchscreen API is useful when you need a touch at a known point without selecting an element first.

Coordinates, movement, and event expectations

  • Coordinates: supply x and y in the page viewport, not document coordinates. Ensure they fall within the current viewport and target the intended point.
  • Movement is optional: call the handle’s move() between start and end when the interaction needs movement. A tap needs no explicit movement.
  • Moves may be coalesced or throttled: a call to touchMove() does not guarantee a corresponding browser touchmove event each time. Browser optimizations and Chrome throttling can affect delivery.
  • End the gesture: call end() after the intended movement or hold is complete. This dispatches touchend.
  • Element taps: use an element handle’s tap() when the goal is to activate an element rather than test a precise viewport coordinate.

Do not infer multi-touch ordering or concurrency behavior from these methods alone. The API descriptions establish per-handle ending and the first-active-touch behavior of touchEnd(), but do not specify a general policy for overlapping touches.

Version and API notes

Puppeteer’s API documentation pages consulted for these methods carry different version labels, including 25.2.1, 25.3.0, 25.5.0, and 25.12.0. Those labels are not a guarantee that every older package version has identical signatures or semantics. If a method is missing or behaves differently, check the API reference matching the Puppeteer version installed in your project.

Troubleshooting

Symptom Likely cause Fix
The interaction never completes The code started a touch but did not call an end method. Retain the returned handle and call await touch.end(), or use touchEnd() when ending the first active touch is intended.
A tap triggers the wrong point The coordinates are outside the viewport or were calculated in document rather than viewport space. Check the current viewport dimensions and use viewport coordinates; for an element, use elementHandle.tap().
elementHandle.tap() throws The element detached from the DOM before Puppeteer tapped it. Find the element again after the page update, then tap the fresh handle. If the page is still rendering, wait for the relevant element before selecting it.
Not every requested move produces a visible movement Browser optimization or Chrome throttling can mean not every call yields a browser touchmove event. Do not treat each method call as a guaranteed DOM event. Check the resulting interaction and use the smallest sequence of movement calls the scenario requires.
The installed package does not expose the expected method The project may use a different Puppeteer version from the documentation page. Check the API reference for the installed version and update code to its documented API.

Performance, reliability, and cost

A tap is a single convenience call; a manually controlled gesture gives you explicit start, optional movement, and end steps. Keep the sequence focused on the events your test needs. Since browser event delivery can throttle moves, adding many movement calls does not necessarily create an equivalent number of delivered touchmove events.

For reliable automation, make sure the page and target are ready before interacting, use coordinates measured against the active viewport, and always reach an end call after starting a touch. Put browser cleanup in a finally block so a failed interaction does not leave the launched browser process open. The cited API material provides no benchmark or pricing figures for Puppeteer; infrastructure and browser runtime costs depend on where and how the automation runs.

Or skip the browser setup

If your goal is to capture a page image rather than automate touch input, ScreenshotNeo returns a screenshot or PDF from one GET request. Its cookie and consent handling removes known consent banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed, and responses indicate the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, inspect page information, and capture PDFs. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

For API options and configuration, see the ScreenshotNeo documentation. This call saves a WebP screenshot of the target URL:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

ScreenshotNeo is a website screenshot API and MCP server by Yorker Media. Create an account to get 1,000 free screenshots a month, with no card.

FAQ

Does touch.end() end the touch represented by its handle?

Yes. It dispatches touchend for that touch.

Is touchEnd() the same as ending the most recent touch?

No such ordering guarantee is established by the documented description. It ends the first touch that is active.

Should I use tap() or touchStart()?

Use tap() for a complete tap. Use touchStart() when you need to control a touch before explicitly ending it.