CMD+ K menu for Webflow

Open the menu and search the page to navigate

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:

  1. Open the "Navigator" and copy the menu
  2. Paste it in your project
  3. Customize the static page:
    1. Change the layout from "None" to "Flex" of the modal menu
    2. Update the content and the link to your main static pages
  4. Add CMS collection and any other section
  5. Make it nice and yours by continuing the work :)

Recommendations:

  1. Create a "Component" for the menu section and add it to every page.
  2. I added some rules for the "tag" on the CMS collection, feel free to change them.
  3. 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>