Introduction

Vanilla JavaScript refers to pure JavaScript code that is used for the client-side of web programs. This was JavaScript's original purpose when it was adopted by Internet Explorer in 1995: to breathe life into static HTML pages. This all changed, however, in 2009 when Node.js was created, allowing JavaScript to be used to write back-end code. Many frameworks have since been created to solve the many challenges that have arisen in web development. Some notable front-end frameworks and libraries have been built to help create easily scalable applications, most notably jQuery, React.js, and Angular.js. Back-end frameworks and libraries have also been created more recently, e.g. Express.js, Next.js, and Gatsby.js.

While there are many cutting edge frameworks, many opt to use pure JavaScript code (Vanilla JS) to directly manipulate elements in the Document Object Model (DOM), a programming interface for web documents. As implied by the name, all web pages are organized into objects that can be manipulated directly using a programming language like JavaScript, a language that supports object-oriented programming. To read more about JavaScript as a programming language, refer to freeCodeCamp's technical documentation or Mozilla's JavaSript documentation.

Setup

To add javascript to an HTML document, valid JavaScript code can be placed within the following HTML tags:

<script type="text/javascript"></script>

If one wants to write javascript code in a separate document, an href attribute can be added to the script tag. If in the same directory, prefix the file name with ./. If in another directory, use ../ to go back to the parent directory and / to get to the root of the current drive. See below for an example of linking a JavaScript file called 'index.js' that's in the same directory as the HTML page.

<script type="text/javascript" href="./index.js"></script>

