【jQuery】WordPressのカテゴリーをプラグインなしでアコーディオン化

カテゴリーが長くなったときのため、クリックで開閉できるアコーディオンをつくっておくことにしました。

WordPressを運用している当ブログは、できるかぎりプラグインは使用せず、自力でできることは全部自力でやる、という方針で運営しているのですが、今回新たな問題が発生しました。

記事をこまかく分類したカテゴリーが増えてきて、表示する場所をとるようになってきてしまったのです。

これを解決させる方法はあれしかありません。そう、「jQuery」というものを使ってカテゴリーをクリックまたはマウスオーバーで開閉型(折りたたみ)にすることができる、「アコーディオンメニュー」を実装すればいいのです。

しかし、そうはいったものの、この「jQuery」への理解がとぼしい私にとっては、この作業は難解をきわめるもので、そもそも、これはそう簡単に実装できるようなシロモノでもなかったようにも思えたのですが、悪戦苦闘のすえ、これもなんとかクリアすることに成功。

今回はプラグインなしでカテゴリーをアコーディオン化した方法を、今後実装する「予定」の私自身のためにも、ここに残しておきたいと思います。

カテゴリーは長いとユーザビリティの低下にも

ブログを長く運営していると、それにともなって記事も増えてくるため、とくに扱うジャンルが増えやすい雑記ブログなどでは、記事を分類するために作成した「カテゴリー」がどんどん増えていき、サイドバーにながながと表示されてしまう、といったようなことも起きてしまうと思います。

しかしこれが少々やっかいで、PCではそこまで問題はないものの、スマホの場合はこれによってスクロールするのが大変になってくることもあり、場合によっては、ユーザビリティを損なう原因となってしまうこともあるわけです。

そのため、WordPressでは、カテゴリーはあらかじめドロップダウン(すべてが1か所のボックスにおさめられ、そこから選ぶことができるプルダウン形式)にすることもできるようになっているのですが、どうも私はこれがあまり好きではなく、実際にやるとなったらあれしかないと考えていました。

「jQuery」というものを使った、アコーディオン(メニュー)です。

アコーディオンのメリット

このアコーディオン(メニュー)というものは、カテゴリーの場合でいうと、初期状態では親カテゴリーのみを表示させ、親カテゴリーをクリックまたはマウスオーバーしたときにかぎり、それに内包された子カテゴリーが表示される、ということができるもの。

ふだんは多く(長く)なってしまうこともある子カテゴリーは表示させないことによって、カテゴリーのスペースを省くことができるのです。

ドロップダウンでは、スクロールしなければ目に入らないようなものでも、アコーディオンではぱっと見ですべての親カテゴリーが目に入るので、直感的にカテゴリーにはなにがあるのかがわかりやすいというわけですね。

そして、それを実現させるのが、今回の作業の肝である「jQuery」。

この「jQuery」とは、サイトを動かすプログラムのようなものである「JavaScript」のコードを簡単にしたもので、画像を動かしたり、文章を出したり隠したりと、ものを動かすときに活用されていることが多いような気がします。

ちなみに、この「jQuery」ですが、これをWordPressで使う場合は、「footer.php」の下のほうにコードを書いておけば使える、程度の知識しか私にはありませんでした。

はっきりいって、なにもわかっていないにひとしいレベルです。そんな私に、今回の作業ができるのかというのは、きわめてあやしいものがあったといえるでしょう。

しかし、気になったのであれば突っ込んでいかなければ気がすまない私は、じつはまだ実装する予定ではなかったにもかかわらず、先のことを考えていたら気になってきてしまったので、今回も突っ込んでいくことにしたのです。

jQueryでカテゴリーをアコーディオン化する

カテゴリーのスペースを省略する前に、この前置きを短縮したほうがいいような気もしますが、それはさておき、今回の作業で目指すのは以下の3点です。

アコーディオンメニューの完成予定図

  1. クリックでカテゴリーを開閉させる
  2. カテゴリーの文字列への動作でイベントを発火させ、ボタンなどは設置しない
  3. 子カテゴリーがないものに関しては、親カテゴリーをクリックすればそのままリンク先にとぶようにする

ちなみに、カテゴリーの左側にあるのが親カテゴリーで、右側に少しずれているのが子カテゴリーです。

クリックで開閉させる

アコーディオンの開閉方法は、クリックとマウスオーバー(マウスカーソルを起点の上に置く)の2種類になるものと思われますが、これはクリックで開閉させるものの実装を目指します。

その理由については後述もします。

ボタンは設置しない

「イベントの発火」というのは、開閉(というイベント)を開始することをいうようで、その起点となるのはあくまでカテゴリーの文字列とし、開閉を示す「+や-」といったボタンは設置しないことにします。

これに関しては、当ブログでは、すでにアーカイブ(月別の投稿数表示)がそのようになっているというのもありますが、私はシンプルなものがいいからです。

親カテゴリーのみ(子カテゴリーなし)はそのまま

アコーディオンでは、親要素へのアクションで子要素が開きますが、親カテゴリーに子カテゴリーがない場合(親のみの場合)は、それをクリックすればそのままリンク先にとぶようにしておきます。

「子のあるなし」によって動作を変えるというのは、できるのかどうかはかなりあやしいものがありました。が、なにもわからないような状態では、そもそも考えていてもしかたがないというものでしょう。

とにかく、ただ、やるだけ。いよいよ作業開始です。

基本形でマウスオーバーでの折りたたみに成功

<script type="text/javascript">
(function($) {
$(function(){
$(".cat-item .children").hide();
$(".cat-item").hover(function() {
$(this).children("ul").slideToggle(400);
});
});
})(jQuery);
</script>

「jQuery」でのアコーディオンの基本形はこのような感じになっているようで、先になにがどうなっているのかを(まちがっている部分もあるかもしれませんが、私がなんとなく理解している範囲で)簡単に解説しておきます。

まず、WordPressに実装する場合は、「<script type=”text/javascript”> (function($) { …… })(jQuery); </script>」で囲む必要があり、これがないと動きません。コードを書く場所は、読み込み速度などの関係で、「footer.php」の</body>前に書くのがいいそうです。

続いて書かれている内容ですが、4行目の「$(“.〇〇 .△△”).hide();」の〇〇部分には親カテゴリーの、△△部分には子カテゴリーのクラス名を入れ、「.hide」とすることで子カテゴリー(アコーディオンの中身)がかくれます。反対に「.show」にすると、表示される状態がデフォルトになると。

5行目の「.hover」は、開閉イベントを発火(作動)させるアクションで、「.hover」はマウスオーバー、「.click」がクリックです。

6行目の「this」は、イベントが発生した要素を指しているようで、「.slideToggke」という動作は、アコーディオンの中身がスライドしておりてくる「slideDown」と、中身がスライドして戻っていく「slideUp」をかねたもの。かっこ内の数字を変えたりすることで、開閉速度を変更することができます。

だいたいこんなところでしょうか。ちなみに、上記のもので、ためしに実装してみることにした、マウスオーバーでのアコーディオンはできました。

開閉アニメーションが止まらないへの対策

<script type="text/javascript">
(function($) {
$(function(){
$(".cat-item .children").hide();
$(".cat-item").hover(function() {
$(this).children("ul").stop().slideToggle(400);
});
});
})(jQuery);
</script>

先ほどのコードではマウスオーバーでのアコーディオンに成功しましたが、マウスカーソルの置き場所によっては、カテゴリーが開いたり閉じたりといった波打つような動作が延々と繰り返され、見ているだけで腹が立ってくる現象が起こりました。

これへの対策は、6行目に「.stop()」を加えることで改善しましたが、アコーディオンをマウスオーバーにすることの最大の問題点は、スマホで見たときにあります。

スマホは基本動作がタップなので(マウスオーバーはないので)、なんと、これを実装したカテゴリーをスマホでタップすると、一瞬だけカテゴリーが開いて、そのまま遷移先の親カテゴリーのページにとぶということが起きてしまうのです。

ゆえに、やはりマウスオーバーは却下。私はクリック型の実装にいどみます。

クリックでリンク先にとぶ遷移を阻止する(aタグの無効化)

