Faire des menus contextuels personnalisés avec le bouton droit de la souris pour mon application Web

J'ai quelques sites Web comme google-docs et map-quest qui ont des menus déroulants personnalisés lorsque vous cliquez avec le bouton droit de la souris. D'une manière ou d'une autre, ils remplacent le menu déroulant du menu déroulant du navigateur, et je suis sûr maintenant de savoir comment ils le font. J'ai trouvé un plugin jQuery qui fait cela, mais je suis toujours curieux de quelques choses:

  • Comment cela marche-t-il? Est-ce que le menu déroulant du navigateur est effectivement surchargé, ou l'effet est-il simplement simulé? Si c'est le cas, comment?
  • Qu'est-ce que le plugin abstrait? Que se passe-t-il dans les coulisses?
  • Est-ce le seul moyen d'atteindre cet effet?

Image du menu contextuel personnalisé

Voir plusieurs menus contextuels en action

Je sais que cette question est très ancienne, mais je viens de résoudre le même problème et l'ai résolu moi-même, alors je réponds au cas où quelqu'un trouverait cela via Google comme je l'ai fait. J'ai basé ma solution sur @ Andrew, mais j'ai finalement modifié tout après.

EDIT : en voyant combien il a été populaire ces derniers temps, j'ai décidé de mettre à jour aussi les styles pour que cela ressemble plus à 2014 et moins à Windows 95. J'ai réparé les bugs @Quantico et @Trengot repérés alors maintenant, c'est une réponse plus solide.

EDIT 2 : Je l'ai configuré avec StackSnippets car ils sont une nouvelle fonctionnalité vraiment cool. Je laisse le bon jsfiddle ici pour la pensée de référence (cliquez sur le 4ème panneau pour les voir fonctionner).

Nouvel extrait de pile:

// 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 

Comme l'a dit Adrian, les plugins fonctionnent de la même manière. Il y a trois éléments de base dont vous aurez besoin:

1: 'contextmenu' événements 'contextmenu' événement 'contextmenu' :

 $(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"}); }); 

Ici, vous pouvez lier le gestionnaire d'événements à n'importe quel sélecteur pour lequel vous souhaitez afficher un menu. J'ai choisi le document entier.

2: Gestionnaire d'événements pour événement 'click' (pour fermer le menu personnalisé):

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

3: CSS pour contrôler la position du menu:

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

L'important avec le CSS est d'inclure l' z-index et la position: absolute

Il ne serait pas trop difficile d'enrouler tout cela dans un plugin lisse jQuery.

Vous pouvez voir une démonstration simple ici: 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> 

Le menu contextuel du navigateur est surpassé. Il n'y a aucun moyen d'augmenter le menu contextuel natif dans un navigateur majeur.

Comme le plugin crée son propre menu, la seule partie qui est vraiment abstraite est l'événement du menu contextuel du navigateur. Le plugin crée un menu html en fonction de votre configuration, puis place ce contenu à l'emplacement de votre clic.

Oui, c'est la seule façon de créer un menu contextuel personnalisé. De toute évidence, les différents plugins font des choses légèrement différentes, mais ils annuleront l'événement du navigateur et placeront leur propre menu basé sur html dans l'endroit correct.

Vous pouvez regarder ce tutoriel: http://www.youtube.com/watch?v=iDyEfKWCzhg Assurez-vous que le menu contextuel est caché au début et qu'il a une position absolue. Cela garantira qu'il n'y aura pas de menu contextuel multiple et de création inutile de menu contextuel. Le lien vers la page est placé dans la description de la vidéo YouTube.

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

Voici un exemple pour le menu contextuel du clic droit en javascript: Menu contextuel du clic droit

Code javasScript utilisé pour la fonctionnalité du menu contextuel. Pouvez-vous vérifier cela, espérons que cela vous aidera.

Code Live:

 (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> 

Je sais que c'est aussi vieux. J'ai récemment eu le besoin de créer un menu contextuel que je lance dans d'autres sites qui possèdent des propriétés différentes basées sur l'élément cliqué.

C'est assez brutal, et il existe de meilleures façons d'atteindre cet objectif. Il utilise le menu contextuel jQuery situé ici

J'ai bien aimé le créer et bien que vous puissiez vous en sortir.

Voici le violon . J'espère pouvoir espérer aider quelqu'un là-bas.

 $(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() }; } }); });