htmz
htmz copied to clipboard
Loader extension does not reflect request state properly
The loader extension is a bit too shallow: it will only show a loading spinner once the server has begun sending the first byte.
This means it is not a true loading indicator (like htmx's hx-indicator
), which is fine but quite limiting in terms of functionality, especially when trying to mitigate the "laggy" feeling of hypermedia-driven applications.
(the loader extension also uses onload
, which is deprecated. It should rather use onbeforeload
).
I have two proposed solutions:
- improve the extension to make it 1-1 with hx-indicator. This will involve more code and a custom attribute, so more complex overall.
- leave the extension as is, and add "htmz vs htmx" examples.
What would you prefer? Depending on what you like best I can submit a PR!
Hello! I couldn't see where it says it's supposed to behave exactly the same as htmx's loader. Is that a goal?
I couldn't see where it says it's supposed to behave exactly the same as htmx's loader
Not quite what I wrote. That's an oversimplification of my issue :) (Huge Gleam fan btw)
Wow! Thank you. It's weird being recognised on unrelated projects 😅
To bring a bit more details:
- the goal is not necessarily that it would behave exactly the same. But in its current state, the use cases of the extension are very limited
- so my idea was that a target loader element could be toggled on when the request is in the air, which kinda resembles
hx-indicator
. But maybe there is a simpler way to achieve this 🤔
Thank you
I didn't realize that unload
has been deprecated. I'll look into it.
@Kalabasa you wrote: // The initial about:blank page also counts when unloading.
So the loader
class should be added before the new iframe's src
starts to load.
Maybe using onbeforeunload
will fire it even earlier?
@niutech the problem with onbeforeunload
is that it will only access the unloading url. So you can't access the hash and can't apply the loading state properly :/
Can't you get the hash inside onbeforeunload
using document.activeElement.href
?
That will only work with links. I don't think it will work with a form...
The solution I have is this:
<progress><iframe name="htmz" onload="htmz(this)"></iframe></progress>
everything inside progress
is hidden by default so no need for the hidden
tag anymore.
Then the extension would look like this:
function htmz(){
frame.contentWindow.addEventListener("beforeunload", () => {
setTimeout(() => {
frame.parentElement.style.opacity = 1;
});
});
frame.parentElement.style.opacity = 0;
// main htmz code
}
The obvious downside is that you can have only one loader per target iframe.
I have a solution that doesn't work for all scenarios, but I think that's the one I'm going to roll with for my own project:
- You listen for any
click
event on the document - If the emitter has an
href
attribute, try to split its value with'#'
to get the selector of the container that will be replaced with the HTML response - Add a loading class to this container
Using the :empty
pseudo-class you can detect if the destination container is empty and choose to fill it with content to indicate status. This code is incomplete and makes a lot of assumptions (e.g. only covers links, not submit buttons within forms, assumes you will only have '#' in your href for htmz, etc.) but just so you have an idea:
document.addEventListener('click', (e) => {
const dest = e.target?.href?.split('#')[1]
if (dest) { document.querySelector(`#${dest}`).classList.add('htmz-load') }
})
And here is what it looks like with some CSS:
https://github.com/user-attachments/assets/dca33e9a-5087-46af-839e-83f15ff2f705
EDIT. Here is a completed version that handles both forms and links. I managed to get it down to 279 bytes minified.
Here's the readable version
document.addEventListener('click', (e) => {
if (e.target.target == 'htmz') {
document.querySelector('#' + e.target.href.split('#')[1]).classList.add('htmz-load')
}
})
document.addEventListener('submit', (e) => {
if (e.srcElement.target == 'htmz') {
document.querySelector('#' + e.srcElement.action.split('#')[1]).classList.add('htmz-load')
}
})
Here's the minified version
((a,q)=>{a("click",t=>"htmz"==t.target.target&&q("#"+t.target.href.split("#")[1]).classList.add("htmz-load"));a("submit",t=>"htmz"==t.srcElement.target&&q("#"+t.srcElement.action.split("#")[1]).classList.add("htmz-load"))})(document.addEventListener,s=>document.querySelector(s))
(The loader extension also uses
onload
, which is deprecated. It should rather useonbeforeload
.)
Reading the doc on MDN, pagehide
feels closer from semantics? The doc mentioned that beforeunload
was intended for “prompt to save”.
I wrote beforeload
, not beforeunload
I wrote
beforeload
, notbeforeunload
I wasn’t able to find any documentation for the beforeload
event, so I assumed that it was a typo. In addition, other comments in this thread has been talking about beforeunload
.
Yea you are correct 🤔 actually, the load event is completely valid.
I think I meant unload
and beforeunload
. Also reading MDN your comment about pagehide
makes sense! 👌
I am going to close this issue though, it feels a bit pointless since the repo is unmaintained. Also it's not a big issue.