Assuming I have the following:
<div id="parent">
<div id="child-one"><p>Child One</p></div>
<div id="child-two"><p>Child Two</p></div>
<div id="child-three"><p>Child Three</p></div>
</div>
As I understand it, hyperHTML sets the innerHTML of the the element that you bind to. If I want to not only change the innerHTML of #child-two but also change its attributes without touching #child-one and #child-three, what is the best way to accomplish this using hyperHTML?
Is there a way to bind directly to an element to be modified rather than its parent?
I know I could use wire to create a new element and then completely replace the existing element, but then I would not have the ability to continue to bind and update only what has changed.
There are few misconceptions here, and I'll try to address all of them.
As I understand it, hyperHTML sets the innerHTML ...
hyperHTML is not innerHMTL
People see template literals, they instinctively assume it's innerHTML
, but hyperHTML
is everything but innerHTML
.
The name is similar on purpose, but the implementation has nothing to do with strings, strings are just the declarative facade used to map "all the things" and glue the logic together.
Differences between the two:
innerHTML
trashes every time every node and create everything from the scratch, while hyperHTML
relate always see nodes to a specific contextinnerHTML
always requires a parent element to be injected, hyperHTML
has the wire
which is also what you are looking for, but I'll explain this laterinnerHTML
let you define broken layouts, hyperHTML
will throw up if you do something wrong and expect specific semantics (i.e. no partial attributes shenanigans)Apologies if you knew this already, but I think it's mandatory to clarify the very root concept behind hyperHTML
.
Now, let's move on :-)
Is there a way to bind directly to an element to be modified rather than its parent?
Yes, the wire.
I know I could use wire to create a new element and then completely replace the existing element, but then I would not have the ability to continue to bind and update only what has changed.
No. Wires have ids, so that each ID will always return the exact same node.
If you want to wire #child-two
to its parent, but you could also relate it globally, if needed, you can simply wire it through an id.
const {bind, wire} = hyperHTML;
const outer = bind(document.body);
const inner = wire(document.body, ':child-two');
// you could wire inline but if you want
// to reuse the same node anywhere
// you need a callback to be sure the template literal
// is unique and not a different one each time (latest ES specs)
const child2 = model => inner`
<div id="child-two" onclick=${model.onclick}>
<p>${model.text}</p>
</div>`;
// you can update the parent node every time you want
const update = model => {
outer`
<div id="parent" onclick=${model.onclick}>
<div id="child-one"><p>Child One</p></div>
${
// and pass along any DOM node or wire you want
child2(model)
}
<div id="child-three"><p>Child Three</p></div>
</div>`;
};
update({
onclick(event) {
// will log "parent" or "child-two"
event.stopPropagation();
console.log(event.currentTarget.id);
},
text: 'This is Child 2'
});
You can test above code live in code pen.