-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Running ready function on readyState === 'interactive' #2264
Comments
@alexpetros the always burning topic! |
First of all, this is a very well-written issue that engages with all the past discussion and I just want to publicly appreciate that!
This is the wrong way to load htmx. Full stop. We do our absolute best to accommodate it, but it is a lower priority than all functionality that is supported by using htmx when loaded correctly, via either a script tag or a ES6 module. And as you noted, we have caused regressions in the past overstretching on our ability to support asnyc loading. So my first question is—why do you need to load htmx with Magneto? Why can't you just include htmx as a separate script tag in your document?
In practice, this does not seem to be exactly the case. If this were airtight, we could just say "if this readystate, listen for this event, otherwise activate." The code as is works because if you load htmx correctly, then the document will not be in a All this having been said, I don't oppose this change. It's easy to understand and causes no harm and it would solve your problem. I still wouldn't say that we "support" |
@andrew-ebsco did you try loading htmx into magento directly via script tag? when I do that it, console returns 'Uncaught Error: Mismatched anonymous define() module' |
I stumbled across this comment in the HTMX codebase (also pasted in this issue): /**
* Execute a function now if DOMContentLoaded has fired, otherwise listen for it.
*
* This function uses isReady because there is no reliable way to ask the browser whether
* the DOMContentLoaded event has already been fired; there's a gap between DOMContentLoaded
* firing and readystate=complete.
*/
function ready(fn) {
// Checking readyState here is a failsafe in case the htmx script tag entered the DOM by
// some means other than the initial page load. If MDN and the HTML Spec is correct, then looking for "interactive" means there won't be a gap between DOMContentLoaded and readyState anymore, correct? This is just a thought, but we should be able to remove the DOMContentLoaded listener that sets Although, this would revert #1672 to the On another note: My use-case is a bit special, I need to initialize HTMX manually before DOMContentLoaded, as I am streaming data in. If I can manually initialize HTMX via a Here's my rough suggestion: function initialize() {
triggerEvent(getDocument(), "htmx:ready", {});
}
getDocument().addEventListener("DOMContentLoaded", initialize);
htmx.initialize = initialize;
function once(fn) {
var executed = false;
return (...args) => {
!executed && fn(...args);
executed = true;
};
}
/**
* Execute a function now if document is interactive, otherwise listen for `htmx:ready`.
*/
function ready(fn) {
// Checking readyState here is a failsafe in case the htmx script tag entered the DOM by
// some means other than the initial page load.
if (getDocument().readyState !== "loading") {
once(fn)();
} else {
getDocument().addEventListener("htmx:ready", once(fn));
}
}
ready(function () {
...
getWindow().setTimeout(function () {
triggerEvent(body, "htmx:load", {}); // give ready handlers a chance to load up before firing this event
body = null; // kill reference for gc
}, 0)
});
|
All I can say about this is I've tried multiple times and what we have right now seems to be working for the most number of people, relative to anything we've tried before. Every time I come up with a theoretically airtight way to initialize the attributes, someone's use-case breaks it.
I know this has been discussed before, but I can't find it immediately. Possibly @xhaggi had a version of this? I'm open to it, but not causing regressions in our initialization logic is priority 1. |
My experience is I have to always modify htmx code to cover interactive event if I want to boost a magento page. |
Could you share exactly how you modify it? That might help us come up with a solution. |
@alexpetros it is the exact same code modification that @andrew-ebsco suggested in this post
so maybe we can add a config |
Reading back over my original comment in Februrary:
So yeah, if you want to PR that one-line change specifically, go for it. |
In my case, that one-line change is not enough to initialize HTMX in a streamed page environment, as even interactive DOMContentLoaded only runs when the page has finished streaming. For reference on why this is needed; see NextJS Partial Prerendering and Remix streaming. |
@alexpetros and @kuafucode, apologies, I dropped the ball on creating a PR for this. We modified our copy of htmx with the change which solved our immediate need. The positive news is it resolved our issue and has been working well with Magento for several months. I can submit a PR today, if you haven't already. @alexpetros, to answer your question, I did try loading htmx as a separate script tag, but it caused another error. I'm guessing it was an issue with some scripts loading in RequireJS and some not. |
Just submitted it: #2808. Thanks everyone! |
I have done some testing for the common methods to load htmx. Only with async loading (
|
I think the issue with the initial readystate check (i.e. |
You`re right @Telroshan, forgot about that. My comment #1469 (comment) explaining the issue with using extensions when htmx is used in a module environment. I think we need some further investigations. |
BTW some time ago I created a PR #1681 that would fix this issue, but was rejected by @alexpetros in favor of the current implementation. |
@Telroshan Would you prefer a discussion about the ability to do early initialization for streamed pages in a separate issue? I feel it's related, but kind of overlooked in this discussion and may be better suited as a separate issue. None of the issues listed talk about this (at least no conclusion about it has been made), but considering many modern JS frameworks are moving towards streamed pages (especially now that Lambda supports it), any framework wanting to do the same approach wouldn't be able to reliably use HTMX for it (which is my case). |
@AlexanderArvidsson yes, that should be a separate issue, but I will say that my initial reaction is that htmx is a framework intended to optimize large-grain hypermedia requests—streaming pages isn't really in the scope of what we do. If it's a thing we can support without adding too much complexity to the core, there might be a route. Either way, it's a separate issue. |
@alexpetros @Telroshan Considering the potential for causing loading issues, sounds like the initialize function is what is needed, so users can decide when they want to htmx to load in the page load cycle. Do I need to close the associated PR? Any ideas around how that should be implemented? I could take a stab at it when time permits. |
Hey @andrew-ebsco , appreciate that you're taking time to look into this.
So it turns out, we completely forgot that Though, this was in november 2023, time has passed, and minds can change. So, I'll think we'll have to dive into this whole topic with @alexpetros @1cg again, to see if we still agree with our past selves, or if we should try something else. As for your PR @andrew-ebsco, I'd say it doesn't make sense to merge it in its current state in my opinion, as, as I commented on it, this would take us back to the very first loading check we had in the lib, that we already know is going to cause issues, as those very issues have been reported in the past (cf all issues and PRs listed above) If you want to give a shot at the initialize function, go ahead! Note that #1485 might be a good inspiration, as it was a PR precisely about adding a way to manually initialize the lib. |
@Telroshan This is similar to what I was thinking as well. I want to clarify my issue mentioned previously about streamed pages; HTMX works fine for streamed pages today, it's just that it won't initialize until the page is fully streamed in. This is separate from HTMX-initialized requests that might not support streams (which is fine and what @alexpetros said might not be in scope for HTMX) and I suspect that is how my issue was understood as separate, which is not necessarily what I'm asking for. All that is needed to make server-initialized streamed pages work with HTMX is a way to do early initialization via an initialize function, which as @Telroshan says is what was forgotten in the past and that the recent PR suggested will do a full circle back to the very first check. Hence, if we can get a manual initialization, which will ultimately be useful for anyone encountering initialization problems (an escape-hatch), that's probably the way to go and we haven't gone back in a full circle :) |
Hello htmx team!
I have been working on a confusing bug that I was able to fix, but want to present it to the team before making a pull request. I have started using htmx in Magento for some custom features. Magento uses RequireJS to load AMD modules and htmx runs well in the set up. However, RequireJS loads all modules with
async=""
on all script tags. The bug I ran into is that the htmx initialization function was not running sometimes. Since the document body was not being processed, it required a page refresh to get the htmx behavior to execute. After some debugging, I found the issue was in how theready
function checks for a complete DOM.In the above code, there is a scenario where
fn
is never executed. Sometimes the htmx code was being executed after theDOMContentLoaded
event had fired, but beforedocument.readyState === 'complete'
. After doing some research, I found thatDOMContentLoaded
can fire whendocument.readyState === 'interactive'
. According to both MDN and the HTML Spec, theDOMContentLoaded
event and a readyState of interactive are equivalent.As a result, my proposal is to change the
ready
function to check for both a readyState of interactive as well as complete.The change allows
fn
to be immediately executed ifDOMContentLoaded
has already fired, but thereadyState
equals interactive. SinceDOMContentLoaded
andreadyState === 'interactive'
are functionally equivalent, this small change should fix the async bug I have encountered without introducing a regression. I have run the test suite locally and all tests passed without issue.It looks like there was an attempt to fix loading bugs in PR 1972, but the code was more complex by watching the
readystatechange
event. It was eventually reverted in PR 2040 in favor of creating a new initialize function. That initialization function has not been created yet and as a result theasync
bug persists.I believe my proposal is a much simpler fix, is inline with the HTML spec and maintains htmx's easy to install behavior. Of course, there are edge cases I don't know about. If this change would cause problems, I would like to hear about them. However, if the htmx team likes the fix, I can submit a pull request.
Thanks!
The text was updated successfully, but these errors were encountered: