Search code examples
hyperhtml

Using hyperHTML, is there a way to bind directly to an element to be modified rather than its parent?


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.


Solution

  • 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:

    1. innerHTML trashes every time every node and create everything from the scratch, while hyperHTML relate always see nodes to a specific context
    2. innerHTML 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 later
    3. innerHTML 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.