現象
ハンバーガーメニューにページ内アンカーリンクがある場合、ハンバーガーメニューが閉じられない、遷移が起こらないという現象が起きました。
今まではページ内リンクが含まれていることはなかったのでこの問題は起きませんでした。
ページ遷移が起きる場合は、JSが再度初期状態から実行されるため、初期状態のハンバーガーメニューが閉じられた状態に戻ります。しかし、ページ内リンクの場合ページ遷移が起きないためページ遷移が起きる場合と挙動が異なります。
解決方法
ページ内リンクをクリックした場合はハンバーガーメニューを閉じるというコードを追加すれば解決。
/**
* トグルメニューの開閉
* ページ内アンカーリンクがある場合はメニューを閉じる処理を行う
*/
function clickToggleMenu() {
const body = document.getElementById('body')
const navContent = document.getElementById('navContent')
const toggleMenu = document.getElementById('toggleMenu')
// toggleMenuがdisplay:noneで非表示の場合は処理を終了
if (window.getComputedStyle(toggleMenu).display === 'none') return
// メニューを開閉する際のクラス名
const toggleOpenClassName = 'js-open'
// メニューを開く
function openMenu() {
if (!navContent || !body) return
// ナビゲーションの表示切り替え
navContent.classList.add(toggleOpenClassName)
// メニュー表示時のscroll制御
body.style.overflow = 'hidden'
}
// メニューを閉じる
function closeMenu() {
if (!navContent || !body) return
// ナビゲーションの表示切り替え
navContent.classList.remove(toggleOpenClassName)
// メニュー表示時のscroll制御
body.style.overflow = ''
}
// ページ内アンカーリンクの場合はメニューを閉じる
function closeMenuAnchorLink() {
// 現在のURLを取得する
const currentPathName = location.pathname
const menuLinks = document.getElementById('menu-menu').querySelectorAll('a')
if (menuLinks.length == 0 || !currentPathName) return
menuLinks.forEach((menuLink) => {
const href = new URL(menuLink.href)
// 同じページかつURLに'#'が含まれている場合
if (currentPathName === href.pathname && href.hash) {
menuLink.addEventListener('click', () => {
closeMenu()
})
}
})
}
closeMenuAnchorLink()
if (navContent && toggleMenu) {
/**
* トグルメニューをクリックしnavを開閉
*/
toggleMenu.addEventListener('click', () => {
// メニューオープン時のクラスがなければメニューを開く
if (!navContent.classList.contains(toggleOpenClassName)) {
openMenu()
} else {
closeMenu()
}
})
}
}
clickToggleMenu()
解説
// toggleMenuがdisplay:noneで非表示の場合は処理を終了
if (window.getComputedStyle(toggleMenu).display === 'none') return
PCの表示の場合はトグルメニューの処理が不要なので無駄な処理を実行したくないため早期リターンを行っています。
ハンバーガーメニュー部分を display:none
で非表示にしているので、この情報を分岐に使用しています。
// メニューを開閉する際のクラス名
const toggleOpenClassName = 'js-open'
ハンバーガーメニューの開閉はスタイルで行なっています。開いている場合は js-open
というクラス名をつけ、閉じている場合は取るという処理を後々しています。
クラス名を変更する際に書き換える箇所を減らしたので変数に格納しています。
// メニューを開く
function openMenu() {
if (!navContent || !body) return
// ナビゲーションの表示切り替え
navContent.classList.add(toggleOpenClassName)
// メニュー表示時のscroll制御
body.style.overflow = 'hidden'
}
メニューを開く処理です。
クラスを追加し、このクラスに表示する場合のスタイルを当てます。
これだけだとトグルメニューの大きさに関係なくスクロールバーが表示されスクロールが可能になってしまいます。
これを阻止するために body
に overflow:hidden
を指定します。
// メニューを閉じる
function closeMenu() {
if (!navContent || !body) return
// ナビゲーションの表示切り替え
navContent.classList.remove(toggleOpenClassName)
// メニュー表示時のscroll制御
body.style.overflow = ''
}
開く時と逆のことをして初期の状態に戻すことで閉じています。
// ページ内アンカーリンクの場合はメニューを閉じる
function closeMenuAnchorLink() {
// 現在のURLを取得する
const currentPathName = location.pathname
const menuLinks = document.getElementById('menu-menu').querySelectorAll('a')
if (menuLinks.length == 0 || !currentPathName) return
menuLinks.forEach((menuLink) => {
const href = new URL(menuLink.href)
// 同じページかつURLに'#'が含まれている場合
if (currentPathName === href.pathname && href.hash) {
menuLink.addEventListener('click', () => {
closeMenu()
})
}
})
}
closeMenuAnchorLink()
今回の本題のところ。
ページ内リンクの可能性がある箇所(今回だとWPなのでidが menu-menu
の要素内)のaタグを全て取得します。 forEach
で全てのaタグに対し、現在開いているページのURLのpathnameとaタグのpathnameが同じ、かつhashがある場合はクリックしたらメニューを閉じるという処理を追加します。
(hash は Location インターフェイスのプロパティで、 '#' に URL のフラグメント識別子が続く文字列 です。)
if (navContent && toggleMenu) {
/**
* トグルメニューをクリックしnavを開閉
*/
toggleMenu.addEventListener('click', () => {
// メニューオープン時のクラスがなければメニューを開く
if (!navContent.classList.contains(toggleOpenClassName)) {
openMenu()
} else {
closeMenu()
}
})
}
ハンバーガーメニューをクリックし、開閉に使用するクラスがついていなければ開く処理を実行し、なければ閉じる処理を実行しています。
これでハンバーガーメニュークリックでメニューの表示を切り替えができるようになります。