Erstellen von benutzerdefinierten Kontextmenüs mit Rechtsklick für meine Web-App

127

Ich habe einige Websites wie Google-Docs und Map-Quest, die benutzerdefinierte Dropdown-Menüs haben, wenn Sie mit der rechten Maustaste klicken. Irgendwie überschreiben sie das Verhalten des Browsers im Dropdown-Menü, und ich bin mir jetzt sicher, wie sie es genau tun. Ich habe ein jQuery-Plugin gefunden , das dies tut, bin aber immer noch neugierig auf einige Dinge:

  • Wie funktioniert das? Wird das Dropdown-Menü des Browsers tatsächlich überschrieben oder wird der Effekt nur simuliert? Wenn das so ist, wie?
  • Was abstrahiert das Plugin? Was ist hinter den Kulissen los?
  • Ist dies der einzige Weg, um diesen Effekt zu erzielen?

benutzerdefiniertes Kontextmenü Bild

Sehen Sie mehrere benutzerdefinierte Kontextmenüs in Aktion

Gordon Gustafson
quelle

Antworten:

219

Ich weiß, dass diese Frage sehr alt ist, habe aber das gleiche Problem gefunden und es selbst gelöst. Ich antworte also, falls jemand dies über Google findet, wie ich es getan habe. Ich habe meine Lösung auf die von @ Andrew gestützt, aber danach im Grunde alles geändert.

BEARBEITEN : Da dies in letzter Zeit sehr beliebt war, habe ich beschlossen, auch die Stile zu aktualisieren, damit es eher wie 2014 und weniger wie Windows 95 aussieht. Ich habe die Fehler @Quantico und @Trengot behoben, sodass es jetzt eine solidere Antwort ist.

EDIT 2 : Ich habe es mit StackSnippets eingerichtet, da sie eine wirklich coole neue Funktion sind. Ich lasse die gute jsfiddle hier als Referenzgedanke (klicken Sie auf das 4. Panel, um zu sehen, wie sie funktionieren).

Neues Stapel-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

Hinweis: Möglicherweise werden einige kleine Fehler angezeigt (Dropdown-Liste weit vom Cursor entfernt usw.). Stellen Sie sicher, dass dies in der jsfiddle funktioniert , da dies Ihrer Webseite ähnlicher ist als StackSnippets.

Francisco Presencia
quelle
1
Ich denke, Sie haben möglicherweise ein Problem mit dem Mousedown. Dies kann zu einer Rennbedingung führen, da das Klicken auf einen Menüpunkt einen Klick auslöst, bei dem es sich um eine Maus und eine Maus nach oben handelt.
Quantico
2
Danke @Quantico, das ist wahr und jetzt sollte es behoben werden, sowohl im Code als auch in der jsfiddle. Irgendein anderes Problem? Nebenbemerkung: Wow, 170 frühere Änderungen an der jsfiddle, es wurde sicherlich populär.
Francisco Presencia
1
Wenn Sie die neue Geige verwenden, wird das Popup transparent angezeigt, wenn Sie andere HTML-Elemente auf der Seite verwenden. BEARBEITEN: Durch Hinzufügen einer Hintergrundfarbe zu CSS wird dies behoben.
Holloway
1
Ein weiteres kleines Problem: Wenn Sie mit der rechten Maustaste auf eine Stelle klicken, während das Menü sichtbar ist, flackert es vor dem Anzeigen. Ich denke, es sollte sich entweder verstecken (wie die Standardeinstellung) oder verstecken und dann an der neuen Position erscheinen.
Holloway
@ChetanJoshi scheint laut MDN auf IE11 zu funktionieren: developer.mozilla.org/en-US/docs/Web/Events/… Sehen Sie einen Fehler?
Francisco Presencia
63

Wie Adrian sagte, werden die Plugins genauso funktionieren. Es gibt drei grundlegende Teile, die Sie benötigen werden:

1: Ereignishandler für 'contextmenu'Ereignis:

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

Hier können Sie den Ereignishandler an einen beliebigen Selektor binden, für den Sie ein Menü anzeigen möchten. Ich habe das gesamte Dokument ausgewählt.

2: Ereignishandler für 'click'Ereignis (um das benutzerdefinierte Menü zu schließen):

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

3: CSS zur Steuerung der Position des Menüs:

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

Das Wichtigste beim CSS ist, das z-indexund einzuschließenposition: absolute