先ほどのコードの「.hover」を、ただ「.click」に変えればいけるのではないか。そう私は思ったのですが、アコーディオンメニューの実装は、そんなに甘いものではありませんでした。

マウスオーバーではカーソルを乗せるだけでアコーディオンが作動するのに対し、クリックはクリックまたはタップしなければ作動しないので、起点となるカテゴリーの文字列に「aタグ」が含まれ、それがリンクになっている場合は、クリックするとリンク先にとんでしまうのです。

あたりまえといえばあたりまえのことでしょう。とはいえ、この「遷移」をなんとかしなければアコーディオンは動作しないので、以下のようにして、親カテゴリーの「aタグ」を無効化することにしました。

<script type="text/javascript">
(function($) {
$(function(){
$(".cat-item .children").hide();
$(".cat-item").click(function(e) {
e.preventDefault(); //aタグのリンク無効化
$(this).children("ul").slideToggle(400);
});
});
})(jQuery);
</script>

「.click」のあとを「function(e) e.preventDefault();」とし、「preventDefault」というデフォルトのイベントがキャンセルされるものを使用することで、「aタグ」を含んだリンクをクリックしたあとの「ページ遷移」という動作は、すべて無効化されるわけです。

しかし、これではすべてのリンクが無効化されてしまい、子カテゴリーをクリックしてもなにも起こらなくなってしまったため、これもだめ。

加えて、なんとなく形だけはできたものの、子カテゴリーをクリックしてもスライドのイベントが発生するというのも気になりました。

親要素のみ(子要素なし)を判別して開閉+通常リンク

<script type="text/javascript">
(function($) {
$(function(){
$(".cat-item .children").hide();
$(".cat-item").click(function() {
$(this).children("ul").slideToggle(400);
});
});

$(".cat-item .children") // 子カテゴリの ul
.closest("li") // 最も近い親要素を探す(この場合 .cat-item の中で ul を持つ li)
.children("a") // 子カテゴリを持つ親カテゴリの a要素(リンク無効にする要素)
.on("click", function(e){
e.preventDefault();
});
})(jQuery);
</script>

幾度にもわたる失敗と検証を繰り返し、ようやくたどりついたのがこれ。

こちらのQ&Aサイトにあった、同じことで困っている方に向けた回答から、コードの後半部分に特定の条件を付与できることが判明。

後半部分の内容をそのまま使わせてもらうことで、親カテゴリーをクリックしても遷移せず、子カテゴリーがあるものは親カテゴリーをクリックでアコーディオンが開き、子カテゴリーがないものはそのままリンク先に遷移、ということができました。

しかし、これでカテゴリーのアコーディオン化は完了したかと思われましたが、どうしても1つだけ気になることが。

アコーディオンを開いて子要素をクリック(して遷移)すると、ロード(読み込み)がおこなわれているあいだに、アコーディオンが閉じてしまうのです!

リロードまたは遷移後の開閉状態を保持(cookieを使用)

当ブログでいえば、月別のアーカイブをクリックしてもらえればわかりやすいと思うのですが、アコーディオンが開いたあとに子要素をクリックすると、アコーディオンの動きは停止したままページの遷移がおこなわれます。

一方、先ほどのコードでは、子要素をクリックすると同時にアコーディオンが閉じるようになってしまい、それがスタイリッシュといえばそうかもしれませんが、ロード中に動いているというのがどうも違和感があり、私はそれが気になってしかたがありませんでした。

まる1日におよぶ作業で体力はとうに限界状態。しかし、もう少しでいけるような気がしていたため、「cookie」を利用すればリロードや遷移後の開閉状態を保持できるという情報から、これを試してみることに。

こちらのサイトを参考に、以下を「header.php」内の</head>よりも前に記述し、

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script>

以下のコードを同じく「footer.php」内に記述。

