Why am I taking this course?
Though I've been coding for about 3 years now since I finished my boot camp at the tail end of 2020, and have used React extensively during my stops at LeanIX and Twilio, I found that I still had gaps in my mental model of what the underlying implementation of VanillaJS was doing to render out my JSX, and ultimately, the components I was building. Though I know enough to build things and be a productive engineer, I wanted to take the time to immersive myself in the VanillaJS world and DOM APIs from first principles to solidify my understanding of JavaScript itself and any frontend library or framework that I'll come across in my software engineering journey.
What is VanillaJS?
VanillaJS is the usage of the core language and browser APIs to create web apps without any additional libraries or frameworks added on top.
Why Vanilla JS?
Add one more tool to my toolbox
Understand what my library is doing
Extend my library with plugins
Be a better web developer
To mix with libraries
Frontendmasters.com is written in VanillaJS!
To use it! Can create simple and fast web apps with no CLI, no build process
Main advantages of VanillaJS (according to Max Firtman):
Lightweight
Control and Power
Simplicity
Flexibility
Performance
Compatibility
No node-modules
Main fears of VanillaJS (according to Max Firtman):
Routing for Single-Page Applications
Too verbose and time-consuming
State Management
Templating
Complexity
Reusable Components
Maintenance
Learning Curve
Browser Compatibility
Reinventing the wheel every time
Scalability
The DOM API
The DOM API is available on many objects:
window global object
- windowdocument object
- represents the current DOMone object per HTML element and other nodes in our document
<html>
<body>
<header>
<h1> The DOM</h1>
<p class='tagline'> Document Object Model</p>
<img hidden src='dom.png'>
</header>
</body>
</html>
Each element is represented by an object (class) of the HTMLElement interface or other interfaces that inherit it
They have instance properties and methods
Changes in properties or children will trigger updates in the user interface when we release the thread.
What does it mean to release the thread in JS?
When our function ends (event handler finishes)
If we don’t release the thread, the browser doesn’t have enough time to relay out repaints, re-renders, etc and so the browser won't be able to engage with the user (it will ask to kill the process). When we have high-intensity CPU usage, we should instead put that in a Web Worker
We can listen to events happening in that element and react as a consequence
To work with DOM elements, we can
Pick them from the current DOM
Create them and then inject them into the DOM
To have a reference to an Element we can:
Read its content
Change its content
Remove it
Add new elements
Finding Elements in the DOM
Select elements from the DOM
By ID
By Class Name
By Name
By CSS Selector
Navigating DOM structure
When selecting elements, some functions return:
One HTML Element: (HTMLElement)
Live HTML Element Collection: (HTMLCollection) allows you to change collections on the fly
Static Element Collection (NodeList): Have modern array-like methods (
map
filter
reduce
, etc.)
Most common functions to get a reference to one DOM element
getElementById
: To get one element, if it has ID. From the first-ish version of the DOMquerySelector
: More modern, will let you get the first element matching CSS selector
null
if no node was found.const element = document.getElementById('one-item');
if (element !== null) {
//element found
}
Most common functions to get a reference to multiple Dom elements:
getElementsByTagName
getElementsByClassName
querySelectorAll()
: a static collectiongetElementsByName
In the following example, elements
is a static NodeList
. This means that the collection we receive has a length property and can be accessed as an array using []
syntax and, since it is array-like, it has the array of modern interfaces we know.
const elements = document.querySelectorAll('#nav-menu li');
if (elements.length > 0){
//elements found
const firstElement = elements[0];
}
//can also use forEach
elements.forEach(element => console.log(element));
In our next example, our elements
variable is a live HTMLCollection
, and so the collection we receive has a length property as well and can be accessed using []
syntax, BUT it's not an Array, so we have no access to all the common methods we would expect.
const elements = document.getElementsByClassName('important');
//no forEach() available, so need to use for loop
for (let currentlElement of elements){
//we process each currentElement
}
HTMLCollections (live) don’t have all the modern Array
interface, such as filter
, map
, reduce
, or forEach
. We can add modern array functions to it by creating an Array
from it using Array.from(collection)
:
// we enhace the HTMLCollection into an Array
const elements = Array.from(document.getElementsByClassName('important'))
// we can use array methods now
elements.filter(e => e.tagName === 'p');
With an HTML Element in JavaScript we can:
Read and change attributes’ values
Read and change styles
Hook event listeners
Add, remove or move children elements
Read and change its contents
More APIs
Modifying the DOM
How to read or change attributes of a Dom element
We can use dot syntax to access properties mapped from HTML attributes. Most attributes have the same name as JS properties but exceptions apply, such as className
and htmlFor
.
element.hiddern = false;
element.src = 'logo.png';
element.className = 'myClass'; // class HTML attribute
How to read or change styles of a DOM element:
We can use dot syntax to access a style object. That object will have a map to every CSS property we can inline in HTML. Just remember to change kebab syntax (-) to camel case syntax for JS.
element.style.color = 'blue';
element.style.fontSize = '1.2em'; // font-size
element.style.borderRightColor '#FCFCFC'; // border-right-color
How to listen to events of an HTML element
We can also use dot syntax and bind a function to an event name using addEventListener
. There is a list of standard events.
function eventHandler(event) {
//do something
}
element.addEventListener('click', eventHandler);
//OR
element.addEventListener('click', function(event) {
//do something
});
//OR
element.addEventListener('click', () => {
//do something
});
Accessing and Editing Contents of the Elements
Accessing
textContent
: access and edit content, does not support HTMLAccess
innerHTML
: supports HTML, can define whole webpage HTML asstring
Using DOM APIs to create new nodes
Working with textContent:
const element = document.querySelector('#message');
//We read the current element's content as string
const contents = element.textContent;
//We change the contents of the element with a new string
element.textContent = "The text has been changed";
Working with innerHTML:
const element = document.querySelector('#section-6 header');
//We read the current element's HTML content as string
const contents = element.innerHTML;
//W3 change the contents of the element with a new HTML string
element.innerHTML = `
<h1>My App</a>
<p>The best platform for learning frontend</p>
`;
innerHTML
for HTML elements, since there's no way of validating that the string is valid HTMLWorking with DOM APIs to create or edit content:
const element = document.querySelector('#section-6 header');
const h1 = document.createElement('h1');
h1.textContent = 'My App';
element.appendChild(h1);
const p = document.createElement('p');
p.textContent = 'The best platform for learning frontend';
element.appendChild(p);
appendChild
allows us to append an element, append()
only allows us to use strings, not elementsThat's it for Day 1! Thanks for reading this far and stay tuned for my progress in Day 2 in my series!