h2o – The HTML Molecule

A JavaScript utility that generates a DOM tree and an object with references to flagged elements by parsing HTML string. This utility eases the process of acquiring reference to elements when inserting HTML into a DOM tree and makes the code cleaner.

html = '<div>' +
           '<span ref="mySpan">some text</span>' +
           '<ul>' +
               '<li ref="listItems[]">list item 1</li>' +
               '<li ref="listItems[]">list item 2</li>' +
           '</ul>' +
       '</div>';
documentFragment = h2o(html, refObj = {});
someElement.appendChild(documentFragment);

refObj.mySpan.innerHTML === "some text"; // true

  Download on Github

Intro

Sometimes, while working on web apps, I find myself writing a lot of code to get element references. Mostly, after inserting some HTML content using innerHTML method:

var html = '<div><span id="span1">Hello</span> <span id="span2">World</span></div>';
someElement.innerHTML = html;
 
var span1 = document.getElementById("span1"),
    span2 = document.getElementById("span2");

As shown in the example above, after inserting HTML into DOM the first thing that is usually done after is getting element references for later manipulation using one of the standard DOM methods (e.g.: getElementById(),
getElementsByClassName(), querySelectorAll(), etc.).

Often, the inserted HTML is more complicated than shown in previous example. Thus, getting lots of element references from the inserted HTML may require writing many lines of similar code. Of course there is another way to achieve same results. And that would be creating elements using standard DOM methods while keeping references of created elements:

var div = document.createElement("div"),
    span1 = document.createElement("span"),
    span2 = document.createElement("span");
 
span1.appendChild(document.createTextNode("Hello"));
span2.appendChild(document.createTextNode("World"));
 
div.appendChild(span1);
div.appendChild(document.createTextNode(" "));
div.appendChild(span2);
 
someElement.appendChild(div);

In my opinion, this way is even worse – it has more code lines, its HTML structure is less readable and it has decreased performance over previous example.

Obviously, the preferred solution would be continue using HTML strings for content insertion but simultaneously creating element references for desirable elements.

h2o way

In short, h2o is an HTML parser that receives an HTML string and generates two objects. The first object, returned by h2o function, is a DocumentFragment holding the DOM of parsed HTML, which could be appended to any other element. And the second object, passed by reference to h2o function, is an object holding reference to elements that have been marked to be referenced. This way you get both readable HTML structure and element references with less lines of code. Yet, the performance is the trade off you might pay for these advantages.

Continuing last example, the new code for generating same results would be:

var html = '<div><span ref="span1">Hello</span> <span ref="span2">World</span></div>';
var docFrag = h2o(html, refObj = {});
someElement.appendChild(docFrag);

Here, references to <span> elements can be accessed from refObj using property names specified in “ref” attributes of referenced elements. In the example above refObj will be augmented in the following way:

{
    "span1": #reference-to-first-span#,
    "span2": #reference-to-second-span#
}

h2o’s prototype

documentFragment = function h2o(htmlString, refObject, options);

Parameters:

  1. htmlString - HTML string to parse.
  2. [refObject] - Optional object for augmentation with references of elements having special “ref” attribute.
  3. [options] - Optional object with additional options.
    • [options.refAttributeName="ref"] - A string specifying the name of a special “ref-path” attribute specifying how to store element references within the refObject. Default value is “ref”.
    • [options.setRefAttribute=false] - Boolean flag indicating whether to include “ref-path” attributes in the returned DOM. Default value is “false” indicating not to include “ref-path” attributes.
    • [options.elementWrapper] - Wrapper function receiving an element node and returning an object which will be used for augmenting refObject instead of the original element node.

Return:

DocumentFragment parsed from passed HTML string.

Visit h2o’s Github page to get its source and its full documentation.

ref-paths

Last example introduced the most basic usage of h2o. But it has more advanced features one of which are ref-paths. Ref-paths utilize dot notations to generate multi-level object-graphs, including generation of arrays. For example you can create array of element references when working with HTML lists:

var html, i, docFrag, refObj;
html = '<ul>';
for (i = 0; i < 5; i++) {
    html += '<li>' +
                '<span ref="listItem[+].span">lorem ipsum</span>' +
                '<a href="#" ref="listItem[].link">link text</a>' + 
            '</li>';
}
html += '</ul>';
docFrag = h2o(html, refObj = {});

In this example, the generated refObj will look like this:

[
    {
        "span": #reference-to-span#,
        "link": #reference-to-link#
    }, {
        "span": #reference-to-span#,
        "link": #reference-to-link#
    },
    ...
]

Take a look at the Github page for more information on ref-paths.

  h2o on Github


comments powered by Disqus