// ============================================================================
// Stylescape | Active Link Highlighter
// ============================================================================
// Automatically highlights navigation links that match the current page URL.
// Supports data-ss-active-link attributes for declarative configuration.
// ============================================================================
/**
* Configuration options for ActiveLinkHighlighter
*/
export interface ActiveLinkHighlighterOptions {
/** CSS class to apply to active links */
activeClass?: string;
/** Selector for links to check (default: all 'a' elements) */
linkSelector?: string;
/** Whether to include query parameters in URL matching */
includeQuery?: boolean;
/** Selectors for elements to skip (e.g., ribbon titles) */
skipSelectors?: string[];
}
/**
* Automatically highlights navigation links matching the current URL.
*
* @example JavaScript
* ```typescript
* // Basic usage
* const highlighter = new ActiveLinkHighlighter("active")
*
* // With options
* const highlighter = new ActiveLinkHighlighter("nav-active", {
* linkSelector: "nav a",
* includeQuery: false
* })
* ```
*
* @example HTML with data-ss
* ```html
*
* ```
*/
export class ActiveLinkHighlighter {
/** CSS class applied to active links */
private activeClass: string;
/**
* Creates a new ActiveLinkHighlighter instance.
*
* @param activeClass - CSS class to apply to matching links (default: "active")
*/
constructor(activeClass: string = "active") {
this.activeClass = activeClass;
this.highlightAllLinks();
}
/**
* Normalizes a URL by extracting and cleaning the pathname and search.
*
* @param url - The URL to normalize
* @returns The normalized path including query parameters
*/
private normalizeUrl(url: string): string {
const a = document.createElement("a");
a.href = url;
const pathname = a.pathname.replace(/\/+$/, ""); // strip trailing slash
return pathname + a.search; // include query parameters
}
/**
* Highlights all links on the page that match the current URL.
* Skips links inside ribbon titles and links without href attributes.
*/
private highlightAllLinks(): void {
const currentPath = this.normalizeUrl(window.location.href);
const links = document.querySelectorAll("a");
links.forEach((link) => {
// Skip links in ribbon titles
if (link.closest(".ribbon__title")) {
return;
}
if (!link.hasAttribute("href") || !link.getAttribute("href")) {
return;
}
const linkPath = this.normalizeUrl(link.href);
if (linkPath === currentPath) {
link.classList.add(this.activeClass);
}
});
}
}
// export class ActiveLinkHighlighter {
// private links: NodeListOf
// private activeClass: string
// constructor(selector: string = "a", activeClass: string = "active") {
// this.links = document.querySelectorAll(selector)
// this.activeClass = activeClass
// this.highlight()
// }
// private normalize(url: string): string {
// const a = document.createElement("a")
// a.href = url
// return a.pathname.replace(/\/$/, "") // remove trailing slash
// }
// private highlight(): void {
// const currentPath = this.normalize(window.location.href)
// this.links.forEach((link) => {
// const linkPath = this.normalize(link.href)
// if (linkPath === currentPath) {
// link.classList.add(this.activeClass)
// }
// })
// }
// }
// Usage:
// new ActiveLinkHighlighter("nav a") // or any specific selector