Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When page.sendEvent('click', x, y) works for one link but not another, check two things first: whether x and y are viewport coordinates, and whether you capture the page before the second link has finished handling the event. Measure the target with getBoundingClientRect(), send the click at the rectangle’s center, and wait for a meaningful DOM, URL, load, or resource change before rendering.

Why one PhantomJS link appears to work

A PhantomJS click is an input event delivered at a coordinate. It is not a promise that the browser has completed the link’s JavaScript, navigation, animation, or network work. Two links on the same page can therefore behave differently even when the script sends the same kind of event.

  • Different coordinate frame: offsetLeft and offsetTop are relative to an element’s offset parent. page.sendEvent needs the mouse position in the page viewport.
  • Different completion time: one link may change the DOM immediately while another starts asynchronous work. Rendering immediately after the event can show the old state.
  • Page-specific behavior: an overlay, iframe, CSS transform, or event handler can affect the result. These are possibilities to inspect, not assumptions about your particular page.

Start with coordinates and timing because they are the two causes supported by the closest documented troubleshooting case. Then add instrumentation so the page itself tells you what happened.

Use viewport coordinates, not parent-relative offsets

Inspect the intended element inside page.evaluate and return only simple serializable data. PhantomJS’s evaluate boundary does not return DOM nodes, closures, or functions as usable values; return numbers and strings instead.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
var target = page.evaluate(function () {
  var el = document.querySelector('#products a.details');
  if (!el) return { found: false };

  var r = el.getBoundingClientRect();
  return {
    found: true,
    left: r.left,
    top: r.top,
    width: r.width,
    height: r.height,
    right: r.right,
    bottom: r.bottom,
    href: el.getAttribute('href'),
    text: el.textContent
  };
});

if (!target.found) {
  console.log('Target was not found');
  phantom.exit(1);
}

console.log(JSON.stringify(target));

The rectangle is expressed in viewport coordinates. Click its center, which avoids relying on a corner that may be outside the clickable area:

var x = Math.round(target.left + target.width / 2);
var y = Math.round(target.top + target.height / 2);
page.sendEvent('click', x, y);

Before clicking, verify that the rectangle has a positive size and lies where you expect. A zero-sized rectangle, negative position, or unexpected dimensions usually means the element is hidden, not laid out yet, or a different matching element was selected.

Measurement Reference frame Use for sendEvent?
offsetLeft/offsetTop Element’s offset parent No, unless you have converted them to viewport coordinates
getBoundingClientRect().left/top Page viewport Yes
Rectangle center Viewport Recommended click point

A complete PhantomJS diagnostic script

This script loads a page, records the target rectangle, clicks the link, waits for either a URL change or a DOM-signature change, and renders only after the result is observable. Replace the URL and selector with values from your page.

var system = require('system');
var page = require('webpage').create();
var address = system.args[1] || 'https://example.com';
var selector = system.args[2] || '#products a.details';
var startUrl = '';
var beforeSignature = '';
var finished = false;

function finish(code) {
  if (finished) return;
  finished = true;
  phantom.exit(code || 0);
}

page.onConsoleMessage = function (msg) {
  console.log('[page console] ' + msg);
};
page.onError = function (msg, trace) {
  console.log('[page error] ' + msg);
  trace.forEach(function (t) { console.log('  ' + t.file + ':' + t.line); });
};
page.onResourceError = function (err) {
  console.log('[resource error] ' + err.url + ' - ' + err.errorString);
};
page.onUrlChanged = function (url) {
  console.log('[url changed] ' + url);
};

