For AI agents: a documentation index is available at /llms.txt
Skip to main content

JavaScript Evaluation with evaluate

The evaluate mutation runs JavaScript in the browser's page environment and returns the result. You can pass either a single expression or a multi-line statement body, and evaluate figures out which form you used.

Single expressions

For a single expression, write the expression and evaluate returns its value. You don't need a return statement:

mutation EvaluateExpression {
goto(url: "https://example.com") {
status
}

evaluate(content: "document.title") {
value
}
}

Multi-line statement bodies

For anything longer than one expression, use triple quotes (""") and write your code as a normal statement body. Use your own return to pass a value back. No IIFE (Immediately Invoked Function Expression) wrapper is required:

mutation MultiLineEvaluate {
goto(url: "https://example.com") {
status
}

evaluate(content: """
const title = document.title;
const url = window.location.href;
return JSON.stringify({ title, url });
""") {
value
}
}

Earlier versions only accepted single expressions, so multi-line code had to be wrapped in an IIFE by hand. That's no longer necessary. Existing IIFE-wrapped scripts still work, so you don't need to rewrite anything that already runs.

Example use cases:

  • Extract complex data from the DOM
  • Manipulate page elements dynamically, such as deleting or adding elements to the HTML
  • Perform calculations or transformations

Advanced Examples

Extracting Complex Data

mutation ExtractComplexData {
goto(url: "https://example.com") {
status
}

evaluate(content: """
try {
// Extract all links with their text and href.
const links = Array.from(document.querySelectorAll('a')).map(link => ({
text: link.textContent.trim(),
href: link.href,
isExternal: !link.href.includes(window.location.hostname)
}));

// Get page metadata.
const meta = {
title: document.title,
description: document.querySelector('meta[name="description"]')?.content || '',
keywords: document.querySelector('meta[name="keywords"]')?.content || '',
viewport: document.querySelector('meta[name="viewport"]')?.content || ''
};

return JSON.stringify({ links, meta, error: null });
} catch (e) {
return JSON.stringify({ links: null, meta: null, error: (e?.message ?? String(e)) });
}
""") {
value
}
}

Dynamic Element Manipulation

mutation ManipulateElements {
goto(url: "https://example.com") {
status
}

evaluate(content: """
try {
// Remove all ads.
const ads = document.querySelectorAll('[class*="ad"], [id*="ad"], [class*="banner"]');
ads.forEach(ad => ad.remove());

// Add a custom element.
const customDiv = document.createElement('div');
customDiv.innerHTML = '<h2>Custom Content Added by BrowserQL</h2>';
customDiv.style.cssText = 'background: #f0f0f0; padding: 20px; margin: 20px 0; border-radius: 5px;';
document.body.insertBefore(customDiv, document.body.firstChild);

return JSON.stringify({
removedAds: ads.length,
addedElement: 'Custom div added to page',
error: null
});
} catch (e) {
return JSON.stringify({ removedAds: null, addedElement: null, error: (e?.message ?? String(e)) });
}
""") {
value
}
}

Data Transformation and Calculations

mutation DataTransformation {
goto(url: "https://example.com") {
status
}

evaluate(content: """
try {
// Extract and transform price data.
const priceElements = document.querySelectorAll('[class*="price"], [class*="cost"]');
const prices = Array.from(priceElements).map(el => {
const text = el.textContent;
const price = parseFloat(text.replace(/[^0-9.]/g, ''));
return {
originalText: text,
numericValue: price,
currency: text.match(/[$€£¥]/)?.[0] || 'unknown'
};
}).filter(p => !isNaN(p.numericValue));

// Calculate statistics.
const values = prices.map(p => p.numericValue);
const total = values.reduce((sum, value) => sum + value, 0);
const average = values.length > 0 ? total / values.length : 0;
const min = values.length > 0 ? Math.min(...values) : null;
const max = values.length > 0 ? Math.max(...values) : null;

return JSON.stringify({
prices,
statistics: { total, average, min, max, count: prices.length },
error: null
});
} catch (e) {
return JSON.stringify({ prices: null, statistics: null, error: (e?.message ?? String(e)) });
}
""") {
value
}
}

When automating interactions with links that have target="_blank", you can modify them to open in the same browser session. This simplifies your script by avoiding the need to switch tabs or manage multiple page contexts.

mutation avoidNewTab {
goto(url: "https://www.example.com", waitUntil: networkIdle) {
status
}
evaluate(content: """
const link = document.querySelector('a');
if (link && link.target === '_blank') {
link.target = '_self';
}
""") {
time
}
click(selector: "a") {
time
}
}

This approach modifies links from opening in new tabs to opening in the same session, keeping your automation workflow simple and avoiding complex tab management.

Best Practices

  1. Pick the right form: Use a bare expression when you need a single value, and a multi-line body with your own return for anything more involved.
  2. Return meaningful data: In a statement body, return the data you need. Without a return, value comes back empty.
  3. Handle errors gracefully: Wrap operations in try-catch blocks so a failure returns a useful message instead of breaking the mutation.
  4. Keep it focused: Write focused, single-purpose scripts for better maintainability.

Next Steps

Ready to explore more advanced BrowserQL features? Check out these related topics: