Skip to content

Instantly share code, notes, and snippets.

@manabuyasuda
Last active October 16, 2023 06:08
Show Gist options
  • Save manabuyasuda/dcd0056f0e0b68ba715f20469d63deb6 to your computer and use it in GitHub Desktop.
Save manabuyasuda/dcd0056f0e0b68ba715f20469d63deb6 to your computer and use it in GitHub Desktop.
Add aria-current to navigation
/**
* ナビゲーションリンクの絶対パスをルート相対パスに変換します。
* @param {HTMLElement|SVGElement} selector - 対象となるHTML要素またはSVG要素
* @returns {string} ルート相対パス
*/
const hrefToRootAbsolutePath = selector => {
// HTMLElementとSVGElementでhrefの取得方法が異なるため、条件分岐を行う
const absolutePath = selector instanceof HTMLElement ? selector.href : selector.href.baseVal
const windowLocation = window.location
// プロトコル(http, https)とホスト(www.example.com)を結合
const protocolHost = `${windowLocation.protocol}//${windowLocation.host}`
// 絶対パスからプロトコルとホストを削除し、index.htmlも削除
return absolutePath.replace(protocolHost, '').replace(/index\.html$/, '')
}
/**
* ルート相対パスを配列に変換します。
* @param {string} absolutePath - ルート相対パス
* @returns {string[]} パスの各部分を含む配列
*/
const rootAbsolutePathToArray = absolutePath => {
return absolutePath
.replace(/\\/g, '/') // バックスラッシュをスラッシュに変換
.replace(/\/[^/]*$/, '') // 最後の/以降を削除
.slice(1) // 先頭の/を削除
.split('/') // /で分割して配列に変換
}
/**
* aria-current属性('location')を設定します。
* @param {string} selectors - 対象となるCSSセレクタ
* @param {number} level - マッチングする階層レベル
*/
const setCurrentLocation = (selectors, level) => {
Array.from(document.querySelectorAll(selectors)).forEach(selector => {
// セレクタのルート相対パスを配列に変換
const paths = rootAbsolutePathToArray(hrefToRootAbsolutePath(selector))
// 指定された階層レベルまでのパスを取得
const matchedPath = paths.map((path, index) => {
if (index <= level - 1) {
return `${path}/`
}
return null
})
// 現在のページのパスを配列に変換
const directories = rootAbsolutePathToArray(window.location.pathname)
// 指定された階層レベルまでのディレクトリを取得
const matchedDirectory = directories.map((directory, index) => {
if (index <= level - 1) {
return `${directory}/`
}
return null
})
// 階層レベルまでのディレクトリを結合
const directory = `/${matchedDirectory.join('')}`
// 階層レベルまでのパスを結合
const href = `/${matchedPath.join('')}`
// 現在のディレクトリとパスが一致する場合、aria-currentを設定
if (href === directory && href !== '//') {
selector.setAttribute('aria-current', 'location')
}
})
}
/**
* aria-current属性('page')を設定します。
* @param {string} selectors - 対象となるCSSセレクタ
*/
const setCurrentPage = selectors => {
Array.from(document.querySelectorAll(selectors)).forEach(selector => {
// セレクタのルート相対パスを取得
const path = hrefToRootAbsolutePath(selector)
// 現在のページのパスからindex.htmlを削除
const locationPathname = window.location.pathname.replace(/index\.html$/, '')
// 現在のページのパスと一致する場合、aria-currentを設定
if (path === locationPathname) {
selector.setAttribute('aria-current', 'page')
}
})
}
/**
* @classdesc ナビゲーションにカレント(`aria-current`)を付与します。
* @author Manabu Yasuda <info@manabuyasuda.com>
* @example
* import Current from '@lib/Current';
* const current = new Current({
* selector: 'nav a',
* level: 1,
* });
* current.init();
*/
export default class Current {
/**
* @param {object} options
* @param {string} options.selector ['nav a'] - カレントの対象になるCSSセレクター
* @param {number} options.level [1] - カレントの対象になる階層
*/
constructor(options) {
const defaultOptions = {
selector: 'nav a',
level: 1,
}
this.options = Object.assign(defaultOptions, options)
Object.keys(this.options).forEach(key => {
this[key] = this.options[key]
})
}
init() {
setCurrentLocation(this.selector, this.level)
setCurrentPage(this.selector)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment