What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

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

Use PhantomJS’s page.evaluate() after the page finishes loading, and read document.documentElement.scrollHeight. That value represents the document’s complete scrollable height rather than only the visible viewport. Compare it with document.body.scrollHeight when diagnosing unusual layouts, and measure a nested scrolling element if the document itself is not the element that scrolls.

Measure the document after it loads

This complete PhantomJS script opens a URL, evaluates JavaScript inside the page, prints the full document height, and exits with an error when loading fails:

var page = require('webpage').create();

page.open('https://example.com/', function (status) {
    if (status !== 'success') {
        console.log('Unable to load the page');
        phantom.exit(1);
        return;
    }

    var height = page.evaluate(function () {
        return document.documentElement.scrollHeight;
    });

    console.log(height);
    phantom.exit();
});

The important expression is document.documentElement.scrollHeight. scrollHeight is a DOM measurement of all content in the scrolling document, including content below the currently visible area. The result is a number in CSS pixels.

Why page.evaluate() is required

PhantomJS code outside the page cannot directly use the page’s DOM. page.evaluate() runs a function in the web page’s JavaScript context, where document and its elements exist. PhantomJS transfers back only simple JSON-serializable values, such as numbers, strings, booleans, arrays, and plain objects. DOM nodes, functions, and closures do not cross that boundary.

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

Use the callback timing correctly

Measure from the page.open callback after a successful load. Measuring before that callback can return a partial or nearly empty document. If the site inserts content later with JavaScript, waits, or asynchronous requests, add a suitable readiness check before evaluating the height.

Diagnose surprising values with all four measurements

If the reported height looks like the viewport height, return several related values in one evaluation:

var measurements = page.evaluate(function () {
    return {
        bodyScrollHeight: document.body.scrollHeight,
        bodyOffsetHeight: document.body.offsetHeight,
        documentClientHeight: document.documentElement.clientHeight,
        documentScrollHeight: document.documentElement.scrollHeight
    };
});

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

Interpret the values as follows:

  • document.documentElement.scrollHeight: the primary full-document height to try first.
  • document.body.scrollHeight: an alternative document height used by some page structures.
  • document.body.offsetHeight: the body’s border-box layout height, which can expose differences between body layout and scrolling.
  • document.documentElement.clientHeight: the visible client area, generally close to the viewport height. It is not the complete page height.

Compare the two scroll heights and the body offset height. If all values are close to the viewport, first verify that the intended content has loaded. A page can also place its actual content inside a child element with overflow: auto or overflow: scroll; in that case, the child owns the scrollable height.

Measure a nested scrolling container

When the page has an internal scrolling panel, select that element and read its scrollHeight instead of assuming the root document scrolls:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
var panelHeight = page.evaluate(function () {
    var panel = document.querySelector('.results-panel');
    return panel ? panel.scrollHeight : null;
});

console.log(panelHeight);

Replace .results-panel with the site’s selector. A returned null means the selector did not match. This distinction matters for dashboards, modal bodies, chat panes, code editors, and other layouts where the window remains short while an inner element contains the long content.

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

Set a representative PhantomJS viewport

Responsive pages can produce different DOM structures and heights at different browser dimensions. PhantomJS’s default viewport is commonly described as 400 by 300 pixels; treat that as a build-sensitive diagnostic detail rather than a universal guarantee. Set the viewport before opening the page when you need desktop, tablet, or another specific layout:

var page = require('webpage').create();
page.viewportSize = { width: 1280, height: 800 };

page.open('https://example.com/', function (status) {
    if (status !== 'success') {
        phantom.exit(1);
        return;
    }

    var height = page.evaluate(function () {
        return document.documentElement.scrollHeight;
    });

    console.log(height);
    phantom.exit();
});

The viewport height affects what is visible, while the page’s layout rules determine how content wraps and which responsive components appear. Therefore, record the viewport dimensions alongside a measurement if you need reproducible results.

Wait for content that is loaded after navigation

page.open confirms that navigation completed; it does not guarantee that every lazy image, client-rendered list, or delayed request has finished. A simple fixed delay can help with known page behavior:

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.
var page = require('webpage').create();
page.viewportSize = { width: 1280, height: 800 };

page.open('https://example.com/', function (status) {
    if (status !== 'success') {
        console.log('Unable to load the page');
        phantom.exit(1);
        return;
    }

    window.setTimeout(function () {
        var height = page.evaluate(function () {
            return document.documentElement.scrollHeight;
        });
        console.log(height);
        phantom.exit();
    }, 1000);
});

Use a delay appropriate to the application, or poll for a selector that indicates the content is ready. A delay is only a timing workaround: it cannot guarantee that a slow or failed request has produced the expected content, so inspect the DOM when accuracy matters.

