Everyone has their favorite framework, and most developers aren’t shy about sharing those opinions. I guarantee you right now two developers are arguing about their favorite frameworks. Personally, I’ve been using JavaScript frameworks since JQuery was introduced. I’ve written applications for clients using Knockout.js, Angular 1+, React (since before v15), and have made some small learning apps using Stencil and Vue.
One of the great things that all of these frameworks bring to the table is their easy composability. The ability to make components that you can reuse throughout your app helps so much with development time, code reusability, and testability. You don’t have to use a framework to get these benefits. JavaScript has them built-in, you just have to know where to look. Also, learning how to build components in vanilla JavaScript will help you understand how to make those components in your favorite framework.
In this post, I’ll show you how to build a simple star rating component using nothing but vanilla JavaScript, CSS, and HTML. I’ll be using VS Code and http-server to build and serve a static set of files. You’ll need Node.js and npm installed to run http-server
.
When I build components in any framework (or without one), I like to first figure out all the things I need the component to handle internally. In this case, I’m building a star rating component, so it definitely needs to display stars.
With those requirements in mind for your JavaScript component, let’s dig in!
Start by creating a folder somewhere you want to serve the application from. When you run http-server
in a directory it serves that folder as a web server. Once you have the directory created, open it in VS Code and add a file called index.html
. This will be your web application. Inside the index.html
. This file will have two star rating HTML elements on it along with some basic style and a script tag. The complete contents:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<style>
.star-rater .star {
font-size: 5rem;
color: gray;
}
</style>
<title>Rater</title>
</head>
<body>
<h2>Star Rating</h2>
<div id="rater-1" class="star-rater" data-rating="3" role="rater">
<span class="star" data-value="1">★</span>
<span class="star" data-value="2">★</span>
<span class="star" data-value="3">★</span>
<span class="star" data-value="4">★</span>
<span class="star" data-value="5">★</span>
</div>
<div id="rater-2" class="star-rater" data-rating="2" role="rater">
<span class="star" data-value="1">★</span>
<span class="star" data-value="2">★</span>
<span class="star" data-value="3">★</span>
<span class="star" data-value="4">★</span>
<span class="star" data-value="5">★</span>
</div>
<script type="module" src="js/main.js"></script>
</body>
</html>
You’ll notice I’ve added the role="rater"
to each element. That will be the way you tell the main JavaScript file to attach the functionality of your Rater
component to this element.
Now create a folder called js
to house your vanilla JavaScript component code, and then create two files called rater.js
and main.js
inside that new folder. That script
tag above brings in the main.js
file as a module
type. For browsers that support module syntax for JavaScript, this will allow you to use the import
and export
statements introduced in ES2015.
In the rater.js
file add the code to export the rater’s functionality:
export function Rater(ratingElement) {
const stars = ratingElement.querySelectorAll('.star');
}
The first bit of functionality is here as well. The Rater
takes in an HTML element and queries it for all of its elements with a class of star
and puts them into the stars
array.
To finish out the shell, add the code to the main.js
file that finds all the HTML elements with a role of rater
and pass them to the Rater
component.
import { Rater } from './rater.js';
document.addEventListener('DOMContentLoaded', function() {
const raters = document.querySelectorAll('[role=rater]');
raters.forEach(rater => {
new Rater(rater);
});
});
Now you should be able to fire up the application by serving the folder. From the folder where your index.html
file lives in a console run:
http-server .
It will tell you it’s running on http://localhost:8080, and you will see the two rating components with gray stars, but they don’t do anything yet.
Start with add the ability to highlight based on a rating number. In the rater.js
file, add a function to highlight each star up to and including a certain rating:
const highlightRating = (rating) => {
stars.forEach(star => {
star.style.color =
rating >= star.getAttribute('data-value') ? 'yellow' : 'gray';
});
}
Now you’ll want to set the initial highlighting for the component when it renders. Each rater HTML element has a data-value
attribute. That will be used to hold the current rating for the component. Since you’ll use this when the component loads and after the user moves their mouse away from the component, call the function resetRating()
. Add the following function right above the highlightRating()
function:
const resetRating = ev => {
const currentRating = ratingElement.getAttribute('data-rating');
highlightRating(currentRating);
};
Then below the highlightRating()
function call it so that you can set the highlighting for the initial load.
resetRating();
Now when you reload your browser you should see each rater highlighted to its appropriate default rating. In this case, three and two.
The next step is to handle the hovering action so that the rating component highlights as the user hovers over it. You’ll first need to wire up an even to each star. Since you already have all the stars in the component in an array, just loop over them and hang the event listener on them. Add the following code to the bottom of the component function:
stars.forEach(star => {
star.addEventListener('mouseover', ratingHover);
});
Now, you need the event handler for the listener. This even will just get the star the user’s mouse is hovering on and call the highlightRating()
function. Right above the resetRating()
function add:
const ratingHover = ev => {
const currentHover = ev.currentTarget.getAttribute('data-value');
highlightRating(currentHover);
};
You’ll also want the component to reset back to the last selected rating (in this case the default rating) when the user moves their mouse away from the rating component. Add the following line of code to the bottom of the component function:
ratingElement.addEventListener('mouseout', resetRating);
Now if you refresh your page you should see the hovering happening and when you move the mouse away, it should reset the highlighting.
Now there is only one thing left to do, and that is wire up a click event to the stars and update the data-rating
for the component when a user clicks on a star.
To wire up the click event, update the stars.forEach()
line so that it looks like:
stars.forEach(star => {
star.addEventListener('click', setRating);
star.addEventListener('mouseover', ratingHover);
});
Now that you have a listener for the click and the hover, add the setRating()
event handler to set the rating when a user clicks on a star below the first line of the component function:
const setRating = ev => {
ratingElement.setAttribute(
'data-rating',
ev.currentTarget.getAttribute('data-value')
);
};
Now, when you refresh your browser, you should be able to hover over the rating component and see the highlighting happening. Then, when you click on a star, it should update the data-rating
value of the rating component element. You can view this in the developer tools of your browser.
I’ve kept this component simple to illustrate just how to make the component and make it reusable by the import
statement. Now that you know how to build and use a component in vanilla JavaScript, you could make this component do so much more!
You could have the component “draw” the stars and set a max-stars
attribute on the element to tell the component how many stars to draw. You could use something like Font Awesome to display other things besides stars. You could even make the Rater component call another component that sends the rating that the user selects back to an API to be saved to a database. But the skeleton is there.
#JavaScript #HTML #CSS #WebDev