WebReflection / uhtml

A micro HTML/SVG render

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bug? Rendering as dom element broken for 4.4.1, not 4.3.5

greghuc opened this issue · comments

Quick bug report, discovered on upgrading project from uhtml 4.3.5 to 4.4.1:

var htmlAsNode = require('uhtml/node').html;
var cssText = "p { font-weight: bold; }";
var result = htmlAsNode`<style>${cssText}</style>`;
console.log(result);

uhtml 4.3.5
=> <style>p { font-weight: bold; }</style>
=> is working style element

uhtml 4.4.1
=> <style><!--isµ0--></style>
=> is not working style element

Uhm … the node export might need a render indeed … curious to know why you use it but my bad, I haven’t tested that but it’s easy to solve. Apologies for any inconvenience

P.S. even more curious to know if you ever used fragments as node as the persistent fragment is the only option I have there and I’m worried in node export might not always survive.

"the node export might need a render indeed … curious to know why you use it"
=> I'm using uhtml for different projects. Sometimes I need to create a one-off dom tree, so I use "'uhtml/node".html. In the example above, I'm creating a one-off style element from the cssText variable, as I've calculated a sha256 hash from cssText, for use as a style-src hash in a csp policy.

"curious to know if you ever used fragments as node as the persistent fragment"
=> I'm not familiar with fragments. If you have the time, could you please explain, or provide an example (maybe for the bug report above?)

Thanks

Basically, most of the time, I use uhtml as the render part of my own tiny model-view-controller framework. I use uhtml to render and diff-update a view when the underlying model changes. However, I use node export for one-off situations where I want a dom tree, and no clever diff-updates will every be done to the generated tree. Ideally uhtml would cover both use-cases, which it does right now.

Fixed. There is a bump in size but at least it's 100% compatible with regular templates. All you skip in uhtml/node is the cache so that everything else stays the same ... full unroll capability with arrays, fragments, and so on.

Fragments are persistent in here but you need to use the render or always remember to use ref.valueOf() before appending these.

// single span
const span = html`<span />`;
document.body.replaceChildren(span);
// <body><span></span></body>

// persistent fragment with multiple spans
const spans = html`<span /><span /><span />`;
document.body.replaceChildren(spans.valueOf());
// <body><span></span><span></span><span></span></body>

Once you use valueOf() which is a no-op for any other kind of node so it's always safe to invoke, you can reference that fragment as it is and re-append later on anywhere you like in case it gets lost/removed.

@WebReflection thanks for the quick fix. The buggy code is now working. Based on what you said about .valueOf, I now have:

var htmlNode = require('uhtml/node').html;

function htmlAsNode(...args) {
  var result = htmlNode(...args);
  return result.valueOf();
}

for one off operations where you never want to reuse that node you don't need to do anything different ... but in case you ever want to reuse that node you can just:

const htmlAsReusableNode = (...args) => htmlNode(...args).valueOf();

What do you mean by "reusable node"?

I was assuming that the returned value of htmlNode(...args) is just a vanilla dom-element tree (say result1). And if I called htmlNode(...args) again with the same values, I would get a brand-new vanilla dom-element tree (say result2). And:

  • result1 and result2 are completely different dom-element trees..
  • but look identical

by reusable I mean you can re-append it in the future.

// this always works as unique node
const div = html`<div />`;

// this works only the first time if no valueOf is used
const fragment = html`a<button />c`;

document.body.append(div, fragment);

setTimeout(() => {
  // this fails, unless you use valueOf
  div.append(fragment);
});

If that fragment would have been created like this instead:

const fragment = html`a<button />c`.valueOf();

// you can now re-move or re-append that reference
// and all its nodes will move together so previous case
// just works

Two calls to the same template always produce two different nodes unless you use the uhtml/keyed and both the reference and the key are the same ... none of your business here though, it's all good, just watch out fragments, if you ever need those.

@greghuc you know what? this was dumb (from my side) ... latest just does that for you so you don't need to worry about anything.

@WebReflection cool. So from latest code, it looks like .valueOf doesn't need called on result of html function any more, as it's called internally as part of tag function?: https://github.com/WebReflection/uhtml/blob/main/esm/node.js#L8

So my original code should work without any changes..

if you are on latest, yes!