mptf
mptf

Reputation: 61

Generating dynamic HTML elements using Javascript alternatives?

I am building a web application using pure JS without any frameworks. The application is kind of like a dashboard type application that will pull data from an API and display it using HTML. I would like to create reusable HTML components in JS that I can easily maintain and use throughout my application.

My question is if there is a better way to generate dynamic HTML elements in JS?

The following code below is an explanation of my current way to generating dynamic elements.

Currently my set up is to have an HTML div tag with an id. This tag is where I would target in my JS and append dynamically generated elements to it.

<div id="user-anchor"></div>

Target this id and append elements to it with drawUserInformation() function. This function takes an array of objects, creates the dynamic elements, and appends them to the anchor div.

/*
* @param {array} users - array of user objects
*/
function drawUserInformation(users) {
    let userAnchor = document.getElementById("user-anchor");
    for (let user of users) {
        let userCard = createUserCard(user);
        userAnchor.append(userCard);
    }
}

To create the dynamic HTML elements, the createUserCard() function is called. This function takes in an object that represents a user and returns an Element object. I have simplified the amount of data shown for this example.

function createUserCard(user) {
    let userCardEle = document.createElement("div");

    userCardEle.innerHTML = `
            <h5 class="card-title">User</h5>
            <dl class="row">
                <dt class="col-sm-3">Name</dt>
                <dd class="col-sm-9">${user.first_name} ${user.last_name}</dd">
                <dt class="col-sm-3">Score</dt>
                <dd class="col-sm-9">${user.score}</dd">
            </dl>
    `;
    return userCardEle;
}

In my createUserCard() function, is there a better way to generate the HTML for this component without using innerHTML? In my current application, I have multiple of these types of functions for various components and they all user innerHTML. The strings that I write are very long (much longer than this example) and don't have any HTML linting, making it much harder to maintain and debug.

Upvotes: 1

Views: 1974

Answers (2)

trincot
trincot

Reputation: 350147

Another approach could be to ease the DOM access with a function that allows jQuery-like nested element creation:

// Generic DOM element creator
function Elem(type, attr, ...children) {
    let elem = Object.assign(document.createElement(type), attr);
    for (let child of children) {
        elem.appendChild(Object(child) === child 
                         ? child
                         : document.createTextNode(child));
    }
    return elem;
}

function drawUserInformation(users) {
    let userAnchor = document.getElementById("user-anchor");
    for (let user of users) {
        let userCard = createUserCard(user);
        userAnchor.append(userCard);
    }
}

function createUserCard(user) {
    return Elem("div", {},
        Elem("h5", { className: "card-title" }, "User"),
        Elem("dl", { className: "row" }, 
            Elem("dt", { className: "col-sm-3" }, "Name"),
            Elem("dd", { className: "col-sm-9" }, user.first_name + " " + user.last_name),
            Elem("dt", { className: "col-sm-3" }, "Score"),
            Elem("dd", { className: "col-sm-9" }, user.score),
        )
    );
}

let data = [
    { first_name: "Nafi", last_name: "Thiam", score: 6791 },
    { first_name: "Mutaz Essa", last_name: "Barshim", score: 237 },
    { first_name: "Gianmarco", last_name: "Tamberi", score: 237 },
]

drawUserInformation(data);
<div id="user-anchor"></div>

Upvotes: 1

trinalbadger587
trinalbadger587

Reputation: 2109

You can use the template element.

To create an instance of the template element, use tmplElem.content.cloneNode(true).

function createUserCard(user) {
    let userCardEle = userCard.content.cloneNode(true);
    let elems = byName(userCardEle);
    elems.first_name.innerText = user.first_name;
    elems.last_name.innerText = user.last_name;
    elems.score.innerText = user.score;
    return userCardEle;
}

function byName (elem)
{
    return new Proxy({}, {
        get (_, prop) { return elem.querySelector(`[name=${prop}]`); }
    });
}

// Test

document.body.appendChild(createUserCard({first_name: "John", last_name: "Smith", score: 1e+5}));
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">

<template id=userCard>
    <div>
        <h5 class="card-title">User</h5>
        <dl class="row">
            <dt class="col-sm-3">Name</dt>
            <dd class="col-sm-9">
                <span name=first_name></span>
                <span name=last_name></span>
            </dd>
            <dt class="col-sm-3">Score</dt>
            <dd class="col-sm-9"><span name=score></span></dd>
        </dl>
    </div>
</template>

Upvotes: 2

Related Questions