こんにちは、ひとりハーモニーランド以外のソロ活動は大体可能なソロニスト ムラタです。
今回は横並びの要素をボタンで左右にスライドさせるだけの簡易的なスライドのプラグインを作成していこうと思います。
このような単純なものなら既存のプラグインを使わず自分で一から作ってみたほうがjQueryに対する理解も深まり、既存プラグインのカスタマイズなどもしやすくなるのではないでしょうか。
目次
前提
今回スライダーを作るにあたって前提として、
- スライドさせる要素は「li」に限定させる
- それぞれの「li」の幅は固定して統一
- 一度に表示させるliの数や合計値などある程度は手動で設定(今回は4つ表示とします)
- スライドするのは1つずつ
以上の決まりがあります。
これじゃあ使いにくいな~という方はご自分でカスタマイズもしくは一から作ってもらうか、世にあふれているもっと高機能のプラグインをご使用ください。
あくまで簡易ですので・・・
デモ
とりあえず四の五の言わずさっさと動きみせろや、って方も多いと思います(自分もです)ので先にデモページへのリンクを張っておきます。
どういった動きなのかご確認ください。
htmlとcss
それでは早速作っていきましょう。
今回使用したhtmlソースは以下になります。
html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
<div id="sliderSingle" class="slider"> <ul> <li> <dl> <dt>ひとりハーモニーランド</dt> <dd class="nanido">難易度:★★★★</dd> <dd>ひとりディズニーランドを上回る苛酷さ。幼い子どもたちや親子連れに混ざりアトラクションを楽しむ姿はこの世の地獄そのもの。不審者扱い待ったなし。</dd> </dl> </li> <li> <dl> <dt>ひとり焼き肉</dt> <dd class="nanido">難易度:★★☆☆</dd> <dd>最近では都心部で一人焼肉ができるお店も多くないとか。本当に肉が好きなら1人じゃ入りづらいとか甘っちょろいこと言うな。</dd> </dl> </li> <li> <dl> <dt>ひとり旅行</dt> <dd class="nanido">難易度:★☆☆☆</dd> <dd>誰のペースにあわせることなく、急遽計画を変更しようがとにかく自由なのでむしろひとりのほうが楽しいのでは。</dd> </dl> </li> <li> <dl> <dt>ひとりバースデー</dt> <dd class="nanido">難易度:★★★☆</dd> <dd>己の誕生日を己自身で祝う苦行。ハッピーバースデイ ディア オレの掛け声と共にろうそくの火を感謝の正拳突きで消す。</dd> </dl> </li> <li> <dl> <dt>ひとり花火大会</dt> <dd class="nanido">難易度:★★☆☆</dd> <dd>撮影か見学かで大きく難易度が変わるイベント。撮影ならばカメラと同化し一心不乱にシャッターを切ることで</dd> </dl> </li> </ul> </div> |
「li」タグをスライドさせる要素として、それを「div」で括っています。
なので必要最低限のタグは「div」「ul」「li」の3つだけです。
あ、当然ですがhead内でjQueryを読み込んでおきましょう。
css
cssはこんな感じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
.slider{ width: 760px; overflow: hidden; } .slider ul{ height: 210px; overflow: hidden; } .slider li{ float: left; width: 178px; height: 208px; list-style: none; border: 1px solid #CCCCCC; margin-right: 10px; } |
スライダーを囲んでいるdivには幅とはみ出した部分を不可視にするoverflowを設定をします。
ulには高さとoverflowを設定します。幅は後ほどjQueryで計算して設定するのでここでは不要です。
スライドする要素になるliにはflotで一列に並べ幅と高さも指定してやります。
jQuery
では肝心のjQueryのプラグインを作っていきましょう。
1 2 3 4 5 |
(function($){ $.fn.simpleSlider = function(){ //ここにプラグインの処理を書いていく }; })(jQuery); |
これがプラグインを書く上での基本の形になります。こうすることで「$」を使用してもprototype.jsなどの他のJavaScriptフレームワークに影響を与えることがなくなります。
「$.fn.simpleSlider」の部分がメソッド名となり、これ以降に実際の処理を書いていきます。今回はメソッド名を「simpleSlider」としました。
引数の設定
次にオプションを引数で受け取れるようにします。アニメーションのスピードや動きなど決められたオプション値を設定してあげれば、わざわざ本体を編集することなく変更することができるので便利ですね。
設定の方法は以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 |
(function($){ $.fn.simpleSlider = function(options){ var defaults = { next : '', //NEXTボタンになるクラス prev : '', //PREVボタンになるクラス speed: 400, //アニメーションスピード easing: 'linear' //アニメーションの動き }; var setting = $.extend(defaults,options); }; })(jQuery); |
今回設定した引数は「next」「prev」「speed」「easing」の4種にしました。
easingに関してはeasingプラグインを使用していないので「linear」と「swing」の2種のみです。
初期設定
いきなり動きの記述をする前にまず色々と準備をしましょう。
上記コードの「var setting = $.extend…」からの続きになります。
1 2 |
//スライドさせる要素(li)を変数に格納 var itemC = this.find("li"); |
スライドさせる要素「li」を格納するのですがここでいう「this」とはこのプラグインを実行させる際に指定した要素になります。
1 2 3 |
$(function(){ $("#sliderSingle").simpleSlider({speed:1200,easing:'swing'}); }); |
例えば上記なら「#sliderSingle」のことです。
1 2 |
//要素の幅取得(margin含む) var itemW = this.find(itemC).outerWidth(true); |
先ほど格納した要素の幅を取得します。前提で書いた通り全てのli要素の幅が同一なのでこの書き方でも大丈夫なのですが、要素ごとに幅が異なる場合はNGです。
またouterWidth(true)とすることでmargin幅も合計した幅を取得しています。
要素のmargin含む幅を取得したら次に要素の数を取得します。
1 2 |
//要素の数取得 var itemN = this.find(itemC).length; |
はい、そのまんまです。lengthを用いて要素の数を取得しました。
これで要素の幅と数が揃ったので、要素の合計数を親となる「ul」の幅に指定しましょう。
1 2 3 4 |
//要素の親(ul) var itemP = this.find("ul"); //親の幅を指定(要素の幅×要素の数) this.find(itemP).css({width: itemW * itemN+"px"}); |
これで今こういう状態になっています。
最後に残りの「speed」と「easing」の値も変数に格納しときます。
1 2 3 4 |
//スライドスピード var speed = setting.speed; //アニメーションイージング var easing = setting.easing; |
ボタンの設定
左右にスライドさせるためのボタンを用意するのですが、今回引数の「next」と「prev」の指定が無い場合は囲み要素の直下にそれぞれ「button」要素を生成することにしました。
また、今回のスライダーで一度に表示する数を4としていますので、スライドする要素がそれ以下の場合はボタンの生成はしないようにもしておきましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
if( itemN > 4 ){ if( setting.next == '' ){ //指定が無いならbutton要素を追加 this.append('<button class="slideNext" type="button">次へ</button>'); var btnLeft = this.find('.slideNext'); }else{ var btnLeft = setting.next; } if( setting.prev == '' ){ //指定が無いならbutton要素を追加 this.append('<button class="slidePrev" type="button">前へ</button>'); var btnRight = this.find('.slidePrev'); }else{ var btnRight = setting.prev; } } |
これで引数が無い場合はbutton要素が入り引数がある場合は、その要素がそれぞれ次へと前への動きをさせるボタンになります。
上の例の引数ありでは矢印の画像にそれぞれclass「next」と「prev」を割り振り引数に指定しています。表示位置とかはcssで好きなように変更すればOKです。
「次へ」の動き
ようやく動きに関する処理ですが、まずは「次へ」を押した時の処理について見ていきましょう。
1 2 3 4 5 6 7 |
//NEXTボタン処理 $( btnLeft ).click(function(){ itemP.not(":animated").animate({marginLeft: -itemW+"px"},speed,easing,function(){ var firstItem = itemP.find("li").filter(":first").detach(); itemP.css('marginLeft', '0px').append(firstItem); }); }); |
まず「itemP.not(“:animated”)」でアニメーションしておらず停止している場合のみ処理を行います。
こうすることでボタン連打で延々動き続けるようなことを防げます。
次の「animate」以降の処理で1つの要素分左に親(ul)をずらし、最初のli要素を「detach」で削除した後変数に格納。
そして親のずらした位置を戻した後に親の最後に先ほど格納したli要素を挿入。
ようするに最初の要素を最後に持ってきたわけですね。
図で表すとこんな感じ。
「前へ」の動き
最後に「前へ」を押した時の動きですが、こちらは単純にさっきしたことの逆をすればいいんでしょ?とか考えてるとうまくいきません。
逆なのは確かなのですが処理を行う順番が問題となります。
1 2 3 4 5 6 7 |
//PREVボタン処理 $( btnRight ).click(function(){ if( !(itemP.is(":animated")) ){ var lastItem = itemP.find("li").filter(":last").detach(); itemP.prepend(lastItem).css("marginLeft", -itemW+"px").animate({marginLeft: "0px"},speed,easing); }; }); |
「次へ」の時は「左にずらす」→「最初の要素削除」→「ずらした分を戻して最後に挿入」でしたが、「前へ」の時は「最後の要素を削除」→「最初に挿入」→「ずらして戻す」となります。
最後の「ずらして戻す」という部分が「何言ってんだこいつ」と思いますが、図で説明していきましょう。
このような流れになるわけですが、先に右にずらしちゃうと左側(要素の先頭)に空白ができちゃいますよね?なので先に不可視領域にいる最後の要素を削除・格納します。
そして先頭に先ほどの要素を戻すのと1要素分左にずらす処理をほぼ同時に行ってから、アニメーションで右にずらす処理をするという流れです。
最後に
最後にjQueryを最初から最後までまとめたものを載せておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
(function($){ $.fn.simpleSlider = function(options){ var defaults = { next : '', //NEXTボタンになるクラス prev : '', //PREVボタンになるクラス speed: 400, //アニメーションスピード easing: 'linear' //アニメーションの動き }; var setting = $.extend(defaults,options); //スライドさせる要素(li)を変数に格納 var itemC = this.find("li"); //要素の幅取得(margin含む) var itemW = this.find(itemC).outerWidth(true); //要素の数取得 var itemN = this.find(itemC).length; //要素の親(ul) var itemP = this.find("ul"); //親の幅を指定(要素の幅×要素の数) this.find(itemP).css({width: itemW * itemN+"px"}); //スライドスピード var speed = setting.speed; //アニメーションイージング var easing = setting.easing; if( itemN > 4 ){ if( setting.next == '' ){ //指定が無いならbutton要素を追加 this.append('<button class="slideNext" type="button">次へ</button>'); var btnLeft = this.find('.slideNext'); }else{ var btnLeft = setting.next; } if( setting.prev == '' ){ //指定が無いならbutton要素を追加 this.append('<button class="slidePrev" type="button">前へ</button>'); var btnRight = this.find('.slidePrev'); }else{ var btnRight = setting.prev; } } //NEXTボタン処理 $( btnLeft ).click(function(){ itemP.not(":animated").animate({marginLeft: -itemW+"px"},speed,easing,function(){ var firstItem = itemP.find("li").filter(":first").detach(); itemP.css('marginLeft', '0px').append(firstItem); }); }); //PREVボタン処理 $( btnRight ).click(function(){ if( !(itemP.is(":animated")) ){ var lastItem = itemP.find("li").filter(":last").detach(); itemP.prepend(lastItem).css("marginLeft", -itemW+"px").animate({marginLeft: "0px"},speed,easing); }; }); }; })(jQuery); |
例によって例のごとくプログラムバリバリにできる人が見たらおかしい点や無駄な点も多いと思いますが、そこはご勘弁ください。