Kalabasa / htmz

html with targeted manipulation zones
http://leanrada.com/htmz/
Other
1.71k stars 41 forks source link

Loader extension does not reflect request state properly #25

Open Atmos4 opened 6 months ago

Atmos4 commented 6 months ago

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:

What would you prefer? Depending on what you like best I can submit a PR!

lpil commented 6 months ago

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

Atmos4 commented 6 months ago

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)

lpil commented 6 months ago

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

Atmos4 commented 6 months ago

To bring a bit more details:

lpil commented 6 months ago

Thank you

Kalabasa commented 6 months ago

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

niutech commented 5 months ago

@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?

Atmos4 commented 4 months ago

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

niutech commented 4 months ago

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

Atmos4 commented 4 months ago

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

Atmos4 commented 4 months ago

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.

simjnd commented 2 months ago

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:

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