1. MarioninC

    Posted

    MarioninC
Changes in title
+jQuery UI Slider で、ハンドルの数を増減したい話
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,192 @@
+## 前書き
+そもそも、[ここ](https://stackoverflow.com/questions/19348528/jquery-ui-slider-how-to-add-values)の回答を転記、加工して、動作を確認しただけです。
+なぜ書き直したかといいますと、2013年の回答と、現在のバージョンが異なるから、
+サンプルページが動作していなかったため、
+そして、日本語での参考にできる記事がなかったため、検索性を考えて、転記しておこうということです。
+
+ということなので、話はタイトルで分かったから、早くという人は[本家](https://stackoverflow.com/questions/19348528/jquery-ui-slider-how-to-add-values)からコピーしたほうが早いです。元のままでもちゃんと動きました。
+
+もしくは、[本番](#本番)へどうぞ。
+
+## 何がしたいか
+[jQuery UIのSlider](https://jqueryui.com/slider/)という機能、ハンドル(値を持てる箇所)を複数指定する場合
+
+```js
+$('#slider').slider({
+ min : 0,
+ max : 50,
+ values : [0,10,20,30,40,50]
+});
+```
+こんな感じで、指定してあげると、6つのハンドルができます。
+<img width="457" alt="11.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194701/38c753b1-279f-dd7a-8f97-cb44729ac0f1.png">
+
+このハンドルを、操作するには、
+
+```js
+$('#slider').slider('option',{
+ 'values' : [5,15,25,35,45,50]
+});
+```
+こんな感じで、指定すれば、ハンドルを操作することができます。
+<img width="443" alt="12.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194701/cbdeda76-7187-5463-abfd-224cd233a3b3.png">
+
+では、ハンドルの数を変化させたい場合は、
+
+```js
+// 増やす?
+$('#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時点で最新版
+
+## コード
+```haml
+<!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だけダウンロードしてきて、そのままコピペすれば動くはずです。
+### 簡単に説明
+```js
+ // 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を破棄してから、作り直しています。
+
+あとは、ボタンのイベントリスナーからそれぞれのメソッドを呼び出します。
+
+```js
+// ボタンのイベントリスナー
+ $('#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()`だけでは不満なら
+頭から消したかったり、指定して削除したかったり、することもあるかも、と思った。今のところない。
+実装が必要になったら、更新するかもしれない。
+
+### 本家が実装してくれればいいのに
+お願いします。
+
+## 最後に
+サンプルとして投稿しているコードは実際に使ったものを抽象化しているものです。
+動作は保証しませんが、問題があれば、教えていただければ修正するかもしれません。
+
+この投稿のコード権利を主張しませんので、適当にコピペして使って大丈夫です。
+
+質問とか、適当こいてんじゃないよみたいなことがあれば、コメントいただければ。