page.open(address, function (status) {
  if (status !== 'success') {
    console.log('Initial load failed: ' + status);
    finish(1);
    return;
  }

  startUrl = page.url;
  var info = page.evaluate(function (sel) {
    var el = document.querySelector(sel);
    if (!el) return { found: false };
    var r = el.getBoundingClientRect();
    return {
      found: true,
      left: r.left, top: r.top, width: r.width, height: r.height,
      href: el.getAttribute('href'),
      signature: document.body.innerText.slice(0, 500)
    };
  }, selector);

  if (!info.found || info.width <= 0 || info.height <= 0) {
    console.log('Target is missing or has no visible size: ' + JSON.stringify(info));
    finish(1);
    return;
  }

  beforeSignature = info.signature;
  var x = Math.round(info.left + info.width / 2);
  var y = Math.round(info.top + info.height / 2);
  console.log('Clicking ' + selector + ' at ' + x + ',' + y);
  page.sendEvent('click', x, y);

  var deadline = Date.now() + 5000;
  var poll = window.setInterval(function () {
    var state = page.evaluate(function () {
      var el = document.querySelector('body');
      return {
        url: location.href,
        signature: el ? el.innerText.slice(0, 500) : ''
      };
    });

    if (state.url !== startUrl || state.signature !== beforeSignature) {
      window.clearInterval(poll);
      console.log('Observable result received');
      page.render('after-click.png');
      finish(0);
    } else if (Date.now() >= deadline) {
      window.clearInterval(poll);
      console.log('No observable result within 5 seconds');
      page.render('after-click-timeout.png');
      finish(2);
    }
  }, 100);
});

Run it with the PhantomJS executable and pass the URL and CSS selector as arguments. The timeout is deliberately a diagnostic fallback. A production script should wait on the page’s actual completion signal when one exists.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option

Wait for the result your link is supposed to produce

DOM update

For dropdowns, tabs, accordions, and menus, poll a state that proves the action happened: a class, attribute, text value, or visible panel. A body-text signature is only a broad diagnostic. Prefer a specific selector when you know it.

var open = page.evaluate(function () {
  var menu = document.querySelector('#menu');
  return menu && menu.className.indexOf('open') !== -1;
});

Navigation

Record page.url before the event and use page.onUrlChanged or a load callback to detect navigation. Do not render merely because sendEvent returned; that method reports that the event was sent, not that the destination finished loading.

Network or resource activity

Use resource and navigation callbacks when the click starts an asynchronous request. Log request and error callbacks to learn whether the expected asset was requested or failed. If the page exposes no reliable signal, use a bounded polling loop and report a timeout rather than silently treating the click as successful.

Fixed delay

A short delay can confirm that timing is involved, but it is not a universal fix. A delay that is too short still captures stale content; one that is too long wastes time and can hide intermittent failures. Replace it with a state-based wait as soon as you identify the state.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Inspect the page before changing the click

  1. Confirm the load: check the return status from page.open and log page.url.
  2. Record the executable version: the PhantomJS troubleshooting guidance recommends checking the version, especially when more than one installation may be present. Confirm which executable your job actually launches.
  3. Verify the selector: return the element’s text, link target, rectangle, and relevant attributes from evaluate.
  4. Check the viewport: set page.viewportSize deliberately if responsive layout could move or hide the link.
  5. Capture errors: connect page.onError, page.onConsoleMessage, and page.onResourceError. Console messages inside the page are not displayed by default.
  6. Compare links: inspect the working and failing elements with the same routine. Differences in rectangle, href, classes, or surrounding markup often reveal whether you selected the wrong node or clicked before layout settled.

Cases that need separate inspection

Overlays and covered targets

A consent panel, modal, or loading layer can occupy the target’s coordinates. Log visible overlays and temporarily hide them only for diagnosis; do not make that a production workaround unless it reflects the behavior you intend to automate.

CSS transforms and scrolling

Transforms and scrolling can make hand-calculated offsets unreliable. getBoundingClientRect() measures the rendered position after layout. Re-measure immediately before the click if an animation or lazy layout can move the element.

Frames

If the link is inside an iframe, the element’s rectangle belongs to that frame’s viewport. The available case does not establish iframe behavior as the cause, so treat it as a branch to investigate: identify the frame, switch your inspection strategy to its document, and verify coordinates in the context where the event is delivered.

