How LiveView JS commands persists through renders?

This article is a work in progress.

Phoenix LiveView provides a declarative API to describe some limited JavaScript to be executed in the browser in order to avoid server round-trips for absolutely everything. One common use case it updating HTML data- attributes, which in turn drives CSS selectors.

The example below renders a button which on click, changes background color to green:

<button
  style="background-color: attr(data-color type(<color>))"
  phx-click={JS.toggle_attribute({"data-color", "green", "pink"})}
>
  toggle
</button>

Notice the modern CSS function attr() which returns an attribute value and uses it in a CSS property.

The server knows only how to encode the JS command into the HTML element phx-click attribute:

[["toggle_attr",{"attr":["data-color","green","pink"]}]]

The JavaScript part of LiveView wires the "click" event handlers, parses the JS command string, and carries out it's instructions. In this case, toggling the data-color attribute.

But if the DOM changes completely out of knowledge from the server, do those changes survive after the server re-renders?

Demo time

The changes performed purely in the client, through JS commands, survive re-renders. Thanks to smart DOM patching which accounts for sticky operations.

The demo below shows a div which the background color is dictated by the data-color attribute. It's initial value is rendered as the @color assign.

@color="red"

Notice that:

  1. Initially, push_event updates both the @color assign and data-color attr.
  2. set_attribute updates only data-color, and in consequence the styles too.
  3. After set_attribute runs once, push_event won't update data-color anymore.

Sticky DOM operations

LiveView does all the HTML rendering in the server, sending the smallest diffs possible to the client when the page must be updated. In the client, it uses the morphdom library to patch the current DOM tree with the diffs.

Morphdom provides the onBeforeElUpdated(fromEl, toEL) callback to

DOM.applyStickyOperations src