jQuery でシンプルなループスライダーを作る

JavaScript というか jQuery のお勉強。
練習がてらコンテンツスライダーを作ってみました。
この手のものはチュートリアルもプラグインも山ほどありますが、必要な機能だけあって好きに使えるものが欲しかったので1から作りました。
プラグインをお求めの方は こちら をどうぞ。
欲しかった機能
- シンプルに動くコンテンツスライダー。
- ループ機能。最後の次は最初、最初の前は最後、という感じで動く。
- 進む、戻るナビゲーション。
- 自動でページネーションを設置。
これをやります。
デモとダウンロードは以下からどうぞ。
HTML と CSS
<div id="slider">
<div class="slider-view">
<div class="slider-container">
<div><a href="#"><img src="images/image1.jpg" alt="" /></a></div>
<div><a href="#"><img src="images/image2.jpg" alt="" /></a></div>
<div><a href="#"><img src="images/image3.jpg" alt="" /></a></div>
<div><a href="#"><img src="images/image4.jpg" alt="" /></a></div>
<div><a href="#"><img src="images/image5.jpg" alt="" /></a></div>
</div><!-- // .slider-container -->
</div><!-- // .slider-view -->
<a href="#" id="slide-prev">«</a><a href="#" id="slide-next">»</a>
</div>
ナビゲーションがなければ div は1個減らせます。
基本的には .slider-view を固定して .slider-container を横に動かす形となります。
#slider {
position: relative;
margin: 50px auto;
border: 1px solid #aaa;
border-radius: 10px;
width: 482px;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
}
#slider a:focus {
outline: 0;
}
.slider-view {
position: relative;
margin: 10px 40px 30px;
border: 1px solid #bbb;
width: 400px; /* スライダーで表示するエリアの大きさ */
height: 160px;
overflow: hidden; /* 必須 */
}
/* width は js で指定するのでここでは書かない */
.slider-container {
position: absolute;
top: 0;
left: 0;
}
/* スライドするコンテンツ部分 */
.slider-container div {
position: relative; /* ループ処理に使う */
float: left;
width: 400px; /* view と同じ大きさに */
height: 160px;
}
/* デモ用 --------*/
.slider-container div a {
display: block;
}
.slider-container div a:hover {
color: #600;
}
/*-------- ここまで */
/* 進む、戻るの配置とデコレーション */
#slide-prev, #slide-next {
position: absolute;
top: 60px;
color: #e0e0e0;
font-size: 60px;
line-height: 1;
text-decoration: none;
text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.3);
}
#slide-prev {
left: 2px;
}
#slide-next {
right: 2px;
}
#slide-prev:hover,
#slide-next:hover {
color: #ccc;
text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.5);
}
/* ページネーションの配置とデコレーション */
.slider-pagination {
position: absolute;
top: 177px;
left: 50%;
margin-left: -50px;
width: 100px;
}
.slider-pagination a {
float: left;
margin: 5px 5px 0;
border-radius: 5px;
width: 10px;
height: 10px;
overflow: hidden;
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5) inset;
background: #eee;
text-indent: -9999px;
vertical-align: middle;
}
.slider-pagination a:hover {
background: #ccc;
}
/* 現在表示しているコンテンツと同じページナンバー */
.slider-pagination a.current {
box-shadow: 1px 1px 2px rgba( 0, 0, 0, 0.5) inset,
0 0 2px rgba(68, 170, 238, 0.5);
background: #4ae;
}
position, width, height, overflow プロパティさえしっかり指定していればそんなに問題は起きないと思います。
特に .slider-view の overflow: hidden; はないと悲惨なことになるので必ず指定します。
.slider-view の overflow: hidden; を消した場合、ループ処理がどういう仕組になっているか分かりやすいと思いますので、試してみてもいいかもしれません。
jQuery
最初に各種設定をします。
var o = {
speed : 300, // スライドするスピード(ミリ秒)
interval: 3000 // 次のスライドまでの時間(ミリ秒)
};
// 対象となる要素を変数に格納しておく
var $slider = $('#slider'),
$container = $slider.find('div.slider-container'),
$contents = $container.children(),
$firstChild = $contents.filter(':first-child'),
$lastChild = $contents.filter(':last-child');
// スライドが表示されるエリアのサイズ(スライドするコンテンツ1つ分と同じ)
var size = {
width : $container.width(),
height: $container.height() // 今回は使いません
};
// スライドするコンテンツの現在地の管理
var count = {
min : 0,
max : $contents.length,
current: 0
};
// div.slider-container の width を設定する
// 前後にスライドコンテンツ1つ分のスペースを作る(ループ処理に使う)
$container.css({
width : size.width * ($contents.length + 2),
marginLeft : -size.width,
paddingLeft: size.width
});
次にスライドする動きと、共通処理を作ります。
var distance; // 移動距離を指定するのに使う
var slide = {
// スライド(進む)
next: function (index) {
// 移動距離を出すための関数
fnc.range(index, 'positive');
// スライドアニメーション
if(count.current < count.max - 1) {
// 現在地が最後のコンテンツより前の場合の処理
fnc.scroll(distance);
} else {
// 現在地が最後のコンテンツだった場合
// 最初のコンテンツをコンテナの一番後ろまで移動
$firstChild.css('left', size.width * $contents.length);
// 一番最後のコンテンツの次のエリアにスライド
$container.stop(true, false)
.animate({left: -distance}, o.speed,
// アニメーションコールバック関数
function () {
// 移動した最初のコンテンツを元の場所に戻す
$firstChild.css('left', 0);
// スライドしていったコンテナ自体も元の場所に戻す
$container.css('left', 0);
}
);
// 現在地を -1 に (次の処理で 0 になる)
count.current = -1;
}
// 現在地を 1 増やす
fnc.counter(index, 'increment');
// ページネーションのクラスを付け替える
fnc.pageNav(count.current);
},
// スライド(戻る) / 基本的には next の逆の処理をするだけ
prev: function (index) {
fnc.range(index, 'negative');
if(count.current > count.min) {
fnc.scroll(distance);
} else {
$lastChild.css('left', -(size.width * $contents.length));
$container.stop(true, false)
.animate({left: -distance}, o.speed,
function () {
$lastChild.css('left', '');
$container.css('left', -(size.width * ($contents.length - 1)));
}
);
count.current = count.max;
}
fnc.counter(index, 'decrement');
fnc.pageNav(count.current);
}
};
// 共通で使われる関数
var fnc = {
// 移動距離を出す
range : function (n, d) {
if(n >= 0) {
// ページネーションで指定するとき
distance = size.width * n;
} else {
// それ以外 / 自動 or 進む、戻る
var addNum;
if(d === 'negative') addNum = -1; // Next
if(d === 'positive') addNum = +1; // Prev
distance = size.width * (count.current + addNum);
}
},
// シンプルに移動距離分スライド
scroll : function (d) {
$container.stop(true, false).animate({left: -d}, o.speed);
},
// アニメーションするときに現在位置を増減する
counter: function (n, c) {
if(n >= 0) {
// ページネーションで指定するとき
count.current = n;
} else {
if(c === 'increment') count.current++; // 進む
if(c === 'decrement') count.current--; // 戻る
}
},
// ページネーションのクラス名を振りなおす
pageNav: function (n) {
$pagination.children('a').removeClass('current');
$pagination.children('a:eq(' + n + ')').addClass('current');
},
// 進む、戻るをクリックしたときの処理
pager : function (d, e) {
if(!$container.is(':animated')) {
clearInterval(start);
if(d === 'positive') slide.next(); // 進む
if(d === 'negative') slide.prev(); // 戻る
play();
}
e.preventDefault(); // リンククリック動作を無効にする
}
};
説明が足りていないような気もしますが、たぶん分かるんじゃないかと思います。
ごめんなさい。
最後はイベント処理、自動スライド、ページネーションです。
var play, start;
// 自動スライド処理
// o.interval で指定した時間ごとに slide.next() を実行する
play = function () {
start = setInterval(function () {
slide.next();
}, o.interval);
};
// スライドコンテンツにホバーしたときの処理
$contents.hover(
function () {
// ホバーしたら自動スライドを停止
clearInterval(start);
},
function () {
// カーソルが離れたら再開
play();
}
);
// 進む、戻るの処理
// 共通関数部分を参照
$('#slide-prev').click(function (e) {
fnc.pager('negative', e);
});
$('#slide-next').click(function (e) {
fnc.pager('positive', e);
});
// ページネーションを入れる div.slider-pagination を作成
var $pagination = $('<div/>', {'class': 'slider-pagination'});
// スライドするコンテンツ数と同じ数の a 要素を作って div.slider-pagination に追加
$contents.each(function (i) {
$('<a/>', {'href': '#'})
.text(i + 1)
.appendTo($pagination)
// クリックイベントを一緒に追加しておく
.click(function (e) {
e.preventDefault(); // リンククリック動作を無効にする
var indexNum = i; // クリックされたリンクのインデックス番号を取得
clearInterval(start); // 自動スライドを停止
// インデックス番号が現在地より大きい場合
if(indexNum > count.current) {
slide.next(indexNum);
}
// インデックス番号が現在地より小さい場合
else if(indexNum < count.current) {
slide.prev(indexNum);
}
play(); // 自動スライド再開
});
});
// ページネーションを div#slider に追加
$pagination.appendTo($slider);
// ページネーションの最初に current というクラス名を付ける
$pagination.find('a:first-child').addClass('current');
これで処理は完了です。
最後に
play();
を書いて、自動スライドを開始して完成です。
デモに使っているものと説明とは順番が少し違っていますが、書いてあることは同じです。
最終的にできあがるものはデモページで使っている script.js をご覧ください。
このスクリプトを使いたい場合は、ソースを丸ごと転載とかしなければ好きに使ってもらって構いません。
もっと素敵なコードに改良されて使われるといいなと思っています。
あと、写真は自分で撮ったものです。こっちはダウンロードしたデモページ以外では使わないでください。
スライドの順番を管理するために、以下を参考にしました。
CountNum – jsdo.it – Share JavaScript, HTML5 and CSS
練習としてはなかなかのものになったんじゃないかなーと思いますが、もっとよくしていきたいですね。
ページネーションを作るときに一緒にイベントを設定したほうがいいとご指摘を受けたので修正しました。

