The DOM is the Document Object Model - the way in which a HTML page can be represented in JavaScript objects. This means that JavaScript can then be used to read data from these objects, and also to manipulate them. A full DOM reference is available here, however this tutorial provides a simple introduction to simple element selecting and manipulation, which ought to be more than enough to get started!
The simplest way to get started with DOM element selection is to select by ID. You give the HTML element you wish to select an ID value, and then use the DOM's document.getElementById("whatever-the-id-was") function to select the object. An example of this (with associated HTML) is below...
<html>
<head>
<title>DOM Test</title>
</head>
<body>
<p id="my-paragraph">This is a paragraph</p>
<script>
/* this JavaScript is ran after the paragraph above (that we're selecting) is rendered */
var paragraph = document.getElementById("my-paragraph");
/* paragraph now contains the p element we wanted to select, let's check... */
console.log(paragraph);
</script>
</body>
</html>
As noted in the code comments above, you should wait until the page elements are rendered before selecting them. You can do this by placing your JavaScript in the page after the elements, or by some more sophisticated methods which we'll discuss later on.
QuerySelectors provide a far more flexible way of selecting HTML elements from the DOM. Instead of using just ID's, they allow you to use the same selectors as in CSS to select one or multiple matching elements from the DOM. This means you can select all instances of a specific elements, specific class, or more complex selectors such as direct child (e.g. div > p) - just like in CSS.
The relevant JavaScript methods are document.querySelector() and document.querySelectorAll().
The major difference between these is that querySelector selects only ONE element (the first one it matches), whereas querySelectorAll returns an array of ALL of the matched elements - making it far more useful. Both return nil if no elements are matched. An example of these methods in action is below...
<html>
<head>
<title>DOM Test</title>
</head>
<body>
<h1>A big heading</h1>
<p class="fancy-paragraph">This is a paragraph</p>
<p class="fancy-paragraph">This is another paragraph</p>
<p class="fancy-paragraph">And this is yet another paragraph</p>
<script>
/* this JavaScript is ran after the paragraph above (that we're selecting) is rendered */
/* select the one H1 heading... see how we're using the h1 selector - just like in CSS */
var heading = document.querySelector("h1");
/* check we got the heading... */
console.log("The heading is " + heading);
/* select all of the .fancy-paragraph's into an array... */
/* see how we're using the .fancy-paragraph selector -
just like in CSS - to select all instances of that class */
var paragraphs = document.querySelectorAll(".fancy-paragraph");
/* loop through our paragraphs... */
for (var i = 0; i < paragraphs.length; i++) {
/* get the paragraph from the array */
var currentParagraph = paragraphs[i];
/* let's log it too... */
console.log("Fancy paragraph " + String(i) + " is " + currentParagraph);
}
</script>
</body>
</html>
Whilst CSS selectors such as the direct child selector work with QuerySelectors, sadly the CSS pseudo classes such as first-child don't, so consideration must be made for this.
One of the most important features that JavaScript provides to make web pages interactive is the ability to get and set the content of elements.
Firstly, use the QuerySelectors described above to get the element, then, once the element is in a JavaScript variable, JavaScript allows you to very simply and effectively get the text within via the .textContent property - or set the text within by setting a new value to the .textContent property - as in the example below.
To get and set raw HTML, simply replace .textContent with .innerHTML.
<html>
<head>
<title>DOM Test</title>
</head>
<body>
<h1>A big heading</h1>
<p class="fancy-paragraph">This is a paragraph</p>
<p class="fancy-paragraph">This is another paragraph</p>
<p class="fancy-paragraph">And this is yet another paragraph</p>
<script>
/* lets practice some getting... */
/* select the one H1 heading... see how we're using the h1 selector - just like in CSS */
var heading = document.querySelector("h1");
/* check we got the heading... */
console.log("The contents of the heading is " + heading.textContent);
/* now lets practice some setting... */
/* select all of the .fancy-paragraph's into an array... */
var paragraphs = document.querySelectorAll(".fancy-paragraph");
/* loop through our paragraphs... */
for (var i = 0; i < paragraphs.length; i++) {
/* get the paragraph from the array */
var currentParagraph = paragraphs[i];
/* set the paragraph's content to be the number in the array that it's at... */
currentParagraph.textContent = String(i);
}
</script>
</body>
</html>
A video example of using QuerySelectors to get and set the content of HTML elements is below so you can follow along...
Another important way in which elements can be made interactive with JavaScript is by changing their style.
The DOM provides ways to add, remove and alter CSS styles - both in terms of individual style rules, and also in terms of adding ID's and classes to elements, to style them differently.
The simplest way to modify styles is by using the .style property of elements - this property allows you to change any CSS property simply by appending the property to .style - so for example, to change the background colour of 'myElement', you'd call myElement.style.background-color = "rgb(20, 20, 20)"; on the element you've retrieved (from a QuerySelector for example).
You can also style by adding classes or ID tags - for example, to add a class to 'myElement', you'd call myElement.classList.add("newClass") (you can also first use the classList property - which is simply a standard JavaScript array - to check which classes are currently applied to the element).
Additionally, you can add an ID to a selected element using its .id property - for example myElement.id = "cool-unique-id";
An example of some simple and some more complex styling is below...
<html>
<head>
<title>DOM Test</title>
<style>
.cool-text {
background-color: rgb(0, 255, 0);
color: rgb(255, 255, 255);
}
#littleish-heading {
font-size: 16pt;
font-weight: bold;
}
</style>
</head>
<body>
<h1>A big heading</h1>
<h2>A 'lil heading</h2>
<p>This is a paragraph</p>
<script>
/* let's change the heading colour... */
/* select the one H1 heading...*/
var heading = document.querySelector("h1");
/* set it's 'color' style attribute */
heading.style.color = "rgb(255, 0 ,0)";
/* now let's practice adding classes... */
var paragraph = document.querySelector("p");
/* add the cool-text class... */
paragraph.classList.add("cool-text");
/* now let's add an ID to the littler heading (h2) */
var smallHeading = document.querySelector("h2");
/* add the ID... */
smallHeading.id = "littleish-heading";
</script>
</body>
</html>
The next stage of using JavaScript with the DOM to make HTML pages interactive is to add event listeners, meaning that JavaScript functions can be called when certain things happen on the page (such as specific elements being clicked or hovered over, etc.).
A full list of events is available here, but in this tutorial we'll be specifically focusing on adding 'on click' events - although the principle is much the same for any type.
Firstly, you must select the element you wish to add the event to, and then set its .onclick property to a function (remembering here that functions in JavaScript can be passed as variables!).
An example of the onclick listener being added is shown below...
<html>
<head>
<title>DOM Test</title>
</head>
<body>
<p id="click-me">Click me!</p>
<script>
/* write the function to call when clicked */
function thisHappensOnClick() {
// here's the code to be executed on click...
console.log("You clicked!");
}
/* select the clicking element...*/
var clickable = document.querySelector("#click-me");
/* add the function to an onclick listener */
clickable.onclick = thisHappensOnClick;
</script>
</body>
</html>
Because JavaScript supports functions as variables, you can simply pass a function straight to the onclick (e.g. myElement.onclick = function() { /* do stuff */ }) - this use of anonymous functions stops many small functions being created just for button clicks etc.
A video example of adding anonymous event listeners is shown below...
A crucial kind of event to add is an onload event. This is because you cannot know for sure otherwise that the HTML elements within the DOM have been rendered by the browser yet when your JavaScript is ran - and if they haven't then your code won't be able to affect them.
This is clearly a major issue (and has been left out of previous Jschool examples to cut down on code complexity, but may be why some examples don't work straight away).
To ensure that the DOM has been fully rendered before executing your script, you should add an event for the window's .onload event handler (via window.onload). An example of this in action is below...
<html>
<head>
<title>DOM Test</title>
</head>
<body>
<script>
/* define the function to call on DOM load*/
function loaded() {
// here's the code to be executed when the DOM has loaded...
console.log("The DOM has loaded - let the fun and games begin!");
}
/* add the function to the window's onclick listener */
window.onload = loaded;
</script>
</body>
</html>
Some legacy browsers may have poor support for onload events, which when it is such a crucial event is a major issue - however, JavaScript frameworks - like we'll cover later on - can solve this to improve older browser compatibility.