Event-specific handlers

Some controls react to a sequence such as mouseover, mousedown, mouseup, and click, or use delegated handlers on an ancestor. Compare the working element’s event behavior with the failing one and watch page errors. Do not assume that a successful click on one anchor proves every other control uses the same handler.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers

Common symptoms, causes, and fixes

Symptom Likely explanation Action
Screenshot is unchanged immediately after click Rendering occurred before asynchronous work completed Wait for a DOM, URL, load, or resource signal
Click lands beside the link Parent-relative offsets were passed as viewport coordinates Use getBoundingClientRect() and click the center
Selector returns an element with zero dimensions Hidden, collapsed, or not-yet-laid-out target Wait for visibility and re-measure
Script reports success but destination does not load Navigation or resource failure Log URL, navigation, resource, and page errors
Only one installed PhantomJS behaves differently Different executable or version is running Print the version and absolute executable path
Page logs errors you cannot see Console output is not wired to the outer script Attach page.onConsoleMessage and page.onError

Make the script reliable in repeated runs

  • Use stable selectors rather than positional selectors such as “the second link.”
  • Set viewport dimensions, user agent, cookies, and other inputs explicitly when they affect layout or authentication.
  • Keep waits bounded and return a nonzero exit code on timeout so automation can distinguish failure from a successful capture.
  • Save the pre-click rectangle, URL, console output, page errors, and post-click state with the screenshot. This makes intermittent failures comparable.
  • Re-measure after any action that can scroll, expand content, load images, or change responsive layout.
  • Do not interpret a rendered image alone as proof of failure; pair it with URL and DOM evidence.

Or skip the browser setup

If your goal is a clean screenshot rather than debugging PhantomJS input, ScreenshotNeo provides a website screenshot API and MCP server. It accepts cookie and consent banners before capture, removes more than 60 known consent platforms, newsletter popups, and chat widgets, and lets you turn each cleanup step off. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are free, with X-Page-Verdict and X-Billed headers explaining the result.

One GET request returns PNG, JPEG, WebP, or PDF. The parameter names used by other screenshot APIs also work, which can simplify a migration. See the ScreenshotNeo documentation for all options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

It also offers an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients; full-page and element captures, dark mode, device presets, custom CSS and JavaScript, waits, request blocking, cookies and headers, geolocation, signed links, async webhooks, bulk capture of up to 100 URLs per call, and a usage API. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Sign up for the free plan.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

FAQ

Does sendEvent click by CSS selector?

No. It sends a mouse event at the supplied coordinates. Find the element in the page, convert its rendered rectangle to a viewport point, and pass that point.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Should I always add a five-second sleep?

No. Use the page’s observable completion condition when possible. A bounded delay is useful for diagnosis, but it cannot account for every network or application state.

What should I log when the coordinate fix does not help?

Log the PhantomJS version and executable, selector result, rectangle, URL changes, resource errors, page errors, and console messages. Then compare those records with the link that works.

Can this evidence identify the exact cause on my page?

No. Without the page and script, coordinate frame and timing are the first checks, not a universal diagnosis. Frame context, overlays, transforms, and page-specific event code require inspection of the actual DOM and behavior.

Frequently Asked Questions

Does sendEvent click by CSS selector?

No. It sends a mouse event at the supplied coordinates. Find the element in the page, convert its rendered rectangle to a viewport point, and pass that point.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Should I always add a five-second sleep?

No. Use the page’s observable completion condition when possible. A bounded delay is useful for diagnosis, but it cannot account for every network or application state.

What should I log when the coordinate fix does not help?

Log the PhantomJS version and executable, selector result, rectangle, URL changes, resource errors, page errors, and console messages. Then compare those records with the link that works.

Can this evidence identify the exact cause on my page?

No. Without the page and script, coordinate frame and timing are the first checks, not a universal diagnosis. Frame context, overlays, transforms, and page-specific event code require inspection of the actual DOM and behavior.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.