Browser
Background
- JS was initially created for web browser
- Nowadays JS has many uses and platforms (browser, web-server, mobile, ml, ...)
- Every platform provides platform specific functionality – called host enviroment
- Host envitoment provides own objects and functions in addition to JS language core.
- Browser gives a means to control web page, Node.js provides server-side features, etc.
JS in the browser
- JS running in browser
- “root” object called window
- Global object for JS code
- Represents the browser window and provides methods to control it
function sayHi() {
alert("Hello");
}
// global functions are methods of the global object
window.sayHi();
alert(window.innerHeight);
DOM - Document Object Model - 1
DOM represents all page content as objects that can be modified.
The document object is the main “entry point” to the page. Change or create anything on the page using it.
// change the background color to red
document.body.style.background = "red";
// change it back after 1 second
setTimeout(() => (document.body.style.background = ""), 1000);
DOM Living Standard - https://dom.spec.whatwg.org
BOM - Browser Object Model
- Represents additional objects provided by the browser (host environment) for working with everything except the document.
- For example
- The navigator object provides background information about the browser and the operating system.
- The location object allows us to read the current URL and can redirect the browser to a new one.
- Functions alert/confirm/prompt are also a part of BOM: they are directly not related to the document, but represent pure browser methods of communicating with the user.
- BOM is the part of the general HTML specification.
- Also, some parts have additional specs listed at https://spec.whatwg.org
DOM Tree
<!doctype html>
<html>
<head>
<title>About elk</title>
</head>
<body>
The truth about elk.
</body>
</html>
<!doctype html>
<html>
<head>
<title>About elk</title>
</head>
<body>
The truth about elk.
</body>
</html>
DOM Tree, walking
- The DOM allows us to do anything with elements and their contents, but first we need to reach the corresponding DOM object.
- The topmost tree nodes are available directly as document properties:
<html>
= document.documentElement<head>
= document.head<body>
= document.body
- document.body can be null
- A script cannot access an element that doesn’t exist at the moment of running.
DOM Tree - childNodes, firstChild, lastChild
- Child nodes (or children) – elements that are direct children. I.e. they are nested exactly in the given one.
<head>
and<body>
are children of<html>
element. - Descendants – all elements that are nested in the given one, including children, their children and so on.
- The childNodes collection lists all child nodes, including text nodes.
- Properties firstChild and lastChild give fast access to the first and last children.
elem.hasChildNodes()
to check whether there are any child nodes.
for (let i = 0; i < document.body.childNodes.length; i++) {
console.log(document.body.childNodes[i]);
}
Searching: getElement*
-
How to get an arbitrary element of the page?
-
If the element has id
document.getElementById(id)
-
Also, there are global variables named by id that references the element (unless overwritten in js) – bad practice!
-
The id must be unique. There can be only one element in the document with the given id.
-
If there are multiple elements with the same id, then the behavior of methods that use it is unpredictable, e.g.
document.getElementById
may return any of such elements at random.
Searching: querySelector*
elem.querySelectorAll(css)
returns all elements inside elem matching the given CSS selector.- Pseudo-classes in the CSS selector like
:hover
and:active
are also supported.document.querySelectorAll(':hover’)
elem.querySelector(css)
returns the first element for the given CSS selector.
Same aselem.querySelectorAll(css)[0]
matches, closest, contains
-
elem.matches(css)
checks if elem matches the given CSS-selector. Returns true or false. -
Ancestors of an element are: parent, the parent of parent, its parent and so on. The ancestors together form the chain of parents from the element to the top.
-
The method
elem.closest(css)
looks the nearest ancestor that matches the CSS-selector. The elem itself is also included in the search. -
elemA.contains(elemB)
returns true if elemB is inside elemA (a descendant of elemA) or when elemA==elemB.
getElementsBy*
-
elem.getElementsByTagName(tag)
looks for elements with the given tag and returns the collection of them. The tag parameter can also be a star "*" for “any tags”. -
elem.getElementsByClassName(className)
returns elements that have the given CSS class. -
document.getElementsByName(name)
returns elements with the given name attribute, document-wide. -
Mostly history, as
querySelector
is more powerful and shorter to write. -
All methods
getElementsBy*
return a live collection. Such collections always reflect the current state of the document
Finding DOM elements
CSS Selectors - 1
CSS Selectors - 2
- https://www.w3schools.com/cssref/css_selectors.asp
- https://www.w3schools.com/cssref/trysel.asp
- https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors
- https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors
DOM tree - collections
- childNodes looks like an array. It’s not an array, but rather a collection – a special array-like iterable object.
- use
for..of
to iterate over it - array methods won’t work – convert to array with
Array.from(document.body.childNodes)
- DOM collections are read-only
- DOM collections are live
- Don’t use
for..in
to loop over collections (iterates also over special properties)
DOM tree, siblings, parent
- Siblings are nodes that are children of the same parent.
- The next sibling is in nextSibling property, and the previous one – in previousSibling.
- The parent is available as parentNode.
// parent of <body> is <html>
alert(document.body.parentNode === document.documentElement); // true
// after <head> goes <body>
alert(document.head.nextSibling); // HTMLBodyElement
// before <body> goes <head>
alert(document.body.previousSibling); // HTMLHeadElement
Element-only navigation
- In
childNodes
we can see both text nodes, element nodes, and even comment nodes if they exist. - We mostly want to manipulate element nodes that represent tags and form the structure of the page.
children
– only those children that are element nodes.firstElementChild
,lastElementChild
– first and last element children.previousElementSibling
,nextElementSibling
– neighbor elements.parentElement
– parent element.
Dom tree, tables
- Certain types of DOM elements may provide additional properties, specific to their type – for example table
- table.rows – the collection of
<tr>
elements of the table. - table.caption/tHead/tFoot – references to elements
<caption>
,<thead>
,<tfoot>
. table.tBodies
– the collection of<tbody>
elements<thead>
,<tfoot>
,<tbody>
elements provide the rows (tr elements)tr.cells
– the collection of<td>
and<th>
cells inside the given<tr>
.tr.sectionRowIndex
– the position (index) of the given<tr>
inside the enclosing<thead>
/<tbody>
/<tfoot>
.tr.rowIndex
– the number of the<tr>
in the table as a whole (including all table rows).
Node: type, tag, contents
- Different DOM nodes have different properties – but some are shared – nodes form a single hierarchy.
- The root of the hierarchy is EventTarget, that is inherited by Node, and other DOM nodes inherit from it.
- DOM nodes are regular JavaScript objects. They use prototype-based classes for inheritance.
console.log, console.dir, IDL
console.log(elem)
shows the element DOM tree.
console.dir(elem)
shows the element as a DOM object - explore its properties.
IDL
DOM classes aren’t described by using JavaScript, but a special Interface description language (IDL).
In IDL all properties are prepended with their types.
https://developer.mozilla.org/en-US/docs/Glossary/IDL
nodeName, tagName, innerHTML
-
Given a DOM node, we can read its tag name from
nodeName
ortagName
properties- The
tagName
property exists only for Element nodes. - The
nodeName
is defined for any Node
- The
-
The
innerHTML
property allows to get the HTML inside the element as a string.- Modifiable. One of the most powerful ways to change the page.
- If innerHTML inserts a
<script>
tag into the document – it becomes a part of HTML, but doesn’t execute. innerHTML+=
does a full overwrite, causing whole DOM tree to be parsed again (expensive)
outerHTML, nodeValue/data
-
The
outerHTML
property contains the full HTML of the element.
innerHTML
+ the element itself. -
Unlike
innerHTML
, writing to outerHTML does not change the element. Instead, it replaces it in the DOM. -
The
innerHTML
property is only valid for element nodes. -
Text nodes, have their counterpart:
nodeValue
and data properties.
textContent, hidden
-
The textContent provides access to the text inside the element: only text, omits all the
<tags>
. -
Reading it is rarely useful, but writing to it makes it safe – text gets htmlencoded.
-
The "hidden" attribute and the DOM property specifies whether the element is visible or not.
- Technically, hidden works the same as
style="display:none"
- Technically, hidden works the same as
more properties
-
There are many more properties, dependent of elements class
-
value
– the value for<input>
,<select>
and<textarea>
(HTMLInputElement
,HTMLSelectElement…
). -
href
– the “href” for<a href="...">
(HTMLAnchorElement
). -
id
– the value of "id" attribute, for all elements (HTMLElement
). -
Most standard HTML attributes have the corresponding DOM property, and we can access it
DOM Properties
- DOM nodes are regular JavaScript objects. You can alter them.
document.body.myData = {
name: "Caesar",
title: "Imperator",
};
- DOM properties and methods behave just like those of regular JavaScript objects:
- Can have any value.
- Are case-sensitive (elem.nodeType, not elem.NoDeTyPe).
document.body.sayTagName = function () {
alert(this.tagName);
};
Element.prototype.sayHi = function () {
alert(`Hello, I'm ${this.tagName}`);
};
HTML attributes
-
When an element has id or another standard attribute, the corresponding property gets created. But that doesn’t happen if the attribute is non-standard.
-
All attributes are accessible by using the following methods:
elem.hasAttribute(name)
– checks for existence.elem.getAttribute(name)
– gets the value.elem.setAttribute(name, value)
– sets the value.elem.removeAttribute(name)
– removes the attribute.
-
HTML attributes have the following features:
- Their name is case-insensitive (id is same as ID).
- Their values are always strings.
Property-attribute sync
- When a standard attribute changes, the corresponding property is auto-updated, and (with some exceptions) vice versa.
- But there are exclusions, for instance input.value synchronizes only from attribute → to property, but not back.
DOM properties are typed
- DOM properties are not always strings.
- For instance, the input.checked property (for checkboxes) is a boolean.
- The style attribute is a string, but the style property is an object.
- Most properties are strings
- Quite rarely, even if a DOM property type is a string, it may differ from the attribute. For instance, the href DOM property is always a full URL, even if the attribute contains a relative URL or just a #hash.
Custom properties
Avoid! Conflicts!
- All attributes starting with
data-
are reserved for programmers’ use. They are available in the dataset property. - Multiword attributes like
data-order-state
become camel-cased:dataset.orderState
.
Modifying the document - 1
- To create DOM nodes, there are two methods
- Creates a new element node with the given tag document.createElement(tag)
- Creates a new text node with the given text document.createTextNode(text)
- Insertion methods
node.append(...nodes or strings)
– append nodes or strings at the end of node,node.prepend(...nodes or strings)
– insert nodes or strings at the beginning of node,node.before(...nodes or strings)
–- insert nodes or strings before node,node.after(...nodes or strings)
–- insert nodes or strings after node,node.replaceWith(...nodes or strings)
–- replaces node with the given nodes or strings.
Modifying the document - 2
-
insertAdjacentHTML/Text/Element
-
elem.insertAdjacentHTML(where, html)
- insert HTML "as html" -
"where" allowed values
beforebegin
– insert html immediately before elem,afterbegin
– insert html into elem, at the beginning,beforeend
– insert html into elem, at the end,afterend
– insert html immediately after elem
Modifying the document - 3
-
Node removal
- To remove a node, there’s a method
node.remove()
.
- To remove a node, there’s a method
-
All insertion methods automatically remove the node from the old place.
-
Node cloning
elem.cloneNode(true)
- creates a "deep" clone of the element – with all attributes and subelements.elem.cloneNode(false)
- clone is made without child elements.
Styles and classes
-
Two ways to style an element:
- Create a class in CSS and add it:
<div class="...">
- Write properties directly into style:
<div style="...">
.
- Create a class in CSS and add it:
-
elem.className
corresponds to the "class" attribute -
elem.classList
is a special object with methods to add/remove/toggle a single class. -
Methods of classList:
elem.classList.add/remove("class")
– adds/removes the class.elem.classList.toggle("class")
– adds the class if it doesn’t exist, otherwise removes it.elem.classList.contains("class")
– checks for the given class, returns true/false.
Element style
- The property
elem.style
is an object that corresponds to what’s written in thestyle
attribute - For multi-word property the camelCase is used
- Browser-prefixed properties like
-moz-border-radius
follow the same rule: a dash means upper case.
border-left-width: 100px
=>elem.style.borderLeftWidth = ’100px’;
button.style.MozBorderRadius = '5px’;
Resetting the style property - elem.style.<propertyname> = ""
elem.style.cssText
– set the full style as string.
Element size and scrolling - 1
Size
Scroll
Element size and scrolling - 2
- Elements have the following geometry properties:
offsetParent
– is the nearest positioned ancestor or td, th, table, body.offsetLeft
/offsetTop
– coordinates relative to the upper-left edge of offsetParent.offsetWidth
/offsetHeight
– “outer” width/height of an element including borders.clientLeft
/clientTop
– the distances from the upper-left outer corner to the upper-left inner (content + padding) corner.
For left-to-right OS they are always the widths of left/top borders.
For right-to-left OS the vertical scrollbar is on the left so clientLeft includes its width too.clientWidth
/clientHeight
– the width/height of the content including paddings, but without the scrollbar.scrollWidth
/scrollHeight
– the width/height of the content, just likeclientWidth
/clientHeight
, but also include scrolled-out, invisible part of the element.scrollLeft
/scrollTop
– width/height of the scrolled out upper part of the element, starting from its upper-left corner (only ones which are not read-only).
Window size and scroll - 1
- To get window width and height we use clientWidth/clientHeight of
document.documentElement
- Not window.innerWidth/Height – these include the scrollbar
- Document width/height – single property in unreliable. Take max of all.
let scrollHeight = Math.max(
document.body.scrollHeight,
document.documentElement.scrollHeight,
document.body.offsetHeight,
document.documentElement.offsetHeight,
document.body.clientHeight,
document.documentElement.clientHeight
);
Window size and scroll - 2
-
For reading scroll position: window.pageXOffset/pageYOffset
-
Change the current scroll:
window.scrollTo(pageX,pageY)
– absolute coordinates,window.scrollBy(x,y)
– scroll relative the current place,elem.scrollIntoView(top)
– scroll to make elem visible (align with the top/bottom of the window).