Understanding DOM: Selectors & Manipulation
Understanding DOM: Selectors & Manipulation
DOM Selector
Read
1. document
2. [Link]('id')
3. [Link]('element')
4. [Link]('class')
5. [Link]('name')
6. [Link]('query')
7. [Link]()
2. Attribute Node ** - 2
3. Text Node - 3
4. Comment - 8
5. Document - 9
6. DocumentFragment - 11
Node Properties
1. nodeName
2. nodeValue
3. nodeType
4. attributes
Node Method
1. [Link]()
3. firstChild
4. lastChild
5. nextSibling
6. previousSibling
DOM Tree Traversal
1. parentElement
2. children**
4. firstElementChild
5. lastElementChild
6. nextElementSibling
7. previousElementSibling
Inheritance
Prototype Chain of DOM
Basic Operation
DOM Manipulation
Add / Create
1. [Link](); ***
2. [Link]();
3. [Link]();
4. [Link]();
Insert
1. [Link]() ***
2. [Link](a, b) ***
Delete
1. [Link]()
2. [Link]() - ES5 ***
Replace
1. [Link](new, origin)
2. innerText
3. textContent
2. [Link]('prop')
3. [Link]
Window Properties and DOM Offset
Viewport Offset (Width and Height)
1. [Link] / [Link]
[Link]
Rect
1. [Link]()
Size
[Link] & [Link]
Position
2. [Link]
Scrolling Distance
1. [Link] & [Link]
2. [Link](x, y)
Scripted CSS
Read and Write CSS
1. [Link] / [Link]['prop']**
2. [Link]
this
Compatibility
Remove Event Listener
1. [Link] = false / ''/ null
event object, e
Stop Default Event
1. return false
2. [Link]
3. [Link] = false
2. Stop Propagation
3. [Link] / [Link]
Bubbling & Capturing
Bubbling
Capturing
Triggering Order
Stop Propagation
1. [Link]()
2. [Link] = true;
Event Delegation
Event Types
Pointer Event
Get mouse button keys(left, middle, right)
Drag and Drop
Keyboard Event
Comparison between keydown and keypress
Text Field Related Event
Focus Event
Input Event
Change Event
Browser Window Related Event
Scrolling
Loading
Rendering
Asynchronous loading
1. defer
2. async
Loading Timeline
Theory
Practicals
BOM
DOM (Document Object Model)
The data representation of the objects that comprise the structure and content of a document on the
web.
DOM is a programming interface for web documents, allowing us to manipulate HTML and XML with
various built-in DOM methods.
HTML element is only the root element, which is the most important part of the document, but not the
document itself.
<!---->
<document>
<html>
</html>
</document>
DOM cannot directly manipulate CSS, but we can use it to indirectly add inline styles to HTML
elments.
Any collection generated from DOM is an array-like object, thus not able to use array method.
DOM Selector
Read
1. document
2. [Link]('id')
1. returns an Element object representing the element whose id property matches the specified
string.
only one element is returned with given id.
2. IE ver.8 and below, id is case-insensitive.
3. IE ver.8 and below, select an element with name attribute having matching string is possible, if id
attribute is not present.
4. do not rely on id selector too much as connect to backend program might change it.
5. use only when necessary, e.g., each section has an id.
3. [Link]('element')
1. returns a live HTMLCollection - an array-like object of elements with the given tag name.
2. select specific element based on index.
3. select all elements with asterisk(*)
4. very good compatibility in every browser - mainstream
4. [Link]('class')
1. returns an array-like object of all child elements which have all of the given class name(s).
2. IE ver.8 and below not support.
3.
5. [Link]('name')
1. returns a NodeList Collection of elements with a given name attribute in the document.
2. only specific elements works, e.g., form, form elements: e.g., input, img, iframe where name
attribute has meaning.
3. in previous browser ver, elements like div did not support selection by name, but in current
browser, it is already supported.
6. [Link]('query')
1. returns the first Element within the document that matches the specified selector, or group of
selectors. If no matches are found, null is returned.
2. query is similar to how we write CSS selector.
3. IE ver.7 and below not support.
4. manipulate element (styles) is allowed.
5. weakness: not live (a copy): changing (e.g., deleting) doesn't reflect on nodelist, but the element
on display is changed.
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<script>
var div1 = [Link]('div');
[Link]();
[Link](div1); // changes doesn't reflect.
[Link]([Link]('div')[0]); // changes does reflect. index 0 is no
</script>
</body>
7. [Link]()
1. returns a static (not live) NodeList representing a list of the document's elements that match the
specified group of selectors.
2. IE ver.7 and below not support.
3. manipulate element (style) is allowed.
4. weakness: not live (a copy): changing (e.g., deleting) doesn't reflect on nodelist, but the element
on display is changed.
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<script>
var static = [Link]('div');
var live = [Link]('div');
static[0].remove(); // remove the first div
[Link](static); // the nodelist is not updated.
[Link](live); // live HTMLcollection reflect the change accordingly
</script>
</body>
1. Element Node - 1
2. Attribute Node ** - 2
Attr objects inherit the Node interface, but since they are not actually child nodes of the element they
describe, the DOM does not consider them part of the document tree.
3. Text Node - 3
4. Comment - 8
5. Document - 9
6. DocumentFragment - 11
Node Properties
1. nodeName
returns the name of the current node as a string.
changing nodeName is not allowed, it is read-only.
2. nodeValue
returns or sets the value of the current node.
text, comment, CDATA nodes, return the content.
else, return null.
nodes with null nodeValues (e.g., DOM elements) are not modifiable.
3. nodeType
return integers representing node type to distinguishes different kind of nodes from each other.
var div = [Link]('div')[0];
function retElementChild(node) {
var nodeList = [Link];
var len = [Link]; // save efficiency -method chaining every time is not efficient.
var temp = {
length: 0,
push: [Link],
splice: [Link],
};
for (let i = 0; i < len; i ++) {
if (nodeList[i].nodeType === 1) {
[Link](nodeList[i]);
}
}
return [Link](temp);
}
[Link](retElementChild(div));
4. attributes
returns a live collection of all attribute nodes registered to the specified node.
access attribute name by attributes[i].name: we cannot change attribute name.
access attribute value by attributes[i].value: we can change value.
[Link];
[Link][i];
[Link][i].value;
[Link][i].name;
[Link][i].value = 123; // we can change the value
[Link][i].name = 123; // changing name is not allowed
// Example
[Link]('old value: ', [Link][0].value);
[Link]('old name: ', [Link][0].name);
[Link][0].value = 'hahahaha';
[Link][0].name = 'hahahaha';
[Link]('new value: ', [Link][0].value);
[Link]('new name: ', [Link][0].name);
Node Method
1. [Link]()
returns a boolean value indicating whether the given Node has child nodes or not.
<!--Example 1-->
<div></div>
<!--Example 2-->
<div> </div>
<!--Example 3-->
<div>
<!--comment-->
</div>
<script>
// Example 1
[Link]([Link]()); // false
// Example 2
[Link]([Link]()); // true - when there is a space between opening and closing ta
// Example 3
[Link]([Link]()); // true
</script>
1. parentNode
returns the parent of the specified node in the DOM tree.
#document is the top of the node and can't have parentNode (null)
<body>
<div>
<strong></strong>
<span></span>
<em></em>
</div>
<script>
var strong = [Link]('strong')[0];
[Link]([Link]); // <div>...</div>
[Link]([Link]); // <body>...</body>
[Link]([Link]); // #document (the top node
[Link]([Link]); // null
</script>
</body>
2. childNodes
returns a live NodeList of child nodes of the given element where the first child node is assigned
index 0.
<body>
<div>
<!--This is a comments-->
<strong>
<span>
1
</span>
</strong>
<span></span>
<em></em>
</div>
<script>
var div = [Link]('div')[0];
[Link]([Link]); // NodeList(9) [text, comment, text, strong, text, span, text,
[Link]([Link]); // 9 - includes text nodes - empty spaces. Note: remove
</script>
</body>
3. firstChild
returns the node's first child in the tree, or null if the node has no children.
4. lastChild
returns the last child of the node, or null if there are no child nodes.
5. nextSibling
returns the node immediately following the specified one in their parent's childNodes, or returns
null if the specified node is the last child in the parent element.
6. previousSibling
returns the node immediately preceding the specified one in its parent's childNodes list, or null if
the specified node is the first in that list.
1. parentElement
returns the DOM node's parent Element, or null if the node either has no parent, or its parent isn't
a DOM Element.
IE ver. 9 and below not support.
2. children**
similar to [Link], but includes only element nodes.
3. [Link] === [Link]
returns the number of child elements of this element.
IE ver. 9 and below not support.
4. firstElementChild
returns an element's first child, or null if there are no child elements.
IE ver. 9 and below not support.
5. lastElementChild
returns an element's last child Element, or null if there are no child elements.
IE ver. 9 and below not support.
6. nextElementSibling
returns the element immediately following the specified one in its parent's children list, or null if
the specified element is the last one in the list.
7. previousElementSibling
returns the Element immediately prior to the specified one in its parent's children list, or null if the
specified element is the first one in the list.
IE ver. 9 and below not support.
Inheritance
Document Object Model (DOM API) is represented as a hierarchical tree-like structure in web browser,
each individual parts represents as nodes.
Each nodes could correspond to the specific constructor functions and relate to each other via
prototype chain.
<script>
[Link] = 'abc';
var body = [Link]('body')[0];
var head = [Link]('head')[0];
</script>
[Link] = {
__proto__: [Link]
}
thus, DOM elements can access properties and methods from [Link].
[Link]; // <html>...</html>
DOM Manipulation
Add / Create
create nodes by Js, only effective when we insert into HTML.
1. [Link](); ***
2. [Link]();
3. [Link]();
Insert
1. [Link]() ***
every element have this method.
similar to [Link] , adding node to the end of the list of children of a specific parent node.
if the element doesn't exist, it insert the element into another element.
if the element already exist, appendChild cut and move it to the element we append to.
[Link](comment);
[Link](span);
[Link](text);
2. [Link](a, b) ***
inserts a node before a reference node as a child of a specified parent node.
insert a before b.
1. [Link]()
removes a child node from the DOM and returns the removed node.
the removed child is still exist in memory, and can be reused later.
[Link](span);
// removeChild return removed part, which we can store for later use.
var a = [Link](span);
[Link]();
Replace
1. [Link](new, origin)
replaces a child node within the given (parent) node.
the replaced part still exist, store it in a variable for future use.
var p = [Link]('p');
var replaced = [Link](p, strong);
Element Node Properties
1. innerHTML ***
gets or sets the HTML or XML markup contained within the element.
use to set html element, and adding styles.
2. innerText
gets or sets inner text - give you text node.
old Firefox not support.
cause reflow - computational expensive***
setting innerText might remove all of the node's children and replace with a single text node based
on given string value.
3. textContent
get the content of all elements, including <script> and <style> elements.
previous version IE not support.
setting innerText might remove all of the node's children and replace with a single text node based
on given string value.
<script>
// be careful replace text using innerText and textContent
[Link] = 123; // it replaces all the content in div.
</script>
Element Node Method
1. [Link]('prop', 'value')
[Link]('id', 'only');
<!--Example Setting Attr-->
<div></div>
<i></i>
<strong></strong>
<script>
var all = [Link]('*');
for (var i = 0; i < [Link]; i ++) {
all[i].setAttribute('this-name', all[i].nodeName);
}
</script>
2. [Link]('prop')
returns the value of a specified attribute on the element.
[Link]('id'); // only
[Link] = function () {
[Link]([Link]('data-log'));
}
3. [Link]
gets and sets the value of the class attribute of the specified element.
Window Properties and DOM Offset
1. [Link] / [Link]
1. work in IE ver. 8 and below.
2. [Link] &
[Link]
1. in standards mode, every browser supports.
Note:
[Link]
check compatibility mode: standards or quirks mode
<!--Standards Mode-->
<!DOCTYPE html>
<html>
...
</html>
function getViewportOffset() {
if ([Link]) {
[Link]('standard mode');
return {
w : [Link],
h : [Link],
}
} else {
if ([Link] === 'BackCompat') {
[Link]('quirk mode');
return {
w : [Link],
h : [Link],
}
} else {
[Link]('standard mode')
return {
w : [Link],
h : [Link],
}
}
}
}
Rect
1. [Link]()
return a DOMRect object containing information about size and position of an element, relative to the
viewport.
Size
Position
return position of the element relative to the document if the parent position set is default / static.
return position of the element relative to its closest positioned ancestor if the parent has its position set
other than default/ static.
<!--Example 1-->
<body>
<style>
.outer {
width: 300px;
height: 300px;
border: 2px solid black;
position: relative;
top: 100px;
left: 100px;
/* margin-top: 100px;
margin-left: 100px; */
}
.inner {
width: 100px;
height: 100px;
position: absolute;
/* top: 100px;
left: 100px; */
margin-left: 100px;
margin-top: 100px;
background-color: red;
}
</style>
<div class="outer">
<div class="inner"></div>
</div>
<script>
var outer = [Link]('outer')[0];
var inner = [Link]('inner')[0];
[Link]([Link]); // 100 - distance of the element with its parent element
[Link]([Link]); // 100 - distance of the element with its parent element
</script>
</body>
<!--Example 2-->
<body>
<style>
.outer {
width: 300px;
height: 300px;
border: 2px solid black;
position: static;
top: 100px;
left: 100px;
}
.inner {
width: 100px;
height: 100px;
position: absolute;
margin-left: 100px;
margin-top: 100px;
background-color: red;
}
</style>
<div class="outer">
<div class="inner"></div>
</div>
<script>
var outer = [Link]('outer')[0];
var inner = [Link]('inner')[0];
[Link]([Link]); // 210 - relative to the doc: 100 (inner) + 102 (outer + bord
[Link]([Link]); // 202 - relative to the doc: 100 (inner) + 102 (outer + borde
</script>
</body>
Scrolling Distance
return how many pixel the document has scrolled horizontally or vertically.
1. [Link] & [Link]
[Link]: pageXOffset - alias of scrollX
[Link]: pageYOffset - alias of scrollY
3. [Link] &
[Link]
note:
/**
* Compatibility: if [Link] exists, the code inside if will be adopted.
* @returns scroll distance from x and y.
*/
function getScrollOffset() {
if ([Link]) {
return {
x : [Link],
y : [Link],
}
} else {
return {
x : [Link] + [Link],
y : [Link] + [Link],
}
}
}
Scrolling
2. [Link](x, y)
scroll the window to x, y position by a specific amount, and the amount can be accumulated.
[Link](0, 100);
[Link](0, 100); // accumulate
[Link](0, 100); // accumulate
[Link](0, 100); // accumulate
Scripted CSS
the one and only way to write CSS style via Js.
Note:
1. name containing '-' is not allowed in js, consider bracket notation or camelCase.
2. only works for inline style of any dom elements, accessing internal stylesheet or external
stylesheet return an empty string, ''.
3. css property containing 'float' should be replaced with 'cssFloat': float is a reserved word.
4. not recommend writing shorthand property - although it works.
5. value must be string.
<body>
<style>
div {
border-radius:50%;
width: 100px;
height: 200px;
border: 1px solid black;
text-align: center;
line-height: 100px;
}
</style>
<div style="width: 200px; height: 100px; background-color: yellow;">123</div>
<script>
var div = [Link]('div')[0];
// read
[Link]([Link]); // 200px
[Link]([Link]['background-color']); // yellow
[Link]([Link]); // ''
[Link]([Link]); // ''
// write
[Link]['background-color'] = 'red';
[Link] = 'left';
[Link] = '5px dashed white';
</script>
</body>
Example 2 is better:
1. efficient
2. maintainable
e.g., animation.
Note: we can only modify inline-style via js.
<body>
<style>
div {
width: 100px;
height: 100px;
background-color: red;
}
/*Pre-defined Class*/
.active {
width = '200px';
height = '200px';
background-color = 'green';
}
</style>
<div></div>
<script>
var div = [Link]('div')[0];
// Example 1 - through [Link]
[Link] = function () {
[Link] = '200px';
[Link] = '200px';
[Link] = 'green';
}
<body>
<style>
div {
background-color: red;
width: 400px;
height: 100px;
}
.green::after {
content: "";
width: 10px;
height: 10px;
background-color: green;
display: inline-block;
}
.yellow::after {
content: "";
width: 10px;
height: 10px;
background-color: yellow;
display: inline-block;
}
</style>
<div class="green"></div>
<script>
// read element
var div = [Link]('div')[0];
[Link] = function() {
[Link] = 'yellow';
}
</script>
</body>
Read CSS
1. [Link](ele, pseduElem)
return computed styles in the form of live CSSStyleDeclaration object.
Note:
1. computed styles are styles that has been computed and displayed on screen.
2. unlike [Link] , the CSSStyleDeclaration object style is read-only, we can't set style in it.
3. can be used to select pseudo element.**
<body>
<style>
div {
background-color: red;
width: 400px;
height: 100px;
}
div::after {
content: "";
width: 10px;
height: 10px;
background-color: green;
display: inline-block;
}
</style>
<div></div>
<script>
// read element
var div = [Link]('div')[0];
var styles = [Link](div, null);
[Link]([Link]);
// read pseudo element
var pseudoElemStyles = [Link](div, 'after');
[Link]([Link]); // 10px
</script>
</body>
2. [Link]
function similar to [Link] .
unique to IE only.
read-only
returned style value that is eventually displayed on the screen but not converted to absolute unit.
[Link]([Link]['width']);
Combined for Compatibility
Events
Event: action / anything happens inside the programming system (e.g., browser window).
event occurs => system fires a signal => something else happens.
<div >
Q: look like normal property but how could it interacts with event?
// with annonymous fn
[Link]('click', function() {}, false);
function move() {}
similar to [Link]
[Link]('onclick', function(){})
this
[Link]('onclick', function() {
[Link](div);
});
function handle() {
[Link](this);
}
Compatibility
[Link] = function(){};
[Link] = null; // removed
function demo(){}
unique to IE.
note: annonymous fn handler couldn't be found, thus this method couldn't remove the event listener.
event object, e
When an event occurs, browser creates an event object, containing details of the event.
browser will pass event object into handlers - only in non-IE browsers!
[Link] = function(e) {
// compatibility
var event = e || [Link];
}
1. form submission
2. a tag redirection
3. right click menu
4. etc.
1. return false
only works with inline handler function that assigns event handler directly to a specific event property
of an element.
2. [Link]
W3C standard
IE ver.9 and below not support.
[Link] = function (e) {
[Link]();
}
[Link]('contextmenu', function(e) {
[Link]();
}, false)
3. [Link] = false
support IE
[Link] = function(e) {
[Link] = false;
}
<!--
use of one of the URI schemes=> javascript:xxx to prevent default
similar to return undefined / false
-->
<a href="javascript: void(0);">void</a>
<script>
var a = [Link]('a')[0];
//
[Link] = function(e) {
[Link]();
}
</script>
2. Stop Propagation
look at latter section for more details.
3. [Link] / [Link]
[Link] - firefox support.
[Link] - IE support.
Chrome supports both.
Q: who triggers the event?
the one we click on - known as [Link] / [Link] .
<script>
var wrapper = [Link]('wrapper')[0];
var box = [Link]('box')[0];
// get srcElement - compatibility writing
[Link] = function (e) {
var event = e || [Link];
var target = [Link] || [Link];
[Link]('srcElement: 'target);
}
</script>
Bubbling
default
In a structurally nested elements (non-visually), a triggered event bubbles from the inner child
element to the outer parent element.
In any way, yellow div is the innermost child element, while the red div is the outermost parent
element.
<div class="wrapper">
<div class="content">
<div class="box"></div>
</div>
</div>
<script>
var wrapper = [Link]('wrapper')[0];
var content = [Link]('content')[0];
var box = [Link]('box')[0];
[Link]('click', function(){
[Link]('wrapperBubble');
}, false);
[Link]('click', function(){
[Link]('contentBubble');
}, false);
[Link]('click', function(){
[Link]('boxBubble');
}, false);
</script>
Capturing
The opposite of bubbling.
parent elements captures the event triggerred by child element until that child element.
Note:
IE not support.
Chrome, firefox, opera latest ver. support.
[Link]('click', function(){
[Link]('wrapper');
}, true);
[Link]('click', function(){
[Link]('content');
}, true);
[Link]('click', function(){
[Link]('box');
}, true);
</script>
Triggering Order
Capturing first, Bubbling later.
Note:
Each registered handler function for a specific event type of an element can only follow one event
propagation model (bubbling or capturing).
different registered handler functions can be set to different propagation model - run on different
phase.
events such as focus, blur, change, submit, reset, select don't have bubbling.
<div class="wrapper">
<div class="content">
<div class="box"></div>
</div>
</div>
<script>
var wrapper = [Link]('wrapper')[0];
var content = [Link]('content')[0];
var box = [Link]('box')[0];
[Link]('click', function(){
[Link]('contentBubble');
}, false);
[Link]('click', function(){
[Link]('boxBubble');
}, false);
// Capturing model
[Link]('click', function(){
[Link]('wrapper');
}, true);
[Link]('click', function(){
[Link]('content');
}, true);
[Link]('click', function(){
[Link]('box');
}, true);
</script>
Stop Propagation
1. [Link]()
W3C standard
IE ver. 9 and below not support.
Reason: clicking on div (not having event listener yet) does cause bubbling, propagate to docu
Solution: use [Link] in child element div. The propagation is stop, document will n
*/
[Link] = function() {}
var div = [Link]('div')[0];
[Link] = function(e) {
[Link]();
// other codes.
}
</script>
2. [Link] = true;
initially, unique to IE.
now, it is available in chrome.
Event Delegation
bubbling + srcElement
use case:
Benefits:
1. performance: no need to iterate through every element to register event listener.
2. flexible: adding in new elements without the need of registering event listener.
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<!--...-->
</ul>
<script>
var ul = [Link]('ul')[0];
[Link] = function(e) {
var event = e || [Link];
var target = [Link] || [Link];
[Link]([Link]);
}
</script>
Event Types
Pointer Event
types - case sensitive:
[Link] = function(){[Link]('mousedown');}
[Link] = function(){[Link]('mouseup');}
// mousedown - first
// mouseup
// click - last
Note: Hover in CSS is written using JS pointer event, but more effient - it use engine.
0: left
1: middle wheel
2: right
// listening to whichever button click - left, middle, right using mousedown and mouseup
[Link] = function (e) {
[Link](e);
if ([Link] == 2) {
[Link]('right');
} else if ([Link] == 0) {
[Link]('left');
} else {
[Link]('middle');
}
}
function move(e) {
var event = e || [Link];
[Link] = `${[Link] - posX}px`;
[Link] = `${[Link] - posY}px`;
}
function release(e) {
var event = e || [Link];
[Link]('mousemove', move, false);
}
Q: Is there any capturing method to solve pointer out of element issue during drag and drop?
[Link](); // set
[Link](); // release
// capture every event back to elem itself.
unique to IE.
Keyboard Event
1. keydown:
2. keypress:
trigger (continuously) when key is pressed.
fire only for keys that produces a character values (has associated ASCII code)
ASCII code returned can be converted to string letter.
[Link] = function(e) {
[Link]([Link]([Link]));
}
[Link] = function(){[Link]('keydown');}
[Link] = function(){[Link]('keypress');}
[Link] = function(){[Link]('keyup');}
// keydown - first
// keypress
// keyup - last
charcode is 0.
which & keycode based on what is visually on the keyboard.
can't distinguish small and big cap letters.
can't distinguish key values obtained through shift + key .
e.g., small cap alphabet - 'a' has a ASCII code of 97, but keycode & which only shows ASCII
code of 65 (which represents capital A).
keypress:
Input Event
1. input:
trigger every time a value is modified by the user.
not necessary triggered by keyboard, pasting into input field also works.
[Link] = function(e) {
[Link]([Link]);
}
Change Event
1. change: triggers when an element finished changing.
[Link] = function(e) {
[Link]([Link]);
}
[Link] = function() {
[Link]([Link] + " " + [Link]);
}
Loading
1. load
triggers when a page has fully loaded.
<div></div>
meaningless.
performance wise, the slowest!
Use case:
1. use to alert us the moment the page has been fully loaded and running.
2. use to show advertisement after everything on the page has fully shown up.
Rendering
HTML, CSS, JS ==> display after painting
Q: How are HTML, CSS, JS related?
<!--HTML-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Position Fixed</title>
<style>
/* CSS */
div {
width:100px;
height: 100px;
background-color: red;
}
</style>
</head>
<body>
<div>
<strong>
<em></em>
</strong>
</div>
<script>
// JS
var div = [Link]('div')[0];
</script>
</body>
</html>
// DOM
html
/ \
head body
/ | \ |
meta title style div
|
strong
|
em
<div></div>
<img src="xxx">
<iframe src="yyy"></iframe>
<span></span>
DOM tree + CSSOM tree => Render tree ----- rendering engine -----> paint the page
Js dynamically change DOM tree & indirectly change CSS --------> reflow.
Reflow
recreate render tree, possessing performance issue.
Repaint
acceptable
Asynchronous loading
Normal js: download and execute immediately, blocking HTML and CSS parsing.
use case:
1. defer
download js asynchrously in the background.
only execute when DOM is ready, when html parsing is done - this is also done asynchronously.
Compatibility Issue:
Neither works.
compatibility.
e.g., a button on the page only 0.1% user might click, the js resource can be downloaded dynamically
by need - for optimisation performance.
// Example 1 - setTimeout
var script = [Link]('script');
[Link] = "text/javascript";
[Link] = "/[Link]"; // non-blocking download asynchronously -> send a request, and get feed
// Example 2 - Non IE
var script = [Link]('script');
[Link] = "text/javascript";
[Link] = "/[Link]";
[Link](script);
// Example 3 - IE
// IE has readyState to store the status of downloading resource.
[Link] = 'loading';
[Link] = 'complete';
[Link] = 'loaded';
// Example 4 - Compat fn
// extreme case: when having extremely fast network, resource can potentially download in an ins
function loadScript(url, callback) {
var script = [Link]('script');
[Link] = 'text/javascript';
/* [Link] = url; */ // moving this step after registering event listener.
if ([Link]) {
[Link] = function () {
if ([Link] === 'complete' || [Link] === 'loaded') {
callback();
}
}
} else {
[Link] = function () {
callback();
}
}
[Link] = url; // download resource asynchronously after finished registering event listene
[Link](script);
}
Loading Timeline
Theory
1. create Document object & parsing HTML document: [Link] = loading
2. encounter external css in link element, the browser creates thread for loading, continue parsing
html.
3. encounter external js in script element without async / defer, load js files and execute codes, and
block html parsing.
4. encounter external js with async / defer, the browser creates thread for loading, continue parsing
html.
5. encounter img etc, parsing html as usual to create dom tree, browser asynchronously load src,
and continue html parsing.
6. HTML parsing finished (DOM & CSSOM tree has been created): [Link] =
'interactive'
7. In the meantime, execute deferred Js accordingly
8. document object triggers DOMContentLoaded event: ready to listen to events.
9. Asynchronously loaded js executed + img loaded: [Link] = 'complete' + window
object triggers load event.
10. After that, page works as expected - deal with user interaction and network event asynchronously.
Short summary:
Practicals
1. [Link]
2. [Link]
3. [Link]('DOMContentLoaded', handler, useCapture)
<div></div>
<script>
[Link]([Link]); // loading
</script>
<script>
[Link]([Link]); // loading
[Link] = function() {
[Link]([Link]);// interactive
}
[Link]('DOMContentLoaded', function() {
[Link]([Link]); // interactive
}, false)
[Link] = function() {
[Link]([Link]); // complete
}
</script>
Everything before script has been done parsing and form DOM structure, providing opportunity for
codes inside script element to manipulate them.
In fact, the way we wrote is not really waiting until DOM tree fully created - it is a shortcut.
Ideally, script should be loaded and executed when DOM tree is created, during which
[Link] = 'interactive' & DOMContentLoaded event has been triggered by document.
$(document).ready(function(){
})
<!--Example:
Compared to [Link] = function(){}
1. looks neat and tidy
2. better performance: happens once html parsing finished, not need to wait until all src loaded
-->
<head>
<script>
// with DOMContentLoaded, it can handle DOM element in the body, despite the script is in th
[Link]('DOMContentLoaded', function() {
var div = [Link]('div')[0];
[Link](div);
}, false)
</script>
</head>
<body>
<div></div>
</body>
[Link] = function(){} should only be used when everything has been parsed and loaded. It
is the slowest method and should be used cautiously.
Note: the convention way is the fastest as it is short-cut - loaded and execute before html parsing
finishes.
BOM