class attribute { constructor(name, value) { this.name = name; this.value = value; } } class collection { constructor(items) { this.siblings = []; this.children = []; this.attributes = []; this.classes = []; this.tagName = null; this.element = null; for (var i = 0; i < items.length; i++) { if (items[i] instanceof attribute) { this.attributes.push(items[i]); } else if (items[i] instanceof property) { this.children.push(new propertyObserver(items[i])); } else { this.children.push(items[i]); } } } class(...items) { for (var i = 0; i < items.length; i++) { if (items[i] instanceof property) { items[i].onPropertyChange.push((oldValue, newValue) => this.onClassChanged(oldValue, newValue)); } else { this.classes.push(items[i]); } } return this; } div(...items) { this.siblings.push(new div(...items)); return this; } p(...items) { this.siblings.push(new p(...items)); return this; } construct(parent) { if (!parent) { parent = window.document.body; } this.element = window.document.createElement(this.tagName); parent.appendChild(this.element); for (var i = 0; i < this.children.length; i++) { if (this.children[i] instanceof String || typeof this.children[i] === 'string') { this.element.appendChild(document.createTextNode(this.children[i])); } else if (this.children[i] instanceof collection || this.children[i].prototype instanceof collection || this.children[i].prototype.constructor === collection) { this.children[i].construct(this.element); } } for (var i = 0; i < this.siblings.length; i++) { if (this.siblings[i] instanceof String || typeof this.siblings[i] === 'string') { this.parent.appendChild(document.createTextNode(this.siblings[i])); } else if (this.siblings[i] instanceof collection || this.siblings[i].prototype instanceof collection || this.siblings[i].prototype.constructor === collection) { this.siblings[i].construct(parent); } } } onClassChanged(oldValue, newValue) { console.log(`Class changed, oldValue: ${oldValue} newValue: ${newValue}`); if (oldValue && oldValue !== "" && oldValue !== " ") { this.element.classList.remove(oldValue); } if (newValue && newValue !== "" && newValue !== " ") { this.element.classList.add(newValue); } } } class div extends collection { constructor(...items) { super(items); this.tagName = "div"; } } class p extends collection { constructor(...items) { super(items); this.tagName = "p"; } } class h1 extends collection { constructor(...items) { super(items); this.tagName = "h1"; } } class propertyObserver extends collection { constructor(property) { super([]); this.property = property; this.property.onPropertyChange.push((oldValue, newValue) => this.onUpdateProperty(oldValue, newValue)); } construct(parent) { this.element = document.createTextNode(""); parent.appendChild(this.element); } onUpdateProperty(oldValue, newValue) { this.element.nodeValue = newValue; } } class property { constructor() { this.onPropertyChange = []; this._value = null; } get value() { return this._value; } set value(val) { var old = this._value; this._value = val; for (var i = 0; i < this.onPropertyChange.length; i++) { this.onPropertyChange[i](old, val); } } } var prop = new property(); var classProp = new property(); console.log("Property:"); console.log(prop); console.log("Class Property:"); console.log(classProp); var test = new div( new h1("I am a heading").class(classProp), new p(prop) ).div(new p("This is content")); test.construct(window.document.body); console.log(test);