折りたたみが最も簡単 details と summary をjQueryに置き換えるメモ

Page 3: 親にクラスを追加し、隣を開閉させる

最初にシンプルな形を作りました。投稿内で気軽に使います。
1 ターゲットをクリック → ターゲットに .open を追加し、隣を開閉

ターゲット
開閉する部分

次に、三角クリックの形を作りました。テンプレート内で使うことが多そうです。
2 三角印をクリック → 三角印に .open を追加し、隣の隣を開閉

三角印ターゲット
無視(自由に)
開閉する部分

ここでは次のような形を作ります。
3 ターゲットをクリック → ターゲットの上の階層に .open を追加し、隣を開閉

親階層 open を付与する部分

クリックターゲット
開閉する部分

なんですかこのややこしいのは。何に使うのかちょっと後で説明します。スクリプトはこうです。

jQuery('section li.menu-item-has-children > a').each(function(){
  jQuery(this).on('click',function(){
   jQuery(this).parent().toggleClass('open');
   jQuery(this).next().slideToggle(300);
   return false;
  });
});

今までは、ターゲットにクラス名を付けてきました。js-summary だとか js-click です。これにより、ブログ内に簡単なHTMLタグを追加することで本文の中で折り畳みが利用できました。

三つめは目的が異なります。自由にクラス名を付けません。クラス名というか指定する要素は決まっております。それは section li.menu-item-has-children > a です。これ何かというと widget 内に配置したナビゲーションメニューに限定しています。

WordPress の widget 「ナビゲーション」の階層を開閉させる

ウィジェットの「ナビゲーション」ありますね。別途作成しておいたメニューを表示させるやつです。メニューに階層がある場合に、折り畳みで表示します。

この仕組みは、特にスマホ用ハンバーガーメニューを作ってて必要を感じました。ウィジェットのナビゲーションに特化した機能として追加します。

スマホの widget area 内メインナビゲーション

この話を書き始めると他の余計な話も混ざってくるので、先にその余計な部分のあらすじを書いてすっ飛ばしますと、それはこうです。

