Help us understand the problem. What is going on with this article?

jQuery UI Slider で、ハンドルの数を増減したい話

前書き

そもそも、ここの回答を転記、加工して、動作を確認しただけです。
なぜ書き直したかといいますと、2013年の回答と、現在のバージョンが異なるから、
サンプルページが動作していなかったため、
そして、日本語での参考にできる記事がなかったため、検索性を考えて、転記しておこうということです。

ということなので、話はタイトルで分かったから、早くという人は本家からコピーしたほうが早いです。元のままでもちゃんと動きました。

もしくは、本番へどうぞ。

何がしたいか

jQuery UIのSliderという機能、ハンドル(値を持てる箇所)を複数指定する場合

$('#slider').slider({
    min : 0,
    max : 50,
    values : [0,10,20,30,40,50]
});

こんな感じで、指定してあげると、6つのハンドルができます。
11.PNG

このハンドルを、操作するには、

$('#slider').slider('option',{
    'values' : [5,15,25,35,45,50]
});

こんな感じで、指定すれば、ハンドルを操作することができます。
12.PNG

では、ハンドルの数を変化させたい場合は、

// 増やす?
$('#slider').slider('option',{
    'values' : [0,15,10,20,30,40,50]
});

// 減らす?
$('#slider').slider('option',{
    'values' : [0,10,20,30,40]
});

これだと、動かないです。
エラーは起きないのですが、
valuesは初期化したとき作られた、ハンドルは操作できますが、
後からハンドルの数の変動はできない仕様になっています。

今回は、これを、jQuery UI の拡張という手を使って、sliderにmethodを追加して解決しました。

本番

前書きが長くなりましたが、
では本番です。

環境

  • Chrome 80.0.3987.132
  • jQuery 3.3.1
  • jQuery UI 1.12.1

いずれも、2020/03/05時点で最新版

コード

<!DOCTYPE html>
<html lang="ja-jp">
<head>
    <title></title>
    <meta charset="utf-8">
    <link rel="stylesheet" type="text/css" href="./css/jquery-ui.min.css">
    <link rel="stylesheet" type="text/css" href="./css/jquery-ui.structure.min.css">
    <style type="text/css">
        #slider {
            width: 500px;
            margin: 50px
        }
    </style>
</head>
<body>
<script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>
<script src="./js/jquery-ui.min.js"></script>
<div id="slider"></div>
<div><button id="add_btn">追加</button><button id="remove_btn">削除</button></div>

    <script type="text/javascript">
// ハンドルの配列
var slider_handles = [0,10,20,30,40,50];
$(function(){
    // jQuery UI slider の拡張
    $.widget('my-namespace.slider', $.ui.slider, {
        addValue: function(val) {
            this.options.values.push(val);
            this._refresh();
        },
        removeValue: function() {
            this.options.values.pop();
            this._refresh();
        }
    });

    // sliderの初期化
    $('#slider').slider({
        min : 0,
        max : 100,
        values : slider_handles
    });

    // ボタンのイベントリスナ
    $('#add_btn').on('click', function(event) {
        // 一番最後にあるハンドルに+10した場所に新しく作る
        $('#slider').slider('addValue',slider_handles[slider_handles.length-1]+10);
    });
    $('#remove_btn').on('click', function(event) {
        // ハンドルを1つ削除
        $('#slider').slider('removeValue');
    });
});
    </script>
</body>
</html>

こんな感じです。上のソースはjQuery UIだけダウンロードしてきて、そのままコピペすれば動くはずです。

簡単に説明

    // jQuery UI slider の拡張
    $.widget('my-namespace.slider', $.ui.slider, {
        addValue: function(val) {
            this.options.values.push(val);
            this._refresh();
        },
        removeValue: function() {
            this.options.values.pop();
            this._refresh();
        }
    });

の部分で、sliderを拡張しています。addValueと、removeValueを追加しています。
大切なのは、this._refresh()だけです。
this._refresh()は、sliderの初期化時に、処理される部分で、一度sliderを破棄してから、作り直しています。

あとは、ボタンのイベントリスナーからそれぞれのメソッドを呼び出します。

// ボタンのイベントリスナー
    $('#add_btn').on('click', function(event) {
        $('#slider').slider('addValue',slider_handles[slider_handles.length-1]+10);
    });
    $('#remove_btn').on('click', function(event) {
       $('#slider').slider('removeValue');
    });

追加する方のイベントリスナーでは、新しいハンドルの場所をしてしなくてはいけないので、
サンプルとしては、とりあえず、今ある一番最後のハンドルの+10の位置に追加しています。

注意書き

元のsliderをオーバーライトして使ってる話

$.widget('my-namespace.slider', $.ui.slider, {});の部分は、
$.ui.sliderを継承して、my-namespace.sliderを新しく作っています。
今回は、そのまま同じ名前の、sliderを使っているので、何も気にせず、拡張されていますが、
この方法を使うと、拡張部分をコピーし忘れてソースコード中からほかのプログラムにコピペすると、動かないので、
$.widget('my-namespace.custom_slider', $.ui.slider, {});のように、
新しい名前を付けて使うのが普通見たいです。ただ、その場合、$(selector).slider();で書いているところをすべて、
$(slector).custom_slider();に書き換えて使ってください。
散々コードを書いてしまった後で、追加するようなときは、今回のようにすると、そのまま使えます。

配列をグローバルに置いている話

ハンドルの値をしまっている配列をグローバルに置いてるのは、今回わかりやすくするために置いただけで、
$('#slider').slider('values');に取得してあげれば、素直に配列で返ってくるので、グローバルを汚したくない人はこちらがいいかも。

ないのに削除したり、もう入らないのに追加できる話

removeでは、配列の中に1つもなくても、実行できています。
addでは、値がはみ出ても追加できています。
実際に使うときはアラート出すなりして、それぞれ工夫してください。

余談

要はthis._refresh()

this._refresh();だけで動くんだったら、refreshメソッドだけ拡張すればいい気もする。
今回私は、1行ずつの追加削除でよかったけれど、複数行扱うときは、そのほうがいい気がする。

array.pop()だけでは不満なら

頭から消したかったり、指定して削除したかったり、することもあるかも、と思った。今のところない。
実装が必要になったら、更新するかもしれない。

本家が実装してくれればいいのに

お願いします。

最後に

サンプルとして投稿しているコードは実際に使ったものを抽象化しているものです。
動作は保証しませんが、問題があれば、教えていただければ修正するかもしれません。

この投稿のコード権利を主張しませんので、適当にコピペして使って大丈夫です。

質問とか、適当こいてんじゃないよみたいなことがあれば、コメントいただければ。

MarioninC
そういえば、ここだけ@Hisamaroだったので、@MarioninCに統一しました。 PHPをメイン業務に、開発からインフラまでなんでもやるフリーランスです。 どうでもいいけど、アイコンは、ピンボールのターゲット
http://twitter.com/marioninc/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away