Hacker News new | ask | show | jobs
by mbivert 698 days ago
Perhaps this can help to fix the ideas: here's how you could create the HTML by manipulating the DOM (the code indentation mimics the HTML indentation):

   let p = document.createElement("span");
  
    let b = document.createElement("button");
    b.innerText = btnText;
    if (btnClass) b.classList.add(btnClass);
  
    let m = document.createElement("span");
    m.style.display = "none";
    m.classList.add(modalClass);
  
     let q = document.createElement("span");
     q.classList.add(modalContentClass);
  
      let c = document.createElement("button");
      c.innerText = "×";
      c.classList.add(modalBtnCloseClass);
  
      // r is an "external" HTMLElement
      r.classList.add(modalContentClass);
  
     q.append(c, r);
  
    m.appendChild(q);
  
   p.append(b, m);
It's not that different from writing the HTML (with or without a template). But IMO the great thing here is that, at the end of that chunk of code, you've got pointers to every node of interest. You can then register handlers, target arbitrary nodes in such handlers, without having to navigate through the HTML (e.g. by setting ``id`` or other attributes to help identify them, or by relying on a static HTML structure).

And as the parent points, this should prevent some amount of unexpected HTML injection.

There's still a separation between the view and the page structure.

1 comments

I know how to manipulate DOM. You are not invalidating my point. This way involves a lot more mental overhead any time you need to define a structure for a page, compared to a declarative way of writing HTML as a string and delegating DOM manipulation to separate imperative logic (or indeed browser’s native parser).
> This way involves a lot more mental overhead any time you need to define a structure for a page,

How so?

The "imperative logic" of the previous example is parametrized by a few node pointers. Assuming I understand your approach correctly, your separate imperative logic would still need to be parametrized by some node pointers as well.

Except because you wouldn't have direct access to them, you'd need to use class names or IDs to retrieve them. Hence you would need an extra layer to join the two parts that you've separated, which in my opinion is more mental overhead: the management of those attribute comes at a cost (e.g. naming conflict, more class names to remember, confusion between class names).

Perhaps a more concrete example (bits of code) of your approach might help make your point clearer.

Why do you assume there is a requirement to access nodes directly, and why would that be a typical requirement?

(There are ways to achieve that more declaratively than by building the entire DOM imperatively, JSX and React’s refs is one approach, but I wonder why you assume direct access is always needed in the first place.)

> Why do you assume there is a requirement to access nodes directly, and why would that be a typical requirement?

I haven't made a case for it to be a requirement, but rather for direct access to be 1) trivial to achieve 2) more convenient than all of the (Vanilla JS) alternatives that I know of (attributes-based solutions).

> JSX and React’s refs

From a quick glance, I'm not sure this brings anything more than keeping a handful of node pointers around. But it should, at least in some ways, be better that attribute-based solutions.

Well, I have outlined why declarativity is good, so as far as I’m concerned the choice to go imperative and abandon the benefits of declarativity in order to make access to nodes trivial should probably be accompanied by understanding of why that access would be needed in the first place.
To perform any action on the DOM, you need access to the nodes: registering handlers, manually triggering events, setting/getting values, rebuilding them, etc.

I'd encourage you to toy with a concrete example, in case you (or I) are missing a subtle point; the previous piece of code is the "view" for a modal "component" which hosts an external HTMLElement (r). See how you'd implement it with both approaches.

With both, the "view" and the logic are still separated. The view is merely encoded differently.