Bootstrapが組み込まれたUnderstrapテーマを使用した時に、グローバルナビのメニューを階層化した際、親メニューのリンクが無効になる動きに困った時がありました。

なぜリンクが有効にならないのか小一時間悩みました
この記事では、その原因と対策についてまとめています。この記事を読むことで、Bootstrapを使用したテーマで親メニューのリンクを有効化する方法を知ることができます。
なぜBootstrapを使ったテーマでは親メニューのリンクが無効になるのか?
Bootstrapを使った全てのテーマに当てはまるわけではありませんが、Bootstrapを使ったテーマでは親メニューのリンクが無効になる実装になっていることが多いです。
wp-bootstrap-navwalkerが犯人
wp-bootstrap-navwalkerというスクリプトを使っているテーマでは、 親メニューのリンクが無効になる現象が発生します。
Bootstrapテーマでは wp-bootstrap-navwalkerが使われることが多いので、この現象はBootstrapテーマでは頻繁に確認されると思います。
wp-bootstrap-navwalkerはどこで使われている?
wp-bootstrap-navwalkerが使われているのは、wp_nav_menuを定義するところです。 テーマのheader.phpなどで見つけることができます。この記述があると親メニューのリンクが無効になる現象が発生すると思います。
wp_nav_menu( array(
'theme_location' => 'primary',
'depth' => 1, // 1 = with dropdowns, 0 = no dropdowns.
'container' => 'div',
'container_class' => 'collapse navbar-collapse',
'container_id' => 'bs-example-navbar-collapse-1',
'menu_class' => 'navbar-nav mr-auto',
'fallback_cb' => 'WP_Bootstrap_Navwalker::fallback',
'walker' => new WP_Bootstrap_Navwalker()
) );
wp-bootstrap-navwalker内部の処理
wp-bootstrap-navwalker内部の処理を見てみると、class-wp-bootstrap-navwalker.phpの187行目付近に$atts[href]=’#’という記述があります。ここでリンクを無効化しています。
if ( isset( $args->has_children ) && $args->has_children && 0 === $depth && $args->depth > 1 ) {
$atts['href'] = '#';
$atts['data-toggle'] = 'dropdown';
$atts['aria-haspopup'] = 'true';
$atts['aria-expanded'] = 'false';
$atts['class'] = 'dropdown-toggle nav-link';
$atts['id'] = 'menu-item-dropdown-' . $item->ID;
} else {
$atts['href'] = ! empty( $item->url ) ? $item->url : '#';
// For items in dropdowns use .dropdown-item instead of .nav-link.
if ( $depth > 0 ) {
$atts['class'] = 'dropdown-item';
} else {
$atts['class'] = 'nav-link';
}
}
なぜ親メニューのリンクを無効化する処理が必要なのか?
これは推測ですが、階層メニューのトップ(親メニュー)に意味のあるリンクが入っていると、モバイルメニュー時に階層メニューを開くときに、トップに飛んでしまうからだと思います。
つまり、そのような使い方だとモバイル時に問題が出るので、テーマ側で処置していると考えるのが普通かとおもいます。

PCページだけを考えて、なんて使いにくいテーマだと思ったものです。
①Bootstrapで親メニューをリンクを有効化するカスタマイズ
親メニューのリンクが無効になる理由がわかると、ゴリ押ししてカスタマイズする気も失せるのですが、せっかくなのでカスタマイズ方法をご紹介します。
検索すると、phpファイルを直接書き換える例しか見つかりませんが、ご紹介する方法はfunction.phpに追記するだけの方法です。
このカスタマイズでは、モバイル時は親メニューのリンク無効化して、PC時のみ有効化します。場合分けが不要な場合は、wp_is_mobileでの判定をやめればよいです。

