JavaScript is the defacto language for the web at the time of this writing. To understand the basics of how to do simple Document Object Model
mutations and manipulations is something that many newbies start off with.
IMPORTANT: The code snippets for this section will only work on modern browsers that support ES2015 or beyond. For the most part, this will work in Google Chrome, Firefox Quantum, and Microsoft Edge. These examples will not work with Internet Explorer 11 or below
For this section, I am already assuming you, the reader, understand the basics of the following concepts:
id
HTML attributeclass
HTML attributeWith that out of the way, let's look at some examples :).
In this section, I'm going to refrain from using common libraries that function as convenience methods or wrappers around vanilla JavaScript, like lodash
, underscore
, or jQuery
. While these particular tools have been instrumental in the development of the language, the features and functionality these libraries have been adopted into the JavaScript language itself.
For the most part, Elements and Nodes are one in the same. In practical usage, these terms are used interchangeably, since Elements are technically Nodes.
Node
is the generic name for any type of object in the DOM.
Element
is one specific kind of Node
- it can be a Text Node
, a Comment Node
, a <div> Node
For your day-to-day work as a developer, this stuff is good to know, but from a pragmatic and practical standpoint, they're very much similar, and in my typical day-to-day, (opinion!) I use the term "Element."
HTMLCollection
and NodeList
are extremely similar, in that they're both return
types from very common document
methods like querySelectorAll
and getElementsByClassName
.
The biggest difference between the two is:
HTMLCollection
is a live and updating reference to a list of Elements
. As elements on the page change, so do the values inside of HTMLCollection.
getElementsByClassName
, and getElementsByTagName
will return an HTMLCollection
.
NodeList
is static and non-updating. If the Elements or nodes on the document change, the values inside of NodeList will not change.
querySelector
and querySelectorAll
return a NodeList
.
For reference, you can look at the extensive MDN documentation for both HTMLCollection and NodeList
HTMLCollection and NodeList return an "Array-like" object that you can use in a loop without any problems.
const selectedElements = document.getElementsByClassName('js-selected');
selectedElements.length // => a Number
selectedElements[0] // => First element found (<div class="js-selected">...</div>)
But we won't be able to do things like this:
// Grab all our form elements
const formElements = document.getElementsByTagName('form');
selectedElements.forEach( form => { /* ... */ }); // => Error! forEach is undefined
HTMLCollection doesn't have all the Array
methods on it like map
, forEach
, reduce
, join
, etc.
If you want to get around this, you can use Array.from()
and create a new array from an HTMLCollection
or NodeList
.
const selectedArray = Array.from(document.getElementsByClassName('js-selected'));
selectedArray.forEach( item => { /* ... */}); // Works!
selectedArray.map( item => { /* ... */}); // Also Works!
selectedArray.reduce( (accum, item) => { /* ... */}); // Yay! It works!
A strange oddity to note is that NodeList
does have .forEach
, but it's only supported in modern browsers. There are some very nuanced differences in methods between NodeList
and HTMLCollection
, but for the most part, these are "good to know" concepts and tools.
At the heart of any Javascript framework like React
, Angular
, or Vue
is one major component - DOM Manipulation. It's important to know that no matter what you're doing on the web to make elements appear, disappear, move, grow, shrink, etc - you're doing DOM Manipulation.
This is a small reference of what I find to be the most important parts of basic DOM manipulation that I strongly feel every developer should know. While vanilla DOM manipulation in single page applications is seldom, if not frowned upon (looking at you React
!) it's still good to know and have this knowledge should someone fall into limitations where they can't use tools or frameworks to do simple things.
Running document.getElementsByClassName()
grabs all the elements on the HTML document. Returns an HTMLCollection
.
// Grab all of the bold text elements on the page:
const boldTextElements = document.getElementsByClassName('bold');
Since class
attributes are often attributed to styling with CSS, you can grab particularly styled elements (like bold text or selected elements) and restyle them with classList
methods.
/**
* Unbolds all the elements on the page
* @return {undefined}
*
*/
function unboldElements() {
// Using Array.from() so we can use forEach
const boldTextElements = Array.from(document.getElementsByClassName('bold'));
boldTextElements.forEach( boldText => {
boldText.classList.remove('bold');
});
}
This grabs all the elements by their tag name, like div
, span
, article
, or form
. Returns an HTMLCollection
.
// Grab all the <form> elements
const forms = document.getElementsByTagName('form');
If there's no matching tags, an empty HTMLCollection
will be returned.
Grabs only the first element from a CSS selector string
// Grabs the first <form> element on a page
const form = document.querySelector('form');
// Grabs the first element with the class "selected"
const selectedItem = document.querySelector('.selected');
Returns null
if there's no match.
Same as querySelector
, except it returns a NodeList
of all matching elements.
// Grab ALL elements with the `selected` class
const allSelectedItems = document.querySelectorAll('.selected');
Returns an empty NodeList
if nothing is found.
Element.classList
contains all of the class name operations one could ever ask for:
Takes a string or strings as arguments. Adds the class(es) you pass in:
Single class to add
// <div id="my_element" class="some-class"></div>
const element = document.getElementById('my_element');
element.classList.add('selected');
// <div id="my_element" class="some-class selected"></div>
For multiple classes:
// <div id="my_element" class="some-class"></div>
const element = document.getElementById('my_element');
element.classList.add('selected', 'magical', 'unicorn');
// <div id="my_element" class="some-class selected magical unicorn"></div>
Removes the class(es) passed in.
NOTE - Removing classes that aren't on the element will not throw an error.
Removing one class
// <div id="my_element" class="some-class selected"></div>
const element = document.getElementById('my_element');
element.classList.remove('selected');
// <div id="my_element" class="some-class"></div>
Removing multiple classes
// <div id="my_element" class="some-class selected magical unicorn"></div>
const element = document.getElementById('my_element');
element.classList.remove('selected', 'magical', 'bears');
// <div id="my_element" class="some-class unicorn"></div>
Note that "bears"
didn't exist on the previous class. This is fine and will not throw an error
Toggles the class. If the element has the class passed into the method, toggle
will remove it. If the element does not have the class, it adds it. Great for checkboxes and selecting/unselecting.
// <div id="my_element"></div>
const element = document.getElementById('my_element');
element.classList.toggle('selected');
// <div id="my_element" class="selected"></div>
element.classList.toggle('selected');
// <div id="my_element"></div>
Element.setAttribute
takes 2 parameters:
Element.setAttribute(nameOfAttribute: String, valueOfAttribute: any)
nameOfAttribute
is the attribute to set on an element, like if you wanted to give an id
to an element, it would look like this:
const element = document.getElementsByTagName('div')[0]; // Grab the first div on the page
element.setAttribute('id', 'my_element');
// element => <div id="my_element"></div>
Like its predecessor, setAttribute
, this just returns the value of an attribute like so:
const element = document.getElementsByTagName('div')[0]; // Grab the first div on the page
// element => <div id="my_element"></div>
console.log(element.getAttribute('id')); // => 'my_element'
If you want an element to hold onto data without showing anything inside of it, you can use a data-attribute
. As long as the name of the attribute starts with data-
- You can pretty much put in anything after that.
<div class="my-item" data-name="sprinkler" data-created="11/16/2018">
Bobby the Sprinkler
</div>