25
loading...
This website collects cookies to deliver better user experience
const ratingModule = makeStarRating();
ratingModule
will expose two methods in the name of getStarComponent
and getRating
. <ul class="stcomp">
<li data-rating="1" class="star" ></li>
<li data-rating="2" class="star" ></li>
<li data-rating="3" class="star" ></li>
<li data-rating="4" class="star" ></li>
<li data-rating="5" class="star" ></li>
</ul>
data-rating
custom attribute which will be available to us as dataset.rating
when using DOM APIs. Also CSS isn't the focus of this article. Though, it will available in the final codepen implementation.const makeStarRating = function (noOfStars = 5) {
let rating = 0;
let starComponent;
function changeRating(newRating) {
rating = newRating;
}
function getStarComponent() {
if (!starComponent) {
// create Star Component
}
return starComponent;
}
function renderChanges(rating) {
// render UI changes as per rating passed
}
function getRating() {
return rating;
}
function onMouseClick(){
// click event handler
}
function onMouseOver(){
// mouseover event handler
}
function onMouseLeave(){
// mouseleave event handler
}
return { getRating, getStarComponent };
};
noOfStars
(with default value of 5) as argument to makeStarRating
which will be used by renderChanges(rating)
later on. getStarComponent()
:-function getStarComponent() {
if (!starComponent) {
starComponent = document.createElement("ul");
starComponent.className = "stcomp";
for (let i = 0; i < noOfStars; i++) {
const li = document.createElement("li");
li.setAttribute("data-rating", i + 1);
li.className = "star";
starComponent.append(li);
}
starComponent.addEventListener("mouseover", onMouseOver);
starComponent.addEventListener("mouseleave", onMouseLeave);
starComponent.addEventListener("click", onMouseClick);
}
return starComponent;
}
ul
element and appending to it li
, noOfStars
times. And setting the data-rating
attribute and className
property of each li
element. Finally adding the relevant code for registering event handlers. One important thing to notice is that we are making use of event delegation so that only our parent ul
has one event handler (for each relevant event) which can take care of events bubbling from child li
elements. The event bubbling is only beneficial for click and mouseover events. For mouseleave event we don't need it since we only want the rating
to get reflected once we leave the parent ul
container. And fun fact, mouseleave
doesn't bubble !!renderChanges(rating)
will look like :-function renderChanges(rating) {
for (let index = 0; index < rating; index++) {
starComponent.children[index].classList.add("star-filled");
}
for (let index = rating; index < noOfStars; index++) {
starComponent.children[index].classList.remove("star-filled");
}
}
star-filled
to highlight a star. rating
number, all the stars would be highlighted and after that all the stars will remain non-highlighted.onMouseClick
:-function onMouseClick(e) {
let star = e.target;
let isStar = star.classList.contains("star");
if (isStar) {
let { rating } = star.dataset;
rating = rating === getRating() ? 0 : rating;
changeRating(rating);
renderChanges(rating);
}
}
rating
from the dataset
property. Now we compare it with existing rating
(via getRating()
) and if both are equal, reset the rating
to 0. Then we save this rating
and render the changes.mouseover
and mouseleave
like so :-function onMouseOver(e) {
let isStar = e.target.classList.contains("star");
if (isStar) {
const { rating } = e.target.dataset;
renderChanges(rating);
}
}
function onMouseLeave(e) {
renderChanges(rating);
}
onMouseOver
, we just skip the check for rating
and saving rating
bit which we are earlier doing using changeRating(rating)
inside onMouseClick
. We only want to reflect these changes in the UI but not persist unless click action is performed. mouseleave
, just render the changes with the current saved rating
(Bless you closures!!!).makeStarRating
each time to give us new modules and each of such modules can call their getStarComponent
to return the parent ul
which can be appended to other containers.