WordPress のカテゴリー選択。子カテゴリーを開閉式にしてすっきりさせる

やりたいこと

WordPress のカテゴリーが増えてくると、ずらずらっとカテゴリーが並び、カテゴリーを選ぶのもちょっと億劫になることがある。カテゴリー階層が第1階層の場合はそれほどでも無いけど、子カテゴリーが増えた場合

  • 親カテゴリー1
    • 子カテゴリー1
    • 子カテゴリー2
    • 子カテゴリー3
    • 子カテゴリー4
    • ………
    • ………
  • 親カテゴリー2
    • 子カテゴリー1
    • 子カテゴリー2
    • 子カテゴリー3
    • 子カテゴリー4
    • ………
    • ………
  • 親カテゴリー3

てな具合になると、親カテゴリーがどんなのがあったのかもずっと下にスクロールしないといけなくなり、ちょっと不便。

そもそもそんなに子カテゴリー増やすなよ、というのあると思うけど、例えば「音楽」というカスタムタクソノミーを作って、第1階層を「ジャズ」とかの音楽ジャンルを設定して、その子タームとしてアーティストを設定していく場合はどんどんと子タームが増えてくことになる。

ということで、親カテゴリーのみをとりあえず表示して、どんな子カテゴリーがあるかは開閉式にすることにしたい。以下のような感じ。

カテゴリー選択 子カテゴリー開閉式

以前に Classic Editor でやってみたことあるんだけど、Gutenberg エディタでもうまく行くか試してみたので、以下まとめてみました。

実装方法(Gutenberg エディタ編)

まず、エディタが Gutenberg の場合、スクリプトファイルや css ファイルを適用するには enqueue_block_editor_assets というアクションフックを使用するのがいいようだ。

ということで、以下のコードを functions.php に追加して、投稿画面に対してスクリプトファイルと css ファイルを読み込む。もちろんファイル名などは任意でOK

// functions.php に追加

function add_editor_css_js( ) {
    wp_enqueue_style( 'admin_cat_toggler_style', get_template_directory_uri() . '/css/cat-toggler.css' );
    wp_enqueue_script( 'admin_cat_toggler_script', get_template_directory_uri() . '/js/cat-toggler.js', array('jquery'), '1.0.0', true );
}
add_action( 'enqueue_block_editor_assets', 'add_editor_css_js' );

それで読み込んだ /js/cat-toggler.js にまず

(function($) {
    $(function() {
        setTimeout(function() {
            $('.editor-post-taxonomies__hierarchical-terms-list > .editor-post-taxonomies__hierarchical-terms-choice').each(function(i,el) {
                if ($(this).find('.editor-post-taxonomies__hierarchical-terms-subchoices').length > 0)
                    $(this).prepend('<span class="term-toggler with-child">+</span>');
                else
                    $(this).prepend('<span class="term-toggler no-child">+</span>');
            });
        },1000);
    });
})(jQuery);

と記載する。こうしてやると、第1階層のカテゴリーのチェックボックスの前に「+」が付く( あと、子カテゴリーが無いものにも一旦「+」を付けて、子カテゴリーの有無はクラス名で区別)。

ここで今回ちょっと分からなかったのが、普通に .each を使用してもうまく行かなかったこと。色々調べたら、 Gutenberg ではブロックなどが読み込まれた後に適用してやる必要があるよう。

参考: https://github.com/WordPress/gutenberg/issues/12603#issuecomment-457749298

ってことで、ここでは SetTimeout で対処。

あとは「+」が付いたのに合わせて /css/cat-toggler.css でレイアウトを調整し、子カテゴリーが無いものを非表示にしてやる。

.term-toggler {
    margin-right: 0.2em;
    cursor: pointer;
}

.term-toggler.no-child {
    visibility: hidden;
}

.editor-post-taxonomies__hierarchical-terms-list > .editor-post-taxonomies__hierarchical-terms-choice > .editor-post-taxonomies__hierarchical-terms-subchoices {
    margin-left: 2.5em;
}

すると、以下のようになる。

