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.
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() {
});
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.
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.
Element.children
Element.classList
Element.nextElementSibling
Element.tagName
Element.classList
Below is a sample of mutable properties that can be accessed within the Element
class.
Element.className
Element.id
Element.innerHTML
Element.outerHTML
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.
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
.
There are a few ways to modify styles using JavaScript. All require a CSSStyleDeclaration
object to be exposed. They are as follows:
HTMLElement.style
- The
CSSStyleSheet
API, e.g.CSSStyleSheet.cssRules
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');
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.