funtags
TypeScript icon, indicating that this package has built-in type declarations

1.0.7 • Public • Published

<FunTags :)> - Functional Tags

Create DOM trees from plain vanilla javascript functions. No transpilation nor alien languages. Just a little bit of magic with Proxy Objects.

How to Use

Option 1 - From JSDeliver

<script src="https://cdn.jsdelivr.net/gh/renato-mauro/funtags/dist/main.min.js"></script>

Option 2 - From Repository

<script src="dist/main.min.js"></script>

Option 3 - From NPM

npm install funtags

Option 4 - ObservableHQ

You can play (and use) FunTags in ObservableHQ. If you want to import in your own notebook, just import and have fun!

import { ft } from "@rmauro/funtags"

Option 5 - Copy and Paste entire library in your code

const ft = (function(){
    const HTML_NS = "http://www.w3.org/1999/xhtml";
    const SVG_NS = "http://www.w3.org/2000/svg";
    function tagFactory(NS,tagName) {
        return function(...args) {
            let element = document.createElementNS(NS,tagName);
            function flatten(e) {
                if(e !== null && e !== "") {
                    if(e.forEach) {
                        e.forEach(flatten);
                    } else {
                        if (e instanceof Node) {
                            element.append(e);
                        } else if(e instanceof Object) {
                            for(let k in e) {
                                let v = e[k];
                                if(v !== null) {
                                    let evname = (/^on(.+)$/.exec(k)||[])[1];
                                    if(evname) {
                                        element.addEventListener(evname,v);
                                    } else if(k === 'style') {
                                        for(let ks in v) {
                                            element.style[ks] = v[ks];
                                        }
                                    } else {
                                        element.setAttribute(k,v);
                                    }
                                }
                            }
                        } else {
                            element.append(document.createTextNode(e));
                            element.normalize();
                        }
                    }
                }
            }
            flatten(args);        
            return element;
        }
    }
    return {
        html: new Proxy({}, {get:(target,name)=>(name in target)?target[name]:tagFactory(HTML_NS,name)}),
        svg: new Proxy({}, {get:(target,name)=>(name in target)?target[name]:tagFactory(SVG_NS,name)})
    }
})();

How it Works (Hello World!)

function sayHello() {
    /* "desconstruct" from ft.html tags used by this template */
    const { div, h1, p } = ft.html;

    /* create dom hierarchy by composing function call */
    return div(
        h1("Hello World"),
        p("Lorem ipsum dolor sit amet, consectetur adipiscing elit."),
        p("Donec a nunc mi."),
        p("Vestibulum at felis tempus, suscipit nisi in, semper nisl.")
    );
}

Here we have a function to generate our DOM fragment. As we are going to use tags like div, h1, and p, we have to desconstruct them from the magic object ft.html. They are functions that create DOM elements with the same variable name.

The object ft.html is indeed a Proxy object. Any X property that is requested will be returned as a factory for creating an element of type X, regardless of whether X is a known element type or a custom element.

The value returned from tag factories is a regular DOM tree so that they can be inserted into documents as usual. In this example, we replaced the content of body element with replaceChildren method.

document.body.replaceChildren(sayHello());

See live code in code pen.

More Examples

List of Objects

function fruitList(fruits) {
    /* "desconstruct" from ftags.html tags used by this template */
    const { div, h1, ul, li } = ft.html;

    /* create dom hierarchy by composing function call */
    return div(
        h1("Fruit List"),
        ul(
          ...fruits.map(fruit => li(fruit))
        )
    );
}

document.body.replaceChildren(fruitList(["apple","orange","lemon"]));

In this example we are using array map method with spread operator (...). Each element in array is passed as argument to function.

See live code in code pen.

Attributes and Events

function nameList() {
    const { div, h1, ul, li, input, button } = ft.html;
    let names = [];

    function addName() {
      names.push(nameInput.value);
      nameList.replaceChildren(...names.map(name=>li(name)));
      nameInput.value = "";
      nameInput.focus();
    }
  
    let nameList = ul();
    let nameInput = input({type:"text",placeholder:"new name"});
    let addButton = button({onclick:addName},"add");
  
    return div(
        h1("Name List"),
        div(nameInput, addButton),
        div(nameList)
    );
}

document.body.replaceChildren(nameList());

Attributes are defined as javascript objects. The line

input({type:"text",placeholder:"new name"})

will produce

<input type="text" placeholder="new name">

Attributes starting with the prefix "on" is treated as event handlers.

button({onclick:addName},"add");

will produce

<button>add</button>

with click event handler registered to function addName.

See live code in code pen.

Table with async data

Function userTable creates a HTML table for an array of user objects.

function userTable(data) {
    const { table, thead, tbody, tr, th, td } = ft.html;

    return table(
      thead(
        tr(
          th("#"),th("First Name"),th("Last Name"),th("Gender"),th("Email"),th("Birth Date")
        )
      ),
      tbody(
        ...data.map(d=>tr(
          td(d.id),
          td(d.firstName),
          td(d.lastName),
          td(d.gender),
          td(d.email),
          td(d.birthDate)
        ))
      )
    );
}

Function page bellow creates a HTML fragment with a title and a placeHolder, initially with "loading..." message. An API request is done by fetch function and once fullfilled the request, placeHolder content is replaced by data table.

function page() {
  const { div, h1, p } = ft.html;  
  let placeHolder = div(p("Loading..."));
  
  fetch("https://dummyjson.com/users")
    .then(response=>response.json())
    .then(data=>{placeHolder.replaceChildren(userTable(data.users))})
  ;
  
  return div(
    h1("User Data"),
    placeHolder
  );
}

document.body.replaceChildren(page());

See live code in code pen.

Package Sidebar

Install

npm i funtags

Weekly Downloads

0

Version

1.0.7

License

ISC

Unpacked Size

17.2 kB

Total Files

10

Last publish

Collaborators

  • renato.mauro