Run Custom JS on LivePage Reload

This article is based on a very early version of LiveView and is no longer the right way to run Custom JS code.

As part of my talk at EMPEX 2019 on LiveView, I showed a way to execute custom JavaScript on a LiveView refresh. So where is how I do it? If you know a better way, please reach out to me @a4word to discuss.

If you don't know about LiveView, maybe read more about that first

Problem

How to force JavaScript to be executed each time my LiveView page refreshes itself?

A tick on every message

When ever you return data back to the client through the the websocket, consider adding an auto-incrementer. This will allow you to uniquely track every liveview refresh.

Adding a tick to every message

In friendly copy and paste code...

def mount(_params, _session, socket) do
  {:ok, assign(socket, tick: 1)}
end

defp socket_reply(socket, reply \ :noreply) do
  {reply, socket |> update(:tick, &(&1 + 1))}
end

This is useful for a bunch of things, but let's focus on running our custom javascript.

Reference that @tick in your <script id="xxx">...</script>

In your LiveView LEEX, you can add a script tag and append the @tick to the id. This will force for the MorphDOM differ to always re-render (aka re-run) that code on the client.

Create a script with tick ID

In friendly copy and paste code...

<script id="always-run-<%= @tick %>">
  console.log("I was updated <%= @tick %> time(s)")
</script>

Now on every refresh, the code above will be executed, even if the internals of the code block did not change.

Why? Why! Why?!?

The code above is not that compelling. But here is a more compelling one. Focus. Focusing multiple times on an input using HTML is not (well?) supported. At Dividends.io, we wanted to have a smart focus so that when you were editing your portfolio, the system smartly put focus on the most appropriate input.

So when the page loads, but focus on the input. This can totally be done without JavaScript and plain old autofocus works well.

Autofocus on first input

But, when you want to edit a particular stock, for example, we want to put focus on that input.

Autofocus edit input

And once done editing, put focus back on the original new input.

This was accomplished with the following code

Maintaining autofocus using a script

In friendly copy and paste code...

<script id="autofocus-me-<%= @tick %>">
  <% input_id = case assigns[:index] do
    nil -> "autofocus-new"
    i -> "autofocus-edit-{i}" // should be #
  end %>
  document.getElementById("<%= input_id %>").focus();
</script>

A few caveats.

First, LiveView continues to evolve, so please check out the inter webs to see if this is still a valid approach (and if not, let me know).

Second, don't write lots of this kind of JavaScript. If it hurts, you might not be doing it right so maybe consider taking a step back and trying to figure out what exactly are you trying to achieve and will this approach do as you expect.

Happy LiveViewing.