DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 963,673 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for Simple way to add custom context menus to web pages.
Andrey Germanov
Andrey Germanov

Posted on • Updated on

Simple way to add custom context menus to web pages.

In this tutorial, I'll explain how to add context menus to elements of a web page using my newly developed component. This menu can appear when a user right-clicks on an element of a web page or just puts the mouse cursor on that element. This is an example of how the menus, created using this component work in practice.

Demo

In the next sections I will show how to setup the menu in basic mode and then customize it.

Contents

Install
Create basic menu
React to user actions
Add images to menu items
Styling the menu
Menu API
Conclusion

Install

To include the component to your web page, download context_menu.umd.cjs script and include it to your web page:

<script src="context_menu.umd.cjs"></script>
Enter fullscreen mode Exit fullscreen mode

Also, if you use some JavaScript framework, you can install the component using NPM:

npm install simple_js_menu
Enter fullscreen mode Exit fullscreen mode

and then import Menus factory object to your JavaScript code:

import {Menus} from "simple_js_menu";
Enter fullscreen mode Exit fullscreen mode

Create basic menu

When you install the component using one of the methods described above, you get a Menus factory object that you can use to create context menus and connect them to objects on your web page. Let's assume that we have some <div> element on a web page and want to have a context menu that appears when the user right-clicks on it. Next example shows how to implement this:

<html>
<head>
    <title>Context Menu Demo</title>
    <script src="https://code.germanov.dev/context_menu/context_menu.umd.cjs"></script>
</head>
<body>
<div id="myDiv">My element</div>

<script>
    // obtain a link to the element 
    const div = document.getElementById("myDiv");
    // define menu items
    const items = [
        {id:"item1", title: "Item 1"},
        {id:"item2", title: "Item 2"},
        {id:"item3", title: "Item 3"}
    ];
    // create menu with specified items
    // and bind it to specified DIV element
    const menu = Menus.create(items,div);
</script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

When you open this page, there will be "My element" text inside the DIV. If you right-click on it, you'll see the context menu with three items, as displayed below:

Image description

This is how it basically works. To initialize menu, we executed the following code:

const menu = Menus.create(items,div)
Enter fullscreen mode Exit fullscreen mode

Menus.create method receives items and div arguments, constructs a menu and returns the menu object.

  • items argument defines an array of menu items. Each item is an object with 3 fields: id - unique ID of menu item, title - the text inside menu item, image - URL of image which appears on the left side of menu item.
  • div argument defines an HTML element to which this menu connects. It can be any HTML element, not only a div.

Also, the create method has the third optional argument eventName which defines an event which should happen with div to display the menu. By default eventName equals to contextmenu, which shows a menu when the user right-clicks on the div, but you can change it to show a menu when the user hovers over the element, for example. The standard Javascript mouse events are supported (mouseover, mousedown, mouseup, mousemove, click and so on). For example, this code will show menu, if user moves cursor to the area of the element:

const menu = Menus.create(items,div,'mouseover')
Enter fullscreen mode Exit fullscreen mode

So, using Menus.create we created a basic context menu and received a 'menu' object. In the next sections I will show how you can extend features of the menu using that menu object.

React to user actions

Obviously, the next step is to perform some actions when the user selects items from the menu. In most cases, it means that we have to reply to click events. You can, however, react to any events that the user can initiate using the mouse. To react to user actions with the menu use the menu.on(eventName,handler) method where eventName is an event, to which you want to react. For example you can add to the previous code:

    menu.on('click', (event) => {
        switch (event.itemId) {
            case "item1":
                console.log("User clicked first item");
                console.log("Mouse cursor position X:",
                    event.cursorX,"Y:",event.cursorY);
                break;
            case "item2":
                console.log("User clicked second item");
                console.log("Mouse cursor position X:",
                    event.cursorX,"Y:",event.cursorY);
                break;
            case "item3":
                console.log("User clicked third item");
                console.log("Mouse cursor position X:",
                    event.cursorX,"Y:",event.cursorY);
                break;
        }
    })
Enter fullscreen mode Exit fullscreen mode

Then, when you run and click any of the menu items, you'll see similar lines in the Javascript console:

User clicked first item Mouse cursor position X: 78 Y: 17
User clicked second item Mouse cursor position X: 68 Y: 16
User clicked third item Mouse cursor position X: 64 Y: 18
User clicked third item Mouse cursor position X: 13 Y: 16
Enter fullscreen mode Exit fullscreen mode

Let's discuss the on method more. Basically, it works the same as the 'on' method in JQuery. It receives as arguments a JavaScript event name and a callback function that will be called when this event occurs on the menu object. The callback function receives an event argument that can be used to discover the properties of the event that triggered. This is an MouseEvent object extending by the following properties:

  • itemId - ID of menu item that triggered this event
  • cursorX - the X position of the mouse cursor when the user displays the menu
  • cursorY - the Y position of the mouse cursor when the user displays the menu

So, this way using a single method, you can process clicks from all menu items. Furthermore you can react not only to clicks, but to other mouse events: mouseover,mouseout,dblclick,mousedown,mouseup,mousemove:

