Make a custom range Slider

Make a custom range Slider

Make a range slider with HTML, CSS and Javascript

A range slider, as the name suggests, is an HTML element that helps select a range between a defined min and max values. It can be used as it is or it can be customized just like the other input elements in HTML. I'll be tweaking a few properties of the slider and show how you can make a better looking slider using just HTML,CSS and Javascript. I'll be making a spice-level selector here where a user can select the spice level for chicken wings based on the range provided. Let's dive in.

HTML

So what do I need?

  • The range input to select the range.
    It has a min and max attributes to define the extremes of the range.
    It also has a value property which shows the set the initial position of the slider. Here it is set to 1.
  • An <output> tag to display the range selected. Output tag?? Bet you haven't heard of that before.
    An output tag is used when you need to display the value of a calculation or the outcome of a user's action like selecting a range.
  • I'm going to display images for each selection that's displayed as a <ul> tag.
<h2>How hot do you want your <span>&#x1f357;</span> ??</h2>
<div class="slider-container">
  <output class="icon" id="icon"></output> 
  <input type="range" class="slider" id="slider" min="1" max="4" value="1">
</div>

<ul>
  <li><img src='https://svgshare.com/i/TNr.svg' alt='Mild' /></li>
   <li><img src='https://svgshare.com/i/TNr.svg' alt='Spicy' /><img src='https://svgshare.com/i/TNr.svg' title='1 chili' /></li> 
  <li><img src='https://svgshare.com/i/TQQ.svg' alt='Extra spicy' /></li>
  <li><img src='https://svgshare.com/i/TRX.svg' alt='Mouth melter' /></li>
</ul>

CSS magic

Here is the CSS for the list and the range input

body {
  position:absolute;
  top:50%;
  left:50%;
  transform:translate(-50%,-50%);
  font-family:"Lato";
}

h2 {
  transform:translate(15%,-50%);
  font-weight:900;
}

.slider-container {
  display:flex;
  flex-direction:column;
  align-items:center;
}

.slider {
  -webkit-appearance:none;
  appearance:none;
  background-color: #333;
  border-radius:30px;
  width:500px;
  transition: all 0.5s;
  cursor:pointer;
  margin:1em 0;
  &:focus {
     outline:none;
  }
  &::-webkit-slider-runnable-track    {
    background-color:#000;
  }
   &::-moz-range-track {
    background-color:#000;
  }
   &::-webkit-slider-thumb {
    background-color:pink;
  }
   &::-moz-range-thumb {
    background-color:#fff;
    height:30px;
    width:30px;
    transition: all 0.5s;
  }
} 

span {
  -webkit-box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.75);
  -moz-box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.75);
  box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.75);
  padding: 10px;
  border-radius:10px;
}

.icon {
  font-size:20px;
  font-weight:900;
}

ul {
  display:flex;
  justify-content:space-between;
  padding-left:0;
  li {
    list-style:none; 
    img {
      width:50px;
    }
  }
}

.active {
  transform:scale(1.5);
  transition:transform 0.5s;
}

This is the part where I customize the slider. Let's look at the CSS for the input. There are two components that make up a slider. The slider track and the slider thumb which you can customize with prefixes for browser compatibility. Screenshot 2021-01-24 at 6.35.40 PM.png The slider track is the horizontal line where the slider slides.
The slider thumb is the circular slider which moves across the slider track.
As usual, for any customization of an HTML element, first hide the default appearance of it. Then add custom styles to the slider track and the slider thumb. Make sure to add the prefixes for browser compatability.

Javascript

I've added javascript to highlight the selected spice level and add a little CSS on selection. Below is the code.

let spiceLevel = document.getElementById("slider");
let selectedSpiceLevel = document.getElementById("choice");
document.querySelectorAll('li:nth-child(1)')[0].classList.add("active"); 
selectedSpiceLevel.innerHTML = "Mild";   
spiceLevelObject = {
  1:"Mild",
  2:"Spicy",
  3:"Extra spicy",
  4:"Mouth melter"
}

spiceLevel.addEventListener("input",changeSliderImage);

function changeSliderImage() {    
  for(var i = 1; i < 5; i++) {
    if(spiceLevel.value === i.toString()) {
      selectedSpiceLevel.innerHTML = spiceLevelObject[i];
      document.querySelectorAll('li:nth-child('+i+')')[0].classList.add("active"); 
      document.querySelectorAll('li:not(:nth-child('+i+'))').forEach(i=>i.classList.remove("active")); 
    }
  }
}

There are 4 values to be chosen from in the slider. For each value, there is an icon and text that is assigned. The default state is when the Mild level is selected and the first image is highlighted. Below is the code breakdown

  • Grab the input and the output tags.
  • Add an eventListener on the input tag. Use the input event handler to trigger the changeSliderImage function.
  • I made an object to hold the spice level for each value of the range input.
  • According to the value of the input, the output tag will be updated and the appropriate icon will be highlighted.
    spiceLevel.value === i.toString() checks to see which value is selected.
    In order to selectively add CSS to the selected level's image, I need to iterate through all the list items and find the current list item. For that I use the querySelectorAll selector.
  • querySelectorAll selects all the elements that match a certain value and returns a NodeList. A NodeList is an array-like structure. It is not an array but is a list/collection of nodes. According to the MDN,

Although NodeList is not an Array, it is possible to iterate over it with forEach(). It can also be converted to a real Array using Array.from().

You can find a little more about them here.

  • li:nth-child(m) matches the mth child element of all the <li> elements. :nth-child() is a common CSS selector. You can find more info about it here.
  • querySelectorAll('li:nth-child(0)') returns a NodeList of all the list items that match the above pattern. It looks like this

Screenshot 2021-01-24 at 6.54.27 PM.png

Since the NodeList is array-like, I can access it's values using indices, just like a regular array. The required list item is at the 0th position so I write document.querySelectorAll('li:nth-child(3)')[0].

  • Next, to highlight the selected item, I add an active class to it.

We're almost to the end, with just one last tweak required. So, when one icon is highlighted you want to make sure that the others are not.

  • For that, I use the :not(:nth-child(m)). It selects all the elements that are not the selected item. Simple as that.
  • The remaining list items shouldn't have an active class. So I use a foreach loop and iterate over the list items and remove the active class.

Summary

What we learnt

  • Different components of a range input
  • CSS selectors like :nth-child() and :not
  • Selectively add classes to different elements using Javascript
  • NodeList and what they are

Want to know a cool trick?
You can use the :in-range selector in CSS to style a range input only if it's within a specified range. Read more about it here

This was a simple way to create a slider and add your own customization to it. I hope this was helpful to you. And here's the demo for you.