カテゴリー選択 カスタマイズ1

それで次に「+」をクリックすると、子カテゴリーのリストを開閉できるようにしてやる。上記の /js/cat-toggler.js を以下のように追加修正してやる。

(function($) {
    $(function() {
        setTimeout(function() {
            $('.editor-post-taxonomies__hierarchical-terms-list > .editor-post-taxonomies__hierarchical-terms-choice').each(function(i,el) {
                if ($(this).find('.editor-post-taxonomies__hierarchical-terms-subchoices').length > 0)
                    $(this).prepend('<span class="term-toggler with-child">+</span>');
                else
                    $(this).prepend('<span class="term-toggler no-child">+</span>');
            });

           //  「+」をクリックすると開閉するように
            $('.term-toggler.with-child').each(function(i, el) {
                $(this).on('click', function(e) {
                    $(this).parent().children('.editor-post-taxonomies__hierarchical-terms-subchoices').toggle(); // 孫カテゴリー、孫孫カテゴリーがあることも考え .children で子カテゴリーだけを取得
                });
            });

        },1000);
    });
})(jQuery);

それで初期状態は子カテゴリーを隠して親カテゴリーのみを表示ということで /css/cat-toggler.css の ~subchoices のところに display: none; と追加

.editor-post-taxonomies__hierarchical-terms-list > .editor-post-taxonomies__hierarchical-terms-choice > .editor-post-taxonomies__hierarchical-terms-subchoices {
    margin-left: 2.5em;
    display: none;
}

すると、まずは以下のように親カテゴリーのみが表示され、

カテゴリー選択 カスタマイズ1 閉じた状態

それでチェックボックスの前のある「+」をクリックしてやると、それぞれの子カテゴリーが表示される。

カテゴリー選択 カスタマイズ1 カテゴリーを一つ開いた状態

これで目的は達成できたけど、初期状態で子カテゴリーが閉じた状態なので、どの子カテゴリーが選択されているか、いちいち親カテゴリーを開いていかないといけない。そこで、すべてのカテゴリーを閉じたり、開いたりできるようにしてやる。

ってことで /js/cat-toggler.js を以下のように修正

(function($) {
    $(function() {
        setTimeout(function() {
            $('.editor-post-taxonomies__hierarchical-terms-list > .editor-post-taxonomies__hierarchical-terms-choice').each(function(i,el) {
                if ($(this).find('.editor-post-taxonomies__hierarchical-terms-subchoices').length > 0)
                    $(this).prepend('<span class="term-toggler with-child">+</span>');
                else
                    $(this).prepend('<span class="term-toggler no-child">+</span>');
            });

            $('.term-toggler.with-child').each(function(i, el) {
                $(this).on('click', function(e) {
                    $(this).parent().children('.editor-post-taxonomies__hierarchical-terms-subchoices').toggle();
                });
            });

            // カテゴリーリスト前に「すべて開く」を追加
            $('.editor-post-taxonomies__hierarchical-terms-list').prepend('<div class="all-term-toggler all-closed">すべて開く</div>');

            // 「すべて開く」をクリックしたら全カテゴリーを表示して、「すべて閉じる」に。「すべて閉じる」をクリックしたら全カテゴリーを非表示
            $('.all-term-toggler').each(function(i, el) {
                $(this).on('click', function() {
                    if ($(this).hasClass('all-closed')) {
                        $(this).parent().find('.editor-post-taxonomies__hierarchical-terms-subchoices').show();
                        $(this).removeClass('all-closed').text('すべて閉じる');
                    } else {
                        $(this).parent().find('.editor-post-taxonomies__hierarchical-terms-subchoices').hide();
                        $(this).addClass('all-closed').text('すべて開く');
                    }
                });
            });

        },1000);
    });
})(jQuery);

/css/cat-toggler.css もちょい修正して、「すべて閉じる(開く)」のカーソルを変更してやり、最終的以下のように

.term-toggler {
    margin-right: 0.2em;
    cursor: pointer;
}

.term-toggler.no-child {
    visibility: hidden;
}

