Try it now: CMD + K to open the menu
or click the menu button at the bottom right corner.
List of features:
- cmd + k to open the menu - also works with "ctr + k"
- A floating button to open the menu
- A button to open the menu on Mobile
- A search input field to quickly find your page
- Works for static and CMS collections
How to get started:
- Open the "Navigator" and copy the menu
- Paste it in your project
- Customize the static page:
- Change the layout from "None" to "Flex" of the modal menu
- Update the content and the link to your main static pages
- Add CMS collection and any other section
- Make it nice and yours by continuing the work :)
Recommendations:
- Create a "Component" for the menu section and add it to every page.
- I added some rules for the "tag" on the CMS collection, feel free to change them.
- The input form as an "autocomplete:"off"" to avoid input autocompletion.
This work would have been more painful without the work from:
Will Gibson
CMD + K function on Webflow: https://webflow.com/made-in-webflow/website/command-menu-k
Christopher Wray
Webflow Search and Filter: https://filtering-search.webflow.io/
Here is an overview of the code block. The code block is already attached to the section.
<script>
document.addEventListener('DOMContentLoaded', () => {
console.log("here");
// Selecting necessary elements from the DOM
const modal = document.querySelector('#modal');
const closeButton = document.querySelector('#closeButton');
const menuButton = document.querySelector('#menu-button');
const searchBar = document.querySelector('#search-input');
const list = document.querySelector('#search-list');
const nothing = document.querySelector('#not-found');
// Check if all elements are found in the DOM
if (!modal || !closeButton || !menuButton || !searchBar || !list || !nothing) {
console.error('One or more elements could not be found in the DOM.');
return;
}
// Toggle modal visibility on 'k' + 'command' (Mac) or 'k' + 'control' (Windows)
document.addEventListener('keydown', (event) => {
if (event.key === 'k' && (event.metaKey || event.ctrlKey)) {
event.preventDefault(); // Prevent default Ctrl+K behavior (like focusing search bar)
modal.style.display = modal.style.display === 'flex' ? 'none' : 'flex';
if (modal.style.display === 'flex') {
searchBar.focus(); // Focus on the search bar when the modal is displayed
}
}
});
// Open modal when menu button is clicked
menuButton.addEventListener('click', () => {
modal.style.display = 'flex';
});
// Close modal when close button is clicked
closeButton.addEventListener('click', () => {
modal.style.display = 'none';
});
// Close modal when 'Esc' key is pressed
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && modal.style.display === 'flex') {
modal.style.display = 'none';
}
});
// Close modal when clicking outside of it
window.addEventListener('click', (event) => {
if (event.target === modal) {
modal.style.display = 'none';
}
});
// Arrow key navigation within modal
document.addEventListener('keydown', (event) => {
const arrowKeys = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
if (arrowKeys.includes(event.key) && modal.style.display === 'flex') {
event.preventDefault(); // Prevent default arrow key behavior
const links = Array.from(modal.querySelectorAll('a:not(#ignoreLink)'));
const currentLink = document.activeElement.closest('a:not(#ignoreLink)');
const currentIndex = links.indexOf(currentLink);
const offset = event.key === 'ArrowUp' || event.key === 'ArrowLeft' ? -1 : 1;
const nextIndex = (currentIndex + offset + links.length) % links.length;
links[nextIndex].focus(); // Focus on the next link
modal.scrollTop = links[nextIndex].offsetTop - modal.offsetTop; // Scroll to the next link
}
});
// Search functionality
searchBar.addEventListener('keyup', function(e) {
const term = e.target.value.toLowerCase();
const searchItems = list.getElementsByClassName('modal_link w-inline-block');
let hasResults = false;
Array.from(searchItems).forEach(function(item) {
const text = item.firstElementChild.textContent;
if (text.toLowerCase().indexOf(term) != -1) {
item.style.display = 'flex'; // Display matching items
hasResults = true;
} else {
item.style.display = 'none'; // Hide non-matching items
}
});
// Display 'nothing' message if no results found
if (hasResults) {
nothing.style.display = 'none';
} else {
nothing.style.display = 'block';
}
});
// Prevent form submission on Enter key
searchBar.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
e.preventDefault(); // Prevent form submission
}
});
});
</script>