_hyperscript
_hyperscript copied to clipboard
Event handling doesn't work after putting HTML from a server
I send request to the server. Server renders the same HTML but with blocks in another positions. I get that HTML with hyperscript and replace current HTML on the page. I expect that I still can use arrows to trigger event that sends request to the server. But events doesn't work with the new HTML. In order to make it work I need to refresh the page
It would be great if Hyperscript could connect to new HTML that fetched from server side and listen events
<ul>
<% data.each do |block| %>
<li>
<%= block['pos'] %>
<input
type="text"
value="<%= block['content'] %>"
_="
on keydown[key is 'ArrowUp' and metaKey]
log 'Move up'
fetch <%= move_up_block_path(block['id']) %>
{
method: 'post',
headers: {'X-CSRF-Token': document.getElementsByName('csrf-token')[0].content}
}
log it
put it into #children
end
"
>
</li>
<% end %>
</ul>
Try _hyperscript.processNode(theNewElement)
-- it's a part of the public API, but it's not conveniently documented. That should let hyperscript know about your new elements
Try
_hyperscript.processNode(theNewElement)
-- it's a part of the public API, but it's not conveniently documented. That should let hyperscript know about your new elements
Thank you! It works. New code looks like this
on keydown[key is 'ArrowUp' and metaKey]
log 'Move up'
fetch <%= move_up_block_path(block['id']) %>
{
method: 'post',
headers: {'X-CSRF-Token': document.getElementsByName('csrf-token')[0].content}
}
put it into #children
put first of <input[value=`${me.value}`]/> into el
call el.focus()
call _hyperscript.processNode(#children)
end
I want to clean this code up for the next release. I think we should consider processing nodes any time we add content to the dom, or at least add a command for doing so.
Ideally this:
on keydown[key is 'ArrowUp' and metaKey]
log 'Move up'
fetch <%= move_up_block_path(block['id']) %>
{
method: 'post',
headers: {'X-CSRF-Token': document.getElementsByName('csrf-token')[0].content}
}
put it into #children
put first of <input[value=`${me.value}`]/> into el
call el.focus()
call _hyperscript.processNode(#children)
end
should look like this:
on keydown[key is 'ArrowUp' and metaKey]
log 'Move up'
fetch <%= move_up_block_path(block['id']) %> using POST
with headers
X-CSRF-Token: <name='csrf-token'/>.content
put it into #children
focus() the first of <input[value=`${my value}`]/>
end
We should also handle htmx attributes somehow here.
I think it would also be nice to have an event triggered after a fetch of hyperscript, similar to how HTMX' htmx:afterSwap
works
This would be helpful, but it also starts down the road of replicating a lot of what htmx already does. This might be a good thing, but it would also result in a lot of duplicated code. I'd propose that we hook into the htmx's existing swap functions instead -- something like a swap
function that knows how to use htmx.swap()
fetch /my-server then swap it into #destination
Alternatively, we could add a setting to hyperscript to use htmx.swap features instead of default actions.
I think we should consider processing nodes any time we add content to the dom
I think this would be best. I think having to manually process a node that contains hyperscript is clunky.
or at least add a command for doing so.
In this case, I'd propose activate
, initialize
, or process
(in that order of preference). E.g.
set newElement to '<button _="on click do some hyperscript">Click me</button>'
append newElement to the body then activate it
It would be a nice feature, because now it's not even obvious how to make a clone of html element.
You can do:
<button _="on click put me as HTML after me">Test</button>
but generated items are not handled by hyperscript and you need to do something like this:
<div id="copy">
<button _="on click put me as HTML after me _hyperscript.processNode(#copy)">Test</button>
</div>
which looks strange.
It's super easy to make your own conversions. Here's an example that I made for another project. You might just make it and then publish it. We're still talking about ways to collect everyone's independent extensions outside of the main core.
How can I trigger hyperscript when new elems are "managed" by third party lib? (a virtualized list lib https://clusterize.js.org/)
I'm pushing some new elems using ws like this
ws.send([
<div _="on load log 'loadeded'" class="newelem">
sthsth
</div>,
])
they are handled by socket owner
<div
hx-ext="ws"
ws-connect="/ws"
_=" on htmx:wsBeforeMessage
set elems to JSON.parse(event.detail.message)
$logsi.append(elems)
go to bottom of the #contentArea unless $scrolling
for elem in .newelem
log 'lelo'
log elem
add .hover:bg-red-600 .bg-black to elem
remove .newelem from elem
put 'on click log 2' into elem.classes
add @_='on click log 1' to elem
add @classes='remove bar:1s, add foo:1s' to elem
_hyperscript.processNode(elem)
"
>
they show up fine but hyperscript parts doesn't run. Tried using processNode
mentioned above, didn't help.
Another way of handling this is to call processNode
on a containing element when its children are mutated:
<slot data-notification
_="
on mutation of childList
call _hyperscript.processNode(me)
"
></slot>
This slot
can then be dynamically updated with content from a server response:
<slot data-notification
_="
on slotnotification(html)
set my.innerHTML to html
on mutation of childList
call _hyperscript.processNode(me)
"
></slot>
<button
_="
fetch '/some/endpoint'
-- ...
catch e
-- send the markup to the notification element
send slotnotification(html: e.responseText)
to the first <[data-notification]/>
"
>
do the thing
</button>