題はポップアップですが、スクロールが発生するくらい大きな要素とposition: absoluteな要素があり、position: absoluteな要素の中でアンカーリンクによるスクロールを発生させる場合、共通で起きる問題の解決方法の紹介です。
まず次のデモのような状態を考えます。このデモはポップアップが表示されっぱなしで、ポップアップ上部のアンカーリンクをクリックすると下部にスクロールする、という内容です。
アンカーリンクをクリックすると親スクロールも動作してポップアップが画面の上側に飛んだかと思います。次のような挙動です。
こういった時、ポップアップは動かずにポップアップ内部でのみスクロールして欲しいかと思います。これの解決方法を紹介します。
解決方法は少なくとも2つあります。
1つ目はposition: absoluteの代わりにposition: fixedを使う方法です。デモの状況はwebページを見る側としては画面の決まった部分に固定されているべきなのに実際はposition: absoluteが使われてて固定されていない、という点で差異があり、その差異が親要素のスクロール位置が変わったら相対位置参照のためそれに引きずられて奇妙な挙動を生んでいます。ですのでそれをなくします。position: fixedを使ってposition: absoluteの時と同様の見た目が作れればそれで解決します。
2つ目はposition: absoluteのままJavaScriptでスクロールを制御して解決する方法です。これの実装は次のコードでできます。
/**
* ポップアップ内のハッシュリンクをクリックした際に、指定された要素へスクロールするイベント追加する関数です。
* 処理に必要な各要素が存在しない場合はエラーメッセージがコンソールに表示されます。
* @param {string} popupElSelector - ポップアップ要素のセレクタ
* @param {string} scrollElSelector - スクロールが発生している要素のセレクタ
*/
function setupPopupHashLinkScrolling(popupElSelector, scrollElSelector) {
// ポップアップ要素の取得
const popupEl = document.querySelector(popupElSelector);
if (!popupEl) {
console.error('ポップアップの要素が見つかりませんでした');
return;
}
// スクロールしたい要素
const scrollTgEl = document.querySelector(scrollElSelector);
if (!scrollTgEl) {
console.error('スクロール対象の要素が見つかりませんでした');
return;
}
// ポップアップにスクロール用のイベントリスナー追加
popupEl.addEventListener('click', scrollAction);
function scrollAction(e) {
// ポップアップ要素の中のハッシュリンクがクリックされた時だけ処理する
if (e.target.tagName !== 'A' || !e.target.hash) {
return;
}
e.preventDefault(); // リンクのデフォルト動作をキャンセル
const viewTgtEl = document.querySelector(e.target.hash); // ハッシュ部は#xxxなのでそれで画面内に映したい要素を取得
if (!viewTgtEl) {
console.error('スクロール先の要素が見つかりませんでした');
return;
}
// スクロール位置を計算
// 要素の位置 - スクロール対象要素の位置 + スクロール対象要素のスクロール量
const scrollToPosition = viewTgtEl.getBoundingClientRect().top - scrollTgEl.getBoundingClientRect().top + scrollTgEl.scrollTop;
// スクロール実行
scrollTgEl.scrollTo({top: scrollToPosition});
}
// 後からclickイベントリスナーを削除する時はこの返り値を元にする
return scrollAction;
}
やってる処理は制御したい要素を見つけ、アンカーリンクがクリックされた場合はそれにしたがってスクロールを制御する、という処理です。これを使うと次の様になります。
こんな感じでアンカーリンクがクリックされた時にいい感じにスクロールできます。