wp_is_mobileを使うとキャッシュ系プラグインで問題が起きやすいです
このカスタマイズと合わせて、PCとモバイルで別のメニューを用意して、モバイルは親メニューのリンクが無くても良い構成、PCは親メニューがあっても良い構成にだし分けることが可能です。(後述)。もしくは、メニューを共通化してPCの時だけ不要な子メニューをメディアクエリでdisplay:noneするなどの対処が必要だと思います。
function my_nav_menu_link_attributes($atts, $item, $args, $depth){
if ( isset( $args->has_children ) && $args->has_children && 0 === $depth && $args->depth > 1 ) {
if ( wp_is_mobile() ) {
// Parent link inactive
$atts['href'] = '#';
// Parent link toggles dropdown
$atts['data-toggle'] = 'dropdown';
} else {
// Parent link active
$atts['href'] = ! empty( $item->url ) ? $item->url : '';
// Parent link shows dropdown on hover
$atts['data-hover'] = 'dropdown';
};
};
return $atts;
}
add_filter( 'nav_menu_link_attributes', 'my_nav_menu_link_attributes', 10, 4 );
②PCとモバイルでメニューを切り替えるカスタマイズ
今までのカスタマイズ(①Bootstrapで親メニューをリンクを有効化するカスタマイズ)に加えて、PCは親メニューでリンクあり、モバイルは親メニューリンク無しにするカスタマイズを行います。

カスタマイズ例はUnderstrapテーマをベースにしていますが、考え方はどのテーマでも同じです。
- Understrap_WP_Bootstrap_Navwalkerの挙動をレスポンシブに
- PCは親メニューのリンクあり
- モバイルは親メニューのリンク無し
- PCとモバイルを別メニューに分ける
- モバイルの親メニューはリンク無しでメニューオープンに特化したメニュー構成
- PCの親メニューはリンクあり+メニューオープン機能併用を前提としたメニュー構成
モバイル用メニューの定義を追加
Understrapはグローバルメニューは一つだけでPCとモバイルのメニューは分かれていません。
モバイルメニューを追加するために、function.phpに以下の記述を追記します。これで、ダッシュボードのメニューに配置先(モバイルメニュー)が追加されます。
register_nav_menu( 'mobile-menu', 'モバイルメニュー' );
PC用メニューをコピー
既存のメニューを複製して、PC用とモバイル用にメニューを分けます。
duplicate-menuというプラグインを使うと既存のメニューを複製できます。

このプラグインはメニュー定義が沢山あるテーマではとても便利です
プラグインをインストールして、メニューを複製してモバイルメニューとします。
モバイルメニューの配置先をモバイルメニューに指定
メニュー⇒位置の管理と進んで、作成したもモバイルメニューの配置先をモバイルメニューとします。
ハンバーガーメニューにモバイルメニューを割り当てる
ハンバーガーメニュー用のbuttonの参照先をnavbarNavDropdown_spに変更し、モバイル用にnav_menuを追加します。
<button class="navbar-toggler collapsed" type="button" data-toggle="collapse" data-target="#navbarNavDropdown_sp" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="<?php esc_attr_e( 'Toggle navigation', 'understrap' ); ?>">
<span> </span>
<span> </span>
<span> </span>
</button>
<!-- The WordPress Menu goes here -->
<?php wp_nav_menu(
array(
'theme_location' => 'primary',
'container_class' => 'collapse navbar-collapse',
'container_id' => 'navbarNavDropdown',
'menu_class' => 'navbar-nav ml-auto',
'fallback_cb' => '',
'menu_id' => 'main-menu',
'depth' => 2,
'walker' => new Understrap_WP_Bootstrap_Navwalker(),
)
); ?>
<?php wp_nav_menu(
array(
'theme_location' => 'mobile-menu',
'container_class' => 'collapse navbar-collapse',
'container_id' => 'navbarNavDropdown_sp',
'menu_class' => 'navbar-nav ml-auto',
'fallback_cb' => '',
'menu_id' => 'mobile-menu',
'depth' => 2,
'walker' => new Understrap_WP_Bootstrap_Navwalker(),
)
); ?>

このままだとPC用のメニューが2つ表示されますので、CSSでハンバーガーメニュー非表示時にモバイル用メニューはdisplay:noneしてください。
まとめ
最近真面目にBootstrapをいじり始めて、細かいところまで作り込まれていて使いやすい反面、変な癖があって、今回ご紹介したようになぜ??と思う現象もありました。
これだけ大勢の人が使っているフレームワークですから、理由があってそうなっていることがほとんどで、使い方の問題で回避できるはずです。
今回の親メニューが無効になる現象が発生しなければ、モバイルで親メニューのリンクがあると使えないことに気が付いたはずです。その時点で、PC用メニューの構成を見直したはずでしたが、バカ除けが邪魔してそこまで行きませんでした。
今回紹介したカスタマイズも本来は不要で、メニューの使い方で回避できる話ですが、理由を想像できずに勝手にリンクが無効になる理由とその対処方法を探すのに時間をかけてしまいました。