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
}
}
Handling Links That Open in New Tabs
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
- Pick the right form: Use a bare expression when you need a single value, and a multi-line body with your own
returnfor anything more involved. - Return meaningful data: In a statement body,
returnthe data you need. Without areturn,valuecomes back empty. - Handle errors gracefully: Wrap operations in try-catch blocks so a failure returns a useful message instead of breaking the mutation.
- 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: