WebReflection / uhtml

A micro HTML/SVG render

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Exception thrown when rendering a <template> element with placeholder content

rzane opened this issue · comments

A <template> tag with a placeholder throws an exception. To reproduce, open the following document in the browser:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <script type="module">
    import {html, render} from 'https://unpkg.com/uhtml@4.4.9';

    const hello = 'world';
    render(document.body, html`<template>${hello}</template>`);
  </script>
</head>

<body>
</body>

</html>

You'll see the following exception in the console:

keyed.js:1 Uncaught TypeError: Cannot read properties of null (reading 'nodeType')
    at _ (keyed.js:1:4826)
    at keyed.js:1:5389
    at keyed.js:1:3521
    at U (keyed.js:1:5482)
    at Y.toDOM (keyed.js:1:5973)
    at keyed.js:2:220
    at se (keyed.js:2:285)
    at index.html:12:5
_ @ keyed.js:1
(anonymous) @ keyed.js:1
(anonymous) @ keyed.js:1
U @ keyed.js:1
toDOM @ keyed.js:1
(anonymous) @ keyed.js:2
se @ keyed.js:2
(anonymous) @ index.html:12

what's the use case, if I might ask? I am pretty sure as soon as you have an element around you won't have issues, right? like a <div> or something else ... thanks

If I replace <template> with <div>, it works just fine.

I'm creating a Storybook to demonstrate the use of some existing Stimulus components that rely on <template> tags. Storybook takes a render function that is expected to return an HTMLElement or a string. Strings aren't cutting it, and imperatively creating HTML elements is a chore. I figured uhtml/node could help me with that task.

if uhtml/node solves this specific case (although I am not super sure why it would) I think we can close this.

My other suggestion is uhtml/dom which gives you SSR capabilities ... after thinking about this, I also have other concerns ... allow me to expand on that ...

The <template> tag itself is usually good already live or to create any contextual element as uhtml uses it already.

However, in uhtml case it's used to map the static parts and relate its interpolations, then to clone the node if reused (JIT cache).

If you use it to create templates themselves and you have, as example, listeners attached, those nodes are not live or meaningless and you can't clone those listeners neither so it's a slippery slope to further issues.

As summary, could you please try node or dom variant and see if you are good with those? Thank you!

P.S. the node variant might work because you don't lose listeners as you don't clone internals via cached templates so indeed there is a huge difference there and it might be what you're looking for ... having it by default in uhtml is a bit of a YAGNI slowdown for all other 99.9% use cases and dedicated exports are indeed there to help with that ... I hope you understand my reasoning here, thanks.

if uhtml/node solves this specific case (although I am not super sure why it would) I think we can close this.

The exact same exception is thrown when using uhtml/node:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <script type="module">
    import {html, render} from 'https://unpkg.com/uhtml@4.4.9/node';

    const hello = 'world';
    html`<template>${hello}</template>`;
  </script>
</head>

<body>
</body>

</html>

could you try uhtml/dom instead, if it's strings you are after?

<!DOCTYPE html>
<script type="module">
import { Document } from 'https://esm.run/uhtml/dom';
import init from 'https://esm.run/uhtml/init';

const { render, html } = init(new Document);

render(document.body, html`<template>${'hello'}</template>`);
</script>

That returns a string so you see it as text and if I understood you correctly you want to render your examples as text ... if that's not the case you can still:

// ... previous code ...
render(document.body, html`<template>${'hello'}</template>`);
// ... and then ...
document.body.innerHTML = document.body.textContent;

Replace document.body with any id or target node and you should be good? 🤔

I am really skeptical to slow down everything to parse every single node and diverge logic for template tags around, specially because, like I've said, on the client side this is easily misleading as listeners won't work and stuff will break ... I'd rather state that template in tags on the client are not supported and that there are other ways to shoot yourself in your foot if you need to.

uhtml/dom isn't useful to me because I'm hoping to produce HTML elements. I was hoping to use uhtml/node so that I could attach event listeners. I wasn't expecting to be able to attach event listeners to elements within the template tag.

If this isn't something you want to support, I understand. It sounds like there are trade-offs to supporting this feature. We can close this issue and I can find another package that allows me to generate HTML that includes template tags.

I wasn't expecting to be able to attach event listeners to elements within the template tag.

I can't prevent people from shooting their foot so I rather not offer anything special around the template element and explain that's a bad idea because of listeners and other things.

Templates are "ghost nodes" on the DOM and should be handled as such .. having these as elements doesn't give you much and, most importantly, having these elements referencing their fragment content out of the box will lead to confusion and issues I am afraid I won't have time to address (and I probably don't want to neither as it's a slippery slope to chaos to me).

I am closing this but I will point at this if this request ever comes up again, thanks for your understanding and your patience.