DEV Community

Richard Umoffia
Richard Umoffia

Posted on

How to create a custom context menu for your web application

Before we begin, here's a preview of what we'll be creating.

Context menu preview

Let's discuss a few use cases of a custom context menu.
  1. You wish to display a custom context menu in an area of your web app rather than the default one.

I've exhausted my use case examples but you can think up a few other ones I'm sure of it

Let's code!

First, we'll create a list of items that will represent our custom context menu using HTML.

<div class="menu">
  <ul class="menu-options">
    <li class="menu-option">Back</li>
    <li class="menu-option">Reload</li>
    <li class="menu-option">Save</li>
    <li class="menu-option">Save As</li>
    <li class="menu-option">Inspect</li>
  </ul>
</div>
Enter fullscreen mode Exit fullscreen mode

Then, we'll add a few lines of scss to fresh up our custom menu. Make it look nice and clean.

.menu {
  width: 120px;
  box-shadow: 0 4px 5px 3px rgba(0, 0, 0, 0.2);
  position: relative;
  display: none;

  .menu-options {
    list-style: none;
    padding: 10px 0;

    .menu-option {
      font-weight: 500;
      font-size: 14px;
      padding: 10px 40px 10px 20px;
      cursor: pointer;

      &:hover {
        background: rgba(0, 0, 0, 0.2);
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

To the main course

Whenever we right click to view a context menu on our page, an event is triggered. The event name is (you guessed it right) contextmenu. Crazy right? Not so much.
The first thing we'll do is to listen for this event, then prevent the default behaviour from triggering.

window.addEventListener("contextmenu", e => {
  e.preventDefault();
});

Enter fullscreen mode Exit fullscreen mode

Here we are listening for a contextmenu event on the whole window, but you can always specify a specific area rather than the whole window.

Now that we've cleared that up, we'll set up the display of our custom context menu right there in the event callback. We are going to be making use of two properties on the event object to determine where we'll display our menu. It's going to be the area where the user's click originated.

Event screenshoot

The pageX and pageY property of the event callback describes the position of the mouse when the request for a context menu was triggered. You'll want to place your context menu at that point. Let's update our js code to reflect these changes.

const toggleMenu = command => {
  menu.style.display = command === "show" ? "block" : "none";
};

const setPosition = ({ top, left }) => {
  menu.style.left = `${left}px`;
  menu.style.top = `${top}px`;
  toggleMenu('show');
};


window.addEventListener("contextmenu", e => {
  e.preventDefault();
  const origin = {
    left: e.pageX,
    top: e.pageY
  };
  setPosition(origin);
  return false;
});
Enter fullscreen mode Exit fullscreen mode

The toggleMenu function toggles the display of the menu and the setPosition function sets the position of the menu. The function names are a really self-explanatory.

By now we should have a fully functional context menu but there's the issue of our menu not going away, it's just there. So we'll have to set up another event listener to hide our context menu if it is visible. To do this, we'll listen for a click event on the window or area where you've setup your custom context menu.

const menu = document.querySelector(".menu");
let menuVisible = false;

const toggleMenu = command => {
  menu.style.display = command === "show" ? "block" : "none";
  menuVisible = !menuVisible;
};

const setPosition = ({ top, left }) => {
  menu.style.left = `${left}px`;
  menu.style.top = `${top}px`;
  toggleMenu("show");
};

window.addEventListener("click", e => {
  if(menuVisible)toggleMenu("hide");
});

window.addEventListener("contextmenu", e => {
  e.preventDefault();
  const origin = {
    left: e.pageX,
    top: e.pageY
  };
  setPosition(origin);
  return false;
});
Enter fullscreen mode Exit fullscreen mode

There's the final look of our js file. We have a working context menu but as usual there are still a few enhancements we'll need for our context menu to be real classy.

  1. How to position the menu when the click is very close to the bottom or top of the window.
  2. Adding a few animations to smoothen the entry and exit of our menu.

It'll take just a few lines of code to achieve this. I believe you can figure it out. Check out the codepen for the full code base. Hit me up on twitter for any suggestions and issues @iamAfro

Top comments (13)

Collapse
 
kosm profile image
Kos-M

pretty cool post nice !
in addition if someone wants context triggered only on certain element :

window.addEventListener("contextmenu", e => {
    const origin = {
        left: e.pageX,
        top: e.pageY
    };
    var targetElement = event.target
    if (targetElement.nodeName === "TD") {
        e.preventDefault();
        console.log(targetElement)
        setPosition(origin);
    }

    return false;
});
Collapse
 
yannik_sc profile image
Yannik_Sc

The more interesting is: how to extend the existing context menu.

Custom is always bad, it hides the browser's one, looks sometimes odd, if you have a custom OS theme or something like that and it often does not behave, like you would expect it (from the normal one).
This is at least my thinking of it, and there might be more reasons why a custom context menu is bad.

Collapse
 
nexthazard profile image
Jared Richards • Edited

Any Advice to not make the ContextMenu not deform the Page such as Crating a Space over all content so when right clicking the page does not get deformed or not expand (Scrollbars.

Awesome Tut Thx :)

Collapse
 
iamafro profile image
Richard Umoffia

To achieve that Jared, you'll have to set the menu's position to fixed or absolute. I just realised I had set it to relative in the tut. Setting the position to fixed or absolute should solve that. I'm glad you enjoyed the article. Thanks  😇

Collapse
 
humoyun profile image
Humoyun Ahmad

Exactly!

Collapse
 
pbkarlsson profile image
Philip Karlsson

Cool, I've always thought that it's super-complicated and that's why so few does it. I stand corrected. Thanks :)

Collapse
 
iamafro profile image
Richard Umoffia

I thought so too until I decided to try it. Glad you liked it.

Collapse
 
neurogirl47 profile image
Udochi Okeke

This is the best explanation on this topic I have seen! Thanks!

Collapse
 
ahmadsuleiman profile image
ahmadsuleiman

how to add sub list to list item?

Collapse
 
abishekaasaari profile image
Abishek Aasaari

But when we click near the edge of the screen the context menu is hidden by the edge of the screen. Is there any solution for that ?

Collapse
 
midnightlead profile image
Ivan O

Also, here is a problem with the menu if you call to at page's bottom and/or right.
setPosition should be more complex. Consider to page and menu size.

Collapse
 
allaboutcode profile image
All-About-Code

Does not work for me on Opra,Chrome, and Firefox!

Does not work!

Collapse
 
chandan11krish1 profile image
chandan krishna

i'm getting some problem when right button is clicked the margin at the top is extending with blank space...
So please anyone suggest solution for the problem...
email : chandan11krishna@gmail.com