Skip to content

Instantly share code, notes, and snippets.

@endymion1818
Last active April 19, 2023 10:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save endymion1818/1cc07dbedc37c430f0f9f30692d0db49 to your computer and use it in GitHub Desktop.
Save endymion1818/1cc07dbedc37c430f0f9f30692d0db49 to your computer and use it in GitHub Desktop.
toggles
document.addEventListener("DOMContentLoaded", function() {
// Theme Toggle
const themeToggle = document.getElementById("dark-mode-toggle");
const themeToggleCircle = document.getElementById("dark-mode-toggle-circle");
// Check if the user has set a theme preference set in localStorage or if the browser preferences
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
// Toggle the "dark" class on the <html> element when the button is clicked
themeToggle?.addEventListener("click", function() {
document.documentElement.classList.toggle("dark");
themeToggleCircle.classList.toggle("translate-x-5");
// Save the user preference in localStorage
if (document.documentElement.classList.contains("dark")) {
localStorage.setItem("theme", "dark");
} else {
localStorage.setItem("theme", "light");
}
});
// All Mega Menu buttons
const megaMenuButtons = document.querySelectorAll('.mega-menu-toggle');
// All mega menus
const megaMenus = document.querySelectorAll('.mega-menu');
// Timeout delay so we can add a transition then hide the menu
const timeoutDelay = 300
// For each button
megaMenuButtons?.forEach(megaMenuButton => {
// listen for clicks on this buttons
megaMenuButton.addEventListener('click', function() {
// Get the targeted menu
const megaMenu = document.getElementById(megaMenuButton.dataset.toggle);
// and the button icon
const megaMenuIcon = megaMenuButton.querySelector('svg');
// Is the menu open already?
const isExpanded = megaMenuButton.getAttribute('aria-expanded');
console.log(megaMenuButton.getAttribute('aria-expanded'))
// If the menu is open, close it
if(isExpanded === 'true') {
megaMenu.classList.add('animate-fade-out-down');
megaMenuButton.classList.remove('bg-primary-800', 'dark:bg-zinc-800');
megaMenuIcon.classList.remove('rotate-180');
megaMenuButton.setAttribute("aria-expanded", 'false');
setTimeout(() => {
megaMenu.classList.add('hidden');
megaMenu.classList.remove('block', 'animate-fade-in-up');
}, timeoutDelay);
return;
}
// Toggle classes on the targeted menu
megaMenu.classList.remove('hidden', 'animate-fade-out-down');
megaMenu.classList.add('block', 'animate-fade-in-up');
// Toggle classes and attributes on the button
megaMenuButton.classList.add('bg-primary-800', 'dark:bg-zinc-800');
megaMenuButton.setAttribute("aria-expanded", 'true');
megaMenuIcon.classList.add('rotate-180');
// Toggle classes on all other mega menus
megaMenus.forEach(megaMenuItem => {
// but only if they're not the current menu
if(megaMenuItem.id !== megaMenu.id) {
megaMenuItem.classList.add('animate-fade-out-down');
megaMenuItem.classList.remove('animate-fade-in-up');
setTimeout(function() {
megaMenuItem.classList.add('hidden');
megaMenu.classList.remove('block', 'animate-fade-in-up');
}, timeoutDelay);
}
});
// Toggle classes on all other mega menu buttons
megaMenuButtons.forEach(megaMenuButtonItem => {
// but only if they're not the current menu button
if(megaMenuButtonItem.dataset.toggle !== megaMenu.id) {
megaMenuButtonItem.classList.remove('bg-primary-800', 'dark:bg-zinc-800');
megaMenuButtonItem.querySelector('svg').classList.remove('rotate-180');
megaMenuButtonItem.setAttribute("aria-expanded", 'false');
}
})
});
});
// Event listener for all toggle buttons in the DOM
const tabToggles = document.querySelectorAll('.tab-toggle');
// We want this to be relative to the parent element
// So do nothing else at this stage
// Except listen for clicks on this buttons
tabToggles?.forEach(tabToggle => {
// Add event for each toggle
tabToggle.addEventListener('click', function() {
// change color of toggle button
tabToggle.classList.remove('text-zinc-400', 'border-b-transparent');
tabToggle.classList.add('text-primary-600', 'border-b-primary-600');
tabToggle.setAttribute("aria-expanded", true);
// reset the color of all other toggle buttons
Object.values(tabToggles).forEach(tabToggleItem => {
if(tabToggleItem.dataset.toggle !== tabToggle.dataset.toggle) {
tabToggleItem.classList.remove('text-primary-600', 'border-b-primary-600');
tabToggleItem.classList.add('text-zinc-400', 'border-b-transparent');
}
});
// Get the parent element for this toggle group
const tabParent = tabToggle.parentElement.parentElement;
// get adjacent panes
const tabPanes = tabParent.getElementsByClassName(`tab-pane`);
// Get the targeted pane
const tabPane = tabParent.querySelector(`#${tabToggle.dataset.toggle}`);
// toggle classes on the targeted pane
tabPane.classList.add('translate-x-15');
setTimeout(function() {
tabPane.classList.remove('hidden');
tabPane.classList.add('block');
}, timeoutDelay);
// toggle classes on all adjacent panes
Object.values(tabPanes).forEach(tabPaneItem => {
// But only if it's not the targeted pane
if(tabPaneItem.id !== tabToggle.dataset.toggle) {
setTimeout(function() {
tabPaneItem.classList.remove('block', 'translate-x-15');
tabPaneItem.classList.add('hidden');
}, timeoutDelay);
}
});
tabToggles.forEach(tabToggleItem => {
if(tabToggleItem.dataset.toggle !== tabToggle.dataset.toggle) {
tabToggleItem.setAttribute("aria-expanded", false);
}
});
});
});
// Toggle the search open on click
const searchButtons = document.querySelectorAll('.search-menu-toggle');
// loop through each search button
searchButtons?.forEach(searchButton => {
// listen for clicks on this button
searchButton.addEventListener('click', function() {
searchButton.classList.add('bg-primary-800', 'dark:bg-zinc-800', 'rounded-l-md');
searchButton.classList.toggle('rounded-md');
// get the element containing the search input
const searchMenu = searchButton.parentElement.querySelector('.search-menu-container');
searchMenu.classList.toggle('hidden');
searchMenu.classList.toggle('block');
searchMenu.querySelector('input').focus();
});
});
// Toggle the location menu open on click
const locationButtons = document.querySelectorAll('.location-menu-toggle');
locationButtons.forEach(locationButton => {
const locationMenu = locationButton.parentElement.querySelector('.location-menu-container');
locationButton.addEventListener('click', function() {
locationButton.querySelector('svg').classList.toggle('rotate-180');
locationMenu.classList.toggle('hidden');
locationMenu.classList.toggle('block');
});
});
// Toggle mobile nav - Mark, this pattern can be re-used everywhere
const toggleMobileNav = document.querySelectorAll(
'button[data-toggle="mobile-menu"]'
);
toggleMobileNav?.forEach((toggleMobileNav) => {
toggleMobileNav.addEventListener("click", () => {
// Stop the body from scrolling when the menu is open
document.body.classList.toggle("overflow-hidden");
// for the menu itself
const target = document.querySelector(
`#${toggleMobileNav.dataset.toggle}`
);
target.classList.toggle("hidden");
target.classList.toggle("block");
// for the button and icon
toggleMobileNav.classList.toggle("bg-primary-800");
toggleMobileNav.querySelector("#icon-open").classList.toggle("hidden");
toggleMobileNav.querySelector("#icon-open").classList.toggle("rotate-180");
toggleMobileNav.setAttribute("aria-expanded", `${!(toggleMobileNav.getAttribute('aria-expanded') === 'true')}`);
toggleMobileNav
.querySelector("#icon-closed")
.classList.toggle("hidden");
});
});
// Nice API for toggling any amount of dropdowns
const dropdownToggles = document.querySelectorAll('button[data-dropdown]');
dropdownToggles?.forEach(dropdownToggle => {
dropdownToggle.addEventListener('click', function (e) {
const hasTransform = !dropdownToggle.querySelector('svg').classList.contains('no-rotate');
hasTransform && dropdownToggle.querySelector('svg').classList.toggle('tw-rotate-180');
const target = document.querySelector(`.${dropdownToggle.dataset.dropdown}`);
target.classList.toggle('tw-hidden');
target.classList.toggle('tw-block');
target.setAttribute("aria-expanded", "true");
});
});
// Also close the menu and search if anywhere outside the menu or search is clicked
document.body.addEventListener('click', function(e) {
const isOnMenuOrMenuButton = e.target.closest('.mega-menu-toggle') !== null
|| e.target.closest('.mega-menu') !== null
megaMenus?.forEach(megaMenu => {
// If the click is on the menu button, do nothing
if(isOnMenuOrMenuButton) {
return
}
// otherwise close the menu
megaMenu.classList.add('animate-fade-out-down');
megaMenu.classList.remove('animate-fade-in-up');
setTimeout(function() {
megaMenu.classList.add('hidden');
megaMenu.classList.remove('block', 'animate-fade-in-up');
}, timeoutDelay);
});
// reset the button state
megaMenuButtons.forEach(megaMenuButton => {
if(isOnMenuOrMenuButton) {
return
}
megaMenuButton.setAttribute("aria-expanded", "false");
megaMenuButton.classList.remove('bg-primary-800', 'dark:bg-zinc-800');
megaMenuButton.querySelector('svg').classList.remove('rotate-180');
});
searchButtons.forEach(searchButton => {
const searchMenu = searchButton.parentElement.querySelector('.search-menu-container');
// If the click is on the menu button, do nothing
if(
e.target.closest('.search-menu-toggle') !== null
|| e.target.closest('input[type=search]') !== null
) {
return
}
searchButton.classList.remove('bg-primary-800', 'dark:bg-zinc-800', 'rounded-l-md')
searchButton.classList.add('rounded-md');
searchMenu.classList.add('hidden');
searchMenu.classList.remove('block');
});
locationButtons.forEach(locationButton => {
const locationMenu = locationButton.parentElement.querySelector('.location-menu-container');
// If the click is on the menu button, do nothing
if(
e.target.closest('.location-menu-toggle') !== null
|| e.target.closest('.location-menu-container') !== null
) {
return
}
locationButton.querySelector('svg').classList.remove('rotate-180');
locationMenu.classList.add('hidden');
locationMenu.classList.remove('block');
});
dropdownToggles.forEach(dropdownToggle => {
const isNearDropdownOrTarget =
e.target.closest('button[data-dropdown]') !== null;
console.log({isNearDropdownOrTarget})
if(!isNearDropdownOrTarget) {
const target = document.querySelector(`.${dropdownToggle.dataset.dropdown}`);
console.log({target})
target.classList.remove('tw-block');
target.classList.add('tw-hidden');
}
});
})
});
@endymion1818
Copy link
Author

Also add this to your tailwind.config.js:

darkMode: 'class',

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment