こんにちは、エムケーワイです。
今回はギャラリーページの第3弾です。
第1弾ではJavaScriptライブラリImagesLoadedとMasonryを使って、20種類の画像のロード完了を待って、それらをタイル状に配置・表示するギャラリーページを作成しました。画像クリックによる拡大画像のモーダル表示も実装しました。
第2弾では画像を89種類に増やし、画像を16種類ずつ追加表示していく機能を追加実装しました。
第3弾では更に画像をカテゴリー別に表示する機能を実装します。
では、早速結果を見ていただきます。
第2弾に対する主な追加・修正ポイントは下記になります。
- カテゴリーを選択するためのラジオボタンを追加。
- 各カテゴリーが選択された際、JSONデータから選択されたカテゴリーの要素のみを取得(フィルタリング)、表示する処理を追加。
では、実現方法を説明して行きます。
1. HTMLコード
今回カスタムHTMLで記述したコードは下記の通りです。
第2弾のコードの前にカテゴリー選択用ラジオボタンを配置するためのコードを追記しました。
<div class="radioButtons">
<form class="cateFilter" id="cateFilter">
<span class="cateItem">
<input type="radio" name="filter" id="cateAll" value="all" checked="">
<label for="cateAll">All</label>
</span>
<span class="cateItem">
<input type="radio" name="filter" id="cateShenzhen" value="Shenzhen">
<label for="cateShenzhen">Shenzhen</label>
</span>
<span class="cateItem">
<input type="radio" name="filter" id="cateHongkong" value="HongKong">
<label for="cateHongkong">Hong Kong</label>
</span>
<span class="cateItem">
<input type="radio" name="filter" id="cateVietnam" value="Vietnam">
<label for="cateVietnam">Vietnam</label>
</span>
<span class="cateItem">
<input type="radio" name="filter" id="cateFlowers" value="Flowers">
<label for="cateFlowers">Flowers</label>
</span>
<span class="cateItem">
<input type="radio" name="filter" id="cateFireworks" value="Fireworks">
<label for="cateFireworks">Fireworks</label>
</span>
</form>
</div>
<div class="mkyGalleryContainer">
<ul class="mkyGallery"></ul>
<button class="loadMore" id="loadMore">Load More</button>
</div>
ラジオボタン配置のコードについて以下に説明します。
- 全体をクラス”radioButtons”を持つdiv要素で囲みます。
- カテゴリーを選択するためのform要素として6つのラジオボタンを配置します。ラジオボタンのvalue属性には、JSONデータのプロパティ”category”の値(“Shenshen”/”HongKong”/”Vietnam”/”Flowers”/”Fireworks”)を設定します。“all”は全カテゴリー選択を意味します。初期状態では”all”に選択状態であることを示すchecked属性が付加されています。
- 各ラジオボタンの横に、対応するカテゴリーを示すラベルを表示します。
ラジオボタンのvalue属性値とJSONデータの”category”プロパティの値を紐づけることで、フィルタリングの際JSONデータから選択されたカテゴリーの要素のみを取得することが可能になります。
2. JSONファイル
今回も第2弾と同じく”galleryFull.json”を使用します。
ようやく”category”プロパティの出番です!
[
{
"title": "Night View",
"category": "HongKong",
"images": {
"thumb": "/wp-content/themes/twentytwenty_ch/gallery/images/thumb-H-8.jpg",
"large": "/wp-content/themes/twentytwenty_ch/gallery/images/large-H-8.jpg"
}
},
{
"title": "Victoria Harbor",
"category": "HongKong",
"images": {
"thumb": "/wp-content/themes/twentytwenty_ch/gallery/images/thumb-H-4.jpg",
"large": "/wp-content/themes/twentytwenty_ch/gallery/images/large-H-4.jpg"
}
},
{ ... }
]
3. JavaScriptライブラリの読み込み
第3弾でもJavaScriptライブラリImagesLoadedとMasonryを使用します。
第1弾、第2弾と同じようにfunctions.phpで上記ライブラリの読み込み設定を行っています。
4. jQueryコード
今回作成したjQueryコードは下記になります。
第2弾のjQueryコードに対して追加・修正した部分をメインに説明します。
jQuery(function() {
jQuery('.mkyGallery').each(function() {
var _gallery = jQuery(this), // ギャラリーオブジェクト
_loadMoreButton = jQuery('#loadMore'), // 追加ボタン
_radioButtons = jQuery('.radioButtons'), // ラジオボタンオブジェクト
_filter = jQuery('#cateFilter'), // フィルターオブジェクト
addImgCount = 16, // クリック毎に追加する画像数
imgCount = 0, // 現在の表示画像数
jsonData = [], // JSONデータ
filteredData = [], // フィルタリングされたJSONデータ
galleryW = _gallery.width(), // ギャラリー領域の幅
imageW = 300, // 画像の幅
gutterW; // gutterの幅
// ラジオボタンを含むブロックの高さを設定
_radioButtons.css('height', _filter.outerHeight());
// gutterの幅を算出
if (galleryW > 900) {
gutterW = Math.floor((galleryW - imageW * 3) / 2);
} else if (galleryW > 600) {
gutterW = galleryW - imageW * 2;
} else {
gutterW = galleryW - imageW;
}
// Masonry初期設定
_gallery.masonry({
columnWidth: 300,
gutter: gutterW,
itemSelector: '.mkyGalleryItem'
});
// JSONデータを取得し、ギャラリーを生成する
jQuery.getJSON('/wp-content/themes/twentytwenty_ch/gallery/galleryFull.json', function(data) {
// 取得した JSON データを格納
jsonData = data;
// 初期状態はフィルタリングせず全データを代入
filteredData = jsonData;
// ギャラリーのHTMLコードを生成し、画像を表示する
createHTML();
// Load Moreボタンクリックで画像を追加
_loadMoreButton.on('click', createHTML);
// カテゴリーが選択されたらフィルタリングを実行
_filter.on('change', 'input[type="radio"]', filterItems);
});
// HTMLコードを生成しギャラリーを表示する
function createHTML(filter) {
var elements = [],
slicedData = filteredData.slice(imgCount, imgCount + addImgCount);
// JSON配列内の要素毎に処理する
jQuery.each(slicedData, function (i, item) {
// HTMLコード(文字列)を生成する
var htmlCode =
'<li class="mkyGalleryItem onLoading">' +
'<a class= "mkyJsModalOpen" href="' + item.images.large + '">' +
'<img src="' + item.images.thumb + '" alt="">' +
'</a>' +
'<div class="mkyModal mkyJsModal" style="display: none;">' +
'<div class="mkyModalBG"></div>' +
'<div class="mkyModalContent">' +
'<figure>' +
'<img src="' + item.images.large + '" alt="">' +
'<span class="caption">' +
'<b class="title">' + item.title + '</b>' +
'</span>' +
'<button class="mkyJsModalClose">×</button>' +
'</figure>' +
'</div>' +
'</div>' +
'</li>';
// HTMLコード(文字列)をDOM要素化し、配列に追加
elements.push(jQuery(htmlCode).get(0));
});
// DOM要素の配列をコンテナーに挿入し、Masonryを実行
_gallery
.append(elements)
.imagesLoaded(function() {
jQuery(elements).removeClass('onLoading');
_gallery.masonry('appended', elements);
// フィルタリング時は再配置
if (filter) {
_gallery.masonry();
}
});
// 現在の表示画像数の更新
imgCount += slicedData.length;
// 全画像が表示されたらLoadMoreボタンを消去
if (imgCount < filteredData.length) {
_loadMoreButton.show();
} else {
_loadMoreButton.hide();
}
// 画像のモーダル表示
jQuery('.mkyGalleryItem').on('click',function(){
jQuery(this).find('.mkyJsModal').fadeIn();
return false;
});
jQuery('.mkyJsModalClose').on('click',function(){
jQuery(this).parents('.mkyJsModal').fadeOut();
return false;
});
}
// カテゴリーフィルター
function filterItems() {
var key = jQuery(this).val(), // 選択されたラジオボタンのvalue
// 表示済みのMasonryアイテム
masonryItems = _gallery.masonry('getItemElements');
// Masonryアイテムを削除
_gallery.masonry('remove', masonryItems);
// フィルタリング後データ、現在の表示画像数をクリア
filteredData = [];
imgCount = 0;
if (key === 'all') {
// allが選択された場合、全JSONデータを格納
filteredData = jsonData;
} else {
// all以外の場合、keyと一致する要素を抽出
filteredData = jQuery.grep(jsonData, function (item) {
return item.category === key;
});
}
// HTMLコードを生成し、要素を表示
createHTML(true);
}
});
});
4-1. 全体の流れ
全体の流れは第2弾とほぼ同じですが、下記の追加・修正を行っています。
- カテゴリー選択時のフィルタリング処理を追加
- HTMLコードの生成、要素の配置・表示を、フィルタリングされたJSONデータを対象とするよう修正
4-2. 変数の宣言、初期化
第1弾に対し下記の変数が追加されています。
変数 | 意味 |
_radioButtons | ラジオボタンオブジェクト |
_filter | カテゴリーフィルターオブジェクト |
filteredData | フィルタリング後のJSONデータを格納する変数 |
4-3. ラジオボタンブロックの高さ設定
ラジオボタンを囲むdiv要素の高さとして、内部要素(_filter)のボーダーラインを含めた高さ(outerHeight())を設定しています。これは、ラジオボタン上にギャラリーの画像が表示されないようにするためです。
_radioButtons.css('height', _filter.outerHeight());
4-4. Masonryの初期設定
第1弾、第2弾と同じです。
4-5. JSONデータの取得とギャラリー生成
jQuery.getJSON('/wp-content/themes/twentytwenty_ch/gallery/galleryFull.json', function(data) {
// 取得した JSON データを格納
jsonData = data;
// 初期状態はフィルタリングせず全データを代入
filteredData = jsonData;
// ギャラリーのHTMLコードを生成し、画像を表示する
createHTML();
// Load Moreボタンクリックで画像を追加
_loadMoreButton.on('click', createHTML);
// カテゴリーが選択されたらフィルタリングを実行
_filter.on('change', 'input[type="radio"]', filterItems);
});
第2弾から追加・修正された部分について説明します。
- getJSON()メソッドで取得したデータを、変数jsonDataと共に変数filteredDataにも代入しています。jsonDataに全データを保存しておき、filteredDataがHTMLコード生成、要素の配置・表示の対象となります。
- 選択カテゴリーが変わったら(changeイベント発生)、フィルタリング処理を行う関数filterItems()を呼び出します。
4-6. HTMLコードの生成と画像の配置・表示
関数createHTML()に対する追加・修正について説明します。
4-6-1. 引数の追加
カテゴリーが変更された場合は、Masonryで要素の再配置を行う必要があります。そのためフィルタリングが行われたかどうかを示す引数filter(trueでフィルタリングあり)を追加しました。
引数なしで呼び出された場合はfalseと判断されます。
4-6-2. HTMLコードの生成
slice()メソッドの対象をjsonDataからfilteredDataに変更しています。
slicedData = filteredData.slice(imgCount, imgCount + addImgCount);
HTMLコード生成処理、HTMLコード(文字列)をDOM要素化して配列elementsに追加する処理は第1弾、第2弾と同じです。
4-6-3. 画像の配置、表示
引数filterがtrueの場合、Masonryで要素の再配置を行う処理を追加しました。再配置を実行しないと、それまで要素が表示されていた領域が空白となり、その後ろにフィルタリングされた要素が追加されてしまいます。
_gallery
.append(elements)
.imagesLoaded(function() {
jQuery(elements).removeClass('onLoading');
_gallery.masonry('appended', elements);
// フィルタリング時は再配置
if (filter) {
_gallery.masonry();
}
});
4-6-4. 「Load More」ボタンの表示・消去
カテゴリーが変更されると、HTMLコードの生成、要素の配置・表示をやり直すことになるので、「Load More」ボタンの表示処理を追加しています。
if (imgCount < filteredData.length) {
_loadMoreButton.show();
} else {
_loadMoreButton.hide();
}
4-7. フィルタリング
選択カテゴリーが変更された際のフィルタリング処理は関数filterItems()にまとめています。
function filterItems() {
var key = jQuery(this).val(), // チェックされたラジオボタンのvalue
// 追加済みの Masonry アイテム
masonryItems = _gallery.masonry('getItemElements');
// Masonry アイテムを削除
_gallery.masonry('remove', masonryItems);
// フィルタリング後のデータと現在の表示画像数をクリア
filteredData = [];
imgCount = 0;
if (key === 'all') {
// allが選択された場合、すべてのJSONデータを格納
filteredData = jsonData;
} else {
// all以外の場合、キーと一致するデータを抽出
filteredData = jQuery.grep(jsonData, function (item) {
return item.category === key;
});
}
// アイテムを追加
createHTML(true);
}
- 選択されたカテゴリー(ラジオボタン)のvalue属性値を変数keyに代入します。
- Masory()メソッドで、現在ギャラリーに表示されている要素を取得し、変数masonryItemsに代入します。
- Masonry()メソッドで、ギャラリーから表示要素(masonryItems)を削除します。
- フィルタリング後のデータ(filteredData)と現在の表示画像数(imgCount)をクリアします。
- カテゴリー”all”が選択された場合、変数filteredDataに全JSONデータ(jsonData)を格納します。
- “all”以外のカテゴリーが選択された場合、全JSONデータ(jsonData)から選択されたカテゴリーの要素を抽出し、変数filteredDataに格納します。
具体的にはjQuery.grep()メソッドを使って、jsonDataからプロパティ”category”の値がkeyと一致する要素を取得しています。
jQuery.grep()メソッドでは、第1引数に処理対象の配列を、第2引数に抽出条件を関数の形で指定します。
関数には引数として配列の要素(item)が渡されます。関数の中身は”return 条件”で、条件を満たし、trueが返された要素のみがfilteredDataに格納されます。今回の条件は”item.category === key”となります。
ちょっと特殊な書き方ですね。
5. CSS設定
ラジオボタンに対する設定を追加しました。
/*
* radio buttons
*/
.radioButtons {
border-bottom: 1px solid gray;
height: auto;
}
/* category filter */
.cateFilter {
float: right;
}
.cateFilter label {
cursor: pointer;
display: inline-block;
font-weight: bold;
padding: 0 4px 0 0;
}
.cateFilter span {
display: inline-block;
}
.cateFilter span:not(:last-child) {
margin-right: 10px;
}
- クラス”radioButtons”(ラジオボタンを囲むdiv要素)に対しアンダーラインを引きます。高さ(height)に”auto”を設定していますが、jQueryで数値が設定されます。
- クラス”cateFilter”に対して、”float :right”を設定しています。これにより、ラジオボタンは右詰めで配置されます。
いかがでしたでしょうか?
ギャラリーページの第3弾として、ラジオボタンでギャラリー要素のカテゴリーを選択できるようにし、選択されたカテゴリーの要素のみ配置・表示するようにしました。
今回の肝は、jQuery.grep()メソッドを使って、配列内の指定条件を満たす要素を抽出(フィルタリング)する処理でしたね。
フィルタリング処理は関数化し、フィルタリング後関数createHTML()を呼び出しています。HTMLコードの生成、要素の配置・表示を関数化したことによって、本ページを表示する際も、「Load More」ボタンクリック時も、カテゴリー変更時も同じ関数を使ってギャラリーを生成、表示できます。なお、関数createHTML()に引数を追加して、カテゴリー変更時はMasonry()メソッドで要素の再配置を行っています。
今回説明を省略した部分については第1弾の投稿と第2弾の投稿をご参照ください。
これでギャラリーページへのトライを終了します。初めてImagesLoaded、Masonryという2つのJavaScriptライブラリを使用しましたが、便利なライブラリが用意されているものですね。もっと色々調べてみる必要がありそうです。
最後までお読みいただきありがとうございました。