bigskysoftware / idiomorph

A DOM-merging algorithm
BSD 2-Clause "Simplified" License
641 stars 32 forks source link

Full page morphing #2

Closed kpietraszko closed 1 year ago

kpietraszko commented 1 year ago

Will morphing whole pages be supported? I'd love a library where you can throw anything at it, and it would "just work". Even including the head tag.

1cg commented 1 year ago

we can try!

ryanflorence commented 1 year ago

I was curious to morph the whole document too, but running into errors.

let html = "<html><head><title>hello</title></head><body>World</body></html>"
document.write(html);
Idiomorph.morph(document.documentElement, html);
TypeError: Cannot read properties of null (reading 'replaceChild')
    at morphOldNodeTo
1cg commented 1 year ago

yeah, to make this work we definitely need to handle a document, rather than element, root

@ryanflorence any interest in taking a look at what that would mean?

I started looking at a head-tag merging extension for htmx here:

https://github.com/bigskysoftware/htmx/blob/master/src/ext/head-support.js

not sure I want to bring all that in to idiomorph, I think we could just use the existing morphing algorithm w/ a few tweaks to make things work, but not sure...

1cg commented 1 year ago

OK, did some quick digging on this and the problem is here:

https://github.com/bigskysoftware/idiomorph/blob/main/src/idiomorph.js#L421

where we are parsing the content in terms of a template tag. This approach is great for handling arbitrary in-body HTML because it handles the nightmare that is table-related tags cleanly. However, it doesn't handle html, head or body tags.

To do that, we'll need to do something more like what the head-support extension in htmx does:

https://github.com/bigskysoftware/htmx/blob/master/src/ext/head-support.js#L17

So I think the right thing here is to detect if the source starts w/ html, head or body, and switch to this mode of parsing the content, if we want to be able to handle full documents.

@ryanflorence lmk what you think and if you are interested in contributing on this.

1cg commented 1 year ago

OK, had some time to play with this, and it looks like this change makes full page merging work:

            function parseContent(newContent) {
                let parser = new DOMParser();
                // if it is content containing an html tag, a head tag or a body, we can simply parse it w/o wrapping
                // it in a template
                if (newContent.match(/<html>/) || newContent.match(/<head>/) || newContent.match(/<body>/)) {
                    let content = parser.parseFromString(newContent, "text/html");
                    content.generatedByIdiomorph = true;
                    return content;
                } else {
                    // if it is partial HTML, wrap it in a template tag to provide a parent element and also to help
                    // deal with touchy tags like tr, tbody, etc.
                    let responseDoc = parser.parseFromString("<body><template>" + newContent + "</template></body>", "text/html");
                    let content = responseDoc.body.querySelector('template').content;
                    content.generatedByIdiomorph = true;
                    return content
                }
            }

I need to think about what the head merging algorithm needs to look like (e.g. we would likely want to re-execute at least some JS in the head to mimic normal page loading) but it's a start.

ryanflorence commented 1 year ago

Hey just came back here. Honestly I'm just playing around and exploring a bit right now so not likely to dig in and contribute 😬

Don't forget that usually you've got attributes on html and body (like <html lang="en"> and <body class="flex-col"> etc.) so gotta be more careful on the regexes there.

1cg commented 1 year ago

Ok spent some time this afternoon thinking and playing with the code. I think we want head merging to work specially, rather than the standard morph setup, because:

I have started to port the head-support code over and I think it will work properly. Hope to have something useful by the end of the weekend.

1cg commented 1 year ago

Full page morphing + special head tag handling is now in dev:

https://github.com/bigskysoftware/idiomorph/commit/372471f2f8f45a6bada1ad04b6550681d9eddc0a

I need to test it and then I'll cut a release, hopefully this weekend

1cg commented 1 year ago

just released idiomorph v0.0.8, with support for full document merging, as well as special handling for the head tag, since it acts kinda different than the body content:

https://github.com/bigskysoftware/idiomorph#the-head-tag