PageSourceSearch

https://clockify.me/blog/wp-content/themes/product-blog/src/js/toc.js?ver=7.1.2

js clockify.me collected 2026-09-24 08:32:43 UTC 7,079 bytes, 184 lines download raw bytes

1document.addEventListener("DOMContentLoaded", function () {
2    const tocLinks = document.querySelectorAll(".toc-link");
3    const headings = document.querySelectorAll("h2, h3");
4
5    // --- 1. SMOOTH SCROLLING ---
6    tocLinks.forEach((link) => {
7        link.addEventListener("click", function (e) {
8            e.preventDefault();
9            const targetHref = this.getAttribute("href"); // e.g., "#36-of-workers..."
10            if (!targetHref) return;
11            const targetId = targetHref.substring(1); // e.g., "36-of-workers..."
12            const targetElement = document.getElementById(targetId);
13
14            if (targetElement) {
15                targetElement.scrollIntoView({
16                    behavior: "smooth",
17                    block: "start",
18                });
19            }
20        });
21    });
22
23    // --- 2. ANIMATION SETUP ---
24
25    // Define Tailwind classes for animation states
26    const baseAnimClasses = ['block', 'transition-all', 'duration-300', 'ease-in-out'];
27
28    // Classes for the VISIBLE state
29    // max-h-20 = 5rem
30    const visibleClasses = ['opacity-100', 'visible', 'max-h-20', 'mb-2'];
31
32    // Classes for the HIDDEN state
33    const hiddenClasses = ['opacity-0', 'invisible', 'max-h-0', 'pt-0', 'pb-0', 'mt-0', 'mb-0', 'pointer-events-none'];
34
35    /**
36     * Helper function to SHOW a submenu link using Tailwind classes
37     * @param {Element} link - The TOC link element to show
38     */
39    function showSubmenuLink(link) {
40        link.classList.remove(...hiddenClasses);
41        link.classList.add(...visibleClasses);
42    }
43
44    /**
45     * Helper function to HIDE a submenu link using Tailwind classes
46     * @param {Element} link - The TOC link element to hide
47     */
48    function hideSubmenuLink(link) {
49        link.classList.remove(...visibleClasses);
50        link.classList.add(...hiddenClasses);
51    }
52
53    // Find all h3/h4 links and prepare them for animation
54    tocLinks.forEach((link) => {
55        const targetHref = link.getAttribute("href");
56        if (!targetHref || targetHref === "#") return;
57        const targetId = targetHref.substring(1);
58        const targetElement = document.getElementById(targetId);
59
60
61        if (targetElement) {
62            const tag = targetElement.tagName.toLowerCase();
63
64            if (tag === 'h2') {
65                // Get the parent <li> element
66                const parentLi = link.parentElement;
67
68                // Style the <li>, NOT the <a>
69                if (parentLi) {
70                    // Remove the "mb-0" from your PHP and add the margin we want
71                    parentLi.classList.remove("mb-0");
72                    parentLi.classList.add("mb-2"); // Or "mb-1"
73                }
74            }
75
76            if (tag === "h3" || tag === "h4") {
77                // Add a class for JS to select them
78                link.classList.add("submenu-item");
79
80                // Add the base classes for transition
81                link.classList.add(...baseAnimClasses);
82
83                // Start them all as hidden
84                hideSubmenuLink(link);
85            }
86        }
87    });
88
89    // --- Utility Function ---
90    function getSubheadingsUnderH2(h2) {
91        const subs = [];
92        let el = h2.nextElementSibling;
93        while (el && el.tagName.toLowerCase() !== "h2") {
94            const tag = el.tagName.toLowerCase();
95            if (tag === "h3" || tag === "h4") {
96                subs.push(el);
97            }
98            el = el.nextElementSibling;
99        }
100        return subs;
101    }
102
103    // --- Observer Logic ---
104    let previouslyActiveH2 = null;
105
106    const observer = new IntersectionObserver(
107        (entries) => {
108            entries.forEach((entry) => {
109                if (!entry.isIntersecting) return;
110
111                const activeHeading = entry.target;
112                const id = activeHeading.getAttribute("id");
113                if (!id) return;
114
115                // Highlighting Logic
116                tocLinks.forEach((link) => {
117                    link.classList.remove("text-primary!", "[text-shadow:0.5px_0_0_var(--color-primary)]");
118                });
119                
120                // We define tocLink here
121                const tocLink = document.querySelector(`.toc-link[href="#${id}"]`);
122                tocLink?.classList.add("text-primary!", "[text-shadow:0.5px_0_0_var(--color-primary)]");
123
124                let currentH2;
125                if (activeHeading.tagName.toLowerCase() === "h2") {
126                    currentH2 = activeHeading;
127                } else if (activeHeading.tagName.toLowerCase() === "h3") {
128                    let el = activeHeading.previousElementSibling;
129                    while (el) {
130                        if (el.tagName.toLowerCase() === "h2") {
131                            currentH2 = el;
132                            break;
133                        }
134                        el = el.previousElementSibling;
135                    }
136                    if (currentH2) {
137                        const parentH2Link = document.querySelector(
138                            `.toc-link[href="#${currentH2.getAttribute("id")}"]`
139                        );
140                        parentH2Link?.classList.add("text-primary!", "[text-shadow:0.5px_0_0_var(--color-primary)]");
141                    }
142                }
143
144                // --- 3. ANIMATION LOGIC (Now using class helpers) ---
145                if (currentH2) {
146                    if (previouslyActiveH2 && previouslyActiveH2 !== currentH2) {
147                        // HIDE old sub-links
148                        const prevSubHeadings = getSubheadingsUnderH2(previouslyActiveH2);
149                        prevSubHeadings.forEach((heading) => {
150                            const subId = heading.getAttribute("id");
151                            if (!subId) return;
152                            const oldLinks = document.querySelectorAll(`a.submenu-item[href="#${subId}"]`);
153                            oldLinks.forEach(hideSubmenuLink);
154                        });
155                    }
156
157                    // SHOW new sub-links
158                    const subHeadings = getSubheadingsUnderH2(currentH2);
159                    subHeadings.forEach((heading) => {
160                        const subId = heading.getAttribute("id");
161                        if (!subId) return;
162                        const links = document.querySelectorAll(`a.submenu-item[href="#${subId}"]`);
163                        links.forEach(showSubmenuLink);
164                    });
165
166                    previouslyActiveH2 = currentH2;
167                }
168
169                // --- 4. SCROLL TOC TO ACTIVE LINK (NEW CODE) ---
170                if (tocLink) {
171                    // Use 'nearest' to avoid the whole page jumping, just the container
172                    tocLink.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
173                }
174            });
175        },
176        {
177            // --- 4. OBSERVER OPTIONS ---
178            threshold: 0,
179            rootMargin: "0px 0px -70% 0px",
180        }
181    );
182
183    headings.forEach((heading) => observer.observe(heading));
184});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.