How to build a pricing slider — HTML & Vanilla JS

Pasquale Vitiello
Mar 16, 2020 · 6 min read

If you are selling pay as you go or subscription plans, there might be chances that you need a landing page with a pricing table controlled by a range slider — just like in the example below 👇

Let’s start with the HTML and JavaScript version, then we will cover React and Vue ones in the next articles!

Creating the HTML structure

I’ve created a very basic HTML structure, with some ready-made CSS from the Cruip framework.

    <div class="pricing-slider">
<label class="form-slider">
<span>How many users do you have?</span>
<input type="range" />
</label>
<div class="pricing-slider-value"></div>
</div>
<div class="pricing-items">
<div class="pricing-item">
<div class="pricing-item-inner">
<div class="pricing-item-content">
<div class="pricing-item-header">
<div class="pricing-item-title">Basic</div>
<div class="pricing-item-price">
<span class="pricing-item-price-currency">$</span>
<span class="pricing-item-price-amount">13</span>
<span class="pricing-item-price-after">/m</span>
</div>
</div>
<div class="pricing-item-features">
<ul class="pricing-item-features-list">
<li class="is-checked">Excepteur sint occaecat</li>
<li class="is-checked">Excepteur sint occaecat</li>
<li class="is-checked">Excepteur sint occaecat</li>
<li>Excepteur sint occaecat</li>
<li>Excepteur sint occaecat</li>
</ul>
</div>
</div>
<div class="pricing-item-cta">
<a class="button" href="#">Buy Now</a>
</div>
</div>
</div>
</div>
</div>

Notice that we have input ⬇️ and output ⬆️ elements.

Input elements

  • The <input type="range" /> element, i.e. the slider control
  • The <div class="pricing-slider-value"> element, into which we will write the current slider value

Output elements

We can have multiple pricing tabs, which means multiple outputs. Each output consists of a <div class="pricing-item-price"> element, that contains 3 more elements:

  • <span class="pricing-item-price-currency"> for the currency sign
  • <span class="pricing-item-price-amount"> for the amount
  • <span class="pricing-item-price-after"> for any other information, such as the billing period

And here is the result 👇

Shaping input and output data

We need to design our data scheme now. I’ve defined a range of slider values (input) and the corresponding price values (output).

Go on adding input and output data to HTML via data attributes.

Input data 👇

Output data looks a little different for structure, since each value is not a string, but an array of strings.

Defining JavaScript variables

Since we might want to display more than one pricing slider on a page, let’s collect all elements having pricing-slider as a class, and loop through them.

if (pricingSliders.length > 0) {
for (let i = 0; i < pricingSliders.length; i++) {
const pricingSlider = pricingSliders[i];
}
}

Now that we have our pricing slider defined by a constant, we can move forward with storing elements and data, for both input and output.

To do that, we are going to create:

  • a pricingInput object that contains stuff dealing with the range slider (the input)
  • a pricingOutput variable, that contains output elements and data. It's an array because, as previously said, we might have more than one output 😉
    // Build the input object
const pricingInput = {
el: pricingSlider.querySelector("input")
};
pricingInput.data = JSON.parse(
pricingInput.el.getAttribute("data-price-input")
);
pricingInput.currentValEl = pricingSlider.querySelector(
".pricing-slider-value"
);
pricingInput.thumbSize = parseInt(
window
.getComputedStyle(pricingInput.currentValEl)
.getPropertyValue("--thumb-size"),
10
);
// Build the output array
const pricingOutputEls = pricingSlider.parentNode.querySelectorAll(
".pricing-item-price"
);
const pricingOutput = [];
for (let i = 0; i < pricingOutputEls.length; i++) {
const pricingOutputEl = pricingOutputEls[i];
const pricingOutputObj = {};
pricingOutputObj.currency = pricingOutputEl.querySelector(
".pricing-item-price-currency"
);
pricingOutputObj.amount = pricingOutputEl.querySelector(
".pricing-item-price-amount"
);
pricingOutputObj.after = pricingOutputEl.querySelector(
".pricing-item-price-after"
);
pricingOutputObj.data = JSON.parse(
pricingOutputEl.getAttribute("data-price-output")
);
pricingOutput.push(pricingOutputObj);
}
}
}

Let’s see what’s inside these objects 📦

Setting range slider attributes

Now we can proceed with setting the range slider min, max, and value attributes.

    // [ ... previously defined variables ... ]    // set input range min attribute (0)
pricingInputEl.setAttribute("min", 0);
// set input range max attribute (9, i.e. the number of values)
pricingInputEl.setAttribute("max", Object.keys(priceInput).length - 1);
// initial slider value (0, or any other value if assigned via HTML)
!pricingInputEl.getAttribute("value") &&
pricingInputEl.setAttribute("value", 0);
}
}

Great! We have a range slider whose values go from 0 to 9 🙌

The next step is outputting the slider value (e.g. 1,000) that corresponds to the current range value (e.g. 0), into the <div class="pricing-slider-value"> element.

To do that, we need to create a function to be invoked every time a user interacts with the slide. As obvious, we need to pass our input and output objects as arguments

Let’s call the function 📢

    // [ ... previously defined variables ... ]
// [ ... previous range slider attributes assignment ... ]
handlePricingSlider(pricingInput, pricingOutput);
window.addEventListener("input", function() {
handlePricingSlider(pricingInput, pricingOutput);
});
}
}

And here is the result 👇

Binding input and output data with JavaScript

We have a working range slider, but it is still disconnected from the visualized price. It’s time to bind input slider values with output price data.

Adjusting the slider value element position

Almost there. 🏁 We want the slider value to be following the slider thumb.

Let’s create a function calculating the left value to be applied to the slider value element.

The function determines the proper slider value position, so that the element is horizontally aligned with the slider thumb. Here is a visual representation of what the function does 👇

Notice that the thumb size value is parsed with the getComputedStyle() method (see the paragraph where we defined the JS variables). That way I can change the thumb size in the CSS, without having to change anything in my JavaScript file.

Setting a default slider value

In case you want to set an initial slider value other than Free, you just need to add a value="n" attribute to the range slider.

For example, <input type="range" value="1" /> will return a range slider with 1,000 as initial slider value.

Conclusions

Here is the final result again. Click on Open Sandbox to see the full code.

I hope you enjoyed this tutorial. If you want to see this in action here is a landing page template where it’s implemented 👉 Surface

Cruip

Beautifully designed landing page templates for startups

Pasquale Vitiello

Written by

Front-end developer 👋 Founder (and Co-*) @ cruip.com, freebiesbug.com, sneakpeekit.com, opendept.net

Cruip

Cruip

Beautifully designed landing page templates for startups

Pasquale Vitiello

Written by

Front-end developer 👋 Founder (and Co-*) @ cruip.com, freebiesbug.com, sneakpeekit.com, opendept.net

Cruip

Cruip

Beautifully designed landing page templates for startups

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store