<script type="text/javascript">
(function($){
$(function(){
if($.cookie(".cat-item")){
$(".cat-item .children").hide();
}
$(".cat-item").click(function() {
if($.cookie(".cat-item")){
$(".cat-item .children").slideDown();
$.removeCookie(".cat-item" , { path: "/" });
}else{
$(".cat-item .children").slideUp();
$.cookie(".cat-item" , "open" , { expires: 1, path: "/" });
}
});
});(以下略)

これでいけるかと思われました。

ところが、たしかにこれで、リロードやページ遷移後の開閉状態は保持することができましたが、かんじんの子要素をクリックした直後に起こるアコーディオンが閉まるのをふせぐことはできず、スライドダウンとアップで分ければいけるのかとも思ったものの、それもちがうということが判明。

クリックすると全部開くという問題もありましたが(これは「open」が原因だったのかもしれません)、それはもはやいいとして、私の目指すところは、不可能なのではないかと思われました。

クリックイベントのバブリング(伝播)を阻止して完成形へ

なかなかエグいぞ「jQuery」。なにごとも、いけると思ったらいけていなかったというのが、精神的にはいちばんこたえるものです。

しかしながら、あとほんの少し、あとちょっとだけ前に進むことができれば完成しそうな気がしていた私は、どうして子カテゴリーをクリックするとアコーディオンが閉まるのかを考えていたところ、これは子カテゴリーが親カテゴリーに内包された要素であり、子要素をクリックしたというアクションが親要素にまで伝わっていることが原因であると判明。

なお、この「子要素をクリックすると親要素にまでイベントが伝播(でんぱ)する」ことをバブリングというそうで、これへの対策には以下の2つがあるようです。

  • stopPropagation(親要素への伝播をキャンセル)
  • return false(子要素も親要素もキャンセル)

これらは作業の途中で見かけていて、後半部分で試してみてもうまくいかなかったので、使い道はないと思っていたのですが、思えば、3番目のコードのように、前半部分で使う方法はまだやっていなかったなと、私は前半部分に「stopPropagation」を組み込んでみることに。

<script type="text/javascript">
(function($) {
$(function(){
$(".cat-item .children").hide();
$(".cat-item").click(function(e) {
$(this).children("ul").slideToggle(400);
e.stopPropagation();
});
});

$(".cat-item .children") // 子カテゴリの ul
.closest("li") // 最も近い親要素を探す
.children("a") // a要素(親)のリンクを無効にする
.on("click", function(e){
e.preventDefault();
});
})(jQuery);
</script>

すると、このコードで、子要素をクリックしたときのイベントの伝播をふせぐことができ、アコーディオンで開いた子カテゴリーをクリックしても、アコーディオンが閉じないようにすることに成功!

親要素のリンク無効化、子要素がないものはそのまま、ページ遷移中にはアコーディオンを動かさない(閉じないようにする)という、すべての目標をクリアすることができたのです。

まる1日を費やし、その翌日にまでおよんだ作業は、これで完了となりました。

今回のまとめ

・マウスオーバーはモバイルユーザビリティに問題がある
・クリック開閉型は難易度が高い
・アイコンはあったほうがいいような気もするけれど今回はここまで

このカテゴリーのアコーディオン化というのは、もう少しカテゴリーが細分化してきたあとでおこなう予定の作業であり、いそいで解決しなければならない問題ではなかったのですが、目標とする形にまではいちおうたどりつくことができたので、これはこれでよしとしましょう。

また、完成までの途中経過こそアコーディオン化してくれ、とも思われる方もいらっしゃるかもしれません。しかし私は、「失敗は成功のもと」というように、途中のうまくいかない部分があったからこそ、最終的な形にまでたどりつくことができたと思っています。

頭がおかしくなりそうな失敗をかさね、完成したときには、私はかるく叫びました。ようするに、こういったものは、すべてが最終的にはつながるので、むしろ途中経過のほうが大事だったりもするわけです。

親要素の「aタグ」を無効化したことでクローラーのエラーが出ないか、子要素のあるなしの見分けがつくように、アイコンはやっぱりあったほうがいいのではないか、ということなどは気になるところですが、ひとまずはアコーディオン化できる方法ということで、どなたかのご参考になればさいわいです。

関連記事

開閉ボタンを作成して実装することにしました
【WordPress】アコーディオン・カテゴリーに開閉ボタンを設置して実装

スポンサーリンク
この記事をシェアする

コメントを残す

日本語が含まれない投稿は反映されませんのでご注意ください。なお、コメントは承認制となっているため、反映までにお時間をいただく場合などがございます。