hyperHTML: Uncaught DOMException: Failed to execute 'insertBefore' on 'Node'

I think I have a fundamental misunderstanding of how the library actually works so I don’t think this is an issue with the library however below is my code and the error I get, I am not sure why this happens.

import { wire, bind } from "hyperhtml/esm";

window.wire = wire;
window.bind = bind;


var appRoot = document.getElementById("root");
var appRenderer = bind(appRoot);
// appRenderer is a function which takes 1 parameter: template
// I should call appRenderer with a template (``) everytime I want to update the UI.


function UI() {

    var timerInstance = timer(0, notify);
    var timerInstance2 = timer(0, notify);


    function render(){
        return  wire()`${timerInstance.render()}, ${timerInstance2.render()}`;
    };

    function notify(){
        console.log("I am notified!")
        subscriber(render());
    }

    var subscriber;

    return {
        subscribe: function (f) {
            subscriber = f;
        }
        , notify: function(){
            notify();
        }
    }
}

function timer(start, notify){
    var counter = start;
    var template = wire()`hi ${counter}`;

    setInterval(function(){
        counter++;
        template = wire()`hi ${counter}`
        notify();
    }, 2000)   
    
    return {
        render: function(){
            return template;
        }
    };
}

var ui = UI();
ui.subscribe(function (template) {
    appRenderer`${template};`
})
ui.notify();
I am notified!
bundle.js:956 Uncaught DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.
    at domdiff (http://localhost:9000/dist/bundle.js:956:20)
    at Array.anyContent (http://localhost:9000/dist/bundle.js:10491:99)
    at Array.update (http://localhost:9000/dist/bundle.js:855:16)
    at HTMLDivElement.render (http://localhost:9000/dist/bundle.js:828:12)
    at http://localhost:9000/dist/bundle.js:9968:16
    at notify (http://localhost:9000/dist/bundle.js:9934:9)
    at http://localhost:9000/dist/bundle.js:9956:9

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 24 (13 by maintainers)

Most upvoted comments

const render = wire();
render`<p>hi</p>` === render`<p>hi</p>`; // true

// VS

wire()`<p>hi</p>` === wire()`<p>hi</p>`; // false, of course

one is a cached reference to a template literal tag function and another one create a new template literal tag function every single time?

Should I wire by passing the questionId as a reference?

yes, or you’ll trash same question content every time, including its state in terms of DOM tree/render/status

If the reference is unique, then the library can find and update quickly, else it completely re-writes?

you weakly relate some content to some data. that means hyperHTML directly update interpolations of the template. You can see that via debugger.

if you don’t relate content to any data, you create new content every single time.

This is never what you want so either you scope const render = wire() and you use that render to return and update the related dom, you you can wire(data) and use it to update data.

Have a look at this little explanation of how things work here: https://gist.github.com/WebReflection/d3aad260ac5007344a0731e797c8b1a4

bind or wire doesn’t matter, wire has no parentNode, bind is the parentNode

every time you wire() without passing a reference you lose the previous content and create new content from the scratch

wire() creates a new DOM element/structure every single time

you are also using zero DOM in the whole example … there’s nothing that actually needs a DOM node.

window.wire = wire;
window.bind = bind;

if you use modules you don’t need to export to the window anything, but I guess this was for testing purpose.

If you change the template value every single time, you are passing a new node. This is not really the way to go. If you want dynamic content you should use an array, so it can grow, reset, or change in the future.

var ui = UI();
ui.subscribe(function (template) {
    appRenderer`${[template]};`
})
ui.notify();

but that’s not even it … you are trashing every time everything instead of updating the only thing you care about: the number:

var appRenderer = bind(appRoot);

function UI() {

    var timerInstance = timer(0, notify);
    var timerInstance2 = timer(0, notify);


    function render(){
        return  wire(self)`${timerInstance.render()}, ${timerInstance2.render()}`;
    };

    function notify(){
        console.log("I am notified!")
        subscriber(render());
    }

    var subscriber;
    var self = {
        subscribe: function (f) {
            subscriber = f;
        }
        , notify: function(){
            notify();
        }
    };

    return self;
}

function timer(counter, notify){
    setInterval(function(){
        counter++;
        notify();
    }, 2000);

    return {
        render: function(){
            return wire(this)`hi ${counter}`;
        }
    };
}

var ui = UI();
ui.subscribe(function (template) {
    appRenderer`${template};`
})
ui.notify();

noe everything is fine because you update some DOM weakly referenced instead of trashing layouts and trash in new layout per each update.

Basically lit-html does the weakly reference internally but it cannot render anywhere else outside its place in the template literal while hyperHTML let you wire any reference and reuse its content updating only what’s needed every single time.

You might also have found a weird edge case with all these new wires and I might have a look if something went wrong but for sure what you were doing was definitively not the way to use this library.

the quick summary:

var counter = 0;
function upate() {
  bind(element)`<div>I'm the element content with a ${
    wire(element || anyObject)`<strong>strong element in it ${counter++}</strong>`
  }</div>`;
}
setInterval(update, 1000);

You can always pre-address bound elements or wired objects as render and use them whenever it’s convenient.

Be sure you read all examples to better understand the logic.