👌

Bootstrapのrangeポインター内に現在値を表示させる

2023/05/22に公開

はじめに

Bootstrapは公式のコードをコピペすれば誰でも簡単にそれっぽいUIが実装できてとても便利です。
ですがその反面細かい所に手が届きにくいデメリットもあります。
今回rangeのポインター内に現在値を表示させたいシーンがあったのですが、bootstarpの見た目を崩さずに表示させるサンプルが見つからなかったので実装してみました。
完成するとこんな感じになります。

前提条件

bootstrap 5.0.2

コード

index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <script src="./index.js"></script>
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
      crossorigin="anonymous"
    />
    <link rel="stylesheet" href="./index.css" />
  </head>
  <body>
    <div style="width: 400px">
      <div class="mb-3">
        <div class="range-area">
          <label for="customRange" id="LabelCustomRange" class="form-label"
            >min:0, max:10, step:1</label
          >
          <input
            type="range"
            name="customRange"
            class="form-range range"
            min="0"
            max="10"
            step="1"
          />
          <div class="range-thumb"></div>
        </div>
      </div>
    </div>
  </body>
</html>
index.js
window.onload = () => {
  const rangeDiv = document.getElementsByClassName("range-area");
  for (let i = 0; i < rangeDiv.length; i++) {
    const rangeDivElement = rangeDiv[i];
    const thumbElement =
      rangeDivElement.getElementsByClassName("range-thumb")[0];
    const rangeElement = rangeDivElement.getElementsByClassName("range")[0];
    // マッピングサイズ(min:0, max:14, step:2の場合は7)
    const mapSize =
      (Number(rangeElement.max) - Number(rangeElement.min)) /
      Number(rangeElement.step);
    const thumbWidth = thumbElement.clientWidth;

    rangeElement.addEventListener("input", (event) => {
      const value = event.target.value;
      const width = event.target.clientWidth;
      // マッピング内の現在値
      const mapValue =
        (Number(rangeElement.value) - Number(rangeElement.min)) /
        Number(rangeElement.step);
      // サムの位置を計算
      const thumbLeft = (mapValue * (width - thumbWidth)) / mapSize;
      thumbElement.style.left = Math.floor(thumbLeft * 100) / 100 + "px";
      // サムに現在値をセット。
      thumbElement.innerText = value;
    });

    // イベントを強制発火させて初期値を表示させる。
    rangeElement.dispatchEvent(new Event("input"));
  }
};
.range-area {
  position: relative;
}

.range-thumb {
  position: absolute;
  left: 0px;
  top: 38px;
  width: 16px;
  text-align: center;
  color: #fff;
  line-height: 11px;
  font-size: 11px;
  pointer-events: none;
}

メカニズム

やっている事はrange上にrangeのvalue値を含むラベルを重ねて表示させているだけです。
rangeのvalue値とmin~maxの範囲で比率を求めてその比率とwidthの積からrangeを基準にしたラベルのleft値を計算します。
あとはrangeのvalue値が変わる度にラベルのleft値を設定してポインターの上に重なるように動かしています。

おわりに

Bootstrapで見た目を気にする場面は少ないと思いますがどこかの誰かの助けになれば幸いです。

GitHubで編集を提案

Discussion