Last active
October 16, 2023 06:08
-
-
Save manabuyasuda/dcd0056f0e0b68ba715f20469d63deb6 to your computer and use it in GitHub Desktop.
Add aria-current to navigation
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
/** | |
* ナビゲーションリンクの絶対パスをルート相対パスに変換します。 | |
* @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