Build a Custom Context Menu with Tailwind and JavaScript

Build a Custom Context Menu with Tailwind and JavaScript

Sometimes when you have a project that is so great and the theme is just so darn amazing, you want everything about the theme to be on point. Or maybe you would like to give your users more options when they right-click a certain element in your app. So how do you customize the context menu? When you right-click on a page in your web browser, what's the menu you see? Since it's built-in you have to do a bit of work around but It's a lot easier than it seems. In this article, you will build a custom context menu using Tailwind.css and JavaScript. I will also be using the icon library Iconoir(it's cute )

Step One: Build and Style the Context Menu

First, set up your document. Here's how your HTML document should be set up to add Tailwind and Iconoir.

If your project is being done locally on your machine don't forget to add <!DOCTYPE html> but if you're using something like Codepen or iCodeThis like I am, it's not necessary.

<html class="">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!--Iconoir-->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/iconoir-icons/iconoir@main/css/iconoir.css">
    <!-- Tailwind.css -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- Config to add dark mode -->
    <script>
        tailwind.config = {
            darkMode: "class"
        }
    </script>
    <style type="text/tailwindcss">

    </style>

</head>

<body class="bg-gray-300 dark:bg-gray-900 text-gray-800 dark:text-gray-100 flex justify-center items-center min-h-screen overflow-hidden">
    <h1>Right-click to see menu</h1>
    <!--context menu goes here -->
</body>

</html>

This template includes the CDN to Tailwind, the CDN to Iconoir, the tailwind config that adds dark mode, and a style tag with the type "text/tailwindcss" to add further customization with Tailwind. Now that your document is set up, let's build the layout for the context menu.

You need three things:

  • A parent div for hiding and showing the context menu

  • ul - the actual context menu container

  • li - the context menu item with its own label and action

The context menu will have only three items; new tab option, dark mode toggle, and print option.

<div id="contextMenu" class="hidden absolute">
    <ul class="menu flex flex-col rounded-md shadow-xl overflow-hidden">
    <!--Opens a new tab on click-->
        <li onclick="window.open('/code/104')" class="context-menu-item">
            <i class="iconoir-open-in-window mr-1"></i>
            Open in New Tab
        </li>
        <!-- toggles light mode and dark mode-->
        <li id="dark-toggle" class="context-menu-item">
            <i class="iconoir-half-moon mr-1"></i>
            Turn On Dark Mode
        </li>
        <!--Opens the print page option-->
        <li onclick="window.print()"
        class="context-menu-item">
           <i class="iconoir-printer"></i>
           Print
         </li>
    </ul>
</div>

Each item in the context menu will have the same exact style so for convenience, add the following code to the style tag.

<style type="text/tailwindcss">
    @layer components {
        .context-menu-item {
            @apply cursor-pointer bg-white dark:bg-gray-800 hover:bg-gray-200 transition-all ease-linear dark:hover:bg-gray-800/50 p-4 w-full h-full text-gray-800 dark:text-gray-200 relative;
        }
    }
</style>

Now the styles have been added, it is now time to add functionality to the context menu.

Step Two: Adding Functionality with JavaScript

In order for this to work, you will first need to disable the built-in context menu. You can't modify so hide it and then implement a function so that when the user right-clicks on the page, the custom context menu is displayed.

//hides the menu if its open
document.onclick = hideMenu;
//opens the context menu if already 
document.oncontextmenu = rightClick;

function hideMenu() {
    menu.classList.add("hidden");
    menu.classList.remove("block");
}

function showMenu() {
    menu.classList.add("block");
    menu.classList.remove("hidden");
}

//Opens the context menu
function rightClick(e) {
    e.preventDefault();

    if (menu.classList.contains("block")) {
        hideMenu();
    } else {
        // Calculate the dimensions of the menu
        menu.style.display = 'block';
        const menuWidth = menu.offsetWidth;
        const menuHeight = menu.offsetHeight;
        menu.style.display = '';

        // Determine position for the menu
        let posX = e.pageX;
        let posY = e.pageY;

        // Check if the menu goes beyond the right edge of the window
        if (posX + menuWidth > window.innerWidth) {
            posX = window.innerWidth - menuWidth;
        }

        // Check if the menu goes beyond the bottom edge of the window
        if (posY + menuHeight > window.innerHeight) {
            posY = window.innerHeight - menuHeight;
        }

        // Set the position of the menu
        menu.style.left = posX + 'px';
        menu.style.top = posY + 'px';

        showMenu();
    }
}

So what's happening here is that when the user right-clicks on the page, it first checks if the context menu is displayed or not. If it is, it will hide the menu and if it's not it will show the menu but before it shows the menu it does the following:

  1. Calculating the dimension of the context menu

  2. The positioning is being found from where the user right-clicked on the page

  3. Make sure that the context menu does not go out of bounds so if the user right-clicked close to the edge it stays within the bounds of the page

  4. Set the position of the menu

  5. Finally, show the menu

The Results

You should get something like the following in this code pen.

Customizing a context menu for your web application can enhance user experience and provide additional functionality. By following the steps outlined in this article, you've learned how to create a custom context menu using Tailwind.css and JavaScript.

Did you find this article valuable?

Support Karelle Hofler by becoming a sponsor. Any amount is appreciated!