Last active
October 16, 2019 16:41
-
-
Save cuongdevjs/b86afca5f4d0b4edc96fb4cacd842952 to your computer and use it in GitHub Desktop.
Create own's Virtual DOM (update, create element)
This file contains hidden or 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
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta name="description" content="Gooact: Example #1"> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width"> | |
<title>JS Bin</title> | |
<style> | |
.btnRerender { | |
border: 1px solid red; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="root"></div> | |
<div class="vdom"></div> | |
<button class="btnRerender">Re-render DOM</button> | |
<script id="jsbin-javascript"> | |
/** @jsx h */ | |
"use strict"; | |
function h(type, props) { | |
for (var _len = arguments.length, children = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { | |
children[_key - 2] = arguments[_key]; | |
} | |
return { type: type, props: props, children: children }; | |
} | |
//? render to real DOM | |
var render = function render(node) { | |
console.log(node); | |
if (!node.type) { | |
return document.createTextNode(node); | |
} | |
var _parent = document.createElement(node.type); | |
node.children.map(function (_node) { | |
return render(_node); | |
}).forEach(function (_node) { | |
return _parent.appendChild(_node); | |
}); | |
return _parent; | |
}; | |
//? compare 2 node (old & new) | |
var isChange = function isChange(vOldNode, vNewNode) { | |
return typeof vOldNode !== typeof vNewNode || typeof vOldNode === "string" && vOldNode !== vNewNode || vOldNode.type !== vNewNode.type; | |
}; | |
//? update to real DOM | |
var updateElement = function updateElement(parent, vNewNode, vOldNode) { | |
var index = arguments.length <= 3 || arguments[3] === undefined ? 0 : arguments[3]; | |
// case vNewNode is text node | |
if (!vOldNode) { | |
console.log(vNewNode); | |
parent.appendChild(render(vNewNode)); | |
} else if (!vNewNode) { | |
parent.removeChild(parent.childNodes[index]); | |
} else if (isChange(vOldNode, vNewNode)) { | |
parent.replaceChild(render(vNewNode), parent.childNodes[index]); | |
} else if (vNewNode.type) { | |
// case vNewNode is node | |
console.log(vNewNode); | |
for (var _idx = 0; _idx < vOldNode.children.length || _idx < vNewNode.children.length; _idx++) { | |
updateElement(parent.childNodes[_idx], vOldNode.children[_idx], vNewNode.children[_idx], _idx); | |
} | |
} | |
}; | |
var oldVDOM = h( | |
"div", | |
{ "class": "parent" }, | |
h( | |
"div", | |
null, | |
"1" | |
), | |
h( | |
"div", | |
null, | |
"2" | |
), | |
h( | |
"div", | |
null, | |
"3" | |
) | |
); | |
var newVDOM = h( | |
"div", | |
{ "class": "parent" }, | |
h( | |
"div", | |
null, | |
"1" | |
), | |
h( | |
"div", | |
null, | |
"3" | |
), | |
h( | |
"div", | |
{ "class": "child" }, | |
h( | |
"div", | |
null, | |
"5" | |
), | |
h( | |
"div", | |
null, | |
"6" | |
) | |
) | |
); | |
var _parent = document.getElementsByClassName("vdom")[0]; | |
var _btn = document.getElementsByClassName("btnRerender")[0]; | |
updateElement(_parent, oldVDOM); | |
_btn.addEventListener("click", function () { | |
updateElement(_parent, newVDOM, oldVDOM); | |
}); | |
</script> | |
<script id="jsbin-source-javascript" type="text/javascript">/** @jsx h */ | |
function h(type, props, ...children) { | |
return { type, props, children }; | |
} | |
//? render to real DOM | |
const render = node => { | |
console.log(node); | |
if (!node.type) { | |
return document.createTextNode(node); | |
} | |
let _parent = document.createElement(node.type); | |
node.children | |
.map(_node => render(_node)) | |
.forEach(_node => _parent.appendChild(_node)); | |
return _parent; | |
}; | |
//? compare 2 node (old & new) | |
const isChange = (vOldNode, vNewNode) => { | |
return ( | |
typeof vOldNode !== typeof vNewNode || | |
(typeof vOldNode === "string" && vOldNode !== vNewNode) || | |
vOldNode.type !== vNewNode.type | |
); | |
}; | |
//? update to real DOM | |
const updateElement = (parent, vNewNode, vOldNode, index = 0) => { | |
// case vNewNode is text node | |
if (!vOldNode) { | |
console.log(vNewNode); | |
parent.appendChild(render(vNewNode)); | |
} else if (!vNewNode) { | |
parent.removeChild(parent.childNodes[index]); | |
} else if (isChange(vOldNode, vNewNode)) { | |
parent.replaceChild(render(vNewNode), parent.childNodes[index]); | |
} else if (vNewNode.type) { | |
// case vNewNode is node | |
console.log(vNewNode); | |
for ( | |
let _idx = 0; | |
_idx < vOldNode.children.length || _idx < vNewNode.children.length; | |
_idx++ | |
) { | |
updateElement( | |
parent.childNodes[_idx], | |
vOldNode.children[_idx], | |
vNewNode.children[_idx], | |
_idx | |
); | |
} | |
} | |
}; | |
const oldVDOM = ( | |
<div class="parent"> | |
<div>1</div> | |
<div>2</div> | |
<div>3</div> | |
</div> | |
); | |
const newVDOM = ( | |
<div class="parent"> | |
<div>1</div> | |
<div>3</div> | |
<div class="child"> | |
<div>5</div> | |
<div>6</div> | |
</div> | |
</div> | |
); | |
const _parent = document.getElementsByClassName("vdom")[0]; | |
const _btn = document.getElementsByClassName("btnRerender")[0]; | |
updateElement(_parent, oldVDOM); | |
_btn.addEventListener("click", () => { | |
updateElement(_parent, newVDOM, oldVDOM); | |
}) | |
</script></body> | |
</html> |
This file contains hidden or 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
/** @jsx h */ | |
"use strict"; | |
function h(type, props) { | |
for (var _len = arguments.length, children = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { | |
children[_key - 2] = arguments[_key]; | |
} | |
return { type: type, props: props, children: children }; | |
} | |
//? render to real DOM | |
var render = function render(node) { | |
console.log(node); | |
if (!node.type) { | |
return document.createTextNode(node); | |
} | |
var _parent = document.createElement(node.type); | |
node.children.map(function (_node) { | |
return render(_node); | |
}).forEach(function (_node) { | |
return _parent.appendChild(_node); | |
}); | |
return _parent; | |
}; | |
//? compare 2 node (old & new) | |
var isChange = function isChange(vOldNode, vNewNode) { | |
return typeof vOldNode !== typeof vNewNode || typeof vOldNode === "string" && vOldNode !== vNewNode || vOldNode.type !== vNewNode.type; | |
}; | |
//? update to real DOM | |
var updateElement = function updateElement(parent, vNewNode, vOldNode) { | |
var index = arguments.length <= 3 || arguments[3] === undefined ? 0 : arguments[3]; | |
// case vNewNode is text node | |
if (!vOldNode) { | |
console.log(vNewNode); | |
parent.appendChild(render(vNewNode)); | |
} else if (!vNewNode) { | |
parent.removeChild(parent.childNodes[index]); | |
} else if (isChange(vOldNode, vNewNode)) { | |
parent.replaceChild(render(vNewNode), parent.childNodes[index]); | |
} else if (vNewNode.type) { | |
// case vNewNode is node | |
console.log(vNewNode); | |
for (var _idx = 0; _idx < vOldNode.children.length || _idx < vNewNode.children.length; _idx++) { | |
updateElement(parent.childNodes[_idx], vOldNode.children[_idx], vNewNode.children[_idx], _idx); | |
} | |
} | |
}; | |
var oldVDOM = h( | |
"div", | |
{ "class": "parent" }, | |
h( | |
"div", | |
null, | |
"1" | |
), | |
h( | |
"div", | |
null, | |
"2" | |
), | |
h( | |
"div", | |
null, | |
"3" | |
) | |
); | |
var newVDOM = h( | |
"div", | |
{ "class": "parent" }, | |
h( | |
"div", | |
null, | |
"1" | |
), | |
h( | |
"div", | |
null, | |
"3" | |
), | |
h( | |
"div", | |
{ "class": "child" }, | |
h( | |
"div", | |
null, | |
"5" | |
), | |
h( | |
"div", | |
null, | |
"6" | |
) | |
) | |
); | |
var _parent = document.getElementsByClassName("vdom")[0]; | |
var _btn = document.getElementsByClassName("btnRerender")[0]; | |
updateElement(_parent, oldVDOM); | |
_btn.addEventListener("click", function () { | |
updateElement(_parent, newVDOM, oldVDOM); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment