WordPress クリックでカテゴリーをソートする(本サイトの設計図)
今回はjavascriptを使用した、
カテゴリーソート機能をwordpressに実装したいと思います
概要としては、
- ページ上部にカテゴリーの一覧がある
- その下には記事(コンテンツ)が一つづつ並んでいる
- カテゴリーをクリックすると下部の記事がソートされる
- 別のカテゴリーをクリックするとそのカテゴリーは消えて対象のカテゴリーがソートされる
こんな感じですね。
いわゆる非同期処理によってデータベースからアクセスしてコンテンツを入れ替えるやり方ではなく、すでに読み込んであるDOMから対象を選択、分岐して行う方法を取りますのでご了承下さい。
学習が進み、必ず非同期処理を覚えるタイミングが来るため、そのときには非同期処理で実装し直しリファクタリングしたいと思います
さてまずはカテゴリー一覧となるHTMLから見て行こうと言いたいのですが本サイトはwordpressで実装されているためカテゴリーの出力はPHPで行います
カテゴリーとなる要素の作成
functions.php
<?php $productions = get_terms('production',array( 'hide_empty' => false, 'orderby' => 'slug' )); echo '<ul class="production_kind">'; echo '<li><span>すべて</span></li>'; foreach ($productions as $production) { echo '<li><span>'.$production->name.'</span></li>'; } echo '</ul>'; ?>
はいget_terms(スラッグ, パラメータ)
ですね、
もう記事の中では何回目かの登場です 現在の投稿などに関係なく取得してくる場合に使用することが多いですね。
今回は'hide_empty' => false,
にしているので記事が一つもない場合でも取得しています
次に検索対象となる記事につけられたカテゴリーの出力法です
次に検索対象となる記事につけられたカテゴリーの出力法です
functions.php
if ($terms = get_the_terms($post->ID,'production')) : echo ('<h2>'); echo esc_html($terms[0]->name); echo ('</h2>'); endif;
こちらの記事はループ中なのでget_the_terms(ID,スラッグ)
で取得してきました
ループするまでもなく取得した配列の0番目の名前を取り出すだけでOKでした。
さあここで出力した2つの要素を紐付けて、
JavaScriptでソートできるように
それぞれのクラスにCSSで仕掛けをしていきます
記事の出現スタイルを整える
style.scss
.item_detail.check { opacity: 1; visibility: visible; height: auto; margin-bottom: 250px; } .item_detail { border: 1px solid #ccc; background: #fff; overflow: hidden; position: relative; box-shadow: 0px 3px 4px #ccc, 0px 0px 1px 1px #fff inset; opacity: 0; visibility: hidden; height: 0; transition: $anime; }
.item_detailはそれぞれの投稿コンテンツを覆うタグです。
ポイントとなるのは
opacity: 0;
visibility: hidden;
height: 0;
の上記三項に対して存在していないかの装飾指定をしています
そのタグに対して.item_detail.checkと特定のクラスを持った際に出現する様にCSSを設定しました
もう一つliタグにも指定しておきましょう
style.scss
li { font-size: 1rem; font-weight: 500; list-style: none; padding: 5px 3px ; margin-left: 13%; cursor: pointer; &:hover span, &.check span { background-color: $main_color; color: $sub_color; }
それではJavaScriptで要素を照らし合わせてクラスを操作しましょう
JSで表示を制御する
まずはJSで要素を取得するところからです
index.js
const productions = document.querySelectorAll(".production_kind li"); const proTerms = document.querySelectorAll(".main_item_text h2"); const parentPost = (element) => { return element.parentNode.parentNode.parentNode.parentNode; }
今回対象の要素はそれぞれ複数あるためquerySelectorAllを使用し配列形式で取得しました
ここで一つ自作関数を定義しておきます
やっていることはシンプルでparentNodeは対象の親要素を返すメソッドです
つまり受け取った要素の、
親要素の、親要素の、親要素の、親要素を返してくれる関数となります
まあこれは、今回の対象のh2が複雑なレイアウトのためここまで複雑になっていますが親要素を参照したいときはparentNodeを使用すると覚えておきましょう
取得した要素をループで回します
index.js
if (productions) { productions.forEach((production,p) => { production.addEventListener("click",() => { proTerms.forEach((term,t) => { parentPost(term).classList.remove("check"); productions[t].classList.remove("check"); if (production.textContent === term.textContent) { parentPost(term).classList.add("check"); } else if (p === 0) { parentPost(term).classList.add("check"); } }); production.classList.add("check"); }); }) }
JSは一気に書いてしまいましたが、ハマった点も含めてしっかり順番に解説します
自分が感じたポイントなど
最上部は取得した変数をループで回しています、どちらのforeachも第二引数を指定してキーとなる順番も取得しておきます
addEventListenerでクリックされたら探し出す要素に対してもループを回します
制作した順番で解説します
まずは検索する要素を条件分岐しました。
production.textContent === term.textContentの記述でカテゴリーをを検索出来ました
textContentは要素のタグのテキストを参照するメソッドであるため、同じ様にPHPで出力した名前を参照することで分岐出来ます
内容としては、
parentPost(term).classList.add(“check”);の記述でクラスを付与することが出来ました。
else if (p === 0)の部分の解説です
「p」はクリックした際の要素のキーが入っているためこれが「0」ということは、
‘<li><span>すべて</span></li>’の部分を指します。
その際はすべての要素にクラスを付与できるようにしています
そして条件分岐に入る前に最もハマってしまったポイントが、
parentPost(term).classList.remove(“check”);
productions[t].classList.remove(“check”);
ここの部分ですね
条件分岐に入る前にすべての要素のクラスを削除し分岐に入ることでクラスの付替えができるという仕掛けですが、
ここのループ中のtermはいいのですが、
productionは親のループに当たるためそのままforeach中の値を入れてしまうと二重ループになってしまいます。
そこで現ループ中のキーである「t」でループ前のproductions[]でアクセスすることで問題なくクラスの付替えが実現しました。
ちなみに今回はforeach文で記述しましたが
本当はfor文で書いたほうがわかりやすくなったかもしれません。
今回ハマった際にfor分にして自分も理解できたので、
こちらも書き記しておきます
for文で多重にループを回して出力する
sample.js ( for )
for (let i = 0; i < productions.length; i++) { productions[i].addEventListener("click", function() { for (let t = 0; t < proTerms.length; t++) { parentPost(proTerms[t]).classList.remove("check"); productions[t].classList.remove("check"); if(productions[i].textContent === proTerms[t].textContent){ parentPost(proTerms[t]).classList.add("check"); } else if(i === 0 ){ parentPost(proTerms[t]).classList.add("check"); } } productions[i].classList.add("check"); }) }
今回はここまでにします
お疲れ様でした
コメントをお待ちしております