diff --git a/libs/blocks/global-navigation/global-navigation.css b/libs/blocks/global-navigation/global-navigation.css index 2aff0f5aae7..7cb9591cfc3 100644 --- a/libs/blocks/global-navigation/global-navigation.css +++ b/libs/blocks/global-navigation/global-navigation.css @@ -912,6 +912,7 @@ header.new-nav .feds-nav-wrapper--expanded .feds-nav > section.feds-navItem > bu animation: slideleft 0.4s ease, fadein 0.2s ease; animation-fill-mode: forwards; padding: 15px 20px; + will-change: translate, opacity; } [dir = "rtl"] header.new-nav .feds-nav-wrapper--expanded .feds-nav > section.feds-navItem > button.feds-navLink { @@ -925,6 +926,7 @@ header.new-nav .feds-nav > section.feds-navItem > button.feds-navLink { border: none; animation: slideright 0.4s ease, fadeout 0.2s ease; animation-fill-mode: forwards; + will-change: translate, opacity; } [dir = "rtl"] header.new-nav .feds-nav > section.feds-navItem > button.feds-navLink { diff --git a/libs/blocks/global-navigation/global-navigation.js b/libs/blocks/global-navigation/global-navigation.js index 5dbe78d2ee0..35d9c8bf56a 100644 --- a/libs/blocks/global-navigation/global-navigation.js +++ b/libs/blocks/global-navigation/global-navigation.js @@ -745,30 +745,24 @@ class Gnav { if (promo) localNav.classList.add('has-promo'); this.elements.localNav = localNav; firstElem.textContent = title.trim(); - const isAtTop = () => { - const rect = this.elements.localNav.getBoundingClientRect(); - // note: ios safari changes between -0.34375, 0, and 0.328125 - return rect.top === 0; - }; + const stickyObserver = new IntersectionObserver( + ([entry]) => { + this.elements.localNav?.classList.toggle('is-sticky', entry.boundingClientRect.top <= 0); + }, + { threshold: [1] }, + ); + stickyObserver.observe(this.elements.localNav); window.addEventListener('scroll', (e) => { const classList = this.elements.localNav?.classList; - if (classList.contains('feds-localnav--active')) { - trigger({ - element: curtain, - event: e, - type: 'localNav-curtain', - animatedElement: itemWrapper, - animationType: 'transition', - }); - } - if (isAtTop()) { - if (!classList?.contains('is-sticky')) { - classList?.add('is-sticky'); - } - } else { - classList?.remove('is-sticky'); - } - }); + if (!classList?.contains('feds-localnav--active')) return; + trigger({ + element: curtain, + event: e, + type: 'localNav-curtain', + animatedElement: itemWrapper, + animationType: 'transition', + }); + }, { passive: true }); }; decorateTopnavWrapper = async () => { diff --git a/test/blocks/global-navigation/global-navigation.test.js b/test/blocks/global-navigation/global-navigation.test.js index cae730a17a4..3438ae2d8c5 100644 --- a/test/blocks/global-navigation/global-navigation.test.js +++ b/test/blocks/global-navigation/global-navigation.test.js @@ -810,21 +810,27 @@ describe('global navigation', () => { }); it('should remove is-sticky class to localnav on scroll less than localnav placement', async () => { + let stickyCallback; + const OrigIO = window.IntersectionObserver; + window.IntersectionObserver = function IOmock(cb) { stickyCallback = cb; }; + window.IntersectionObserver.prototype = { observe() {}, disconnect() {} }; await createFullGlobalNavigation({ globalNavigation: gnavWithlocalNav }); + stickyCallback([{ boundingClientRect: { top: 20 } }]); + window.IntersectionObserver = OrigIO; const localNav = document.querySelector(selectors.localNav); - sinon.stub(localNav, 'getBoundingClientRect').returns({ top: 20 }); - window.dispatchEvent(new Event('scroll')); - const localNavAfterScroll = document.querySelector(selectors.localNav); - expect(localNavAfterScroll.classList.contains('is-sticky')).to.be.false; + expect(localNav.classList.contains('is-sticky')).to.be.false; }); it('should add is-sticky class to localnav on scroll greater than localnav placement', async () => { + let stickyCallback; + const OrigIO = window.IntersectionObserver; + window.IntersectionObserver = function IOmock(cb) { stickyCallback = cb; }; + window.IntersectionObserver.prototype = { observe() {}, disconnect() {} }; await createFullGlobalNavigation({ globalNavigation: gnavWithlocalNav }); + stickyCallback([{ boundingClientRect: { top: 0 } }]); + window.IntersectionObserver = OrigIO; const localNav = document.querySelector(selectors.localNav); - sinon.stub(localNav, 'getBoundingClientRect').returns({ top: 0 }); - window.dispatchEvent(new Event('scroll')); - const localNavAfterScroll = document.querySelector(selectors.localNav); - expect(localNavAfterScroll.classList.contains('is-sticky')).to.be.true; + expect(localNav.classList.contains('is-sticky')).to.be.true; }); it('should open both screen if localnav is present but shows only level 2 screen', async () => {