-
Notifications
You must be signed in to change notification settings - Fork 2
/
script.js.backup
269 lines (232 loc) · 8.32 KB
/
script.js.backup
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
// ================================================
// The Virtual DOM
// ================================================
// A virtual DOM is a virtual representation of the old browser DOM you're familiar with.
// The first step is to convert the the DOM we want to be displayed in a form that we could operate on.
// We're going to use plain old JavaScript objects for this representation.
function render(type, attributes, children) {
return {
type,
...attributes,
children
};
}
const component = {
state: {
name: ""
},
get name() {
return this.state.name;
},
set name(value) {
this.state.name = value;
},
render(h) {
return h("div", {}, "hello");
}
};
const componentState = {
state: {
name: ""
},
get name() {
console.log("Name is fetched");
return this.state.name;
},
set name(value) {
this.state.name = value;
handleReload(newd);
console.log("Name is updated");
}
};
var vdom = {
type: "div",
children: [
{
type: "input",
events: {
input: e => {
e.preventDefault();
e.stopPropagation();
console.log("Updating value", e.target.value);
componentState.name = e.target.value;
}
},
children: []
},
// {
// type: "p",
// children: [{ type: "div", children: ["Hi!"] }]
// },
{
type: "p",
// children: []
children: [componentState.name]
}
]
};
// If you observe, it has two kinds of elements, objects and strings.
// The objects are our HTML elements, they have two keys, type and children.
// The type key is the element tag. The children key is an array of elements.
// The children of an object may be other objects, or just strings.
// These strings are the second kind of elements. They are the text nodes,
// or the actual information that will wrapped in and then displayed in the parent node.
// But, this DOM will also have to change the real DOM that we update and the browser renders.
// Now we have our virtual dom,
// Next step is to convert it into an actual DOM that the browser could render.
// For this we will be using the JavaScript createElement and createTextNode functions
// ================================================
// Actual HTML Elements from Virtual DOM
// ================================================
// Create DOM node goes recursively through all children nodes, creates element and then appends
// that to its parent.
function createDOMNode(dom) {
console.log("Creating dom node", dom);
// First we check if the node is a string.
// That means, it is a text node and does not have any children.
// So let's just create the text node
if (typeof dom === "string") {
return document.createTextNode(dom);
}
// If the node isn't a string then we create the element node.
var newEl = document.createElement(dom.type);
if (dom.events) {
Object.keys(dom.events).forEach(event => {
newEl.addEventListener(event, dom.events[event]);
});
}
// Great. Now it's time for the child nodes.
if (dom.children && dom.children.length > 0) {
// This just checks if the node has children
// Now, let's loop through all the children of the node and call the same createDOMNode function
// on each one of them
// Then, append the returned function to immediate parent element we just created.
dom.children.forEach(function(element) {
var innerElement = createDOMNode(element);
newEl.appendChild(innerElement);
}, this);
}
// When all is done, return the element
return newEl;
}
// By now we are able to convert our virtual dom representation in an actual dom
// So if you called the createDOMNode function on the vdom object, it will
// create the actual browser DOM.
// ================================================
// Diffing two virtual DOM nodes
// ================================================
// Now, what we have to focus on is diffing the objects to decide if the dom should be updated.
// How do you diff it?
// Our element object in the virtual dom has two keys, type and children (see section 1)
// This function has a few if conditions that check if the two nodes passed are different
// This just diffs one node. There is no recursion involved here.
// Let's walk you through it step by step.
function diffNodes(oldnode, newnode) {
if (oldnode && newnode) {
if (typeof oldnode != typeof newnode) {
// If the type of both nodes is different
// One is a text node and the other is an element
return true;
} else if (oldnode.type && newnode.type && oldnode.type != newnode.type) {
// If both nodes are elements, but are different.
return true;
} else if (
typeof oldnode === "string" &&
typeof newnode === "string" &&
newnode != oldnode
) {
// If both nodes are string and are not equal
return true;
}
return false;
} else {
throw new Error("Nodes are undefined");
}
}
// ================================================
// Updating the actual DOM based
// ================================================
// There are a few ways these nodes could have changed.
// 1. The node might have been deleted
// 2. A new node might have been added
// 3. the node tag could have been changed
// 4. The node text could have been changed
// In the below function,
// parent = the immediate parent element
// oldnode = the old node in our virtual dom
// newnode = the new node in our virtual dom
// index = index of the child of the parent node. It is zero by default to target the first
// and immediate child of a node
function updateNode(parent, oldnode, newnode, index = 0) {
console.log("Update node called", { parent, oldnode, newnode, index });
// Let's start simple. If the old node does not exist.
if (!oldnode) {
console.log("Old node does not exist", {
oldnode,
newnode
});
// Then, just simple append the new node
console.log(JSON.stringify(parent));
parent.appendChild(createDOMNode(newnode));
console.log(JSON.stringify(parent));
} else if (!newnode) {
console.log("new node does not exist");
// If the new node doesn't exist, remove that node
parent.removeChild(parent.childNodes[index]);
} else if (diffNodes(oldnode, newnode)) {
console.log(parent, parent.childNodes, index);
// If both the nodes exist. Check if the new node is different.
// If they are, then replace the old node with the new node
parent.replaceChild(createDOMNode(newnode), parent.childNodes[index]);
} else if (newnode.type && (newnode.children || oldnode.children)) {
console.log("shit's same. going down");
// If the old and new nodes haven't changed, check if their children have changed
// Recursion to the rescue. Loop through all children and call this function on each one of them.
for (
var i = 0;
i < newnode.children.length || i < oldnode.children.length;
i++
) {
console.log("Going for child, ", i);
updateNode(
parent.childNodes[index],
oldnode.children[i],
newnode.children[i],
i
);
}
}
}
// ========================================
// That's it.
// You're now able to create nodes, diff them and then update the actual dom based on your diff.
// In other words,
// You now have fair idea of how this Virtual DOM thing you keep hearing about actually works.
// ========================================
// Oh, this function, it doesn't do much.
// This is the function that's called when you click on "Start"
// It just renders the dom representation above
function handleStart() {
var root = document.getElementById("app");
updateNode(root, null, vdom);
}
document.addEventListener("DOMContentLoaded", () => {
handleStart();
});
// This function is called when you click on reload
// It makes some changes to the old dom to get the new dom representation
// Then, just calls the updateNode function with both the doms
function handleReload(newdom) {
// console.log("Handle reload called", newdom, vdom);
// var newdom = JSON.parse(JSON.stringify(vdom));
// newdom.children.push({
// type: 'p',
// children: [ 'How are you?' ]
// });
// newdom.children.splice(0, 1);
// newdom.children[1].children[0] = "Nishant!";
console.log("Handle reload called", newdom, vdom);
var root = document.getElementById("app");
updateNode(root, vdom, newdom);
vdom = newdom;
}