diff --git a/articles/flow/component-internals/element-api/calling-javascript.adoc b/articles/flow/component-internals/element-api/calling-javascript.adoc index 0dcc61b114..adbc50da21 100644 --- a/articles/flow/component-internals/element-api/calling-javascript.adoc +++ b/articles/flow/component-internals/element-api/calling-javascript.adoc @@ -241,4 +241,39 @@ getElement().executeJs( If the body always needs to reference the same element regardless of how the function is called, pass it as a capture instead of relying on `this`. +[[add-js-initializer]] +== Client-Side Lifecycle With `addJsInitializer` [since:com.vaadin:vaadin@V25.2] + +The [methodname]`Element.addJsInitializer()` method registers a JavaScript expression that runs each time a client-side DOM node is created for the element, and whose returned cleanup callback runs when that DOM node is discarded or the returned [classname]`Registration` is removed. + +Use this when you need to install something on the client-side DOM – an event listener, a third-party widget, an observer – and reliably tear it down. A one-shot [methodname]`executeJs()` call doesn't cover two cases: a real re-attach gives the element a brand-new DOM node that no longer has your listener, and cleanup from a server-side detach listener cannot be delivered because the element is leaving the tree. + +The expression syntax is the same as [methodname]`executeJs()`: `this` is the host element on the client, and parameters are referenced as `$0`, `$1`, …. If the expression returns a function, that function is invoked at teardown. + +.Installing a listener with cleanup +[example] +==== +[source,java] +---- +Registration registration = getElement().addJsInitializer(""" + const handler = (event) => console.log('clicked', event.target); + this.addEventListener('click', handler); + return () => this.removeEventListener('click', handler); + """); +---- +==== + +Remove the registration on the server when the listener is no longer needed; the cleanup callback then runs on the client. + + +=== Re-Attach Semantics + +The initializer is re-run after a real re-attach – when the element is removed from the DOM in one round trip and re-added in a later one, and so the browser receives a fresh DOM node. It is *not* re-run when the element is detached and re-attached on the server inside a single round trip, because the client never discarded its DOM in that case. + + +=== Cleanup Constraints + +The return value of the expression is read synchronously. A function is treated as the cleanup callback; returning nothing (no `return`, or `undefined`) means there's no cleanup. Returning any other value – including a `Promise` resolving to a function – is logged as an error on the client. If you need asynchronous setup, do the async work inside the body and store the teardown in a synchronously returned function. + + [discussion-id]`AB7EDF45-DB22-4560-AF27-FF1DC6944482`