htmz icon indicating copy to clipboard operation
htmz copied to clipboard

Loader extension does not reflect request state properly

Open Atmos4 opened this issue 11 months ago • 12 comments

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!

Atmos4 avatar Mar 14 '24 17:03 Atmos4

Hello! I couldn't see where it says it's supposed to behave exactly the same as htmx's loader. Is that a goal?

lpil avatar Mar 14 '24 17:03 lpil

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)

Atmos4 avatar Mar 14 '24 18:03 Atmos4

Wow! Thank you. It's weird being recognised on unrelated projects 😅

lpil avatar Mar 14 '24 18:03 lpil

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 🤔

Atmos4 avatar Mar 14 '24 18:03 Atmos4

Thank you

lpil avatar Mar 14 '24 18:03 lpil

I didn't realize that unload has been deprecated. I'll look into it.

Kalabasa avatar Mar 18 '24 10:03 Kalabasa

@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 avatar Apr 09 '24 09:04 niutech

@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 :/

Atmos4 avatar Apr 24 '24 14:04 Atmos4

Can't you get the hash inside onbeforeunload using document.activeElement.href?

niutech avatar Apr 24 '24 14:04 niutech

That will only work with links. I don't think it will work with a form...

Atmos4 avatar Apr 25 '24 20:04 Atmos4

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.

Atmos4 avatar Apr 25 '24 20:04 Atmos4

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))

simjnd avatar Jul 15 '24 11:07 simjnd

(The loader extension also uses onload, which is deprecated. It should rather use onbeforeload.)

Reading the doc on MDN, pagehide feels closer from semantics? The doc mentioned that beforeunload was intended for “prompt to save”.

FranklinYu avatar Oct 15 '24 08:10 FranklinYu

I wrote beforeload, not beforeunload

Atmos4 avatar Oct 15 '24 12:10 Atmos4

I wrote beforeload, not beforeunload

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.

FranklinYu avatar Oct 16 '24 03:10 FranklinYu

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! 👌

Atmos4 avatar Oct 16 '24 04:10 Atmos4

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.

Atmos4 avatar Oct 16 '24 04:10 Atmos4