- Implemented comprehensive trainer navigation system:
* Horizontal and vertical navigation layouts
* Multi-level menu with dropdowns for Events, Venues, Organizers, Profile
* Responsive mobile navigation with hamburger menu
* Keyboard navigation support (Arrow keys, Enter, Escape)
* Active page highlighting
* Master trainer menu items for users with appropriate role
- Created breadcrumb system:
* Automatic breadcrumb generation based on URL structure
* Shortcode support [hvac_breadcrumbs]
* SEO-friendly with structured data (Schema.org)
* Multiple style options (default, pills, arrows)
* Responsive design
- Technical implementation:
* HVAC_Trainer_Navigation class for menu management
* HVAC_Breadcrumbs class for breadcrumb generation
* CSS for both navigation and breadcrumbs
* JavaScript for interactive menu behaviors
* Template part for easy inclusion
Navigation provides easy access to all trainer features and improves UX.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
257 lines
No EOL
8.2 KiB
JavaScript
257 lines
No EOL
8.2 KiB
JavaScript
/**
|
|
* HVAC Trainer Navigation JavaScript
|
|
*
|
|
* @package HVAC_Community_Events
|
|
* @version 2.0.0
|
|
*/
|
|
|
|
jQuery(document).ready(function($) {
|
|
// Cache DOM elements
|
|
const $nav = $('.hvac-trainer-nav');
|
|
const $mobileToggle = $('.hvac-nav-mobile-toggle');
|
|
const $body = $('body');
|
|
|
|
// Initialize navigation
|
|
function initNavigation() {
|
|
// Handle mobile toggle
|
|
if ($mobileToggle.length) {
|
|
$mobileToggle.on('click', function(e) {
|
|
e.preventDefault();
|
|
toggleMobileNav();
|
|
});
|
|
}
|
|
|
|
// Handle submenu behavior based on data attribute
|
|
const submenuBehavior = $nav.data('submenu');
|
|
|
|
if (submenuBehavior === 'click') {
|
|
initClickSubmenus();
|
|
} else if (submenuBehavior === 'hover') {
|
|
initHoverSubmenus();
|
|
}
|
|
|
|
// Handle keyboard navigation
|
|
initKeyboardNav();
|
|
|
|
// Handle responsive behavior
|
|
initResponsive();
|
|
}
|
|
|
|
// Toggle mobile navigation
|
|
function toggleMobileNav() {
|
|
$body.toggleClass('hvac-nav-mobile-open');
|
|
|
|
// Update aria attributes
|
|
const isOpen = $body.hasClass('hvac-nav-mobile-open');
|
|
$mobileToggle.attr('aria-expanded', isOpen);
|
|
|
|
// Prevent body scroll when mobile nav is open
|
|
if (isOpen) {
|
|
$body.css('overflow', 'hidden');
|
|
} else {
|
|
$body.css('overflow', '');
|
|
}
|
|
}
|
|
|
|
// Initialize click-based submenus
|
|
function initClickSubmenus() {
|
|
$('.hvac-nav-has-submenu > .hvac-nav-link').on('click', function(e) {
|
|
const $link = $(this);
|
|
const href = $link.attr('href');
|
|
|
|
// If link is just '#', prevent default and toggle submenu
|
|
if (href === '#') {
|
|
e.preventDefault();
|
|
toggleSubmenu($link.parent());
|
|
}
|
|
});
|
|
|
|
// Close submenus when clicking outside
|
|
$(document).on('click', function(e) {
|
|
if (!$(e.target).closest('.hvac-nav-has-submenu').length) {
|
|
closeAllSubmenus();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Initialize hover-based submenus
|
|
function initHoverSubmenus() {
|
|
// Add hover intent to prevent accidental triggers
|
|
let hoverTimeout;
|
|
|
|
$('.hvac-nav-has-submenu').on('mouseenter', function() {
|
|
const $item = $(this);
|
|
clearTimeout(hoverTimeout);
|
|
|
|
hoverTimeout = setTimeout(function() {
|
|
openSubmenu($item);
|
|
}, 200);
|
|
}).on('mouseleave', function() {
|
|
const $item = $(this);
|
|
clearTimeout(hoverTimeout);
|
|
|
|
hoverTimeout = setTimeout(function() {
|
|
closeSubmenu($item);
|
|
}, 300);
|
|
});
|
|
}
|
|
|
|
// Toggle submenu
|
|
function toggleSubmenu($item) {
|
|
if ($item.hasClass('hvac-nav-open')) {
|
|
closeSubmenu($item);
|
|
} else {
|
|
// Close other submenus first
|
|
closeAllSubmenus();
|
|
openSubmenu($item);
|
|
}
|
|
}
|
|
|
|
// Open submenu
|
|
function openSubmenu($item) {
|
|
$item.addClass('hvac-nav-open');
|
|
$item.find('.hvac-nav-submenu').stop(true, true).slideDown(200);
|
|
|
|
// Update aria attributes
|
|
$item.find('> .hvac-nav-link').attr('aria-expanded', 'true');
|
|
}
|
|
|
|
// Close submenu
|
|
function closeSubmenu($item) {
|
|
$item.removeClass('hvac-nav-open');
|
|
$item.find('.hvac-nav-submenu').stop(true, true).slideUp(200);
|
|
|
|
// Update aria attributes
|
|
$item.find('> .hvac-nav-link').attr('aria-expanded', 'false');
|
|
}
|
|
|
|
// Close all submenus
|
|
function closeAllSubmenus() {
|
|
$('.hvac-nav-has-submenu').each(function() {
|
|
closeSubmenu($(this));
|
|
});
|
|
}
|
|
|
|
// Initialize keyboard navigation
|
|
function initKeyboardNav() {
|
|
$('.hvac-nav-link, .hvac-nav-sublink').on('keydown', function(e) {
|
|
const $link = $(this);
|
|
const $item = $link.parent();
|
|
|
|
switch(e.key) {
|
|
case 'Enter':
|
|
case ' ':
|
|
if ($item.hasClass('hvac-nav-has-submenu') && $link.attr('href') === '#') {
|
|
e.preventDefault();
|
|
toggleSubmenu($item);
|
|
}
|
|
break;
|
|
|
|
case 'Escape':
|
|
if ($item.closest('.hvac-nav-submenu').length) {
|
|
e.preventDefault();
|
|
const $parentItem = $item.closest('.hvac-nav-has-submenu');
|
|
closeSubmenu($parentItem);
|
|
$parentItem.find('> .hvac-nav-link').focus();
|
|
}
|
|
break;
|
|
|
|
case 'ArrowDown':
|
|
e.preventDefault();
|
|
focusNextMenuItem($link);
|
|
break;
|
|
|
|
case 'ArrowUp':
|
|
e.preventDefault();
|
|
focusPrevMenuItem($link);
|
|
break;
|
|
|
|
case 'ArrowRight':
|
|
if ($item.hasClass('hvac-nav-has-submenu')) {
|
|
e.preventDefault();
|
|
openSubmenu($item);
|
|
$item.find('.hvac-nav-sublink').first().focus();
|
|
}
|
|
break;
|
|
|
|
case 'ArrowLeft':
|
|
if ($item.closest('.hvac-nav-submenu').length) {
|
|
e.preventDefault();
|
|
const $parentItem = $item.closest('.hvac-nav-has-submenu');
|
|
closeSubmenu($parentItem);
|
|
$parentItem.find('> .hvac-nav-link').focus();
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
// Focus next menu item
|
|
function focusNextMenuItem($currentLink) {
|
|
const $allLinks = $('.hvac-nav-link:visible, .hvac-nav-sublink:visible');
|
|
const currentIndex = $allLinks.index($currentLink);
|
|
|
|
if (currentIndex < $allLinks.length - 1) {
|
|
$allLinks.eq(currentIndex + 1).focus();
|
|
}
|
|
}
|
|
|
|
// Focus previous menu item
|
|
function focusPrevMenuItem($currentLink) {
|
|
const $allLinks = $('.hvac-nav-link:visible, .hvac-nav-sublink:visible');
|
|
const currentIndex = $allLinks.index($currentLink);
|
|
|
|
if (currentIndex > 0) {
|
|
$allLinks.eq(currentIndex - 1).focus();
|
|
}
|
|
}
|
|
|
|
// Initialize responsive behavior
|
|
function initResponsive() {
|
|
let windowWidth = $(window).width();
|
|
|
|
$(window).on('resize', function() {
|
|
const newWidth = $(window).width();
|
|
|
|
// Check if we've crossed the mobile breakpoint
|
|
if ((windowWidth > 768 && newWidth <= 768) || (windowWidth <= 768 && newWidth > 768)) {
|
|
// Reset mobile nav state
|
|
$body.removeClass('hvac-nav-mobile-open');
|
|
$body.css('overflow', '');
|
|
closeAllSubmenus();
|
|
}
|
|
|
|
windowWidth = newWidth;
|
|
});
|
|
}
|
|
|
|
// Highlight current page in navigation
|
|
function highlightCurrentPage() {
|
|
const currentPath = window.location.pathname;
|
|
|
|
// Remove any existing active classes
|
|
$('.hvac-nav-active').removeClass('hvac-nav-active');
|
|
|
|
// Find matching link
|
|
$('.hvac-nav-link, .hvac-nav-sublink').each(function() {
|
|
const $link = $(this);
|
|
const href = $link.attr('href');
|
|
|
|
if (href && href !== '#' && currentPath.indexOf(href) !== -1) {
|
|
$link.parent().addClass('hvac-nav-active');
|
|
|
|
// If it's a submenu item, also highlight parent
|
|
const $parentItem = $link.closest('.hvac-nav-has-submenu');
|
|
if ($parentItem.length) {
|
|
$parentItem.addClass('hvac-nav-active');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Initialize everything
|
|
if ($nav.length) {
|
|
initNavigation();
|
|
highlightCurrentPage();
|
|
}
|
|
}); |