Keep in mind that the order of the placement of this tag matters within the HTML document. If placed before the body elements, any elements in the body selected in the JavaScript file cannot be manipulated (as they haven't been declared yet). To not have to put the script tags after the html file, the Document event listener DOMContentLoaded can be called first and all JavaScript code can be nested inside.

document.addEventListener('DOMContentLoaded', function() {

});
Selectors

To dynamically manipulate web page, e.g. changing a div's contents upon pressing a button, an element of an HTML document must first be selected. There are a few methods for selecting documents. The first uses an object's class, id, or tag name. They are as follows:

Document.getElementById(idName)
Document.getElementsByClassName(className)
Document.getElementsByTagName(tagName)

The first method in the list returns an Element object while the latter two return an HTMLCollection (array-like object) of Element objects. These methods can only be used on the global document object. This means that the methods cannot be used on other objects that are within the document, e.g. a div element or a paragraph element.

A second set of methods take CSS selectors as arguments to select elements in the DOM:

Document.querySelector(selector)
Document.querySelectorAll(selector)

The first method in the list returns the first Element object while the latter returns an HTMLCollection of of Element objects. These methods can be used on all objects in the DOM.

Here are some examples of code that could be used to select elements on this webpage.

document.querySelectorAll('#navbar a')
document.getElementsByClassName('block-code')
document.getElementById('navbar').querySelectorAll('a')

The Document class, which implements the Node and the EventTarget interfaces.

Accessing Properties

The Element class is the most basic class from which all the elements in the DOM inherit. The class itself inherits from the Node interface, which itself inherits from the EventTarget interface. There are a series of properties of the Element class that can be accessed, some of which are read-only and others which are mutable.

Below is a sample of read-only properties that can be accessed within the Element class.

Below is a sample of mutable properties that can be accessed within the Element class.

Element.innerHTML is particularly useful when needing to change the contents of an HTML element. Oftentimes, this will select the text of an element. However, if the element is a complex group of nested elements, the Node.textContent property from the Node interface can be used. Element.outerHTML selects the contents of the HTML element and the tags which define the element itself.

Refer to the Mozilla documentation on the Node interface and the Element class to see other methods and properties available when selecting HTML elements, which are children of the Node class.

As noted in the previous section, when multiple elements within the DOM are selected, i.e. with Document.querySelectorAll() or Document.getElementsByClassName(), an HTMLCollection is returned. Instances of the HTMLCollection have very limited functionality. As noted in the HTMLCollection Mozilla documentation, the class only has 1 property and 2 methods. It can be helpful to use Array.from() to transform this iterable object into an Array in order to take advantage of the large functionality of the Array class.

Modifying Class

In the Accessing Properties section, one can predict that the properties classList and className must be similar. The former is immutable (read-only) while the second one is mutable. This means that the classList property cannot be set to any value and be changed. While the property className can be set to a value, all potential classes of an element will be replaced by whatever string is entered.

To add a class to an element without getting rid of other classes associated with an element, use the DOMTokenList method add() on the returned value of Element.classList. Similarly, the methods remove(), replace(), and toggle() are also available to instances of the DOMTokenList class.

Some other methods available may look familiar. Those who have worked with the Array class may recognize the forEach() method. Similarly, the methods entries(), keys(), and values() seem to match up with the built-in JavaScript type Object.

Modifying Styles

There are a few ways to modify styles using JavaScript. All require a CSSStyleDeclaration object to be exposed. They are as follows:

  1. HTMLElement.style
  2. The CSSStyleSheet API, e.g. CSSStyleSheet.cssRules
  3. Window.getComputedStyle()

The simplest and most common way to access the styles is probably the first. The return value is a CSSStyleDeclaration object with keys representing CSS properties mapped to their respective values. However, keep in mind that this way only reveals the inline styles, that is, the styles applied to HTML tags directly using the style attribute. Since JavaScript is compiled differently from CSS, the CSS properties are converted such that the dashes are removed and camel case is applied. For example, in JavaScript, the CSS property background-color is converted to backgroundColor and border-bottom-color is converted to borderBottomColor. Refer to W3 Schools HTML DOM Style Object for more examples.

To change the backgroundColor of the main section of this HTML page, the following statement can be executed.

document.getElementById('main-section').style.backgroundColor = 'yellow';

When using the second way to expose the styles, a DOMExceptionError may be thrown as accessing a stylesheet loaded from the local filesystem violates a CORS policy. Below is an example of the code that would be used to reveal the first CSS rule in the document's first style sheet.

document.styleSheets[0].cssRules[0].style

The final way to expose the styles takes the information directly from the window after applying the styles and calculating any values that must be calculated. Here is some sample code that can be used to select the color property of the :

window.getComputedStyle(document.querySelectorAll('.main-section')).getPropertyValue('color');
Event Listeners

As mentioned earlier, the Node interface inherits from the EventTarget interface. This means that all methods available to EventTarget are available to instances of the Element class. This means that the methods addEventListener(), removeEventListener(), and dispatchEvent() can be used on HTML elements selected in the DOM.

This means that an element can "listen" for a particular event to trigger a change written in JavaScript code. These changes can be much more detailed than any that could be written using CSS. The 2 required parameters of the addEventListener() method are type followed by the listener, which will be some type of function trigged once an event of that type occurs. Below is a table of common event types that are associated with the Element class. Refer to the Mozilla event reference for a full list.

Event Event Type Event Description
click MouseEvent A click event fires when a pointing device is pressed (a mousedown event) and released (a mouseup event) while the pointer is inside the element
mousedown MouseEvent A mousedown event fires when a pointing device is pressed (but not necessarily released) while the pointer is inside the element
mouseover MouseEvent A mouseover event fires when a pointing device is used to move the cursor onto an element or its children
keydown KeyboardEvent A keydown event fires when a key is pressed even if it doesn't produce a character value; it's an updated version of the keypress event
select Event A select event fires when text is selected, i.e. highlighted

As can be seen in the table, events are grouped by type. Just like with HTML elements, events have properties that can also be accessed and modified using JavaScript. For events of type KeyboardEvent, it's common to want to access the code property. For example, pressing 'a' will trigger a keydown event with a code property of 61. For events of type MouseEvent, it's common to want to access the coordinates of the event. The properties x and y will return the coordinates within the whole document while the properties pageX and pageY will return the coordinates relative to the whole document.