JSFiddle - React, Tailwind, and code Playground
HTML
<div id="root"></div>
<script>
let name;
const container = document.getElementById('root');
const redraw = () => render(container, createFragment());
function createFragment() {
return {
tag: 'div',
attrs: {class: "app"},
children: [
{tag: "h1", children: `Hi, ${name || '%username%'}!`},
{tag: "button", attrs: {
class: "button",
name: name ? "exit" : "enter",
onClick: handleClick,
}, children: name ? "Exit" : "Enter"},
],
};
}
function handleClick(evt) {
name = evt.currentTarget.name === "enter" && prompt("Name?");
redraw();
}
redraw();
</script>
CSS
.app {
text-align: center;
marging: 30px;
}
.button {
font-size: 30px;
}
JavaScript
//
// API
//
const TEXT_NODE = '#';
const R_IS_EVENT = /^on[A-Z]/;
const R_IS_TEXT = /string|number|boolean/;
const CLASS_ATTR = 'class';
const VALUE_ATTR = 'value';
function normalize(node) {
if (node == null) {
return {tag: TEXT_NODE, children: ''};
} else if (node.tag == null) {
return {tag: TEXT_NODE, children: node};
} else {
return node;
}
}
function create(/* Object */node, /* HTMLElement */parent) {
node = normalize(node); // "str" -> {tag: "#", children: "..."}
let el;
if (TEXT_NODE === node.tag) {
el = document.createTextNode(node.children);
} else {
el = document.createElement(node.tag);
updateAttrs(el, node.attrs); // добавляем аттрибуты
createChildren(node, el); // создаеём детишек
}
node.el = el; // запоминаем DOM
(parent !== null) && parent.appendChild(el);
return node;
}
function updateAttrs(/* HTMLElement */el, attrs, oldAttrs) {
let name;
let value;
for (name in attrs) {
value = attrs[name];
if (oldAttrs == null || oldAttrs[name] !== value) {
if (CLASS_ATTR === name) { // className
el.className = value;
}
else if (VALUE_ATTR === name) { // value
(el[name] !== value) && (el[name] = value);
}
else if (R_IS_EVENT.test(name)) { // события
el.addEventListener(name.substr(2).toLowerCase(), value, false);
}
else { // все остальные
el.setAttribute(name, value);
}
}
}
if (oldAttrs != null) {
for (name in oldAttrs) {
value = oldAttrs[name];
if (attrs[name] == null && attrs[name] !== value) {
if (R_IS_EVENT.test(name)) {
el.removeEventListener(name.substr(2).toLowerCase(), value, false);
} else {
el.removeAttribute(name);
}
}
}
}
}
function createChildren(/* Object */node, /* HTMLElement */parent) {
const children = node.children;
const length = children.length;
if (length == null || R_IS_TEXT.test(typeof children))...