Free Strategy Call

Want Elvis to Apply This to Your Business?

Book a free 30-minute SEO call. Walk away with a strategy, not a sales pitch.

Follow Elvis

Table of Contents

Wordpress Security Expert: Ensuring A Fortified And Trustworthy Online Presence

Share this article:
Picture of Elvis Ekoigiawe
Elvis Ekoigiawe

Elvis Ekoigiawe is an SEO Specialist who drives organic growth through technical SEO, content strategy, and digital marketing expertise helping businesses rank higher and generate more revenue from search engines.

. However, some themes and Elementor defer template rendering, so the header HTML (Snippet 2, injected at wp_body_open) may not be fully parsed yet when this script first evaluates. Wrapping inside init() called via DOMContentLoaded guarantees every getElementById / querySelector finds its element.WHY NO SINGLE EARLY-RETURN GUARD: The old code had `if (!header) return` which exited the ENTIRE script if even one element was missing — killing the scroll handler, the overlay, the hamburger, and the offset all at once. Each section below has its OWN null check so the rest of the script always runs regardless. ---------------------------------------------------------------- */ function init() {/* ---------------------------------------------------------- ELEMENT REFERENCES (re-queried inside init so they are always fresh after DOMContentLoaded) ---------------------------------------------------------- */ var header = document.getElementById('ee-header'); var wrapper = document.getElementById('ee-header-wrapper'); var ticker = document.getElementById('ee-ticker'); var hamburger = document.getElementById('ee-hamburger'); var mobileNav = document.getElementById('ee-mobile-nav'); var overlay = document.getElementById('ee-services-overlay'); var overlayClose = document.getElementById('ee-overlay-close'); var overlayBackdrop = document.getElementById('ee-overlay-backdrop'); var servicesBtns = document.querySelectorAll('#ee-services-btn, #ee-services-mobile-btn'); var overlayTimer = null;/* ---------------------------------------------------------- 1. CSS VARIABLE --ee-hh (nav height) Sets the nav height as a CSS variable used by: a) #ee-ticker { margin-top: var(--ee-hh) } — ticker appears directly below the fixed nav on page load. b) Home-page hero rule — pulls first section background behind the transparent nav. The ticker is in document flow after the fixed wrapper, so its margin-top handles all vertical spacing automatically. No JS-applied content padding is needed. ---------------------------------------------------------- */ function applyContentOffset() { if (!wrapper) return; document.documentElement.style.setProperty('--ee-hh', wrapper.offsetHeight + 'px'); }applyContentOffset(); window.addEventListener('resize', applyContentOffset, { passive: true });/* ---------------------------------------------------------- 2. SCROLL HANDLER — transparent → solid background Nav is always fixed at top: 0. When the ticker scrolls far enough to disappear behind the nav, .scrolled fires the background-color transition to #0D0D0D. Ticker height ≈ 36px desktop / 30px mobile. ---------------------------------------------------------- */ if (header) { function handleScroll() { if (window.pageYOffset > 0) { header.classList.add('scrolled'); } else { header.classList.remove('scrolled'); } }window.addEventListener('scroll', handleScroll, { passive: true }); handleScroll(); /* run immediately — handles mid-page load */ }/* ---------------------------------------------------------- 3. MOBILE NAV PANEL (hamburger toggle) Null-checked independently — won't affect scroll or overlay. ---------------------------------------------------------- */ function setMobileNav(open) { if (!hamburger || !mobileNav) return; hamburger.classList.toggle('open', open); mobileNav.classList.toggle('open', open); hamburger.setAttribute('aria-expanded', open ? 'true' : 'false'); hamburger.setAttribute('aria-label', open ? 'Close Navigation Menu' : 'Open Navigation Menu'); mobileNav.setAttribute('aria-hidden', open ? 'false' : 'true'); document.body.style.overflow = open ? 'hidden' : ''; }if (hamburger && mobileNav) { hamburger.addEventListener('click', function () { setMobileNav(!mobileNav.classList.contains('open')); }); }/* ---------------------------------------------------------- 4. SERVICES FULL-SCREEN OVERLAY open → backdrop fades in, content slides from left (desktop) / right (mobile) — matches JSON entrance animations close → content slides out to right, backdrop fades out Null-checked independently from everything else. ---------------------------------------------------------- */ function openOverlay() { if (!overlay) return;if (overlayTimer) { clearTimeout(overlayTimer); overlayTimer = null; overlay.classList.remove('closing'); }overlay.classList.add('open'); overlay.setAttribute('aria-hidden', 'false');/* Close mobile nav if it happens to be open */ if (mobileNav && mobileNav.classList.contains('open')) { setMobileNav(false); }document.body.style.overflow = 'hidden';servicesBtns.forEach(function (btn) { btn.setAttribute('aria-expanded', 'true'); });/* Defer focus so CSS transition doesn't interrupt it */ if (overlayClose) { setTimeout(function () { overlayClose.focus(); }, 120); } }function closeOverlay() { if (!overlay) return;overlay.classList.add('closing'); overlay.setAttribute('aria-hidden', 'true');servicesBtns.forEach(function (btn) { btn.setAttribute('aria-expanded', 'false'); });/* Remove classes after CSS exit transition finishes (420ms) */ overlayTimer = setTimeout(function () { overlay.classList.remove('open', 'closing'); document.body.style.overflow = ''; overlayTimer = null; }, 420); }/* Click on any Services trigger (desktop + mobile nav). e.preventDefault() stops the href="#ee-services-overlay" from changing the URL hash — JS handles the overlay directly instead. */ servicesBtns.forEach(function (btn) { btn.addEventListener('click', function (e) { e.preventDefault(); if (overlay && overlay.classList.contains('open')) { closeOverlay(); } else { openOverlay(); } }); });/* Close by clicking the dark backdrop */ if (overlayBackdrop) { overlayBackdrop.addEventListener('click', function (e) { e.preventDefault(); closeOverlay(); }); }/* Close by clicking the × button */ if (overlayClose) { overlayClose.addEventListener('click', function (e) { e.preventDefault(); closeOverlay(); }); }/* ---------------------------------------------------------- 5. KEYBOARD: Escape closes overlay or mobile nav ---------------------------------------------------------- */ document.addEventListener('keydown', function (e) { var key = e.key || e.keyCode; if (key === 'Escape' || key === 'Esc' || key === 27) { if (overlay && overlay.classList.contains('open')) { closeOverlay(); var trigger = document.getElementById('ee-services-btn'); if (trigger) trigger.focus(); } else if (mobileNav && mobileNav.classList.contains('open')) { setMobileNav(false); if (hamburger) hamburger.focus(); } } });/* ---------------------------------------------------------- 6. FOCUS TRAP inside overlay (accessibility) Keeps Tab cycling within the overlay while it's open. ---------------------------------------------------------- */ if (overlay) { overlay.addEventListener('keydown', function (e) { if (!overlay.classList.contains('open')) return; if (e.key !== 'Tab' && e.keyCode !== 9) return;var focusable = overlay.querySelectorAll( 'a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])' ); var first = focusable[0]; var last = focusable[focusable.length - 1];if (e.shiftKey) { if (document.activeElement === first) { e.preventDefault(); last.focus(); } } else { if (document.activeElement === last) { e.preventDefault(); first.focus(); } } }); }} /* end init() *//* ---------------------------------------------------------------- CALL INIT: run after DOM is parsed so all elements exist. If readyState is already 'interactive' or 'complete' (common in footer scripts), call init() immediately. ---------------------------------------------------------------- */ if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); }})();