An easier way to manage HTML DOM using Javascript

Why you should use HTML Code generation methods as a last resort

Training

We hire freshers and train them in-house here at Freshbits. A lot of our own learning happens when we teach basic stuff to them.

Out of that, one of the random basic stuff that I would like to share today is about HTML code generation using Javascript.

Examples

We get the beginners to add some interactivity to the HTML pages that they have built using Javascript. It could be:

  • Add to cart functionality on the e-commerce page
  • Delete item functionality on the products listing page
  • Add address functionality on the checkout page, etc.

Javascript frameworks like React, Vue, or Angular are not in their syllabus yet. They use jQuery or vanilla JS for these kinds of developments.

bugs-bunny-race.gif

With the fresh opportunity on hand and limited working experience, they are generally excited to go for it and come up with something like the following.

The go-to old way ๐Ÿ’ฉ

I will take the code done by our newest team member, Utsav, as an example. He was given the task of coding the add to cart functionality. As we expected, he coded the functionality this way:

function displayCart() {
    // Abbreviating other stuff to focus on the core topic...

    for (let i = 0; i < cart.length; i++) {
        var imageTag = document.createElement('img');
        imageTag.src = cart[i].img;
        imageTag.setAttribute('class', 'w-10 h-10 object-cover rounded-md');
        document.getElementById('cart-item-' + i).appendChild(imageTag);

        var nameTag = document.createElement('span');
        nameTag.setAttribute('class', 'ml-4 font-semibold text-sm');
        nameTag.innerHTML = cart[i].name;
        document.getElementById('cart-item-' + id).appendChild(nameTag);

        var priceTag = document.createElement('div');
        priceTag.innerHTML = cart[i].price;
        document.getElementById('cart-item-' + id).appendChild(priceTag);

        var deleteTag = document.createElement('button');
        deleteTag.setAttribute('class', 'px-3 py-1 rounded-md bg-red-300 text-white');
        deleteTag.setAttribute('onclick', 'removeFromCart(' + cart[i].id + ')');
        deleteTag.innerHTML = "x";
        document.getElementById('cart-item-' + id).appendChild(deleteTag);

        // ...
    }
}

There is nothing wrong with this approach. It is just less readable and maintainable. What if we want to add more elements to this cart? What if we wish to change styles?

Some might argue jQuery can make it better. While those long lines of code would be shorter, the main issue still stays there: managing changes in the final HTML output

Reading and modifying the HTML directly is easier. Let's see how.

Another way ๐Ÿ™…

We can rewrite the same example as the following:

function displayCart() {
    // Abbreviating other stuff to focus on the core topic...

    for (let i = 0; i < cart.length; i++) {
        var cartItemHtml = [
            '<div id="cart-item-' + i + '">',
                '<img class="w-10 h-10 object-cover rounded-md" src="' + cart[i].img + '" />',
                '<span class="ml-4 font-semibold text-sm">' + cart[i].name + '</span>',
                '<div>' + cart[i].price + '</div>',
                '<button class="px-3 py-1 rounded-md bg-red-300 text-white" onclick="removeFromCart(' + cart[i].id + ')">x</button>',
            '</div>'
        ].join("\n");

        // ...
    }
}

There is much better readability now. We can see what the final HTML is gonna look like. But, the HTML code is merged into Javascript so we have to play with those quotes a lot. One step further is to make the HTML code independent.

Even better way ๐Ÿค—

There is a template HTML tag that is not shown on the page by default. We can use it to host our HTML code like:

<template id="cart-item">
    <div>
        <img class="w-10 h-10 object-cover rounded-md cart-item-image" />
        <span class="ml-4 font-semibold text-sm cart-item-name"></span>
        <div class="cart-item-price"></div>
        <button class="px-3 py-1 rounded-md bg-red-300 text-white cart-item-delete">x</button>
    </div>
</template>

Looks like the code we can easily maintain, right? Now how do we use it in the Javascript you asked?

function displayCart() {
    // Abbreviating other stuff to focus on the core topic...

    var template = document.getElementById('cart-item');

    for (let i = 0; i < cart.length; i++) {
        var cartItemTag = template.content.cloneNode(true);

        cartItemTag.querySelector("img.cart-item-image").src = cart[i].img; 
        cartItemTag.querySelector("span.cart-item-name").innerHTML = cart[i].name;
        cartItemTag.querySelector("div.cart-item-price").innerHTML = cart[i].price;
        cartItemTag.querySelector("button.cart-item-delete").onclick = function() { removeFromCart(cart[i].id) };
    }
}

This way, our Javascript can directly target each element inside the template and set the attributes and content respectively.

Is there any way to take this one step further? If you have any ideas, please do share.

Wind up

This learning is mainly for beginners when they are getting started with HTML and Javascript. As they progress, there are more options like using templating engines or going with full-fledged JS frameworks where you manage the data and the framework takes care of the DOM updates via reactivity.

That's it. If you have some JS code generating HTML, try the template tag today and let us know how it goes. See you on the flip side.

ย