IsotopeでPinterest風レイアウトを実装する。Ajaxロードもあるよ。

#isotope, #javascript, #masonry

今回、Isotopeプラグインを使ってArchiveページを実装したのでメモ。
Masonryプラグインを使った実装は、前回の記事を参照して下さい。

基本的な実装

Masonryとほぼ同じです。
Isotopeでは、レイアウトオプションが増えたため、masonryプロパティに設定を書きます。

1
2
3
4
5
6
7
8
$(function() {
$('#container').isotope({
itemSelector: '.item',
masonry: {
columnWidth: 50
}
});
});

ちょっと修正する

前回同様、小気味よい感じにするために手を加える。

HTML

HTMLのマークアップはMasonryと同様。
描画が終わるまでローディングを表示したいので、ローディング用のdivを置き、#containerにグリッド幅計算用の.grid-sizerを用意します。例によって、#containerに直接.itemを置くのではなく、display:none;#hidden-itemsに退避。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<section role="main">
<div class="loader"></div>
<div id="container">
<div class="grid-sizer"></div>
</div>
<div id="hidden-items">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
:
:
</div>
</section>

JavaScript

#hidden-itemsから.itemを取得して、#containerにappendするように実装を修正します。MasonryではDOMにappendしてから更にMasonryのappendedメソッドを使って.itemを認識させないといけなかったが、Isotopeでは上手く改善されている。

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
(function() {
var $items = $('#hidden-items').find('.item'),
$container = $('#container');
if ( ! $container.length ) return;
// Masonry
// $container
// .masonry({
// itemSelector: '.item',
// isFitWidth: true,
// columnWidth: '.grid-sizer'
// })
// .append( $items )
// .masonry( 'appended', $items );
// Isotope
$container
.isotope({
itemSelector: '.item',
masonry: {
columnWidth: '.grid-sizer',
isFitWidth: true
}
})
.isotope('insert', $items); // 改善されてる!
$('.loader').hide();
}());

簡潔に記述できました。

Ajaxでitemを読み込む

これだけじゃつまらないので、「load more」をクリックすると、次のページをAjaxで読み込んで、コンテナに追加していくアレを実装します。
以下のように、コンテナのフッターに次ページがあれば次ページのリンクが表示されるようにします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<section role="main">
<div class="loader"></div>
<div id="container">
<div class="grid-sizer"></div>
</div>
<!-- 次ページへのリンク -->
<a href="page/2" class="load-more-link">load more</a>
<div id="hidden-items">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
:
:
</div>
</section>

このリンクのクリックイベントをフックして、Ajaxで次ページのitemを取得すれば良い。
エラー処理や細かなハンドリングは省いてます。

1
2
3
4
5
6
7
8
9
10
11
12
$('.load-more-link').on('click', function() {
$.ajax({
type: 'GET',
url: $(this).attr('href'),
dataType: 'html'
}).done(function(res) {
// レスポンスから item を抜き出す
var $items = $(res).find('.item');
// コンテナに追加する
$cotainer.isotope('insert', $items);
});
});

PHPなど使って自由にAPIを書ける場合はJSONを受け取って、JavaScriptテンプレートなどを使ってDOMを生成するのがMV*っぽくてよいと思います。今回実装したのが、Middlemanのような静的なページだったので、htmlをそのまま受け取っています。

あとは、読み込むたびにload moreのリンク先を次のページに書き換えてあげればOKです。

スクロールに応じて自動的に読み込む

これは、フックするイベントがスクロールイベントになるだけで、上のAjaxの処理を切り出してスクロールイベントで呼び出すとよいでしょう。あとスクロール量とWindowの高さの差が一定のしきい値以下になったときに呼び出せば出来上がり。