Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

See several custom-context menus in action

like image 963
Gordon Gustafson Avatar asked Dec 21 '10 01:12

Gordon Gustafson


People also ask

How do I create a custom right-click menu?

To make this HTML menu visible when right-clicking in the browser, we need to listen for contextmenu events. We can bind the event listener directly on the document or we can limit the area that a user can right-click to see the custom context menu. In our example, the area for right-click is the entire body element.

How do I add a custom right-click menu to my website?

To do this, we will use two properties pageX and pageY of the mouse click event which will give us the coordinates where the right button was clicked. We will hide the context menu on the mouse button click if it is already being displayed. JavaScript code: HTML.

How do you implement a context menu?

Open the app -> Java -> Package -> Mainactivity.In this step, add the code to show the ContextMenu. Whenever the app will strat make a long click on a text and display the number of options to select of them for specific purposes. Comments are added inside the code to understand the code in more detail.

How do you right click a context menu?

Right-click on the “shell” key, and then choose New > Key from the menu. Give the new key the name that you want to show up on the desktop context menu.


9 Answers

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

Note: you might see some small bugs (dropdown far from the cursor, etc), please make sure that it works in the jsfiddle, as that's more similar to your webpage than StackSnippets might be.

like image 76
Francisco Presencia Avatar answered Oct 19 '22 08:10

Francisco Presencia


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/

like image 25
Andrew Whitaker Avatar answered Oct 19 '22 09:10

Andrew Whitaker


<!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>
like image 39
Limitless isa Avatar answered Oct 19 '22 08:10

Limitless isa


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>
like image 20
Robin Hossain Avatar answered Oct 19 '22 09:10

Robin Hossain


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.

like image 25
Adrian Gonzales Avatar answered Oct 19 '22 09:10

Adrian Gonzales


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();
});
like image 37
Asif Mallik Avatar answered Oct 19 '22 08:10

Asif Mallik


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()
      };
    }
  });
});
like image 31
Cody Fidler Avatar answered Oct 19 '22 07:10

Cody Fidler


Simple One

  1. show context menu when right click anywhere in document
  2. avoid context menu hide when click inside context menu
  3. close context menu when press left mouse button

Note: dont use display:none instead use opacity to hide and show

var menu= document.querySelector('.context_menu');
document.addEventListener("contextmenu", function(e) {      
            e.preventDefault();  
            menu.style.position = 'absolute';
            menu.style.left = e.pageX + 'px';
            menu.style.top = e.pageY + 'px';        
             menu.style.opacity = 1;
        });
       
  document.addEventListener("click", function(e){
  if(e.target.closest('.context_menu'))
  return;
      menu.style.opacity = 0;
  });
.context_menu{

width:70px;
background:lightgrey;
padding:5px;
 opacity :0;
}
.context_menu div{
margin:5px;
background:grey;

}
.context_menu div:hover{
margin:5px;
background:red;
   cursor:pointer;

}
<div class="context_menu">
<div>menu 1</div>
<div>menu 2</div>
</div>

extra css

var menu= document.querySelector('.context_menu');
document.addEventListener("contextmenu", function(e) {      
            e.preventDefault();  
            menu.style.position = 'absolute';
            menu.style.left = e.pageX + 'px';
            menu.style.top = e.pageY + 'px';        
             menu.style.opacity = 1;
        });
       
  document.addEventListener("click", function(e){
  if(e.target.closest('.context_menu'))
  return;
      menu.style.opacity = 0;
  });
.context_menu{

width:120px;
background:white;
border:1px solid lightgrey;

 opacity :0;
}
.context_menu div{
padding:5px;
padding-left:15px;
margin:5px 2px;
border-bottom:1px solid lightgrey;
}
.context_menu div:last-child {
border:none;
}
.context_menu div:hover{

background:lightgrey;
   cursor:pointer;

}
<div class="context_menu">
<div>menu 1</div>
<div>menu 2</div>
<div>menu 3</div>
<div>menu 4</div>
</div>
like image 22
ßãlãjî Avatar answered Oct 19 '22 08:10

ßãlãjî


I have a nice and easy implementation using Bootstrap as follows.

<select class="custom-select" id="list" multiple></select>

<div class="dropdown-menu" id="menu-right-click" style=>
    <h6 class="dropdown-header">Actions</h6>
    <a class="dropdown-item" href="" onclick="option1();">Option 1</a>
    <a class="dropdown-item" href="" onclick="option2();">Option 2</a>
</div>

<script>
    $("#menu-right-click").hide();

    $(document).on("contextmenu", "#list", function (e) {
        $("#menu-right-click")
            .css({
                position: 'absolute',
                left: e.pageX,
                top: e.pageY,
                display: 'block'
            })
        return false;
    });

    function option1() {
        // something you want...
        $("#menu-right-click").hide();
    }

    function option2() {
        // something else 
        $("#menu-right-click").hide();
    }
</script>
like image 34
Alexandre Crivellaro Avatar answered Oct 19 '22 08:10

Alexandre Crivellaro