Sleepful

Sleepful

How to turn my client-side DOM manipulations into DOM-patch aware manipulations?

I want to accomplish something similar as Phoenix.LiveView.JS, per the docs:

While these operations can be accomplished via client-side hooks, JS commands are DOM-patch aware, so operations applied by the JS APIs will stick to elements across patches from the server.

so I wonder, what’s the “trick” that makes LiveView.JS DOM-patch aware?

Because I want to use this “trick” for my own client-side DOM manipulation. I searched the source code for a while but I did not find the mechanism by which JS.operations are able to be DOM-patch aware.

Some alternatives that serve a similar purpose but feel too blunt for me:

  • phx-update="ignore": This is easy, it works mostly, but I don’t like it because the children cannot leverage LiveView events, it also feels clunky to sprinkle the HEEX template with this directive.
  • cloning nodes inside onBeforeElUpdated (docs): I don’t like this, there’s very little documentation about how the cloning function should work. It is also a tricky operation, if you clone a node then it might discard any LiveView operations that should have been applied to the node and its subtree. On top of that, it becomes necessary to “mark” with an HTML attribute the nodes that would get cloned.

Most Liked

cmo

cmo

It might be referring to the morphdom patching. In livebook, they set attributes and ask morphdom to leave 'em alone.

https://github.com/livebook-dev/livebook/blob/main/assets/js/dom.js

Sleepful

Sleepful

This is a bit of a crazy ramble so if it doesn’t make sense, that’s my fault…

Watched that talk, very interesting. He reaches a conclusion similar to my premise, there shouldn’t be connections to the server for merely UI changes, the difference is that he still finds liveview convenient for coordinating client-side state between separate components, I don’t share that. So far I am happy using pub/sub between separate components to keep state, I just need to get it to play nice with LV…

Here is an interesting bit I found about morphdom in the readme:

Because morphdom is using the real DOM, the DOM that the web browser is maintaining will always be the source of truth. Even if you have code that manually manipulates the DOM things will still work as expected.

which is exactly what I want, but counter-intuitively LiveView gets rid of this benefit.

The ShadowDom seems really interesting to accomplish this, I need to read more about that though.

One of the reasons I don’t like phx-update=ignore is that you are unable to use phx-update in the child nodes, although I suppose the child-nodes could use the push function to communicate with the server… but how would they receive information? they couldn’t receive diffs from the server, but these nodes might desire to send new input data events to validate, then the issue becomes receiving the server response.

Sidenote: I am overcomplicating myself if Shadow Dom has the same effects… I guess shadow dom would be more capable if it is capable of nesting normal-dom inside shadow-dom, which would be a little crazy and cool. (TODO: research this).

Event listening for server pushed events feels a bit messy because you have to keep track of the event-name and it has to be listened-to “globally” on the client-side, I wonder if I can listen to the server event that happens with a “socket assign” update in a given element (e.g. result from handle_event("validate"...), that would be what I need but I haven’t seen information about it, I think the usual render result from a live_view only sends the computed diff, no option to attach additional payload to that message.

I have not given server-pushed events a good test-run so I might come back to them later. However, making special events for every client-side component sounds a bit exhausting when ideally one would use the same “validate” event for the entire form.

So I guess, ideally there would be “assigns” that are sent to the client as plain JSON and then the client is in charge of “diffing” it (showing an error or a validation), and ideally the client JS would also be able to eavesdrop on these messages to get the values for its own operations, as opposed to the default client-side logic that simply “diffs”. So I wonder if I can receive the changeset struct from a phx-change="validate" in the client side, on top of the usual diff. I might have to explore handleEvent client hook on a beforeUpdate maybe.

TunkShif

TunkShif

I was also wondering how LiveView JS commands could achieve “DOM-patch aware” DOM manipulations, so I just tried to read the source code of liveview js lib a few days ago. And here is my understanding:

When LiveView executes a JS command on the client-side, for example, a JS.set_attribute operation, it not only sets the attribute of that specific element, but also puts some private information into the element properties. It is shown in the code snippet below in the DOM.putSticky function:

https://github.com/phoenixframework/phoenix_live_view/blob/v0.18.3/assets/js/phoenix_live_view/js.js#L206-L218

And you can also inspect these private properties in Chrome DevTools.

All these executed JS commands will be stored in this phxPrivate.sticky property, so that when DOM-patching happens, these DOM changes made by JS commands will remain intact.

So if you want to achieve something similar to JS commands, here is a kind of tricky approach: just executing LiveView JS commands from JavaScript. Here’s how you can do it:

In your heex template, store JS commands in a data attribute:

<div id="my-element" data-js={JS.set_attribute({"aria-expanded", "true"})}>
</div>

And on the JavaScript side:

let liveSocket = new LiveSocket()

const el = document.getElementById("my-element")
liveSocket.execJS(el, el.dataset.js)

I think this approach is also used in GitHub - fly-apps/live_beats · GitHub

https://github.com/fly-apps/live_beats/blob/master/assets/js/app.js#L10-L12

Where Next?

Popular in Questions Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
dotdotdotPaul
Okay, I’m having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I’m sure I’...
New
earth10
Hi, I’m just starting to build a side-project with Elixir and Phoenix and doing some basic test with Elixir alone. What strikes me is th...
New

Other popular topics Top

axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 48475 226
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
Qqwy
Update: How to use the Blogs &amp; Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3271 127536 1222
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

We're in Beta

About us Mission Statement