read.cash Log in
c@codixir more from that month

Create A Reusable Star Rating Widget using OOP : Frontend Interview

Introduction In this part of the series, we will apply the principles of object oriented programming (**oop**) to re-create the star-rating widget we saw in part 1 and make it reusable in the process. This is a common frontend engineering interview question. Part 1 of this series can be found here. https://medium.com/javascript-in-plain-english/create-a-star-rating-ui-frontend-interview-problem-f05022ebfdc1 The widget we built in part 1 was not reusable. The widget we will build in this article should be flexible to be used as many times as one wants whether on the same page or inside any part of an application. All you need to have is an HTML `div` element, with a unique class attribute, that is going to serve as a container for the widget wherever it gets inserted. To reach our goal, we will create a **Javascript** class from which widget instances will be created. Setup The setup will be the same as it was in the first article. We will use the **JS Template** from **codesandbox.io**. This template comes with a `src` folder that contains`index.js` ,`style.css` files and an `index.html` file outside of `src`. This time we won't need to add any styles in the styles.css file. https://codesandbox.io/ HTML The HTML part is very simple. Only one line needs to be changed. Go into the `index.html` file and replace the existing `div` with the following line. <div class="stars-container1"></div> This `div` will serve as a container for the stars widget we are going to create later. Javascript Since we are using object oriented programming to create the widget, let’s go ahead and create the skeleton of our class inside the `index.js` file. class Stars { constructor() {} init() {} setRating() {} } This class named `Stars` and, currently, has empty `constructor`, `init` and `setRating` methods. Constructor The `constructor` will help us build unique widget instances. We will make it accept parameters that determine: where the widget will be placed the number of stars it will have styles for each list item element which will serve as container for an individual star The `className` , `numOfStars` and `styleOptions` parameters will have an empty string, 0 and an object literal with a `margin` attribute as default values respectively. constructor( className = "", numOfStars = 0, styleOptions = { margin: "5px" } ) { this.isValid = false; this.numOfStars = numOfStars; this.stars = []; this.styleOptions = styleOptions; this.starsContainer = document.querySelector(className); ​ try { if (this.starsContainer) { this.className = className; this.isValid = true; } else { this.isValid = false; throw new Error(`${className} does not exist.`); } } catch (e) { console.log(e.message); } ​ if (this.isValid && this.numOfStars > 0) { this.init(); } } ​ In the above, you can see that we are adding several attributes on the `this` object. The variable `this.isValid` will serve as a flag to indicate the parameters that get passed into the constructor are valid. It is important to do validation at this stage to ensure proper values are passed into the `constructor` at the time the widget is being built. We also need to ensure the `className` that gets passed into the constructor is a valid class name of an exiting element in the `index.html` file. This is important because that element will be the one on which we will mount the widget. In addition, if the user passed in `0` as the number of stars, there won't be any widget. Therefore, if we don’t have valid values for the `className` and `numOfStars` parameters, the value of `this.isValid` will be `false` and that will save us from invoking the `init` function unnecessarily. We also have a `this.stars` variable that has an empty array as its value and `this.starsContainer` whose value is the returned value of `querySelector`. Both will play important roles shortly. **Exception handling** A `try-catch` block is added to to catch exceptions that could arise due to invalid `classNames`. Inside the try block, we check whether or not the value of `this.starsContainer` exists. If it does exist, we assign the `className` to `this.className` and the value `true` to `this.isValid`. If that is not the case, we assign the value `false` to `this.isValid` and throw an error using the built in Javascript `Error` constructor. You can pass into this `Error` constructor any message that you think will be properly express the issue. This is a good approach for creating generic errors. When an error is thrown, our program execution will jump into the `catch` block. Inside the `catch` we can open an error modal to show the error message. But, for this article, I will just print the error message on the console. Outside of the `try-catch` block, the last piece of code we write is a condition that checks if the value of `this.isValid` is true and the value of `this.numOfStars` is greater than `0`. And, if that is the case, we can invoke the`init` method. Init The `init` method is there to abstract away from the constructor logic we need to: create an empty unorderd list (`ul`) element generate list items and anchor elements add star HTML entities to the anchor elements add click event listeners to the anchor elements and implement the logic that will change the colours of the stars append each anchor element to its respective list item element append each list item element to the `ul` element we created append the `ul` element to the the `starsContainer` element init() { const ul = document.createElement("ul"); for (let i = 0; i < this.numOfStars; i++) { this.stars.push({ id: i + 1 }); } ul.style.listStyleType = "none"; ul.style.display = "flex"; const stars = this.stars.map((star) => { const li = document.createElement("li"); const a = document.createElement("a"); li.style.margin = this.styleOptions.margin; a.style.cursor = "pointer"; a.innerHTML = "&#9733"; a.id = star.id; a.addEventListener("click", (e) => { this.setRating(ul, e); }); li.appendChild(a); return li; }); const fragment = document.createDocumentFragment(); for (const star of stars) { fragment.appendChild(star); } ul.appendChild(fragment); this.starsContainer.appendChild(ul); } You can see the `ul` element is created using the `createElement` method of the `document` object. We also have a `for` loop which lets us push into the `this.stars` array an object literal with an `id` attribute and a value equal to the current iteration variable for the loop. Once that is done, we apply styles to remove list item bullet points and horizontally align the list items we are going to create shortly. Now, we have an array of objects stored inside the `this.stars` variable. We will use this array to generate `li` elements which in number will be equivalent to the value stored in `this.numOfElements`. To do that we need to invoke map on the `this.stars` array and transform each object that we have in the array into the a `li` element that wraps an anchor element with a star `HTML entity`. Right away, inside the callback function, we create two elements `li` and `a` and apply some styles to both. In addition, we set an `HTML entity` which will appear as a star when rendered on the browser to the `innerHTML` attribute of the anchor element. We also invoke the `addEventListener` method on the anchor element. This will help us listen to `click` events, whenever the element is clicked. The logic we apply inside the callback will help us change the colours of the stars. This logic will be implemented in the `setRating` method. The method will need `ul` and the event object as parameters to do its job. Now all we need to do is append the anchor element to the list item element, which can be returned from the function. This will give as an array of list items, stored in the `stars` constant. For performance, we create a `document fragment` and append to it each list item element that is in the stars array. We then append the fragment to the `ul` element, which in turn gets appended to the element stored in `this.starsContainer`. Implement `setRating` Inside the `setRating` method, we create the logic that will set the golden colour to the star that is clicked and all the sibling stars that are to its left. But, on every click, we need to reset the colours of all the stars. setRating(ul, e) { const listItems = ul.querySelectorAll("li"); const currentId = Number(e.target.id); for (const item of listItems) { const a = item.querySelector("a"); a.style.color = ""; if (a.id <= currentId) { a.style.color = "#ccac00"; } else { a.style.color = ""; } } } The first thing we are doing inside this function is use the `querySelectorAll` method to get all the list item elements that are inside the `ul` element. This method returns a Javascript array containing the `li` elements. Now we can loop over the elements of the array and get access to the anchor element that is inside each element. After getting the anchor element of the current item, using `querySelector`, we set an empty string to its style’s `color` attribute. This is a reset operation. Then we check if the `id` of the current anchor element we are looping over is less than or equal to the `id` of the anchor element we just clicked on. We are able to get that id using the target attribute of the event object. If this condition is true, then we set the hex value of the golden colour to the `color` attribute of the style of the anchor element. Otherwise, the colour will have an empty string as its value. Usage Now, to create the widget, you can go outside of the class and create a new instance using the `Stars` constructor. const stars1 = new Stars(".stars-container1", 5); You can find the source code here. https://github.com/codixir/star-rating-widget-js Summary We were able to create a reusable widget with the application of Object Oriented Programming (OOP) principles. Checkout these TOP JAVASCRIPT courses on Udemy: 1 — The Complete JavaScript Course 2021: From Zero to Expert! https://click.linksynergy.com/link?id=JAsBp5BFozE&offerid=507388.851712&type=2&murl=https%3A%2F%2Fwww.udemy.com%2Fcourse%2Fthe-complete-javascript-course%2F 2 — JavaScript: Understanding the Weird Parts https://click.linksynergy.com/link?id=JAsBp5BFozE&offerid=507388.364426&type=2&murl=https%3A%2F%2Fwww.udemy.com%2Fcourse%2Funderstand-javascript%2F 3 — JavaScript — The Complete Guide 2021 (Beginner + Advanced) https://click.linksynergy.com/link?id=JAsBp5BFozE&offerid=507388.2508942&type=2&murl=https%3A%2F%2Fwww.udemy.com%2Fcourse%2Fjavascript-the-complete-guide-2020-beginner-advanced%2F 4 — Modern Javascript From The Beginning https://click.linksynergy.com/link?id=JAsBp5BFozE&offerid=507388.1463348&type=2&murl=https%3A%2F%2Fwww.udemy.com%2Fcourse%2Fmodern-javascript-from-the-beginning%2F 5 — The Modern JavaScript Bootcamp https://click.linksynergy.com/link?id=JAsBp5BFozE&offerid=507388.1470810&type=2&murl=https%3A%2F%2Fwww.udemy.com%2Fcourse%2Fmodern-javascript%2F Checkout my youtube channel for programming courses and tutorials: https://www.youtube.com/channel/UCA5ZfHC6koHsukCLzCzxuGA If you are looking for a Software development job, checkout HIRED. https://hired.com/x/fd35d132f87b1210edf7a5fc32ad79a9

No comments yet

Log in to join in Reading is open to everyone. Replying needs an account.