PageSourceSearch

https://www.routinewellness.com/wp-content/themes/shopkeeper-child/js/review-sticky-nav.js?ver=1791582740

js routinewellness.com collected 2026-10-09 21:53:46 UTC 4,982 bytes, 141 lines download raw bytes

1class ReviewStickyNav {
2    constructor() {
3        this.lastScrollTop = 0;
4        this.isMobile = window.innerWidth <= 767;
5        this.scrollHandler = this.handleScroll.bind(this);
6        this.scrollToReviewsHandler = this.scrollToReviews.bind(this);
7        
8        this.init();
9    }
10
11    init() {
12        this.waitForElement(".tab-review-tags-nav", () => {
13            this.setupStickyNavigation();
14        });
15    }
16
17    waitForElement(selector, callback) {
18        const observer = new MutationObserver((mutations, observer) => {
19            const element = document.querySelector(selector);
20            if (element) {
21                observer.disconnect();
22                callback(element);
23            }
24        });
25
26        observer.observe(document.body, {
27            childList: true,
28            subtree: true,
29        });
30    }
31
32    setupStickyNavigation() {
33        this.filtersNav = document.querySelector(".tab-review-tags-nav");
34        this.filters = document.querySelectorAll(".tab-review-tags-nav-item");
35        this.filterParagraph = document.querySelector(".tab-review-tags > p");
36        this.reviewsHeader = document.querySelector("#reviews_header");
37        this.reviewsSections = document.querySelectorAll("#revive-reviews");
38
39        if (!this.filtersNav || !this.filterParagraph || !this.reviewsHeader) return;
40
41        this.prepareNavigation();
42        this.setupIntersectionObserver();
43    }
44
45    prepareNavigation() {
46        const filterCopy = this.filterParagraph.cloneNode(true);
47        const firstNavItem = this.filtersNav.querySelector(".tab-review-tags-nav-item:first-child");
48        this.filtersNav.insertBefore(filterCopy, firstNavItem);
49    }
50
51    setupIntersectionObserver() {
52        this.observer = new IntersectionObserver(
53            ([entry]) => {
54                const isBelowTop = entry.boundingClientRect.top < 0;
55
56                if (isBelowTop && !entry.isIntersecting) {
57                    this.activateStickyNav();
58                } else {
59                    this.deactivateStickyNav();
60                }
61            },
62            { threshold: 0 }
63        );
64
65        this.observer.observe(this.reviewsHeader);
66    }
67
68    activateStickyNav() {
69        if (this.isMobile) {
70            this.setupMobileScrollHandler();
71        }
72        this.filtersNav.classList.add("fixed");
73        this.reviewsSections.forEach((section) => section.classList.add("extra-margin-xs"));
74        this.filters.forEach((filter) => filter.addEventListener("click", this.scrollToReviewsHandler));
75    }
76
77    deactivateStickyNav() {
78        this.filtersNav.classList.remove("fixed");
79        this.reviewsSections.forEach((section) => section.classList.remove("extra-margin-xs"));
80        this.filters.forEach((filter) => filter.removeEventListener("click", this.scrollToReviewsHandler));
81    }
82
83    setupMobileScrollHandler() {
84        window.addEventListener("scroll", this.scrollHandler, { passive: true });
85    }
86
87    handleScroll() {
88        const fixedNav = document.querySelector(".tab-review-tags-nav.fixed");
89        if (!fixedNav) return;
90
91        const currentScrollTop = window.pageYOffset;
92        const scrollingUp = currentScrollTop < this.lastScrollTop;
93        this.lastScrollTop = currentScrollTop;
94
95        if (scrollingUp) {
96            fixedNav.classList.add("overwrite-fixed-top");
97        } else {
98            fixedNav.classList.remove("overwrite-fixed-top");
99        }
100    }
101
102    scrollToReviews(e) {
103        const href = e.target.getAttribute("data-href") || e.target.querySelector("[data-href]")?.getAttribute("data-href");
104        if (!href) return;
105
106        const isMobile = window.innerWidth <= 767;
107        const nav = document.querySelector(".tab-review-tags-nav");
108        const navHeight = nav ? nav.offsetHeight : 0;
109        const extraMobileOffset = isMobile ? 100 : 0;
110
111        let scrollPos;
112
113        if (href === "#tab-review-tags-all") {
114            const reviewsHeader = document.querySelector("#reviews_header");
115            if (!reviewsHeader) return;
116
117            const baseOffset = isMobile ? extraMobileOffset * 0.75 : navHeight / 2;
118            scrollPos = reviewsHeader.getBoundingClientRect().top + window.pageYOffset - baseOffset;
119        } else {
120            const reviewSection = document.querySelector(`${href} #revive-reviews`);
121            if (!reviewSection) return;
122
123            scrollPos = reviewSection.getBoundingClientRect().top + window.pageYOffset - navHeight - 40 - extraMobileOffset;
124        }
125
126        window.scrollTo({ top: scrollPos, behavior: "smooth" });
127    }
128
129    destroy() {
130        if (this.observer) {
131            this.observer.disconnect();
132        }
133        window.removeEventListener("scroll", this.scrollHandler);
134        this.filters.forEach((filter) => filter.removeEventListener("click", this.scrollToReviewsHandler));
135    }
136}
137
138// Initialize the class when DOM is ready
139document.addEventListener('DOMContentLoaded', function() {
140    new ReviewStickyNav();
141});

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.