Es wäre nicht allzu schwierig, all dies in ein schickes jQuery-Plugin zu packen.

Eine einfache Demo finden Sie hier: http://jsfiddle.net/andrewwhitaker/fELma/

Andrew Whitaker
quelle
Ich denke, dieses Kontextmenü wäre nützlicher, wenn es geöffnet bleiben würde, wenn der Benutzer darauf klickt (aber geschlossen, wenn der Benutzer außerhalb darauf klickt). Könnte es geändert werden, um so zu funktionieren?
Anderson Green
2
Sie würden sich das event.targetInnere der Klickbindung auf dem ansehen document. Wenn es nicht im Kontextmenü ist, verstecken Sie das Menü: jsfiddle.net/fELma/286
Andrew Whitaker
2
Ich habe es leicht modifiziert (so dass nicht mehrere Menüs gleichzeitig angezeigt werden
Anderson Green
Ich versuche, ein radiales Kontextmenü mit der rechten Maustaste zu erstellen (wie hier: push-pixels.org/wp-content/uploads/2012/07/… ). Dies ist ein guter Anfang, um zu verstehen, wie es geht, danke!
Boris
@ AndrewWhitaker Ihre Antwort besagt, dass sie auf das gesamte Dokument angewendet wird. Was ist, wenn ich möchte, dass es auf ein bestimmtes Steuerelement angewendet wird, z. B. auf eine Schaltfläche (vorausgesetzt, die ID lautet button1)?
Tk1993
8

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

Grenzenlose isa
quelle
4

hier ist ein Beispiel für die rechte Maustaste im Kontextmenü in javascript: Rechtsklick - Kontextmenü

Verwendeter roher JavasScript-Code für die Kontextmenüfunktionalität. Können Sie dies bitte überprüfen, hoffen Sie, dass dies Ihnen helfen wird.

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>

Robin Hossain
quelle
Großartige Arbeit mit Vanilla JS und einem sauberen Layout!
Kurt
3

Das Kontextmenü des Browsers wird überschrieben. Es gibt keine Möglichkeit, das native Kontextmenü in einem größeren Browser zu erweitern.

Da das Plugin ein eigenes Menü erstellt, wird nur das Kontextmenü des Browsers abstrahiert. Das Plugin erstellt basierend auf Ihrer Konfiguration ein HTML-Menü und platziert diesen Inhalt an der Stelle Ihres Klicks.

Ja, nur so können Sie ein benutzerdefiniertes Kontextmenü erstellen. Natürlich machen verschiedene Plugins etwas andere Dinge, aber sie alle überschreiben das Ereignis des Browsers und platzieren ihr eigenes HTML-basiertes Menü an der richtigen Stelle.

Adrian Gonzales
quelle
2
Nur um zu erwähnen, dass Firefox jetzt Unterstützung für das native HTML5-Kontextmenü hinzufügt (deklariert durch Markup). Es ist jetzt in Firefox 8 Beta verfügbar. ( developer.mozilla.org/en/Firefox_8_for_developers ).
poshaughnessy
2

Sie können dieses Tutorial ansehen: http://www.youtube.com/watch?v=iDyEfKWCzhg Stellen Sie sicher, dass das Kontextmenü zuerst ausgeblendet ist und eine absolute Position hat. Dadurch wird sichergestellt, dass es nicht mehrere Kontextmenüs und keine nutzlose Erstellung von Kontextmenüs gibt. Der Link zur Seite befindet sich in der Beschreibung des YouTube-Videos.

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

Ich weiß, dass das auch ziemlich alt ist. Vor kurzem musste ich ein Kontextmenü erstellen, das ich in andere Sites einfüge, die je nach angeklicktem Element andere Eigenschaften haben.

Es ist ziemlich rau und es gibt wahrscheinlich bessere Möglichkeiten, dies zu erreichen. Es verwendet das jQuery-Kontextmenü Bibliothek hier

Ich habe es genossen, es zu kreieren und obwohl ihr vielleicht etwas davon habt.

Hier ist die Geige . Ich hoffe, dass es hoffentlich jemandem da draußen helfen kann.

$(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()
      };
    }
  });
});
Cody Fidler
quelle
0

Ich habe eine schöne und einfache Implementierung mit Bootstrap wie folgt.

<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>
Alexandre Crivellaro
quelle