DEV Community

Discussion on: Post an Elegant Code Snippet You Love.

Collapse
 
efpage profile image
Eckehard • Edited

I like the way you handle the props in your function, I just would prefer to immediately add some content to the new element. I slightly extended your function to add some content (a text, a child node or an array of child nodes)

function createElement(nodeName, content , props = {}) {
    const { style = {}, ...propsNoStyle } = props;
    const element = Object.assign(document.createElement(nodeName), propsNoStyle);
    Object.entries(style).forEach(([key, value]) => { element.style[key] = value; });

    // append child or array of childs
    function append(cl) { 
        if (typeof (cl) === 'string') cl = document.createTextNode(cl)
        element.appendChild(cl)
    }
    if (Array.isArray(content)) content.map(cl => append(cl))
    else append(content)

    return element;
}
Enter fullscreen mode Exit fullscreen mode

There is a really tricky pattern to build "creator" functions for DOM elements based on your function (thanks to Tao from VanJS) :

makefnc = function (typ) {
    return (...args) =>
        createElement(typ, ...args)
}

tags = new Proxy({}, { 
    get: (tag, name) => {
        return makefnc(name)
    }
})
Enter fullscreen mode Exit fullscreen mode

The code above could be written more compact, but this makes it really hard to understand. makefnc returns a function that creates a special type of element. You can use it that way:

const h1 = makefnc("h1")
let el = h1("This is a headline", {style: {color: "red"}})
Enter fullscreen mode Exit fullscreen mode

Using the tags function is even more elegant. Below, I show a little code example:

const { div, h1, button } = tags // create tag functions for div, h1 and button

let h, b, count = 0
h = h1(`${count}`, { style: { color: "red" } });
(b = button("CLICK")).onclick = () => h.innerText = ++count

document.body.append(div([h, b]))
Enter fullscreen mode Exit fullscreen mode

There is an even more compact approach on the same principle presented here, that adds an auto-append feature, so you do not even need to append the elements manually.

Collapse
 
nickytonline profile image
Nick Taylor

I didn’t bother with the content as you can just add a prop textContent or similar, but glad to see you’re extending it!

Thread Thread
 
efpage profile image
Eckehard

Sure, in that case it's just a question of convenience. But you can also build nested elements, so it´s a way to compose your page or parts of it :

        let d, h, b, count = 0
        d = div([
            h = h1(`${count}`, { style: { color: "red" } }),
            b = button("CLICK")
        ])
        b.onclick = () => h.innerText = ++count
        document.body.append(d)
Enter fullscreen mode Exit fullscreen mode

By the way, CSS styles use a syntax, which is a bit different from JSON:

<p style="color:blue;font-size:46px;">
Enter fullscreen mode Exit fullscreen mode

With your approach, people need to translate the style definition like this:

 createElement("p",{style: {color:"blue";font-size:"46px;"})
Enter fullscreen mode Exit fullscreen mode

As you need to process the styles anyway, would it not be better to use the usual syntax?

 createElement("p",{style: "color:blue;font-size:46px;")
Enter fullscreen mode Exit fullscreen mode

If you do not want to process the CSS manually, you can use this function:

function setStyle(el, css){
  el.setAttribute('style', el.getAttribute('style') + ';' + css);
}
Enter fullscreen mode Exit fullscreen mode