DOM is not only for browsers. A server program can walk through DOM.
-
Anything in HTML is represented in it's DOM Tree, which includes even the whitespace, newlines and comments along wiht the tags.
-
The DOM tree of a web page starts with root element as
document. It has twochildNodesin general: -
There are 12 types of nodes in a DOM Tree (doctype, document, element, attribute, text, comment etc)
Text node
- Whitespace anywhere inside a
<head>tag and<body>tag is represented astextnodes in DOM. And, the whitespace between<head>tag and<body>tag is also represented astextnode in DOM. Other whitespaces are ignored. - The text inside an element is also represented as
textnode. - So the text node is just a string, which has no children, and is always the leaf node of a DOM tree.
- You can traverse through the DOM in two ways.
| Walk over all types of Nodes (including whitespace, comment, element etc) | Walk over only element Nodes |
|---|---|
![]() |
![]() |
Note:
document.body.firstChild may not be your first tag element inside HTML body. It could be a whitespace text node if your HTML is formatted with spaces like a normal page. But, document.body.firstElementChild is always gonna be the first tag element.
-
To iterate the nodes collection returned by DOM methods, you can try any of the following:
for..ofiteration on collectionfor..initeration on collection withhasOwnPropertycheck- ordinary
for()loop iteration on collection - convert collection into array and iterate using array methods like:
Array.from(collection).forEach()
The properties and methods present in a tag are inherited from a hierarchy of different classes.
For example, for an anchor tag
- anchor tag specific attributes such as
hrefare inherited fromHTMLAnchorElementclass - common html attributes are inherited from
HTMLElementclass nextElementSiblingproperty inherited fromElementclassnextSiblingproperty inherited fromNodeclasseventsare attributed fromEventTargetclass
A node object is like a regular JavaScript object. So, you can add new properties and methods to it.
You can use console.log(element) to explore the HTML representing that element,
whereas console.dir(element) to display the object representing that element.
nodeType
nodeValue/data- is used to get content of a nodes (other than element type)nodeName- is used to get the name of "any nodes" (including element nodes)tagName- is used to get only the name of "element nodes"innerHTML- is used to get/set HTML content of an element (valide only for element nodes)textContent- is used to get/set text content of an element (all HTML tags are stripped off during get, written as text during set)outerHTML
You can access the attributes of an element using it's object model like inputElem.value. But, some attributes exist only on certain type of nodes. Also, using object model you cannot access custom attributes you added to an element. So, there is a better way to access any attributes.
elem.hasAttribute(name)elem.getAttribute(name)returns string value of that attributeelem.setAttribute(name,value)elem.removeAttribute(name)elem.attributes- get collection of all attributes as attribute objects that inherits fromAttrclass. Then you can callattrObj.name,attrObj.valueto get attribute details.
Attributes are case-insensitive. But, other DOM properties in an element object are case sensitive.
When a user types some input element's value, that new value is updated in the DOM object property. But, that will not be updated in attribute. So, attribute always sticks to the value which was present in the HTML. Changes to that can be tracked from object property whereas original value can be tracked from attribute itself.
You can mark any custom value to an element using CSS classes. But, they are least elegant for this use case. Better approach is to use custom attributes. But, there is a catch that as HTML is a living standard, they might introduce an attribute similar to your own custom attribute. To avoid that conflict you can use data-* attribute. Any custom attributes you add with prefix data- will not conflict as they are reserved for programmers. And, such values are also accessible via elem.dataset property.
//HTML
<body data-hobby="cycling">
//JavaScript
document.body.dataset.hobby // cycling
//CSS
body[data-hobby] {
color:red;
}
//HTML
<body data-professional-hobby="cycling">
//JavaScript
document.body.dataset.professionalHobby // cycling
//CSS
body[data-professional-hobby] {
color:red;
}
elem.classNameelem.classList
elem.style.{camel cased css property}elem.style.cssTextto rewrite css entirelygetComputedStyle(elem, [pseudo])
-
Complete geometry of an element
Summary
offset*is measured from the borderline of element:- offsetParent
- offsetLeft
- offsetTop
- offsetWidth
- offsetHeight
client*is measured from the inner content (content width + padding)- clientLeft = borderWidth (but in case of arabic, borderWidth + scrollbar width)
- clientWidth = contentWidth + padding
The events associated with page load are :
DOMContentLoaded- this event is fired when HTML and JavaScript is loaded, DOM is ready, and all JavaScript has finished execution. This event can ignore the following modes of script tags :<script type="module">orasync. At this time both images<img>and CSS are not loaded.loadbeforeunloadunload. At this time, you can send any last moment statistics to server bynavigator.sendBeacon(url,data).
document.readyState
There are 3 ready states which indicates different stages of page load.
loadinginteractiveis at the same time asDOMContentLoadedevent firescompleteis at the same time asloadevent fires
Yuo can listen to these document state changes from event readystatechange.
Load scripts in defer & async mode
When HTML is followed by <script> tag, script gets access to the DOM rendered from HTML above. But, if a <script> is before HTML, then it doesn't get visibility to those HTML. Also, browser would wait for the <script> to load and run before parsing rest of HTML.
To improve this, it is better to write <script> tags at the end of document. But, there are even better strategies:
deferattribute on<script>tag lets browser to load that script only after all the HTML has been loaded already. But, this works only if there is ansrcattribute to load JavaScript externally.DOMContentLoadedevent will fire only afterdeferscripts are also loaded.asyncattribute on<script>tag works completely independent. It may load even before HTML is loaded, or may load later. So, use this approach only for DOM independent scripts such loading advertisement, google analytics etc.DOMContentLoadedevent doesn't care aboutasyncscripts.
When you dynaically create <script> using JavaScript and insert into document, it works like an async script. To change it into a regular script, use property <script-element>.async = false.
Track loading of external resources
Any external loading of resources - <script src=""> or <img src=""> or <iframe> can be tracked using two events :
load- fires on that element when loading is completed successfullyerror- fires on that element when loading fails. An exception to this is<iframe>which fires onlyloadeven if it fails. You can usewindow.onerrorto catch any JavaScript errors in page.
Create a new Range object
let range = new Range();
Range object properties:
range.startContainerrange.startOffsetrange.endContainerrange.endOffsetrange.commonAncestorContainer
Range object methods to select nodes:
range.setStart(node.offset)range.setEnd(node,offset)range.setStartBefore(node)range.setEndBefore(node)range.selectNode(node)range.selectNodeContents(node)range.cloneRange()
Range object methods to manipulate nodes:
range.deleteContents()range.extractContents()range.cloneContents()range.insertNode(node)range.surroundContents(node)range.collapse(toStart)
Selection events
selectstartselectionchange
Selection methods to operate on Range
selection.getRangeAt(index)selection.addRange(range)selection.removeRange(range)selection.removeAllRanges()
Form input selection properties:
input.selectionStartinput.selectionEnd
Form input selection methods:
input.select()input.setRangeText(replacement)
Form input selection events:
select
Example to add range to document selection;
let range = new Range();
range.setStart(node,offset);
range.setEnd(node,offset);
let selection = document.getSelection();
selection.removeAllRanges()
selection.addRange(range);


















