auto-hide-nav.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. /**
  2. * Auto-Hide Navigation Script
  3. * Smoothly hides navigation when scrolling down and shows it when scrolling up
  4. */
  5. (function() {
  6. 'use strict';
  7. // Configuration
  8. const config = {
  9. scrollThreshold: 100, // Start hiding after this many pixels
  10. mouseRevealZone: 100, // Show header when mouse is within this many pixels from top
  11. animationDuration: 300, // Animation duration in ms
  12. debounceDelay: 10 // Scroll event debounce delay
  13. };
  14. // State management
  15. let state = {
  16. lastScrollTop: 0,
  17. isVisible: true,
  18. headerHeight: 0,
  19. ticking: false
  20. };
  21. // DOM elements
  22. let elements = {};
  23. // Initialize when DOM is ready
  24. document.addEventListener('DOMContentLoaded', init);
  25. function init() {
  26. // Find header elements - Twenty Twenty-Five specific selectors
  27. elements.header = document.querySelector('header') ||
  28. document.querySelector('.wp-site-header') ||
  29. document.querySelector('.wp-block-template-part[data-area="header"]') ||
  30. document.querySelector('.wp-block-group.is-layout-constrained') ||
  31. document.querySelector('[class*="header"]');
  32. elements.body = document.body;
  33. // Debug: Log found header element
  34. console.log('Auto-hide navigation: Header element found:', elements.header);
  35. if (!elements.header) {
  36. console.warn('Auto-hide navigation: No header element found');
  37. // Try to find any element that might be the header
  38. const possibleHeaders = document.querySelectorAll('nav, [class*="nav"], [class*="header"], [class*="menu"]');
  39. console.log('Possible header elements:', possibleHeaders);
  40. return;
  41. }
  42. // Add identifying class for our CSS
  43. elements.header.classList.add('srh-auto-hide-header');
  44. // Setup
  45. calculateHeaderHeight();
  46. bindEvents();
  47. // Initial state
  48. updateHeaderState();
  49. console.log('Auto-hide navigation initialized successfully');
  50. }
  51. function calculateHeaderHeight() {
  52. if (!elements.header) return;
  53. state.headerHeight = elements.header.offsetHeight || 80;
  54. document.documentElement.style.setProperty('--header-height', state.headerHeight + 'px');
  55. }
  56. function bindEvents() {
  57. // Scroll event with debouncing
  58. window.addEventListener('scroll', debounce(handleScroll, config.debounceDelay));
  59. // Resize event
  60. window.addEventListener('resize', debounce(calculateHeaderHeight, 100));
  61. // Mouse movement for revealing header
  62. document.addEventListener('mousemove', handleMouseMove);
  63. // Keyboard events for accessibility
  64. document.addEventListener('keydown', handleKeydown);
  65. // Focus events for accessibility
  66. if (elements.header) {
  67. const focusableElements = elements.header.querySelectorAll('a, button, input, textarea, select, [tabindex]');
  68. focusableElements.forEach(el => {
  69. el.addEventListener('focus', showHeader);
  70. });
  71. }
  72. // Smooth scroll for anchor links
  73. document.addEventListener('click', handleAnchorClick);
  74. }
  75. function handleScroll() {
  76. if (!state.ticking) {
  77. requestAnimationFrame(updateHeaderState);
  78. state.ticking = true;
  79. }
  80. }
  81. function updateHeaderState() {
  82. const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
  83. // Add scrolled class for styling
  84. if (scrollTop > 50) {
  85. elements.body.classList.add('header-offset');
  86. elements.header.classList.add('header-scrolled');
  87. } else {
  88. elements.body.classList.remove('header-offset');
  89. elements.header.classList.remove('header-scrolled');
  90. }
  91. // Hide/show logic
  92. if (scrollTop > config.scrollThreshold) {
  93. if (scrollTop > state.lastScrollTop && state.isVisible) {
  94. // Scrolling down - hide header
  95. hideHeader();
  96. } else if (scrollTop < state.lastScrollTop && !state.isVisible) {
  97. // Scrolling up - show header
  98. showHeader();
  99. }
  100. } else if (!state.isVisible) {
  101. // Near top - always show header
  102. showHeader();
  103. }
  104. state.lastScrollTop = scrollTop;
  105. state.ticking = false;
  106. }
  107. function hideHeader() {
  108. if (!state.isVisible) return;
  109. state.isVisible = false;
  110. elements.header.classList.remove('header-visible');
  111. elements.header.classList.add('header-hidden');
  112. // Dispatch custom event
  113. dispatchCustomEvent('headerHidden');
  114. }
  115. function showHeader() {
  116. if (state.isVisible) return;
  117. state.isVisible = true;
  118. elements.header.classList.remove('header-hidden');
  119. elements.header.classList.add('header-visible');
  120. // Dispatch custom event
  121. dispatchCustomEvent('headerVisible');
  122. }
  123. function handleMouseMove(e) {
  124. if (e.clientY < config.mouseRevealZone &&
  125. window.pageYOffset > config.scrollThreshold &&
  126. !state.isVisible) {
  127. showHeader();
  128. }
  129. }
  130. function handleKeydown(e) {
  131. // Show header on Tab or Escape for accessibility
  132. if (e.key === 'Tab' || e.key === 'Escape') {
  133. showHeader();
  134. }
  135. }
  136. function handleAnchorClick(e) {
  137. const link = e.target.closest('a[href^=\"#\"]');
  138. if (!link) return;
  139. const targetId = link.getAttribute('href').substring(1);
  140. const target = document.getElementById(targetId);
  141. if (target) {
  142. e.preventDefault();
  143. const offsetTop = target.getBoundingClientRect().top + window.pageYOffset - state.headerHeight;
  144. window.scrollTo({
  145. top: offsetTop,
  146. behavior: 'smooth'
  147. });
  148. }
  149. }
  150. function dispatchCustomEvent(eventName) {
  151. const event = new CustomEvent(eventName, {
  152. detail: {
  153. headerElement: elements.header,
  154. isVisible: state.isVisible,
  155. scrollTop: state.lastScrollTop
  156. }
  157. });
  158. document.dispatchEvent(event);
  159. }
  160. function debounce(func, wait) {
  161. let timeout;
  162. return function executedFunction(...args) {
  163. const later = () => {
  164. clearTimeout(timeout);
  165. func(...args);
  166. };
  167. clearTimeout(timeout);
  168. timeout = setTimeout(later, wait);
  169. };
  170. }
  171. // Public API
  172. window.SRHNavigation = {
  173. show: showHeader,
  174. hide: hideHeader,
  175. toggle: function() {
  176. state.isVisible ? hideHeader() : showHeader();
  177. },
  178. getState: function() {
  179. return { ...state };
  180. },
  181. updateConfig: function(newConfig) {
  182. Object.assign(config, newConfig);
  183. }
  184. };
  185. })();