Skip to content

Instantly share code, notes, and snippets.

@tak-dcxi
Last active April 23, 2024 12:19
Show Gist options
  • Save tak-dcxi/1c5f8d34d750c1cfeaa8e9be1d402d34 to your computer and use it in GitHub Desktop.
Save tak-dcxi/1c5f8d34d750c1cfeaa8e9be1d402d34 to your computer and use it in GitHub Desktop.
initializeSmoothScroll
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