发布于 2026-01-06 0 阅读
0

如何构建价格滑块 - HTML 和原生 JavaScript

如何构建价格滑块 - HTML 和原生 JavaScript

如果您销售的是按需付费订阅计划,您可能需要一个带有价格表的落地页,该价格表可通过价格范围滑块进行控制——就像下面的示例一样👇

我最近为Cruip 的着陆页模板构建了它,所以我决定写一系列文章来向您展示我是如何开发它(使用 HTML、React 和 Vue),以及我遵循哪些流程来实现最棘手的部分。

让我们先从HTML 和 JavaScript版本开始,然后我们将在下一篇文章中介绍 React 和 Vue 版本!

创建 HTML 结构

我创建了一个非常基本的 HTML 结构,并使用了Cruip 框架中的一些现成 CSS



<div class="pricing">

    <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>


Enter fullscreen mode Exit fullscreen mode

请注意,我们有输入元素⬇️和输出元素⬆️。

元素X射线

输入元素

  • <input type="range" />元素,即滑块控件
  • <div class="pricing-slider-value">我们将把当前滑块值写入该元素中。

输出元素

我们可以设置多个定价标签页,这意味着会有多个输出结果。每个输出结果包含一个<div class="pricing-item-price">元素,该元素又包含三个子元素:

  • <span class="pricing-item-price-currency">货币符号
  • <span class="pricing-item-price-amount">金额
  • <span class="pricing-item-price-after">如需了解其他信息,例如账单周期,请联系我们。

结果如下👇

输入和输出数据的结构化

现在我们需要设计数据方案。我已经定义了一系列滑块值(输入)和相应的价格值(输出)。

钥匙 滑块值 价格,货币 价格,金额 价格(之后)
0 1,000 自由的
1 1,250 $ 13 /m
2 1,500 $ 17 /m
3 2,000 $ 21 /m
4 2,500 $ 25 /m
5 3,500 $ 42 /m
6 6,000 $ 58 /m
7 15,000 $ 117 /m
8 50,000 $ 208 /m
9 50,000+ 联系我们

继续通过数据属性向HTML添加输入输出数据。

输入数据👇



<input
  type="range"
  data-price-input='{
      "0": "1,000",
      "1": "1,250",
      "2": "1,500",
      "3": "2,000",
      "4": "2,500",
      "5": "3,500",
      "6": "6,000",
      "7": "15,000",
      "8": "50,000",
      "9": "50,000+"                        
    }'
/>


Enter fullscreen mode Exit fullscreen mode

输出数据的结构看起来略有不同,因为每个值都不是一个字符串,而是一个字符串数组。



<div
  class="pricing-item-price"
  data-price-output='{
    "0": ["", "Free", ""],
    "1": ["$", "13", "/m"],
    "2": ["$", "17", "/m"],
    "3": ["$", "21", "/m"],
    "4": ["$", "25", "/m"],
    "5": ["$", "42", "/m"],
    "6": ["$", "58", "/m"],
    "7": ["$", "117", "/m"],
    "8": ["$", "208", "/m"],
    "9": ["", "Contact us", ""]
  }'
>


Enter fullscreen mode Exit fullscreen mode

定义 JavaScript 变量

由于我们可能需要在页面上显示多个价格滑块,让我们收集所有具有pricing-slider该类名的元素,并遍历它们。



const pricingSliders = document.querySelectorAll(".pricing-slider");

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


Enter fullscreen mode Exit fullscreen mode

现在我们已经用常量定义了价格滑块,我们可以继续存储元素和数据,包括输入输出

为此,我们将创建:

  • pricingInput包含与范围滑块(输入)相关的内容的对象
  • 一个pricingOutput包含输出元素和数据的变量。它是一个数组,因为正如前面所说,我们可能有多个输出😉


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

    // 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);
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

让我们看看这些物体里面有什么📦

多变的 返回
pricingInput.el <input type="range" />元素
pricingInput.data {0: "1,000", 1: "1,250", ... }目的
pricingInput.currentValEl <div class="pricing-slider-value">元素
pricingInput.thumbSize 36滑块滑块大小(从 CSS 解析)
pricingOutput[n].currency <span class="pricing-item-price-currency">元素
pricingOutput[n].amount <span class="pricing-item-price-amount">元素
pricingOutput[n].after <span class="pricing-item-price-after">元素
pricingOutput[n].data {0: ["", "Free", ""], ... }目的

设置范围滑块属性

现在我们可以继续设置范围滑块minmaxvalue属性。



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

    // [ ... 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);
  }
}


Enter fullscreen mode Exit fullscreen mode

太棒了!我们有一个范围滑块,数值从 0 到 9 🙌

下一步是将与当前范围值(例如)对应的滑块值(例如)输出元素中。1,0000<div class="pricing-slider-value">

为此,我们需要创建一个函数,以便在用户每次与幻灯片交互时调用该函数。显然,我们需要将输入和输出对象作为参数传递。



function handlePricingSlide(input, output) {
  // output the current slider value
  if (input.currentValEl)
    input.currentValEl.innerHTML = input.data[input.el.value];
}


Enter fullscreen mode Exit fullscreen mode

我们把这个函数叫做📢



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

    // [ ... previously defined variables ... ]
    // [ ... previous range slider attributes assignment ... ]

    handlePricingSlider(pricingInput, pricingOutput);
    window.addEventListener("input", function() {
      handlePricingSlider(pricingInput, pricingOutput);
    });
  }
}


Enter fullscreen mode Exit fullscreen mode

结果如下👇

使用 JavaScript 绑定输入和输出数据

我们已经有了可用的价格范围滑块,但它仍然与可视化的价格数据脱节。现在需要将滑块的输入值输出价格数据绑定起来。



function handlePricingSlide(input, output) {
  // output the current slider value
  if (input.currentValEl)
    input.currentValEl.innerHTML = input.data[input.el.value];
  // update prices
  for (let i = 0; i < output.length; i++) {
    const outputObj = output[i];
    if (outputObj.currency) outputObj.currency.innerHTML = outputObj.data[input.el.value][0];
    if (outputObj.amount) outputObj.amount.innerHTML = outputObj.data[input.el.value][1];
    if (outputObj.after) outputObj.after.innerHTML = outputObj.data[input.el.value][2];
  }
}


Enter fullscreen mode Exit fullscreen mode

我们基本上是遍历每个pricingOutput对象,并将值输出currencyamount目标after元素中。

调整滑块值元素位置

快完成了。🏁 我们希望滑块的值能够跟随滑块滑块的移动而变化。

让我们创建一个函数来计算要应用于滑块值元素的左侧值。



function handleSliderValuePosition(input) {
  const multiplier = input.el.value / input.el.max;
  const thumbOffset = input.thumbSize * multiplier;
  const priceInputOffset =
    (input.thumbSize - input.currentValEl.clientWidth) / 2;
  input.currentValEl.style.left =
    input.el.clientWidth * multiplier - thumbOffset + priceInputOffset + "px";
}


Enter fullscreen mode Exit fullscreen mode

该函数用于确定滑块值的正确位置,使元素与滑块滑块水平对齐。以下是该函数功能的示意图👇

滑块值对齐

请注意,缩略图大小值是通过该getComputedStyle()方法解析的(参见我们定义 JS 变量的段落)。这样,我就可以在 CSS 中更改缩略图大小,而无需更改 JavaScript 文件中的任何内容。

设置默认滑块值

如果您想设置除 0 以外的初始滑块值Free,只需value="n"向范围滑块添加一个属性即可。

例如,<input type="range" value="1" />将返回一个范围滑块,其1,000初始滑块值为。

结论

以下是最终结果。点击“打开沙盒”查看完整代码。

希望您喜欢这篇教程。如果您想查看实际应用示例,这里有一个应用了此功能的落地页模板 👉 Surface

Surface 模板中的定价组件

文章来源:https://dev.to/cruip/how-to-build-a-pricing-slider-html-vanilla-js-53d0