Vanilla JS Monday
I already found JavaScript, but it was such a good learning experience that I wanted to do a rerun of Chris Ferdinandi’s Vanilla JS Academy for the class of October 2021. Then both ✨ new job ✨ and life in general happened, and I never got around to completing. But this is it.
At work, we currently have a week of “flexible work” that can be used for learning. My goal is to build all the academy projects — and publish daily notes to keep myself accountable. (Direct quotes are from either MDN or the Vanilla JS Academy course material.)
Grab elements from the DOM
- Pass any valid CSS selector into either…
document.querySelector()
to get the first element in the DOMdocument.querySelectorAll()
to get multiple elements
Get & set element properties 🔨
I have the HTML elements. To actually do stuff with them in my scripts, I can access their properties. They have a whole range! Some read only, others can both get and set values. Examples:
Element.classList
will read a list of class attributesElement.className
can read but also set a new CSS class to an elementElement.id
to get or set a value as an IDElement.innerHTML
to get or set markup within the elementNode.parentElement
to read a DOM node’s parent element
I’m still somewhat confused by when something relates to the Element
vs Node
. I understand that they are different interfaces in the Web APIs. Perhaps that is all I need to know at the moment? And that when looking for properties and methods on MDN, I may want to check multiple lists:
The DOM Node interface is an abstract base class upon which many other DOM API objects are based, thus letting those object types to be used similarly and often interchangeably. As an abstract class, there is no such thing as a plain Node object. All objects that implement Node functionality are based on one of its subclasses. Most notable are Document, Element, and DocumentFragment.
Hm, okay? I’m sure this will make more sense to me later.
Listen for events 🎧
The world of events are a bit of a mind melt to approach, trying to get an overview or a mental model of it all. But getting familiar with the humble click event in basic projects is a good start.
let btn = document.querySelector("#click-me");
btn.addEventListener("click", function (event) {
console.log(event); // The event details
console.log(event.target); // The clicked element
});
Run the EventTarget.addEventListener() method on the element you want to listen for events on. It accepts two arguments: the event to listen for, and a callback function to run when the event happens. You can pass the event into the callback function as an argument. The event.target property is the element that triggered the event.
Loop over elements ♻️
for…of
to define a variable but no indexArray.forEach()
to iterate over arrays with access to indexNodeList.forEach()
same but for list of nodes
The forEach() methods really are for each, I can't break the loop.
Check if an element matches a selector 👯
Element.matches()
will tell me if the element matches the selector I pass into the method.
Delegate event listening 👔
Because I can only add event listeners to individual elements, not to a node list. It’s possible to loop though elements, for example all the buttons on a page. But event delegation is generally better for performance.
- Attatch listener to a parent element, like
document
- Events on elements inside will bubble up
- …and then use
event.target
to know exactly what was clicked
document.addEventListener("click", function (event) {
if (event.target.matches(".click-me")) {
// do stuff
}
});
Work with data attributes 💙
<div
id="evening-plans"
data-obligations="something that can wait for tomorrow"
data-movie="Star Wars"
data-drink="beer"
></div>
const el = document.querySelector("#evening-plans");
// Get and remove attributes
console.log("Watching: ", el.getAttribute("data-movie"));
el.removeAttribute("data-obligations");
// Set a different drink value
el.setAttribute("data-drink", "wine");
// Has we got snacks?! No?!? Then add popcorn
if (!el.hasAttribute("data-snack")) {
el.setAttribute("data-snack", "popcorn");
}
// The evening plans element now has these data attributes:
// data-movie="Star Wars" data-drink="wine" data-snack="popcorn"
These four methods can also be used to get, set, remove, and check for other types of attributes that I may want for my elements (not just data attributes). Examples from the Toggle Password project in the course:
- Use element IDs in a data attribute:
data-toggle="#current-password, #new-password"
- and then get the attribute value with
getAttribute("data-toggle")
- Also event delegation with
if (!event.target.matches("[data-toggle]")) return;
Grab text and change text 📝
Node.textContent
gets all the text content, including inside hidden elementsElement.innerText
returns only rendered text like what I can select in the browser- The difference between the textContent and innerText in vanilla JS
Count words
For the Word Count project in the course, I have text from a textarea. A neat way to count the words, is to first turn this string into an array, and then access that array’s length property.
String.split()
method to return an array of substrings, split on a delimiterArray.prototype.length
property to return the number of elements in this array
Filter an array
create a new array with only elements that pass a test you include as a callback function. The callback accepts three arguments: the current item in the loop’s value, its index, and the array itself. All three are optional.
let numbers = [2, 7, 8, 11, 20, 113];
// Create new array with only numbers above 10
// This array method will loop through each item under the hood
let biggerThanTen = numbers.filter(function (item) {
// The test to pass to be included in the new array
return item > 10; // True or false?
});
MDN has syntax examples, and I found it helpful to see these 3 variants next to each other:
// Arrow function
filter((element) => { /* ... */ });
// Callback function
filter(callbackFn);
// Inline callback function
filter(function (element) {
/* ... */
});
Great first day of javascripting recap. Tomorrow we Promise
and fetch()
from APIs.