.editor-post-taxonomies__hierarchical-terms-list > .editor-post-taxonomies__hierarchical-terms-choice > .editor-post-taxonomies__hierarchical-terms-subchoices {
    margin-left: 2.5em;
    display: none;
}

.all-term-toggler {
    cursor: pointer;
}

これで、以下のようにすべてのカテゴリーを閉じたり、開いたりできる。

上記の例ではカテゴリーのほか、カスタムタクソノミーがあれば、それらにもこの開閉の仕組みが適用されます。どうも Gutenberg ではカテゴリーやタクソノミーの選択ボックスに id とか付いていないようなので、面倒なのですべてに適用されるようにしてみた。

Classic Editor の場合

Classic Editor の場合は、カテゴリー選択のところの html の構造などが Gutenberg とは異なっているので、それに合わせる必要がある。

ただ、その前にまず、Classic Editor の場合はカテゴリーを選択して保存すると、カテゴリー選択のところで、以下のようにカテゴリー階層が崩れてしまう。

カテゴリー選択 チェックされたもの

ので、まず functions.php に次を追加。

function keep_category_order( $args, $post_id ) {
	$args['checked_ontop'] = false;

	return $args;
}
add_action( 'wp_terms_checklist_args', 'keep_category_order', 10, 2 );

それで後は、Gutenberg のときと同じようにスクリプトファイルと css ファイルを読み込めば良いが Classic Editor の場合は admin_enqueue_scripts アクションフィルタを使って以下のように読み込む(Gutenberg でも次のようにしても良い)。

function add_editor_css_js( $hook ) {
    if ( $hook == 'post-new.php' || $hook == 'post.php' ) {
        wp_enqueue_style( 'admin_cat_toggler_style', get_template_directory_uri() . '/css/cat-toggler.css' );
        wp_enqueue_script( 'admin_cat_toggler_script', get_template_directory_uri() . '/js/cat-toggler.js', array('jquery'), '1.0.0', true );
    }
}
add_action( 'admin_enqueue_scripts', 'add_editor_css_js' );

ここで admin_enqueue_scripts アクションフィルタは管理画面全体に適用されるので、$hook で新規投稿画面と投稿編集画面でのみファイルを読み込むようにしている。

後は Classic Editor に合わせて、スクリプトファイルと css ファイルを修正してやる。下記でうまく行くはず。Gutenberg のときと違い setTimeout は不要。

(function($) {

    $(function() {

        $('.categorychecklist > li').each(function(i,el) {
            if ($(this).find('.children').length > 0)
                $(this).prepend('<span class="term-toggler with-child">+</span>');
            else
                $(this).prepend('<span class="term-toggler no-child">+</span>');
        });

            $('.term-toggler.with-child').each(function(i, el) {
                $(this).on('click', function(e) {
                    $(this).parent().children('.children').toggle();
                });
            });

            $('.categorychecklist').prepend('<div class="all-term-toggler all-closed">すべて開く</div>');

            $('.all-term-toggler').each(function(i, el) {
                $(this).on('click', function() {
                    if ($(this).hasClass('all-closed')) {
                        $(this).parent().find('.children').show();
                        $(this).removeClass('all-closed').text('すべて閉じる');
                    } else {
                        $(this).parent().find('.children').hide();
                        $(this).addClass('all-closed').text('すべて開く');
                    }
                });
            });

    });

})(jQuery);

.term-toggler {
    margin-right: 0.2em;
    cursor: pointer;
}

.term-toggler.no-child {
    visibility: hidden;
}

.categorychecklist > li > .children {
    margin-left: 2.5em !important;
    display: none;
}

.all-term-toggler {
    cursor: pointer;
}

Classic Editor の場合は、#categorychecklist や(music ってカスタムタクソノミーであれば) #musicchecklist といったように id が付与されているので個別に適用することも簡単。

ということで以上です。Gutenberg のところで setTimeout で対処しているのがいいのかちょっと自信がないけど、まあとにかくうまく動くはずです。

コメントを残す

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