Calling some JS code upon a LiveView update?

Dear Elixir forum,

We have a web page in which we stream output from processing jobs. We’ve set up LiveView so that the content of our box reflects the current job output. It works great but we’d like to keep the job output in a fixed height box and always show the latest lines (unless the user scrolls up), similar to GitLab CI job output. We’ve got a pure CSS solution but unfortunately because of an old Firefox bug it works only on Chrome.

So we’re wondering if it’s possible to plug a small piece of JS code that would be called when LiveView triggers an update. We could use this code to scroll down the bottom of our output box whenever new content gets appended to it.

Another use case I’m thinking of for being able to call some custom JS code upon LiveView update is when adding items to a list, we could use a piece of JS to highlight the new item in order to make it more noticeable by the user.

JS interop is an on-going effort.

So in case there isn’t a suggestion from anyone more familiar with LiveView forthcoming, it may be worthwhile to check out MutationObserver (as already suggested here) in order to run some client code in response to any changes in the DOM.

1 Like

It’s also possible to listen for "phx:update" events which are (at least to my knowledge) emitted when the DOM is done being patched.

I used it in some of my “experiments” with client side js libs (similarly to LiveView: How to trigger JS function? (Update chart.js on data change)) to create/destroy/recreate their objects when the DOM elements they were “bound” to got “morphdomed”.

3 Likes

Works with thermostat_live.ex:

import css from "../css/app.css";
import "phoenix_html"
import {LiveSocket, debug} from "phoenix_live_view"

let liveSocket = new LiveSocket("/live")
liveSocket.connect()

// grab time and weather temperature from thermostat
// --- assets/app.js --- vvv New vvv

document.addEventListener('phx:update', phxUpdateListener);

function phxUpdateListener(_event) {
  const time = extractTime()
  if (!time) {
    return
  }

  const temperature = extractTemperature()
  if (!temperature) {
    return
  }

  const message =  `${time}: ${temperature}`
  console.log(message)
}

function extractTime() {
  const timeSpan = document.querySelector('div.bar > span')
  return timeSpan && timeSpan.textContent
}

function extractTemperature() {
  const weatherForm = document.querySelector('span.weather form')
  if (!weatherForm) {
     return null
  }

  const result = /[+-]?\d+°[CF]/i.exec(weatherForm.lastChild.textContent)

  return result && result[0]
}

Certainly simpler than using a MutationObserver.

9 Likes

Thank you both. The phx:update event did the trick for us. Many thanks.

2 Likes