Comments
aki
こんにちは。
質問なのですが
上記スライダーのpagenationに
スライダーごとのタイトルを表記することって可能でしょうか?
まとり
@aki さん
スライダーごとのタイトルというと、こういう感じでしょうか。
http://unformedbuilding.com/demo/test/jquery.simpleLoopSlider.js.custom/
aki
まとりさま。
早速お返事いただきまして大変恐縮です。
ありがとうございます。
ご提示いただきましたサンプル拝見いたしました。
まさに私のやってみたいのはそれです!!
jQuery初心者なのですが、一から作るのは無理にしても
カスタマイズできるようになりたい、と思っていたところ
まとりさまのサイトのこのページを発見し
コードとにらめっこして試行錯誤しております。
ど、どうすれば、できるのでしょうか!
ご教示いただけますと幸いです。
ネコ、可愛いですね・・・。
まとり
@aki さん
上記のものは、この記事のスライダーをプラグインにした
http://unformedbuilding.com/articles/jquery-simple-loop-slider-js/
に少し手を加えたものです。ですので、
http://unformedbuilding.com/demo/test/jquery.simpleLoopSlider.js.custom/js/jquery.simpleloopslider.js
をそのまま使ってもらって構いません。
使い方は元々のプラグインと同じです。
こんな感じで、最後のオプションが追加されています。
タイトルは配列に入れてください。ただ、これは適当に追加したのでスライドページの数と合わない場合はバグるかもしれません。すみません。
元のものとはページネーションを作る部分が少し変更されています。どのへんが違うかは比べながらご確認ください。
私も初心者同然なので、そんなに難しいことはしていません。
改良点できそうなら改造して使っていただけると嬉しいです。
>ネコ、可愛いですね・・・。
ありがとうございますw
aki
まとりさま。
やったー!!できました!!
いや、もちろんプラグインを使用させていただくべきだってぇことは
重々わかっておりました。。。が、上記プラグイン化される前の
スクリプトでどうしてもスライダのタイトルを表示したく、ごちゃごちゃ
やってたら、ついに出来ました・・・(涙
あ、でもサイトの表示には作っていただいたプラグインを使いまっす。
絶対そっちのほうが便利ですもん。でも、できないのが悔しくて!
この度は、どうもありがとうございました~~感謝いたします。
まとり
@aki さん
それは何よりです。お疲れ様でした。
まずは自分でやってみようとするのはいいことだと思いますので、これからも頑張ってください。
aki
まとりさま
ありがとうございます!
頑張ります!
けい
こちらを使わせて頂きました。
一つ疑問なのですがこちらはスライドにリンクをはることは出来ないのでしょうか?
リンクを画像にはったのですが、更新してもリンクが押せない状態になっていまして・・・
まとり
@けい さん
この記事内からダウンロードできるスクリプトには最後の方に
という記述があるかと思います。
これはデモページのスライドでリンクを無効にするために書いています。
実際に使う場合には必要ありませんので消してしまっても結構です。
余計なお世話かもしれませんが、プラグイン化したものを使われたほうが楽かと思います。
やっていることはこの記事とほとんど同じです。
けい
ありがとうございました。うまくいきました!