0
# JavaScript Execution
1
2
Interface for executing JavaScript code in the browser context with support for synchronous/asynchronous execution, script pinning, and argument passing.
3
4
## Capabilities
5
6
### JavascriptExecutor Interface
7
8
Interface for executing JavaScript code within the browser context with parameter passing and return value handling.
9
10
```java { .api }
11
/**
12
* JavascriptExecutor interface for executing JavaScript code in browser context
13
* Provides synchronous and asynchronous script execution with argument passing
14
*/
15
interface JavascriptExecutor {
16
/**
17
* Execute JavaScript synchronously in browser context
18
* @param script - JavaScript code to execute
19
* @param args - Arguments to pass to script (accessible as arguments[0], arguments[1], etc.)
20
* @return Result of script execution (may be null, primitive, WebElement, or List)
21
*/
22
Object executeScript(String script, Object... args);
23
24
/**
25
* Execute JavaScript asynchronously in browser context
26
* @param script - JavaScript code to execute (must call callback to complete)
27
* @param args - Arguments to pass to script
28
* @return Result of script execution
29
*/
30
Object executeAsyncScript(String script, Object... args);
31
32
/**
33
* Pin script for reuse with better performance
34
* @param script - JavaScript code to pin
35
* @return ScriptKey for referencing pinned script
36
*/
37
ScriptKey pin(String script);
38
39
/**
40
* Unpin previously pinned script
41
* @param key - ScriptKey of script to unpin
42
*/
43
void unpin(ScriptKey key);
44
45
/**
46
* Get all currently pinned scripts
47
* @return Set of ScriptKey objects for pinned scripts
48
*/
49
Set<ScriptKey> getPinnedScripts();
50
51
/**
52
* Execute pinned script by key
53
* @param key - ScriptKey of pinned script
54
* @param args - Arguments to pass to script
55
* @return Result of script execution
56
*/
57
Object executeScript(ScriptKey key, Object... args);
58
}
59
```
60
61
### ScriptKey Class
62
63
Reference key for pinned JavaScript scripts enabling efficient reuse.
64
65
```java { .api }
66
/**
67
* ScriptKey class representing a pinned JavaScript script
68
* Used for efficient execution of frequently used scripts
69
*/
70
class ScriptKey {
71
/**
72
* Get script identifier
73
* @return Unique identifier for the pinned script
74
*/
75
String getId();
76
77
/**
78
* Get original script content
79
* @return JavaScript code that was pinned
80
*/
81
String getScript();
82
}
83
```
84
85
## Usage Examples
86
87
### Basic JavaScript Execution
88
89
```java
90
import org.openqa.selenium.WebDriver;
91
import org.openqa.selenium.JavascriptExecutor;
92
import org.openqa.selenium.chrome.ChromeDriver;
93
94
WebDriver driver = new ChromeDriver();
95
JavascriptExecutor js = (JavascriptExecutor) driver;
96
97
try {
98
driver.get("https://example.com");
99
100
// Simple script execution
101
String title = (String) js.executeScript("return document.title;");
102
System.out.println("Page title: " + title);
103
104
// Execute script with return value
105
Long scrollHeight = (Long) js.executeScript("return document.body.scrollHeight;");
106
System.out.println("Page height: " + scrollHeight);
107
108
// Execute script that modifies the page
109
js.executeScript("document.body.style.backgroundColor = 'lightblue';");
110
111
// Execute script with no return value
112
js.executeScript("console.log('Hello from WebDriver!');");
113
114
} finally {
115
driver.quit();
116
}
117
```
118
119
### JavaScript with Arguments
120
121
```java
122
// Pass arguments to JavaScript
123
WebElement element = driver.findElement(By.id("myElement"));
124
String elementText = (String) js.executeScript(
125
"return arguments[0].textContent;",
126
element
127
);
128
129
// Multiple arguments
130
js.executeScript(
131
"arguments[0].style.backgroundColor = arguments[1]; arguments[0].style.color = arguments[2];",
132
element,
133
"yellow",
134
"red"
135
);
136
137
// Pass different types of arguments
138
js.executeScript(
139
"var element = arguments[0]; var show = arguments[1]; var delay = arguments[2]; " +
140
"if (show) { element.style.display = 'block'; } " +
141
"setTimeout(function() { element.style.opacity = '1'; }, delay);",
142
element,
143
true,
144
1000
145
);
146
147
// Return complex objects
148
Map<String, Object> elementInfo = (Map<String, Object>) js.executeScript(
149
"var elem = arguments[0]; " +
150
"return { " +
151
" tagName: elem.tagName, " +
152
" id: elem.id, " +
153
" className: elem.className, " +
154
" visible: elem.offsetWidth > 0 && elem.offsetHeight > 0 " +
155
"};",
156
element
157
);
158
```
159
160
### DOM Manipulation and Queries
161
162
```java
163
// Scroll to element
164
WebElement targetElement = driver.findElement(By.id("target"));
165
js.executeScript("arguments[0].scrollIntoView(true);", targetElement);
166
167
// Highlight element
168
js.executeScript(
169
"arguments[0].style.border = '3px solid red';" +
170
"arguments[0].style.backgroundColor = 'yellow';",
171
targetElement
172
);
173
174
// Get element position and size
175
Map<String, Object> elementBounds = (Map<String, Object>) js.executeScript(
176
"var rect = arguments[0].getBoundingClientRect(); " +
177
"return { " +
178
" x: rect.left, " +
179
" y: rect.top, " +
180
" width: rect.width, " +
181
" height: rect.height " +
182
"};",
183
targetElement
184
);
185
186
// Check if element is in viewport
187
Boolean isInViewport = (Boolean) js.executeScript(
188
"var rect = arguments[0].getBoundingClientRect(); " +
189
"return rect.top >= 0 && rect.left >= 0 && " +
190
"rect.bottom <= window.innerHeight && rect.right <= window.innerWidth;",
191
targetElement
192
);
193
194
// Find elements by complex criteria
195
List<WebElement> visibleElements = (List<WebElement>) js.executeScript(
196
"var elements = document.querySelectorAll(arguments[0]); " +
197
"var visible = []; " +
198
"for (var i = 0; i < elements.length; i++) { " +
199
" if (elements[i].offsetWidth > 0 && elements[i].offsetHeight > 0) { " +
200
" visible.push(elements[i]); " +
201
" } " +
202
"} " +
203
"return visible;",
204
".product-card"
205
);
206
```
207
208
### Asynchronous JavaScript Execution
209
210
```java
211
// Set script timeout for async operations
212
driver.manage().timeouts().setScriptTimeout(Duration.ofSeconds(30));
213
214
// Simple async script with callback
215
String result = (String) js.executeAsyncScript(
216
"var callback = arguments[arguments.length - 1]; " +
217
"setTimeout(function() { " +
218
" callback('Async operation completed'); " +
219
"}, 2000);"
220
);
221
222
// AJAX request with async script
223
String responseText = (String) js.executeAsyncScript(
224
"var callback = arguments[arguments.length - 1]; " +
225
"var xhr = new XMLHttpRequest(); " +
226
"xhr.open('GET', arguments[0], true); " +
227
"xhr.onreadystatechange = function() { " +
228
" if (xhr.readyState === 4) { " +
229
" callback(xhr.responseText); " +
230
" } " +
231
"}; " +
232
"xhr.send();",
233
"https://api.example.com/data"
234
);
235
236
// Wait for page element to load asynchronously
237
WebElement dynamicElement = (WebElement) js.executeAsyncScript(
238
"var callback = arguments[arguments.length - 1]; " +
239
"var checkElement = function() { " +
240
" var element = document.querySelector(arguments[0]); " +
241
" if (element) { " +
242
" callback(element); " +
243
" } else { " +
244
" setTimeout(checkElement, 100); " +
245
" } " +
246
"}; " +
247
"checkElement();",
248
"#dynamic-content"
249
);
250
```
251
252
### Script Pinning for Performance
253
254
```java
255
// Pin frequently used scripts
256
ScriptKey scrollScript = js.pin(
257
"window.scrollTo(0, arguments[0]);"
258
);
259
260
ScriptKey highlightScript = js.pin(
261
"arguments[0].style.border = '2px solid ' + arguments[1];" +
262
"arguments[0].style.backgroundColor = arguments[2];"
263
);
264
265
ScriptKey getElementInfoScript = js.pin(
266
"var elem = arguments[0]; " +
267
"return { " +
268
" text: elem.textContent, " +
269
" visible: elem.offsetWidth > 0 && elem.offsetHeight > 0, " +
270
" classes: elem.className.split(' '), " +
271
" attributes: Array.from(elem.attributes).map(attr => ({name: attr.name, value: attr.value})) " +
272
"};"
273
);
274
275
// Use pinned scripts multiple times
276
js.executeScript(scrollScript, 500);
277
js.executeScript(scrollScript, 1000);
278
279
WebElement button1 = driver.findElement(By.id("btn1"));
280
WebElement button2 = driver.findElement(By.id("btn2"));
281
282
js.executeScript(highlightScript, button1, "red", "yellow");
283
js.executeScript(highlightScript, button2, "blue", "lightgreen");
284
285
Map<String, Object> info1 = (Map<String, Object>) js.executeScript(getElementInfoScript, button1);
286
Map<String, Object> info2 = (Map<String, Object>) js.executeScript(getElementInfoScript, button2);
287
288
// Clean up pinned scripts when done
289
js.unpin(scrollScript);
290
js.unpin(highlightScript);
291
js.unpin(getElementInfoScript);
292
293
// Or get all pinned scripts and unpin them
294
Set<ScriptKey> pinnedScripts = js.getPinnedScripts();
295
pinnedScripts.forEach(js::unpin);
296
```
297
298
### Page Analysis and Testing
299
300
```java
301
// Performance metrics
302
Map<String, Object> performanceData = (Map<String, Object>) js.executeScript(
303
"var perf = window.performance; " +
304
"var timing = perf.timing; " +
305
"return { " +
306
" loadTime: timing.loadEventEnd - timing.navigationStart, " +
307
" domContentLoaded: timing.domContentLoadedEventEnd - timing.navigationStart, " +
308
" firstPaint: perf.getEntriesByType('paint')[0]?.startTime || null, " +
309
" memoryUsage: perf.memory ? perf.memory.usedJSHeapSize : null " +
310
"};"
311
);
312
313
// Accessibility checks
314
List<Map<String, Object>> accessibilityIssues = (List<Map<String, Object>>) js.executeScript(
315
"var issues = []; " +
316
"var images = document.querySelectorAll('img'); " +
317
"images.forEach(function(img, index) { " +
318
" if (!img.alt) { " +
319
" issues.push({type: 'missing-alt', element: 'img', index: index}); " +
320
" } " +
321
"}); " +
322
"var links = document.querySelectorAll('a'); " +
323
"links.forEach(function(link, index) { " +
324
" if (!link.textContent.trim() && !link.getAttribute('aria-label')) { " +
325
" issues.push({type: 'empty-link', element: 'a', index: index}); " +
326
" } " +
327
"}); " +
328
"return issues;"
329
);
330
331
// Form validation status
332
Boolean isFormValid = (Boolean) js.executeScript(
333
"var form = arguments[0]; " +
334
"return form.checkValidity();",
335
driver.findElement(By.id("myForm"))
336
);
337
338
// Get all form errors
339
List<String> formErrors = (List<String>) js.executeScript(
340
"var form = arguments[0]; " +
341
"var errors = []; " +
342
"var inputs = form.querySelectorAll('input, select, textarea'); " +
343
"inputs.forEach(function(input) { " +
344
" if (!input.checkValidity()) { " +
345
" errors.push(input.name + ': ' + input.validationMessage); " +
346
" } " +
347
"}); " +
348
"return errors;",
349
driver.findElement(By.id("myForm"))
350
);
351
```
352
353
### Advanced JavaScript Patterns
354
355
```java
356
// Custom event triggering
357
js.executeScript(
358
"var event = new CustomEvent(arguments[1], {detail: arguments[2]}); " +
359
"arguments[0].dispatchEvent(event);",
360
targetElement,
361
"customEvent",
362
Map.of("data", "test")
363
);
364
365
// Cookie manipulation
366
js.executeScript("document.cookie = arguments[0] + '=' + arguments[1] + '; path=/';", "testCookie", "testValue");
367
String cookieValue = (String) js.executeScript(
368
"return document.cookie.split('; ').find(row => row.startsWith(arguments[0] + '=')).split('=')[1];",
369
"testCookie"
370
);
371
372
// Local storage operations
373
js.executeScript("localStorage.setItem(arguments[0], arguments[1]);", "key", "value");
374
String storedValue = (String) js.executeScript("return localStorage.getItem(arguments[0]);", "key");
375
376
// Session storage operations
377
js.executeScript("sessionStorage.setItem(arguments[0], JSON.stringify(arguments[1]));", "userData", Map.of("name", "John", "age", 30));
378
Map<String, Object> userData = (Map<String, Object>) js.executeScript(
379
"return JSON.parse(sessionStorage.getItem(arguments[0]));",
380
"userData"
381
);
382
383
// Dynamic CSS injection
384
js.executeScript(
385
"var style = document.createElement('style'); " +
386
"style.textContent = arguments[0]; " +
387
"document.head.appendChild(style);",
388
".highlight { background-color: yellow; border: 2px solid red; }"
389
);
390
```
391
392
### Error Handling in JavaScript Execution
393
394
```java
395
// Safe script execution with error handling
396
public Object safeExecuteScript(JavascriptExecutor js, String script, Object... args) {
397
try {
398
return js.executeScript(
399
"try { " +
400
" return (function() { " + script + " })(); " +
401
"} catch (e) { " +
402
" return {error: e.message, stack: e.stack}; " +
403
"}",
404
args
405
);
406
} catch (Exception e) {
407
System.err.println("Script execution failed: " + e.getMessage());
408
return null;
409
}
410
}
411
412
// Check if result is an error
413
public boolean isScriptError(Object result) {
414
if (result instanceof Map) {
415
Map<String, Object> resultMap = (Map<String, Object>) result;
416
return resultMap.containsKey("error");
417
}
418
return false;
419
}
420
421
// Usage
422
Object result = safeExecuteScript(js, "return document.querySelector('#nonexistent').textContent;");
423
if (isScriptError(result)) {
424
Map<String, Object> error = (Map<String, Object>) result;
425
System.err.println("JavaScript error: " + error.get("error"));
426
} else {
427
System.out.println("Result: " + result);
428
}
429
430
// Timeout handling for async scripts
431
public Object executeAsyncScriptWithTimeout(JavascriptExecutor js, String script, Duration timeout, Object... args) {
432
// Set script timeout
433
driver.manage().timeouts().setScriptTimeout(timeout);
434
435
try {
436
return js.executeAsyncScript(script, args);
437
} catch (ScriptTimeoutException e) {
438
System.err.println("Async script timed out after " + timeout.getSeconds() + " seconds");
439
return null;
440
} finally {
441
// Reset to default timeout
442
driver.manage().timeouts().setScriptTimeout(Duration.ofSeconds(30));
443
}
444
}
445
```