あらすじ

  1. 昨今、サイドバーが流行りません。スマホのせいです。WordPressでも公式テーマがペラい一枚ものデザインに変わってきています。
  2. サイドバーがあったのはtwentyseventeenまでで、このテンプレートは今でも有用です。
  3. サイドバーがあるテンプレートでは、スマホ表示でサイドバーが下に落ちてカッコ悪い上に使いにくいです。
  4. そこでハンバーガーメニューによるレスポンシブなウインドウ表示がしたくなります。
  5. 最初は専用のメニューを作ったりしていました。 しかしそもそもサイドバーが全部がスライドメニュー内に収まれば万事解決じゃないのかと気づきます。
  6. サイドバー(#secondary)全部を横からせり出すウインドウに収めようと奮闘、成功させます。
  7. スマホ用に、サイドバーの一番上にグローバルナビゲーション(メインメニュー)のwidgetを置きます。
  8. しかし、メニューに階層表示があると階層がだらだらと表示されてしまいます。 ここで「階層メニューを開閉させたい」となります。

そういうわけで、メニューのwidget、階層構造がそのまま全部出力された形ですが、これを折り畳みたい。という話です。

detailsとsummaryでは上手く行きません。slideToggleのスクリプトならいけそうです。

「何に対して命令するのか」のその「何」部分(要素のセレクタです)を自分で書けるからです。すでに仕上がっているリストを折りたたみに対応させることが出来そうに思えます。

Widgetのナビゲーション階層を折り畳み

widget のナビゲーション

widgetから「ナビゲーション」を選んでメニューを選択しますとメニューがサイドバーに現れます。

Widgetにメニュー

普通に置くとこのようになります。

メニューに階層がある場合、すべて展開しています。 この展開されたサブメニュー部分を折りたたみたいわけです。

class を調査

表示されたメニューには内容に応じたclassが付くので、それを折り畳みスクリプトに利用できるかもしれません。まず調べます。

この部分をブラウザのインスペクタで調べると、メニュー名に応じたclassが付与されているのが判ります。また、中身のリストにも細かくclassが付与されていることが判ります。それが判ればこっちのものです。

この場合、.menu-home-container、その中の ul#menu-home というものがありました。これが目的のやつでしょうか。ちょっと違います。それにここにはメニューの名前が含まれていますから汎用で使えませんね。もうちょっと探ります。

何かをクリックしたら何かが開く。これを作りたいわけで、ではそのクリックする何かとは何か。

それは階層を持つ要素です。何かが開くの「何か」は何でしょう。階層を持つ要素が内包する要素です。調べましょう。調べました。

階層がある部分、それは .menu-item-has-children でした。確かに子を持つアイテムです。判りやすいclass名を付けてくださってありがとうございます。

子を持つメニューアイテム li.menu-item-has-children をクリックしたら何かが開きます。「隣接する要素」ではないですね。それは「内包する要素」です。ありました。「ul.sub-menu」がそれです。

何かをクリックしたら何かが開く。 li.menu-item-has-children をクリックしたら内包する sub-menuが開くのです。… と、思ったこともありました。そうではありません。クリックするターゲットの答えは li.menu-item-has-children > a これです。a です。li ではありません。

そうすると、展開すべき sub-menu は、ターゲットが内包するのではなく、まさしくにあるとわかります。

li.menu-item-has-children > a をクリックしたら隣の sub-menu が開閉する
こうです。いいですね。

仕上げは「open」の付与です。open を付与する相手はターゲットである a ではなく li.menu-item-has-children にしました。a を内包する親、上の階層ですね。親を指定できるのか。できました。

jQuery では隣を指定するのは .next() です。隣の隣は .next().next() と続けて書けばOKです。親は .parent() です。

jQuery(this).parent().toggleClass('open');

ターゲットである a を内包する li.menu-item-has-children にクラスを付与できました。 css では .menu-item-has-children.open > a:before というように結局 a に before を付けるのですが、指定方法がよりスマートかなと。わからんけど。

以前、この投稿のこの部分は、ターゲットを li.menu-item-has-children として、開閉する sub-menu を指定する際に「 > 」を追加して指定していましたが、いろいろ試している中で今の方法に落ち着きました。

最後に、この仕組みの動作を widget 内に限定しないといけないので、li.menu-item-has-children をさらに限定するために頭に「section 」を付けて ‘section li.menu-item-has-children > a‘ をターゲットとしました。

CSSでは open 用の三角印をこのように指定しております。

.menu-item-has-children .sub-menu {
	display: none;
}

.menu-item-has-children > a:before {
	content: "► " ;
	display: inline;
	width: 1.1em;
	display: inline-block;
}

.menu-item-has-children.open > a:before {
	content: "▼ " ;
	display: inline;
	width: 1.1em;
	display: inline-block;
}

ul

 li

ターゲット li a
開閉する部分 ul.sum-menu

 

ということで widget のナビゲーションに特化した折り畳みが完成しました。

 

本日この投稿では、次の三つのスクリプトを用意しました。使う場所や目的がそれぞれ少し異なります。

//折りたたみ js-summary + div
// js-summary と隣接するboxを開閉する
jQuery('.js-summary').each(function(){
	jQuery(this).on('click',function(){
		jQuery(this).toggleClass('open');
		jQuery(this).next().slideToggle(300);
		return false;
	});
});

// 折りたたみ js-click + div + div
// js-click の隣の隣のboxを開閉する
jQuery('.js-click').each(function(){
	jQuery(this).on('click',function(){
		jQuery(this).toggleClass('open');
		jQuery(this).next().next().slideToggle(300);
		return false;
	});
});

// 折りたたみ menu-item-has-children の階層
// section 以下 の li.menu-item-has-children > a を対象とし、親(li)にopen、隣(sub-menu)を開閉する
jQuery('section li.menu-item-has-children > a').each(function(){
    jQuery(this).on('click',function(){
       jQuery(this).parent().toggleClass('open');
        jQuery(this).next().slideToggle(300);
        return false;
    });
});

これを js 拡張子で保存して、例えば footer.php の </body>手前で読み込みます。

“折りたたみが最も簡単 details と summary をjQueryに置き換えるメモ” への2件の返信

  1. 詳しいご説明ありがとうございます。初歩的な質問で申し訳ないのですが、ハッシュタグで別ページから飛んできた際、js-containerを開いて表示したいのですがどうすれば良いでしょうか?

  2. ここでは、jQueryでターゲットに class を付けたり外したりしているだけで、挙動はCSSが受け持っています。投稿の中でも書いているとおり、汎用的な動きに関して触れていません。

    条件に応じて初動を変化させようとすれば、デフォルトで “open” classを付けるつけないを、細かく制御してやる必要があります。
    どこかから飛んでくるのなら、飛ばす段階で「リンク先のターゲットのclassを制御する」ということを行わなければなりません。

    そのやり方は・・・難易度高いですね。私には到底答えられません。cookieを使って条件分岐させるのが良いかもしれないですね・・どうでしょう。

    リンク先の特定要素にアプローチする方法を検索しまくって、是非よいやり方を見つけてください。見つけたら教えてください!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください