Skip to content
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

Text nodes with document.implementation.createHTMLDocument #1

Closed
aralroca opened this issue Mar 31, 2024 · 6 comments
Closed

Text nodes with document.implementation.createHTMLDocument #1

aralroca opened this issue Mar 31, 2024 · 6 comments

Comments

@aralroca
Copy link

aralroca commented Mar 31, 2024

I see that in this stream example:

const readable = new ReadableStream({ start: (controller) => {
    controller.enqueue(new TextEncoder().encode('<div>'));
    controller.enqueue(new TextEncoder().encode('text a'));
    controller.enqueue(new TextEncoder().encode("text c"));
    controller.enqueue(new TextEncoder().encode("</div>"));
    controller.close();
  }});

Then the document.implementation.createHTMLDocument with the doc.write is transforming to:

  • After first chunk: <html><head></head><body><div></div></body></html>
  • After second chunk: <html><head></head><body><div>text a</div></body></html>
  • After third chunk: <html><head></head><body><div>text c</div></body></html>
  • After fourth chunk: <html><head></head><body><div></div></body></html>

I did a console.log with doc.documentElement.outerHTML:

const { done, value } = await streamReader.read();
 if (!done) doc.write(decoder.decode(value));
console.log(doc.documentElement.outerHTML)

@dgp1130 I see that it's doing fine for elements, but for text nodes, it does strange things to me. How can I solve this? Thank you!

@aralroca aralroca changed the title question about document.implementation.createHTMLDocument and text nodes Text nodes with document.implementation.createHTMLDocument Mar 31, 2024
@dgp1130
Copy link
Owner

dgp1130 commented Mar 31, 2024

Hi @aralroca, I did a quick test with just the document and it seems to render as I would expect:

const doc = document.implementation.createHTMLDocument();
doc.write('<div>');
console.log(doc.documentElement.outerHTML); // <html><head></head><body><div></div></body></html>
doc.write('text a');
console.log(doc.documentElement.outerHTML); // <html><head></head><body><div>text a</div></body></html>
doc.write('text b');
console.log(doc.documentElement.outerHTML); // <html><head></head><body><div>text atext b</div></body></html>
doc.write('</div>');
console.log(doc.documentElement.outerHTML); // <html><head></head><body><div>text atext b</div></body></html>

I'm not entirely sure if it's related, but you probably want a single TextEncoder instance which encodes each value. I know that's important for TextDecoder because the shunk could be split mid-character, but I'm not sure if that's a real problem for TextEncoder. I tried the same example with your encoding but still got the expected output:

const doc = document.implementation.createHTMLDocument();
const decoder = new TextDecoder();
doc.write(decoder.decode(new TextEncoder().encode('<div>')));
console.log(doc.documentElement.outerHTML); // <html><head></head><body><div></div></body></html>
doc.write(decoder.decode(new TextEncoder().encode('text a')));
console.log(doc.documentElement.outerHTML); // <html><head></head><body><div>text a</div></body></html>
doc.write(decoder.decode(new TextEncoder().encode('text b')));
console.log(doc.documentElement.outerHTML); // <html><head></head><body><div>text atext b</div></body></html>
doc.write(decoder.decode(new TextEncoder().encode('</div>')));
console.log(doc.documentElement.outerHTML); // <html><head></head><body><div>text atext b</div></body></html>

I feel like document.implementation.createHTMLDocument is working reasonably here, so I suspect whatever bug you're seeing is something else going wrong in your logic. If you can find a minimal example with just doc.write which can reproduce that behavior, I'd be very interested to take a look at it.

Also just out of curiosity, how exactly are you trying to use this technique? Are you experiment with HTML fragments for a particular use case? I'm just interested to know what you're hoping to do here.

@aralroca
Copy link
Author

aralroca commented Mar 31, 2024

@dgp1130 I realize that the problem is that is happening only in jsdom, I tried in Chrome and it works fine

Screenshot 2024-03-31 at 21 38 33

I had moved from happy-dom to jsdom to have support for document.implementation.createHTMLDocument but I see it's a bit buggy. I reported now here the issue, but in the meantime do you recommend one to test? thanks a lot!

@aralroca
Copy link
Author

aralroca commented Mar 31, 2024

Also just out of curiosity, how exactly are you trying to use this technique? Are you experiment with HTML fragments for a particular use case? I'm just interested to know what you're hoping to do here.

I don't use fragments, although maybe I could use them, I found out about your article today. I was looking for a solution to a problem, and I saw that you had also been fighting with streaming for something similar.

I implemented a diff DOM algorithm with streaming to solve the server actions in the framework I am developing (transfer HTML), but then I found some bugs and I'm trying to solve them.

However, the test environment works differently than browsers confuses me a lot.

Instead of "fragments" I'm using this implementation: https://github.com/aralroca/parse-html-stream

Do you recommend to change to fragments?

@dgp1130
Copy link
Owner

dgp1130 commented Mar 31, 2024

I had moved from happy-dom to jsdom to have support for document.implementation.createHTMLDocument but I see it's a bit buggy. I reported now here the issue, but in the meantime do you recommend one to test? thanks a lot!

This might be a case where testing with a real browser is useful. Maybe Web Test Runner, Puppeteer, Playwright, Cypress, WebDriverIO might be more appropriate? The e2e testing tools might be a little tricker to use depending on the kind of test you're hoping to write.

Instead of "fragments" I'm using this implementation: https://github.com/aralroca/parse-html-stream

Do you recommend to change to fragments?

I'm not 100% following the source code in your repo, but it seems like you're trying to build something pretty similar to my streamDomFragment function.

"HTML fragment" still doesn't have a great definition, but it's more of an abstract architectural idea based around requesting specific pieces of HTML content after initial page load and then swapping them into the document dynamically. Honestly your repo seems like it's already trying to do that. The web components and declarative shadow DOM stuff in this demo is cool, but not strictly necessary to the general idea of HTML fragments.

@aralroca
Copy link
Author

aralroca commented Apr 1, 2024

Thank you @dgp1130 very much for your help. It's funny because I switched from happy-dom to jsdom because of other bugs with doc.write and now with jsdom I have others. I will take into account what you told me about e2e, it is a good way out. For now I will try to fix the happy-dom bug first. Given the choice I prefer to have good DX with Bun+happy-dom and have the tests pass very fast.

capricorn86/happy-dom#1376

And if it takes too long, I'll look at Playwright.

Feel free to close this issue! 😊 And I'm very happy to know that there are more people solving similar things and that I'm not the only crazy one ! heh

@aralroca
Copy link
Author

aralroca commented Apr 2, 2024

I have a version of the DOM diff algorithm with HTML streaming working 🥳 (~1kb) I will look into making it open-source soon. Thanks!

@aralroca aralroca closed this as completed Apr 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants