Skip to main content

Execute JavaScript

Open in ChatGPT

webforJ runs on the server, but there are times you need to reach the client: scroll the window, focus a field, read a browser value, or call a method on a web component. The HasJsExecution interface provides that bridge. It's implemented at two levels:

  • The Page runs script in the context of the whole page.
  • An Element runs script scoped to a single client element.

Both expose the same three methods, so once you know the shapes below, they read the same whether you call them on Page or an Element.

Execution methods

Each level offers a synchronous method and two asynchronous ones. The difference is whether the calling thread waits and whether a result comes back.

  1. executeJs(String script): runs the script synchronously. The executing thread is blocked until the client returns, which costs one server-to-client round trip. The result comes back as an Object you can cast and use in Java.

  2. executeJsAsync(String script): runs the script asynchronously and doesn't block the executing thread. It returns a PendingResult that completes when the script finishes, so you can react to the result later.

  3. executeJsVoidAsync(String script): runs the script asynchronously and returns nothing to the server. Use it for fire-and-forget work where you don't need the result. Available since 24.11.

Choosing a method

Reach for executeJsVoidAsync by default when you are only causing a side effect on the client (scrolling, focusing, calling a method). Use executeJsAsync when you need the value but want to stay non-blocking, and reserve the synchronous executeJs for the rare case where you must have the result before the next line of Java runs, since it holds the thread for a full round trip.

Reading results

When a script returns a value, webforJ converts it to the matching Java type:

JavaScript valueJava type
numberInteger, Long, or Double
stringString
booleanBoolean
null or undefinednull
any other typeits string representation

Read values with executeJsAsync, which applies the conversion reliably. A returned number can arrive as Integer, Long, or Double, so read it through Number:

Page.getCurrent()
.executeJsAsync("return window.innerWidth;")
.thenAccept(result -> {
int width = ((Number) result).intValue();
// use width
});
Prefer the async form when you need the value

The synchronous executeJs returns null when the execution context isn't ready, for example when it's called before the component is attached. Use executeJsAsync whenever you depend on the returned value, and avoid casting a synchronous result to a specific type.

App-level execution

Call the methods on Page when the script concerns the page as a whole rather than one component. Get the current page with Page.getCurrent().

A common case is scrolling back to the top after a route change. Nothing needs to come back, so executeJsVoidAsync fits:

Page.getCurrent().executeJsVoidAsync(
"window.scrollTo({ top: 0, behavior: 'smooth' });");

When you need a client value on the server, read it asynchronously and act on the result when it arrives:

Page.getCurrent()
.executeJsAsync("return navigator.language;")
.thenAccept(language -> {
// language is the browser locale, for example "en-US"
applyLocale(String.valueOf(language));
});
Page versus element scope

Use element-level execution when the script needs to act on a specific client element rather than the page as a whole.

In the demo below, selecting Copy link runs a script through Page with executeJsVoidAsync to write the invite link to the visitor's clipboard. Copying is a side effect with nothing to return, so the fire-and-forget method is the right fit.

Show Code

Element-level execution

Calling the same methods on an Element scopes the script to that element instead of the page. The return values and the synchronous and asynchronous behavior match the preceding page-level methods.

Element scripts queue until the element is attached to the DOM, then run, so you can call them during setup without waiting for attachment yourself.

Calling a function on an element

When you want to invoke a named client-side function rather than run a script string, Element offers a parallel set of methods. Instead of a script, you pass the function name and its arguments, which webforJ serializes and passes through. Two argument types are handled specially: this is replaced with the client element, and any Component argument is replaced with its client instance once attached.

These mirror the execute methods, differing only in whether the thread waits and whether a result returns:

  1. callJsFunction(String name, Object... args): calls the function synchronously and returns its result as an Object. The executing thread blocks for one round trip.

  2. callJsFunctionAsync(String name, Object... args): calls the function asynchronously without blocking, returning a PendingResult that completes with the function's result. Available since 24.11.

  3. callJsFunctionVoidAsync(String name, Object... args): calls the function asynchronously and returns nothing to the server. Use it for fire-and-forget calls where you don't need the return value. Available since 24.11.

Because the call waits for every Component argument to attach before running, a call that passes a component which never attaches never completes.

// Focus a web component's input by calling its client-side method
searchElement.callJsFunctionVoidAsync("focus");