Last active
April 23, 2024 12:19
-
-
Save tak-dcxi/1c5f8d34d750c1cfeaa8e9be1d402d34 to your computer and use it in GitHub Desktop.
initializeSmoothScroll
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const initializeSmoothScroll = (): void => { | |
// クリックイベントのリスナーを追加 | |
document.addEventListener('click', handleClick, { capture: true }) | |
} | |
// 固定配置のヘッダーのブロックサイズを取得 | |
const getHeaderBlockSize = (): string => { | |
const header = document.querySelector('[data-fixed-header]') as HTMLElement | |
if (!header) return '0' | |
const { position, blockSize } = window.getComputedStyle(header) | |
const isFixed = position === 'fixed' || position === 'sticky' | |
return isFixed ? blockSize : '0' | |
} | |
const scrollToTarget = (element: HTMLElement): void => { | |
const headerBlockSize = getHeaderBlockSize() | |
// 固定配置のヘッダーのブロックサイズを`scrollMarginBlockStart`に設定 | |
element.style.scrollMarginBlockStart = headerBlockSize | |
// ユーザーが視差効果を減らす設定をしているかどうかを判定 | |
const isPrefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches | |
// 視差効果を減らす設定がされている場合は 'instant'、そうでない場合は 'smooth' にスクロール動作を設定 | |
const scrollBehavior = isPrefersReduced ? 'instant' : 'smooth' | |
// 縦書きの場合は左スクロール、横書きの場合は上スクロールを実行 | |
element.scrollIntoView({ behavior: scrollBehavior, inline: 'end' }) | |
} | |
const focusTarget = (element: HTMLElement): void => { | |
// ターゲット要素にフォーカスを設定 | |
element.focus({ preventScroll: true }) | |
// アクティブな要素がターゲット要素でない場合 | |
if (document.activeElement !== element) { | |
// ターゲット要素のtabindexを一時的に-1に設定 | |
element.setAttribute('tabindex', '-1') | |
// 再度フォーカスを設定 | |
element.focus({ preventScroll: true }) | |
} | |
} | |
const handleClick = (event: MouseEvent): void => { | |
// クリックされたボタンが左ボタンでない場合は処理を中断 | |
if (event.button !== 0) return | |
// クリックされたリンク要素を取得 | |
const currentLink = (event.target as HTMLElement).closest<HTMLAnchorElement>('a[href*="#"]') | |
if (!currentLink) return | |
const hash = currentLink.hash | |
// スムーススクロールを無効にする条件をチェックし、スムーススクロールを無効にする場合は処理を中断 | |
if ( | |
!hash || | |
currentLink.getAttribute('role') === 'tab' || | |
currentLink.getAttribute('role') === 'button' || | |
currentLink.getAttribute('data-smooth-scroll') === 'disabled' | |
) | |
return | |
// アンカーリンクのハッシュ部分からターゲット要素を取得 | |
const target = document.getElementById(decodeURIComponent(hash.slice(1))) || (hash === '#top' && document.body) | |
if (target) { | |
// デフォルトのリンク遷移を防止 | |
event.preventDefault() | |
// ターゲット要素までスムーズにスクロール | |
scrollToTarget(target) | |
// ターゲット要素にフォーカスを設定 | |
focusTarget(target) | |
// ブラウザの履歴にアンカーリンクのハッシュを追加 | |
if (!(hash === '#top')) { | |
history.pushState({}, '', hash) | |
} | |
} | |
} | |
export default initializeSmoothScroll |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment