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
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
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
morphdomis 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
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:
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
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #hex










