How do I get DOM info from JavaScript and use that for rendering?

I’ve started learning how to use Phoenix and Elixir recently, and I’m trying to figure out how to do the following:

  1. Send text information from Phoenix to JS.
  2. Have the JS compute the dimensions of the text using browser APIs. (I know how to do this step.)
  3. Send the dimension info from JS to Phoenix.
  4. Have Phoenix use this information to record a “final” state. (I have an algorithm for this step.)
  5. Have Phoenix render the state to HTML. (This step is easy.)

I read the JavaScript interop page, but couldn’t figure out how to accomplish steps 1 and 3. Right now, step 4 is running in the view’s mount function (warning: ugly code), so I think I need to be able to do steps 1-3 during mount.

More context: I’m trying to create some boxes of text and arrange them in a DAG using absolute positioning. One of the inputs to the positioning algorithm is the height of the text boxes. Right now, for testing, I’m using hard-coded heights. For actual use, I’d like to get those from the browser since the height can depend on the user’s font settings etc.

Any suggestions on how I can accomplish steps 1 and 3?

#3. Have you considered making the text boxes hooks and sending a message to Phoenix within it’s mounted function? You use either pushEvent or pushEventTo from the JS hook.

#1. In your hook, add an event handler. Then, from the LV side, you can push_event(socket, "eventname", %{a: 1, b: 2}) to send that event to the hook. This will likely happen inside a handle_event, i.e. the one you sent to LV on mount of the hook.

edit: another place that you sometimes use push_event is in update/2 of a LiveComponent. So each time you update some assigns, do something with them and then send to the hook and it can work its magic from there.

  def update(assigns, socket) do
    {:ok,
     socket
     |> assign(assigns)
     |> push_event("echarts:#{socket.assigns.id}:init", %{
       "option" => assigns.option,
       "merge" => assigns[:merge] || true
     })}
  end
1 Like