class JsEngine { constructor(options) { this.options = options this.models = this.options.data || {} this.lifecycle = this.options.lifecycle || {} this.bindings = {} this.elementToUpdate = {} this.root = document.querySelector(this.options.el) this.searchBindModel() this.proxyBind() } searchBindModel() { this.lifecycle.componentWillMount && this.lifecycle.componentWillMount.call({ data: this.models }) this.root.querySelectorAll('[bind]') .forEach((element) => { this.bindings[element.getAttribute('bind')] = element }) } proxyBind() { const self = this this.models = new Proxy(this.models, { set (target, prop, value) { const element = self.bindings[prop] self.updateTextBind(prop, value) element.value = value element.setAttribute('value', value) return Reflect.set(target, prop, value) } }) this.setInitialModel() this.inputBindingReaction() this.lifecycle.componentDidMount && this.lifecycle.componentDidMount.call({ data: this.models }) this.setTextModels() } setInitialModel() { Object.keys(this.bindings) .map((modelName) => { const element = this.bindings[modelName] const value = this.models[modelName] element.value = value element.setAttribute('value', value) }) } inputBindingReaction() { Object.keys(this.bindings) .map((modelName) => { const element = this.bindings[modelName] element .addEventListener('input', (event) => { this.models[modelName] = event.target.value }) }) } setTextModels() { let { children } = this.root for (let i = 0; i < children.length; i++) { let replaced = children[i].innerText.replace(/{{2}(.+)}{2}/igm, (_, matching) => { matching = matching.trim() this.elementToUpdate[matching] = children[i] if (this.models[matching]) { return this.models[matching] } return undefined }) if (replaced) { children[i].innerText = replaced } } } updateTextBind(prop, currentValue = null) { const element = this.elementToUpdate[prop] || {} element.innerText = currentValue } }