Created
April 20, 2017 20:39
-
-
Save pomber/21d9dde938a5d9ffada1420c5aed0819 to your computer and use it in GitHub Desktop.
Several small functions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export function createElement(type, config, ...args) { | |
const props = Object.assign({}, config); | |
if (args.length) { | |
props.children = [].concat(...args); | |
} | |
return { type, props }; | |
} | |
export function render(element, container) { | |
const instance = new DomComponent(element); | |
const dom = instance.mount(); | |
container.appendChild(dom); | |
} | |
class DomComponent { | |
constructor(element) { | |
this.currentElement = element; | |
this.dom = null; | |
this.childInstances = null; | |
} | |
mount() { | |
const { type, props } = this.currentElement; | |
this.dom = document.createElement(type); | |
this.setAttributes(this.dom, props); | |
this.setEvents(this.dom, props); | |
this.childInstances = this.setChildren(this.dom, props); | |
return this.dom; | |
} | |
update(nextElement) { | |
const prevProps = this.currentElement.props; | |
const nextProps = nextElement.props; | |
this.removeAttributes(this.dom, prevProps); | |
this.setAttributes(this.dom, nextProps); | |
this.removeEvents(this.dom, prevProps); | |
this.setEvents(this.dom, nextProps); | |
this.childInstances = this.updateChildren( | |
this.dom, | |
prevProps, | |
nextProps, | |
this.childInstances | |
); | |
this.currentElement = nextElement; | |
} | |
setAttributes(dom, props) { | |
Object.keys(props).filter(isAttribute).forEach(name => { | |
const value = props[name]; | |
if (value != null && value !== false) { | |
dom[name] = value; | |
} | |
}); | |
} | |
removeAttributes(dom, props) { | |
Object.keys(props).filter(isAttribute).forEach(name => { | |
dom[name] = null; | |
}); | |
} | |
setEvents(dom, props) { | |
Object.keys(props).filter(isEvent).forEach(name => { | |
const eventType = name.toLowerCase().substring(2); | |
dom.addEventListener(eventType, props[name]); | |
}); | |
} | |
removeEvents(dom, props) { | |
Object.keys(props).filter(isEvent).forEach(name => { | |
const eventType = name.toLowerCase().substring(2); | |
dom.removeEventListener(eventType, props[name]); | |
}); | |
} | |
setChildren(dom, props) { | |
const childElements = props.children || []; | |
const childInstances = childElements.map( | |
childElement => new DomComponent(childElement) | |
); | |
childInstances | |
.map(childInstance => childInstance.mount()) | |
.forEach(childDom => dom.appendChild(childDom)); | |
return childInstances; | |
} | |
updateChildren(dom, prevProps, nextProps, prevChildInstances) { | |
const prevChildElements = prevProps.children || []; | |
const nextChildElements = nextProps.children || []; | |
const nextChildInstances = []; | |
const length = Math.max(prevChildElements.length, nextChildElements.length); | |
for (let i = 0; i < length; i++) { | |
const prevChildElement = prevChildElements[i]; | |
const nextChildElement = nextChildElements[i]; | |
const prevChildInstance = prevChildInstances[i]; | |
const nextChildInstance = this.updateChild( | |
this.dom, | |
prevChildElement, | |
nextChildElement, | |
prevChildInstance | |
); | |
if (nextChildInstance) { | |
nextChildInstances.push(nextChildInstance); | |
} | |
} | |
return nextChildInstances; | |
} | |
updateChild(dom, prevChildElement, nextChildElement, prevChildInstance) { | |
if (prevChildElement === undefined) { | |
return this.addChild(this.dom, nextChildElement); | |
} else if (nextChildElement === undefined) { | |
this.removeChild(this.dom, prevChildInstance); | |
return null; | |
} else if (prevChildElement.type === nextChildElement.type) { | |
prevChildInstance.update(nextChildElement); | |
return prevChildInstance; | |
} else { | |
return this.replaceChild(this.dom, prevChildInstance, nextChildElement); | |
} | |
} | |
addChild(dom, childElement) { | |
const childInstance = new DomComponent(nextChildElement); | |
const childDom = childInstance.mount(); | |
dom.appendChild(childDom); | |
return childInstance; | |
} | |
removeChild(dom, childInstance) { | |
const childDom = childInstance.getDom(); | |
dom.removeChild(childDom); | |
} | |
replaceChild(dom, prevChildInstance, nextChildElement) { | |
const nextChildInstance = new DomComponent(nextChildElement); | |
const nextChildDom = nextChildInstance.mount(); | |
const prevChildDom = prevChildInstance.getDom(); | |
dom.replaceChild(nextChildDom, prevChildDom); | |
return nextChildInstance; | |
} | |
getDom() { | |
return this.dom; | |
} | |
} | |
const isEvent = name => name.startsWith("on"); | |
const isAttribute = name => !isEvent(name) && name != "children"; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment