
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
Create DOM trees from plain vanilla javascript functions. No transpilation nor alien languages. Just a little bit trick.
Create DOM trees from plain vanilla javascript functions. No transpilation nor alien languages. Just a little bit of magic with Proxy Objects.
<script src="https://cdn.jsdelivr.net/gh/renato-mauro/funtags/dist/main.min.js"></script>
<script src="dist/main.min.js"></script>
npm install funtags
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"
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)})
}
})();
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.
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.
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.
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.
FAQs
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.