Making custom right-click context menus for my web-app

I’ve a few websites like google-docs and map-quest that have custom drop down menus when you right-click. Somehow they override the browser’s behavior of drop-down menu, and I’m now sure exactly how they do it. I found a jQuery plugin that does this, but I’m still curious about a few things:

  • How does this work? Is the browser’s drop-down menu actually being overridden, or is the effect just simulated? If so, how?
  • What does the plugin abstract away? What’s going on behind the scenes?
  • Is this the only way of achieving this effect?

custom context menu image

  • jQuery ContextMenu event not working in IOS 8.2
  • invoke primefaces context menu from java script
  • Custom Context Menu with Javascript?
  • Issues with Content Menu and Opening Tabs - Chrome Extension
  • Is it possible to use a variable as a function name
  • jstree 3.0.0 contextmenu right click not working. Showing TypeError: vakata_context.element.html is not a function
  • See several custom-context menus in action

  • Trigger right-click
  • Adding keyboard shortcut to context menu for Google Chrome extensions?
  • Modify more than 1 css file with javascript
  • Enabling jQuery contextMenu item on ajax request
  • jQuery ContextMenu event not working in IOS 8.2
  • Disable the context menu in chrome apps?
  • 7 Solutions collect form web for “Making custom right-click context menus for my web-app”

    I know this question is very old, but just came up with the same problem and solved it myself, so I’m answering in case anyone finds this through google as I did. I based my solution on @Andrew’s one, but basically modified everything afterwards.

    EDIT: seeing how popular this has been lately, I decided to update also the styles to make it look more like 2014 and less like windows 95. I fixed the bugs @Quantico and @Trengot spotted so now it’s a more solid answer.

    EDIT 2: I set it up with StackSnippets as they’re a really cool new feature. I leave the good jsfiddle here for reference thought (click on the 4th panel to see them work).

    New Stack Snippet:

    // JAVASCRIPT (jQuery)
    
    // Trigger action when the contexmenu is about to be shown
    $(document).bind("contextmenu", function (event) {
        
        // Avoid the real one
        event.preventDefault();
        
        // Show contextmenu
        $(".custom-menu").finish().toggle(100).
        
        // In the right position (the mouse)
        css({
            top: event.pageY + "px",
            left: event.pageX + "px"
        });
    });
    
    
    // If the document is clicked somewhere
    $(document).bind("mousedown", function (e) {
        
        // If the clicked element is not the menu
        if (!$(e.target).parents(".custom-menu").length > 0) {
            
            // Hide it
            $(".custom-menu").hide(100);
        }
    });
    
    
    // If the menu element is clicked
    $(".custom-menu li").click(function(){
        
        // This is the triggered action name
        switch($(this).attr("data-action")) {
            
            // A case for each action. Your actions here
            case "first": alert("first"); break;
            case "second": alert("second"); break;
            case "third": alert("third"); break;
        }
      
        // Hide it AFTER the action was triggered
        $(".custom-menu").hide(100);
      });
    /* CSS3 */
    
    /* The whole thing */
    .custom-menu {
        display: none;
        z-index: 1000;
        position: absolute;
        overflow: hidden;
        border: 1px solid #CCC;
        white-space: nowrap;
        font-family: sans-serif;
        background: #FFF;
        color: #333;
        border-radius: 5px;
        padding: 0;
    }
    
    /* Each of the items in the list */
    .custom-menu li {
        padding: 8px 12px;
        cursor: pointer;
        list-style-type: none;
        transition: all .3s ease;
        user-select: none;
    }
    
    .custom-menu li:hover {
        background-color: #DEF;
    }
    <!-- HTML -->
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.js"></script>
    
    <ul class='custom-menu'>
      <li data-action="first">First thing</li>
      <li data-action="second">Second thing</li>
      <li data-action="third">Third thing</li>
    </ul>
    
    <!-- Not needed, only for making it clickable on StackOverflow -->
    Right click me

    As Adrian said, the plugins are going to work the same way. There are three basic parts you’re going to need:

    1: Event handler for 'contextmenu' event:

    $(document).bind("contextmenu", function(event) {
        event.preventDefault();
        $("<div class='custom-menu'>Custom menu</div>")
            .appendTo("body")
            .css({top: event.pageY + "px", left: event.pageX + "px"});
    });
    

    Here, you could bind the event handler to any selector that you want to show a menu for. I’ve chosen the entire document.

    2: Event handler for 'click' event (to close the custom menu):

    $(document).bind("click", function(event) {
        $("div.custom-menu").hide();
    });
    

    3: CSS to control the position of the menu:

    .custom-menu {
        z-index:1000;
        position: absolute;
        background-color:#C0C0C0;
        border: 1px solid black;
        padding: 2px;
    }
    

    The important thing with the CSS is to include the z-index and position: absolute

    It wouldn’t be too tough to wrap all of this in a slick jQuery plugin.

    You can see a simple demo here: http://jsfiddle.net/andrewwhitaker/fELma/

    <!DOCTYPE html>
    <html>
    <head>
        <title>Right Click</title>
    
        <link href="https://swisnl.github.io/jQuery-contextMenu/dist/jquery.contextMenu.css" rel="stylesheet" type="text/css" />
    
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
        <script src="https://swisnl.github.io/jQuery-contextMenu/dist/jquery.contextMenu.js" type="text/javascript"></script>
    
        <script src="https://swisnl.github.io/jQuery-contextMenu/dist/jquery.ui.position.min.js" type="text/javascript"></script>
    
    </head>
    <body>
        <span class="context-menu-one" style="border:solid 1px black; padding:5px;">Right Click Me</span>
        <script type="text/javascript">
            
            $(function() {
            $.contextMenu({
                selector: '.context-menu-one', 
                callback: function(key, options) {
                    var m = "clicked: " + key;
                    window.console && console.log(m) || alert(m); 
                },
                items: {
                    "edit": {name: "Edit", icon: "edit"},
                    "cut": {name: "Cut", icon: "cut"},
                   copy: {name: "Copy", icon: "copy"},
                    "paste": {name: "Paste", icon: "paste"},
                    "delete": {name: "Delete", icon: "delete"},
                    "sep1": "---------",
                    "quit": {name: "Quit", icon: function(){
                        return 'context-menu-icon context-menu-icon-quit';
                    }}
                }
            });
    
            $('.context-menu-one').on('click', function(e){
                console.log('clicked', this);
            })    
        });
        </script>
    </body>
    </html>

    The browser’s context menu is being overridden. There is no way to augment the native context menu in any major browser.

    Since the plugin is creating its own menu, the only part thats really being abstracted is the browser’s context menu event. The plugin creates an html menu based on your configuration, then places that content at the location of your click.

    Yes, this is the only way to go about creating a custom context menu. Obviously, different plugins do things slightly different, but they will all override the browser’s event and place their own html-based menu in the correct place.

    You can watch this tutorial: http://www.youtube.com/watch?v=iDyEfKWCzhg
    Make sure the context menu is hidden at first and has a position of absolute. This will
    ensure that there won’t be multiple context menu and useless creation of context menu. The link to the page is placed in the description of the YouTube video.

    $(document).bind("contextmenu", function(event){
    $("#contextmenu").css({"top": event.pageY +  "px", "left": event.pageX +  "px"}).show();
    });
    $(document).bind("click", function(){
    $("#contextmenu").hide();
    });
    

    here is an example for right click context menu in javascript:
    Right Click Context Menu

    Used raw javasScript Code for context menu functionality. Can you please check this, hope this will help you.

    Live Code:

    (function() {
      
      "use strict";
    
    
      /*********************************************** Context Menu Function Only ********************************/
      function clickInsideElement( e, className ) {
        var el = e.srcElement || e.target;
        if ( el.classList.contains(className) ) {
          return el;
        } else {
          while ( el = el.parentNode ) {
            if ( el.classList && el.classList.contains(className) ) {
              return el;
            }
          }
        }
        return false;
      }
    
      function getPosition(e) {
        var posx = 0, posy = 0;
        if (!e) var e = window.event;
        if (e.pageX || e.pageY) {
          posx = e.pageX;
          posy = e.pageY;
        } else if (e.clientX || e.clientY) {
          posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
          posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
        }
        return {
          x: posx,
          y: posy
        }
      }
    
      // Your Menu Class Name
      var taskItemClassName = "thumb";
      var contextMenuClassName = "context-menu",contextMenuItemClassName = "context-menu__item",contextMenuLinkClassName = "context-menu__link", contextMenuActive = "context-menu--active";
      var taskItemInContext, clickCoords, clickCoordsX, clickCoordsY, menu = document.querySelector("#context-menu"), menuItems = menu.querySelectorAll(".context-menu__item");
      var menuState = 0, menuWidth, menuHeight, menuPosition, menuPositionX, menuPositionY, windowWidth, windowHeight;
    
      function initMenuFunction() {
        contextListener();
        clickListener();
        keyupListener();
        resizeListener();
      }
    
      /**
       * Listens for contextmenu events.
       */
      function contextListener() {
        document.addEventListener( "contextmenu", function(e) {
          taskItemInContext = clickInsideElement( e, taskItemClassName );
    
          if ( taskItemInContext ) {
            e.preventDefault();
            toggleMenuOn();
            positionMenu(e);
          } else {
            taskItemInContext = null;
            toggleMenuOff();
          }
        });
      }
    
      /**
       * Listens for click events.
       */
      function clickListener() {
        document.addEventListener( "click", function(e) {
          var clickeElIsLink = clickInsideElement( e, contextMenuLinkClassName );
    
          if ( clickeElIsLink ) {
            e.preventDefault();
            menuItemListener( clickeElIsLink );
          } else {
            var button = e.which || e.button;
            if ( button === 1 ) {
              toggleMenuOff();
            }
          }
        });
      }
    
      /**
       * Listens for keyup events.
       */
      function keyupListener() {
        window.onkeyup = function(e) {
          if ( e.keyCode === 27 ) {
            toggleMenuOff();
          }
        }
      }
    
      /**
       * Window resize event listener
       */
      function resizeListener() {
        window.onresize = function(e) {
          toggleMenuOff();
        };
      }
    
      /**
       * Turns the custom context menu on.
       */
      function toggleMenuOn() {
        if ( menuState !== 1 ) {
          menuState = 1;
          menu.classList.add( contextMenuActive );
        }
      }
    
      /**
       * Turns the custom context menu off.
       */
      function toggleMenuOff() {
        if ( menuState !== 0 ) {
          menuState = 0;
          menu.classList.remove( contextMenuActive );
        }
      }
    
      function positionMenu(e) {
        clickCoords = getPosition(e);
        clickCoordsX = clickCoords.x;
        clickCoordsY = clickCoords.y;
        menuWidth = menu.offsetWidth + 4;
        menuHeight = menu.offsetHeight + 4;
    
        windowWidth = window.innerWidth;
        windowHeight = window.innerHeight;
    
        if ( (windowWidth - clickCoordsX) < menuWidth ) {
          menu.style.left = (windowWidth - menuWidth)-0 + "px";
        } else {
          menu.style.left = clickCoordsX-0 + "px";
        }
    
        // menu.style.top = clickCoordsY + "px";
    
        if ( Math.abs(windowHeight - clickCoordsY) < menuHeight ) {
          menu.style.top = (windowHeight - menuHeight)-0 + "px";
        } else {
          menu.style.top = clickCoordsY-0 + "px";
        }
      }
    
    
      function menuItemListener( link ) {
        var menuSelectedPhotoId = taskItemInContext.getAttribute("data-id");
        console.log('Your Selected Photo: '+menuSelectedPhotoId)
        var moveToAlbumSelectedId = link.getAttribute("data-action");
        if(moveToAlbumSelectedId == 'remove'){
          console.log('You Clicked the remove button')
        }else if(moveToAlbumSelectedId && moveToAlbumSelectedId.length > 7){
          console.log('Clicked Album Name: '+moveToAlbumSelectedId);
        }
        toggleMenuOff();
      }
      initMenuFunction();
    
    })();
    /* For Body Padding and content */
    body { padding-top: 70px; }
    li a { text-decoration: none !important; }
    
    /* Thumbnail only */
    .thumb {
      margin-bottom: 30px;
    }
    .thumb:hover a, .thumb:active a, .thumb:focus a {
      border: 1px solid purple;
    }
    
    /************** For Context menu ***********/
    /* context menu */
    .context-menu {  display: none;  position: absolute;  z-index: 9999;  padding: 12px 0;  width: 200px;  background-color: #fff;  border: solid 1px #dfdfdf;  box-shadow: 1px 1px 2px #cfcfcf;  }
    .context-menu--active {  display: block;  }
    
    .context-menu__items { list-style: none;  margin: 0;  padding: 0;  }
    .context-menu__item { display: block;  margin-bottom: 4px;  }
    .context-menu__item:last-child {  margin-bottom: 0;  }
    .context-menu__link {  display: block;  padding: 4px 12px;  color: #0066aa;  text-decoration: none;  }
    .context-menu__link:hover {  color: #fff;  background-color: #0066aa;  }
    .context-menu__items ul {  position: absolute;  white-space: nowrap;  z-index: 1;  left: -99999em;}
    .context-menu__items > li:hover > ul {  left: auto;  padding-top: 5px  ;  min-width: 100%;  }
    .context-menu__items > li li ul {  border-left:1px solid #fff;}
    .context-menu__items > li li:hover > ul {  left: 100%;  top: -1px;  }
    .context-menu__item ul { background-color: #ffffff; padding: 7px 11px;  list-style-type: none;  text-decoration: none; margin-left: 40px; }
    .page-media .context-menu__items ul li { display: block; }
    /************** For Context menu ***********/
    <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
    <body>
    
    
    
        <!-- Page Content -->
        <div class="container">
    
            <div class="row">
    
                <div class="col-lg-12">
                    <h1 class="page-header">Thumbnail Gallery <small>(Right click to see the context menu)</small></h1>
                </div>
    
                <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                    <a class="thumbnail" href="#">
                        <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                    </a>
                </div>
                <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                    <a class="thumbnail" href="#">
                        <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                    </a>
                </div>
                <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                    <a class="thumbnail" href="#">
                        <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                    </a>
                </div>
                <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                    <a class="thumbnail" href="#">
                        <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                    </a>
                </div>
                <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                    <a class="thumbnail" href="#">
                        <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                    </a>
                </div>
                <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                    <a class="thumbnail" href="#">
                        <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                    </a>
                </div>
                <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                    <a class="thumbnail" href="#">
                        <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                    </a>
                </div>
                <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                    <a class="thumbnail" href="#">
                        <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                    </a>
                </div>
    
            </div>
    
            <hr>
    
    
        </div>
        <!-- /.container -->
    
    
        <!-- / The Context Menu -->
        <nav id="context-menu" class="context-menu">
            <ul class="context-menu__items">
                <li class="context-menu__item">
                    <a href="#" class="context-menu__link" data-action="Delete This Photo"><i class="fa fa-empire"></i> Delete This Photo</a>
                </li>
                <li class="context-menu__item">
                    <a href="#" class="context-menu__link" data-action="Photo Option 2"><i class="fa fa-envira"></i> Photo Option 2</a>
                </li>
                <li class="context-menu__item">
                    <a href="#" class="context-menu__link" data-action="Photo Option 3"><i class="fa fa-first-order"></i> Photo Option 3</a>
                </li>
                <li class="context-menu__item">
                    <a href="#" class="context-menu__link" data-action="Photo Option 4"><i class="fa fa-gitlab"></i> Photo Option 4</a>
                </li>
                <li class="context-menu__item">
                    <a href="#" class="context-menu__link" data-action="Photo Option 5"><i class="fa fa-ioxhost"></i> Photo Option 5</a>
                </li>
                <li class="context-menu__item">
                    <a href="#" class="context-menu__link"><i class="fa fa-arrow-right"></i> Add Photo to</a>
                    <ul>
                        <li><a href="#!" class="context-menu__link" data-action="album-one"><i class="fa fa-camera-retro"></i> Album One</a></li>
                        <li><a href="#!" class="context-menu__link" data-action="album-two"><i class="fa fa-camera-retro"></i> Album Two</a></li>
                        <li><a href="#!" class="context-menu__link" data-action="album-three"><i class="fa fa-camera-retro"></i> Album Three</a></li>
                        <li><a href="#!" class="context-menu__link" data-action="album-four"><i class="fa fa-camera-retro"></i> Album Four</a></li>
                    </ul>
                </li>
            </ul>
        </nav>
    
        <!-- End # Context Menu -->
    
    
    </body>

    I know that this is rather old also. I recently had a need to create a context menu that I inject into other sites that have different properties based n the element clicked.

    It’s rather rough, and there are probable better ways to achieve this.
    It uses the jQuery Context menu Library Located Here

    I enjoyed creating it and though that you guys might have some use out of it.

    Here is the fiddle. I hope that it can hopefully help someone out there.

    $(function() {
      function createSomeMenu() {
        var all_array = '{';
        var x = event.clientX,
          y = event.clientY,
          elementMouseIsOver = document.elementFromPoint(x, y);
        if (elementMouseIsOver.closest('a')) {
          all_array += '"Link-Fold": {"name": "Link", "icon": "fa-external-link", "items": {"fold2-key1": {"name": "Open Site in New Tab"}, "fold2-key2": {"name": "Open Site in Split Tab"}, "fold2-key3": {"name": "Copy URL"}}},';
        }
        if (elementMouseIsOver.closest('img')) {
          all_array += '"Image-Fold": {"name": "Image","icon": "fa-picture-o","items": {"fold1-key1": {"name":"Download Image"},"fold1-key2": {"name": "Copy Image Location"},"fold1-key3": {"name": "Go To Image"}}},';
        }
        all_array += '"copy": {"name": "Copy","icon": "copy"},"paste": {"name": "Paste","icon": "paste"},"edit": {"name": "Edit HTML","icon": "fa-code"}}';
        return JSON.parse(all_array);
      }
    
      // setup context menu
      $.contextMenu({
        selector: 'body',
        build: function($trigger, e) {
          return {
            callback: function(key, options) {
              var m = "clicked: " + key;
              console.log(m);
            },
            items: createSomeMenu()
          };
        }
      });
    });