Height measurement is separate from screenshots and PDFs

Reading scrollHeight answers “how tall is the rendered document?” It does not itself create an image or PDF. PhantomJS’s page.render() renders a page to an image buffer, while clipRect specifies the screen region to capture. Those rendering settings control output, not the DOM value returned by evaluate().

If you need a full-page image, use the measured value as diagnostic information and configure rendering for the target format separately. A screenshot can still be wrong if content was not loaded, if a fixed element overlaps the page, or if the desired content is in a nested scroller.

Common failure modes and fixes

The result equals roughly 300 pixels

This usually indicates the page was measured at PhantomJS’s small default viewport, the document has not populated yet, or the real scrolling element is nested. Set page.viewportSize, wait for the page’s content, and inspect child elements with their own scrollHeight.

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

document is undefined

That error occurs when DOM code is executed outside page.evaluate(). Move the DOM-reading function into evaluate and return a serializable value.

document.documentElement.scrollHeight is too small

Compare it with document.body.scrollHeight and document.body.offsetHeight. Then check whether scripts add content after navigation, whether images or fonts change layout later, and whether a child container owns scrolling.

The height changes between runs

Responsive breakpoints, asynchronous content, rotating ads, and delayed layout changes can all affect the result. Fix the viewport, use a deterministic wait condition, and measure at a known point in the page lifecycle.

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

The page fails to open

Check the status passed to the page.open callback. Do not evaluate the DOM after a failed navigation. Log the URL and exit nonzero so an automation job can detect the failure.

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

The value is correct but the image is clipped

A correct DOM height does not automatically change a render’s clipping rectangle or output configuration. Configure the rendering operation independently, and remember that clipRect captures a specified screen region rather than changing document layout.

A reusable diagnostic script

This version combines a controlled viewport, load-status handling, a short wait, and the four-value diagnostic object:

var page = require('webpage').create();
page.viewportSize = { width: 1280, height: 800 };

page.open('https://example.com/', function (status) {
    if (status !== 'success') {
        console.log('Unable to load the page: ' + status);
        phantom.exit(1);
        return;
    }

    window.setTimeout(function () {
        var result = page.evaluate(function () {
            return {
                bodyScrollHeight: document.body.scrollHeight,
                bodyOffsetHeight: document.body.offsetHeight,
                documentClientHeight: document.documentElement.clientHeight,
                documentScrollHeight: document.documentElement.scrollHeight
            };
        });

        console.log(JSON.stringify(result));
        phantom.exit();
    }, 1000);
});

Use documentScrollHeight as the normal answer, then use the other fields to explain discrepancies. Keep the JSON output in logs when comparing builds or debugging a page that changes over time.

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

Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server when you need an image or PDF rather than a PhantomJS DOM measurement. A single request can return PNG, JPEG, WebP, or PDF output. Before capture, it accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response reports the result in X-Page-Verdict and X-Billed headers.

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

See the ScreenshotNeo documentation for all request options. A basic cURL call is:

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

The equivalent Python request is:

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)

And Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo also supports full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets or custom viewports, retina scale, PDF paper settings and page ranges, custom HTML/CSS and JavaScript, pre-capture clicks, selector hiding, selector/delay/network-idle waits, request and resource blocking, headers, cookies, user agents, Authorization, timezone and geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, an OpenAPI specification, and familiar parameter names for easier migration.

An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. The Free plan includes 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots. Other plans are Starter ($5/3,000), Growth ($15/15,000), Pro ($39/60,000), Scale ($99/250,000), and Business ($249/1,000,000); yearly billing provides two months free, and every feature is included on every plan. Create a free ScreenshotNeo account to start.

Practical checklist

  • Set page.viewportSize before navigation when responsive layout matters.
  • Wait for successful navigation and for client-rendered content that affects height.
  • Read document.documentElement.scrollHeight inside page.evaluate().
  • Compare body scroll and offset heights when the value looks wrong.
  • Inspect nested scrolling elements instead of assuming the root document owns all content.
  • Keep DOM measurement separate from screenshot clipping and rendering configuration.

Frequently Asked Questions

What unit does PhantomJS return for scrollHeight?

It returns a numeric CSS-pixel measurement.

Should I use body.scrollHeight or documentElement.scrollHeight?

Start with document.documentElement.scrollHeight, then compare document.body.scrollHeight when diagnosing a page-specific layout.

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

Can scrollHeight include content in an iframe?

The top document’s measurement does not automatically become the iframe document’s measurement; evaluate inside the relevant frame context and measure that document separately.

Does changing the viewport change the full page height?

It can. Responsive breakpoints and line wrapping may change the rendered layout, so use the viewport that represents the result you need.

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.