menu.on('mouseover', (event) => {
...
}

menu.on('mouseout', (event) => {
...
}

menu.on('mousedown', (event) => {
...
}

Enter fullscreen mode Exit fullscreen mode

Add images to menu items

Menu items may have images. To add an image to each item, need to specify the 'image' field for these items when defining the items array. Let's redefine items array this way:

    const items = [
        {
            id:"item1",
            title: "Batman",
            image:"https://code.germanov.dev/context_menu/assets/batman.svg"
        },
        {
            id:"item2",
            title: "Hacker",
            image:"https://code.germanov.dev/context_menu/assets/hacker.svg"
        },
        {
            id:"item3",
            title: "Santa",
            image:"https://code.germanov.dev/context_menu/assets/santa.svg"
        }
    ];
Enter fullscreen mode Exit fullscreen mode

After run the previous menu example with menu items defined this way you will see this:

Image description

You need not worry about the size of images, because they will be automatically resized to a height of text and aligned properly.

Styling the menu

By default the menu is a gray panel with a gray border. Menu items are displayed as transparent DIVs with black text in the default font and optional images. However, you can redefine the styling by providing your own CSS class to any or all of these parts using the following methods:

  • menu.setPanelClass(className) - override CSS class for menu panel DIV
  • menu.setItemClass(className, id) - override CSS class for menu items DIVs. If id parameter specified, then override CSS class only for item with specified ID.
  • menu.setTextClass(className, id) - override CSS class for text of menu items DIVs. If id parameter specified, then override CSS class only for text of item with specified ID.
  • menu.setImageClass(className, id) - override CSS class for image of menu items DIVs. If id parameter specified, then override CSS class only for image of item with specified ID.

Let's add some CSS classes to redesign the menu:

.panel {
    background-color: #454d55;
    border-width:0;
    color: white;
    box-shadow: 5px 5px #88888855;
    border-radius: 10px;
}
.item {
    border-bottom-style: outset;
    padding:3px;
    border-bottom-width: 1px;
    font-weight:bold;
    font-size:15px;
    text-transform: uppercase;
}
.item:hover {
    background-color: yellow;
    color: black;
}

.last_item {
    padding:3px;
    border-bottom-width: 0;
    font-weight:bold;
    font-size:15px;
    text-transform: uppercase;
}

.last_item:hover {
    background-color: yellow;
    color: black;
}
Enter fullscreen mode Exit fullscreen mode

Then, let's apply these styles to various parts of the menu:

// Set class for menu panel
menu.setPanelClass("panel");
// Set class for all menu items
menu.setItemClass("item");
// Set specific class for last menu item (remove bottom border)
menu.setItemClass("last_item","item3");
// Reset default styles of text to use text color from panel
menu.setTextClass(" ");
Enter fullscreen mode Exit fullscreen mode

If you apply these styles and add this Javascript code to the previous menu, you'll get the following result:

Image description

Notice that I also added :hover styles for items, because it's required to show selected item status when the mouse enters the menu item.

Menu API

In addition to the basic features, described above, the Menu object has other helpful properties and methods. For example, the panel property contains a generated menu panel HTML node. Method .setId(id) can be used to set ID for this HTML node. The method .addItem(id,title,image) can be used to add new items to a menu. Method .removeItem(id) allows to remove menu item with specified ID.

You can find full description of all properties and methods of Menu object in the API documentation.

Conclusion

There are many wonderful UX libraries like MaterialUI, Ant Design or JQuery UI that contains a lot of widgets, including menus. However, if you integrate one of them to your project, it could increase the size of your site up to several megabytes. Also, you can face integration problems between CSS styles of your site and CSS styles of the library. So, if you have a simple project and if you only need a context menu, you can use my new component, and I would be happy if you found this tutorial useful.

The idea for creating this menu component came from Aneta ChwaΕ‚a. She is a frontend developer, the founder of Rock JavaScript Facebook group where I have the honor of being a group expert: https://www.facebook.com/groups/251411400345198

Aneta hosts a YouTube channel where you can learn JavaScript and other frontend technologies:

https://www.youtube.com/c/RockJavaScript.

The full source code for this tutorial you can find here:

https://github.com/AndreyGermanov/context_menu/blob/main/tests/demo_prod.html

GitHub repository of this component: https://github.com/AndreyGermanov/context_menu

Context menu component on NPM: https://www.npmjs.com/package/simple_js_menu

Try these context menus in SmartShape Studio: https://shapes.germanov.dev . Here you can use context menus to work with vector shapes, as shown in the animation at the beginning of this article.

Feel free to connect and follow me on social networks where I publish announcements about my upcoming software, articles, similar to this one and other software development news:

LinkedIn: https://www.linkedin.com/in/andrey-germanov-dev/
Facebook: https://web.facebook.com/AndreyGermanovDev
Twitter: https://twitter.com/GermanovDev

My online services website: https://germanov.dev

Happy coding guys!

Top comments (1)

Collapse
 
fruntend profile image
fruntend

Π‘ongratulations πŸ₯³! Your article hit the top posts for the week - dev.to/fruntend/top-10-posts-for-f...
Keep it up 🫰

Classic DEV Post from 2020:

js visualized

πŸš€βš™οΈ JavaScript Visualized: the JavaScript Engine

As JavaScript devs, we usually don't have to deal with compilers ourselves. However, it's definitely good to know the basics of the JavaScript engine and see how it handles our human-friendly JS code, and turns it into something machines understand! πŸ₯³

Happy coding!