VanillaJs: You Might Not Need A Library.
Muhammad Abu Bakar
Software Engineer || Python || Django || React || Full Stack Developer
Learning Basic JavaScript: A Simple Guide
When we talk about making websites, there’s something called JavaScript that helps make the pages do cool things. Imagine JavaScript as the magic that makes things move, change, or appear on a website when you click a button or type something. But sometimes, people use extra tools to make JavaScript even more powerful. However, there’s a way to use JavaScript in its simplest form, without any extra stuff. This is what we call “Vanilla JavaScript”.
The Basics: What is Vanilla JavaScript?
At the beginning of our learning journey, we found out about “Vanilla JavaScript”. This is just a fancy way of saying we’re using JavaScript all by itself, without any helpers. Even though there are lots of extra tools out there, it’s still really useful to know how to do things the simple way, just like how people made websites before all those tools existed.
Understanding the DOM: A Closer Look
When working with web pages using JavaScript, document.querySelector and document.querySelectorAll are essential tools for selecting elements. Here's how you can use them, including how to apply array methods like "map" and "filter" to the results.
Using querySelector
document.querySelector("header") tells JavaScript to find the first element that matches the "header" tag. It's useful for targeting single elements to manipulate:
// Find the header and change its background color
const header = document.querySelector("header");
header.style.backgroundColor = "lightblue";
Using querySelectorAll and Converting to an Array
document.querySelectorAll("button") returns a NodeList of all button elements on the page. To use "map" and "filter" — which are not available on NodeLists — you first convert the NodeList to an array:
// Find all buttons and convert NodeList to an array
const buttons = Array.from(document.querySelectorAll("button"));
// Example: Change the text of each button using map (forEach would be more appropriate here)
buttons.forEach((button, index) => {
button.textContent = `Button ${index + 1}`;
});
// Example: Use filter to find all buttons with a class 'active' and change their color
const activeButtons = buttons.filter(button => button.classList.contains('active'));
activeButtons.forEach(button => button.style.color = 'green');
Key Points querySelector is for single elements, querySelectorAll is for multiple. querySelectorAll returns a NodeList. To use array methods like "map" and "filter", convert it to an array with Array.from(). This conversion allows you to leverage powerful array methods to manipulate collections of elements efficiently. These examples show the power and flexibility of combining DOM selection with array manipulation methods, enhancing your ability to interact with and modify web page content dynamically.
Getting Information and Moving Around Pages
Another cool thing we can do is get information from other places on the internet to show on our webpage. We use something called fetch to do this. It's like asking JavaScript to go out, grab some information, and then display it on our page.
Let’s build a simple router for a single-page application (SPA) using the Router object structure you provided. The Router will handle navigation within our application without reloading the page. It will use the browser's History API to manage the browser history for a seamless user experience.
Defining the Router
The Router will have two main functions:
Implementation
const Router = {
init: function() {
window.addEventListener('popstate', event => {
const route = event.state?.route || 'home';
this.go(route, false);
});
},
go: function(route, addToHistory = true) {
const content = {
home: '<h1>Home Page</h1>',
about: '<h1>About Page</h1>',
contact: '<h1>Contact Page</h1>',
}[route] || '<h1>404 Not Found</h1>';
document.getElementById('content').innerHTML = content;
if (addToHistory) {
history.pushState({ route }, '', route);
}
}
};
Router.init();
Shadow DOM
The Shadow DOM provides a way to encapsulate the markup, style, and scripts for web components, creating a separate DOM tree. This encapsulation means styles and scripts inside a shadow DOM won’t clash with the main document, allowing for reusable and isolated components. It’s a cornerstone of Web Components, ensuring that a component’s internal workings don’t affect or get affected by the global state of the page.
HTML Templates and Template Literals
HTML Templates use the <template> tag to define chunks of HTML that aren't rendered until explicitly instantiated with JavaScript. This is useful for creating reusable HTML structures that can be activated and inserted into the document as needed.
Template Literals, a JavaScript ES6 feature, allow for easier string interpolation and multi-line strings. Enclosed by backticks (`), they can embed expressions (${expression}), making dynamic content generation straightforward and readable. Combined with custom elements and Shadow DOM, template literals enhance the dynamic creation and manipulation of encapsulated component content.
class MyCustomElement extends HTMLElement {
constructor() {
super(); // Always call super() first in the constructor.
// Attach a shadow root to the element.
this.attachShadow({ mode: 'open' });
}
// Called when the element is connected to the document's DOM
connectedCallback() {
// Add some HTML content to the shadow root
this.shadowRoot.innerHTML = `
<style>
p { color: blue; }
</style>
<p>Hello, this is my custom element!</p>
`;
}
}
customElements.define('my-custom-element', MyCustomElement);
Proxies for Reactive Programming
Lastly, the course touches on JavaScript proxies, a powerful feature for creating reactive programming patterns. Proxies allow developers to intercept and customize fundamental operations on objects. Through a practical example, learners see how to intercept property access to modify return values, enhancing their understanding of reactive programming concepts.
const original = {
name: "John Doe",
age: 30
};
const handler = {
get: function(target, prop) {
if (prop === 'age') {
return `${target[prop]} years old`;
} else {
return target[prop];
}
}
};
const proxy = new Proxy(original, handler);
console.log(proxy.age); // Outputs: "30 years old"
This simple proxy example demonstrates how to augment the behavior of object property access, showcasing the flexibility and power of JavaScript proxies for managing state and interactions in a more controlled manner.
Wrapping Up
Exploring Vanilla JavaScript shows us the powerful and flexible core of web development hidden beneath all the tools and shortcuts we often use. For beginners, diving into these basic ideas makes the whole world of creating websites less mysterious and more manageable. Learning to use plain JavaScript is not moving backwards; it’s actually a big step forward. It helps you understand the building blocks of web pages and gives you the skills to make your own sites work better, with or without extra help from libraries. It’s like learning to cook from scratch — it’s rewarding and gives you a deeper appreciation for what goes into the final dish.