Verwenden Sie das Dropdown-Menü Bootstrap 3 als Kontextmenü


Antworten:


93

Es ist möglich. Ich habe dir eine funktionierende Demo gemacht, um einen guten Start zu ermöglichen.

Arbeitsdemo (Klicken Sie mit der rechten Maustaste auf eine Tabellenzeile, um sie in Aktion zu sehen.)

Erstellen Sie zuerst Ihr Dropdown-Menü, blenden Sie es aus und ändern Sie es positionin absolute:

#contextMenu {
  position: absolute;
  display:none;
}

Binden Sie dann ein contextmenuEreignis an Ihre Tabellenzeilen, sodass das Dropdown- / Kontextmenü angezeigt wird, und positionieren Sie es am Cursor:

var $contextMenu = $("#contextMenu");

$("body").on("contextmenu", "table tr", function(e) {
   $contextMenu.css({
      display: "block",
      left: e.pageX,
      top: e.pageY
   });
   return false;
});

Wenn der Benutzer eine Option zum Ausblenden des Dropdown- / Kontextmenüs auswählt:

$contextMenu.on("click", "a", function() {
   $contextMenu.hide();
});

5
+1, gute Antwort. Mein einziger Vorschlag wäre, dass normalerweise Kontextmenüs für jeden Klick irgendwo auf der Seite verschwinden. Ich habe Ihr Beispiel in meiner Antwort unten hinzugefügt.
KyleMit

danke :) Ich habe das bemerkt, aber ich habe mich entschieden, nicht mehr anzunehmen, als der OP verlangt hat, obwohl er das wahrscheinlich wollen wird
letiagoalves

1
Innerhalb des Rückrufs können Sie verwenden: var $row = $(this)oder wenn Sie nur seine ID möchten, müssen Sie kein jQuery-Objekt erstellen, tun var id = this.id
Sie

Oh, sieht so aus, als wäre es wieder da. Mein Fehler.
Abe Miessler

Wie können Sie verhindern, dass das Menü von der Seite angezeigt wird, wenn Sie rechts neben der Zeile klicken? Google Text & Tabellen zum Beispiel positioniert das Menü immer so, wie es kann. Gibt es eine Lösung für die Positionierung?
Redwall

151

Ich wollte nur die großartige Antwort von letiagoalves mit ein paar weiteren Vorschlägen verbessern .
Hier finden Sie eine exemplarische Vorgehensweise zum Hinzufügen eines Kontextmenüs zu einem beliebigen HTML-Element.

Beginnen wir mit einer funktionierenden Demo in jsFiddle

Markup:

Fügen wir zunächst ein Menü aus dem Dropdown-Steuerelement für den Bootstrap hinzu . Fügen Sie es irgendwo zu Ihrem HTML hinzu, vorzugsweise auf der Stammebene des Körpers. Die .dropdown-menuKlasse wird display:noneso eingestellt, dass sie zunächst unsichtbar ist.
Es sollte so aussehen:

<ul id="contextMenu" class="dropdown-menu" role="menu">
    <li><a tabindex="-1" href="#">Action</a></li>
    <li><a tabindex="-1" href="#">Another action</a></li>
    <li><a tabindex="-1" href="#">Something else here</a></li>
    <li class="divider"></li>
    <li><a tabindex="-1" href="#">Separated link</a></li>
</ul>

Erweiterungseinstellungen:

Um unser Design modular zu halten, fügen wir unseren JavaScript-Code als jQuery-Erweiterung mit dem Namen hinzu contextMenu.

Wenn wir aufrufen $.contextMenu, übergeben wir ein Einstellungsobjekt mit zwei Eigenschaften:

  1. menuSelector Nimmt den jQuery-Selektor des Menüs, das wir zuvor in HTML erstellt haben.
  2. menuSelected wird aufgerufen, wenn auf die Kontextmenüaktion geklickt wird.
$("#myTable").contextMenu({
    menuSelector: "#contextMenu",
    menuSelected: function (invokedOn, selectedMenu) {
        // context menu clicked
    });
});

Plugin-Vorlage:

Basierend auf der Plugin-Vorlage für das jQuery-Boilerplate verwenden wir einen sofort aufgerufenen Funktionsausdruck, damit wir den globalen Namespace nicht durcheinander bringen. Da wir Abhängigkeiten von jQuery haben und Zugriff auf das Fenster benötigen, werden wir diese als Variablen übergeben, damit wir die Minimierung überleben können. Es wird so aussehen:

(function($, window){

    $.fn.contextMenu = function(settings) {  
        return this.each(function() {  
            // Code Goes Here
        }  
    };

})(jQuery, window);

Okay, keine Klempnerarbeiten mehr. Hier ist das Fleisch der Funktion:

Ereignisse mit Rechtsklick behandeln:

Wir werden das contextmenuMausereignis für das Objekt behandeln, das die Erweiterung aufgerufen hat. Wenn das Ereignis ausgelöst wird, greifen wir auf das Dropdown-Menü zu, das wir am Anfang hinzugefügt haben. Wir finden es anhand der Auswahlzeichenfolge, die bei der Initialisierung der Funktion von den Einstellungen übergeben wurde. Wir werden das Menü folgendermaßen ändern:

  • Wir greifen nach der e.targetEigenschaft und speichern sie als Datenattribut mit dem Namen invokedOn, damit wir später das Element identifizieren können, das das Kontextmenü ausgelöst hat.
  • Wir schalten die Menüanzeige mit auf sichtbar .show()
  • Wir positionieren das Element mit .css().
    • Wir müssen sicherstellen, dass es positioneingestellt ist absolute.
    • Dann legen wir die linke und obere Position mithilfe der pageXund pageY-Eigenschaften des Ereignisses fest.
  • Um zu verhindern, dass die Rechtsklick-Aktion ein eigenes Menü öffnet, verhindern wir return false, dass das Javascript etwas anderes verarbeitet.

Es wird so aussehen:

$(this).on("contextmenu", function (e) {
    $(settings.menuSelector)
        .data("invokedOn", $(e.target))
        .show()
        .css({
            position: "absolute",
            left: e.pageX,
            top: e.pageY
        });

    return false;
});

Menükantenfälle beheben:

Dadurch wird das Menü rechts unten neben dem Cursor geöffnet, der es geöffnet hat. Befindet sich der Cursor jedoch ganz rechts auf dem Bildschirm , sollte sich das Menü ganz links öffnen. Wenn sich der Cursor unten befindet, sollte sich das Menü ebenfalls oben öffnen. Es ist auch wichtig, zwischen dem unteren Rand deswindow , der den physischen Frame enthält, und dem unteren Rand des, der documentdas gesamte HTML-DOM darstellt und weit über das Fenster hinaus scrollen kann, zu unterscheiden.

Um dies zu erreichen, legen wir den Speicherort mit den folgenden Funktionen fest:

Wir werden sie so nennen:

.css({
    left: getMenuPosition(e.clientX, 'width', 'scrollLeft'),
    top: getMenuPosition(e.clientY, 'height', 'scrollTop')
});

Was diese Funktion aufruft, um die entsprechende Position zurückzugeben:

function getMenuPosition(mouse, direction, scrollDir) {
    var win = $(window)[direction](),
        scroll = $(window)[scrollDir](),
        menu = $(settings.menuSelector)[direction](),
        position = mouse + scroll;

    // opening menu would pass the side of the page
    if (mouse + menu > win && menu < mouse) 
        position -= menu;

    return position
}

Binden Sie Klickereignisse im Menüelement:

Nachdem wir das Kontextmenü angezeigt haben, müssen wir einen Ereignishandler hinzufügen, um auf Klickereignisse zu warten. Wir entfernen alle anderen Bindungen, die möglicherweise bereits hinzugefügt wurden, damit wir nicht dasselbe Ereignis zweimal auslösen. Diese können jederzeit auftreten, wenn das Menü geöffnet wurde, aber aufgrund des Klickens nichts ausgewählt wurde. Dann können wir dem clickEreignis, in dem wir die Logik im nächsten Abschnitt behandeln , eine neue Bindung hinzufügen .

Wie valepu feststellte , möchten wir keine Klicks auf etwas anderes als Menüelemente registrieren. Daher richten wir einen delegierten Handler ein, indem wir einen Selektor an die onFunktion übergeben, die "die Nachkommen der ausgewählten Elemente filtert, die das Ereignis auslösen".

Bisher sollte die Funktion folgendermaßen aussehen:

$(settings.menuSelector)
    .off('click')
    .on( 'click', "a", function (e) {
        //CODE IN NEXT SECTION GOES HERE
});

Menüklicks behandeln

Sobald wir wissen, dass ein Klick auf das Menü aufgetreten ist, werden wir die folgenden Dinge tun: Wir werden das Menü mit auf dem Bildschirm ausblenden .hide(). Als nächstes wollen wir das Element speichern, für das das Menü ursprünglich aufgerufen wurde, sowie die Auswahl aus dem aktuellen Menü. Schließlich lösen wir die Funktionsoption aus, die an die Erweiterung übergeben wurde, indem wir sie .call()für die Eigenschaft verwenden und die Ereignisziele als Argumente übergeben.

$menu.hide();

var $invokedOn = $menu.data("invokedOn");
var $selectedMenu = $(e.target);

settings.menuSelected.call($(this), $invokedOn, $selectedMenu);

Ausblenden beim Klicken:

Schließlich möchten wir, wie bei den meisten Kontextmenüs, das Menü schließen, wenn ein Benutzer ebenfalls darauf klickt. Dazu warten wir auf Klickereignisse im Body und schließen das Kontextmenü, wenn es wie folgt geöffnet ist:

$('body').click(function () {
    $(settings.menuSelector).hide();
});

Hinweis : Dank Sadhirs Kommentar löst Firefox Linux das Klickereignisdocument bei einem Rechtsklick aus, sodass Sie den Listener einrichten müssen body.

Beispielsyntax:

Die Erweiterung wird mit dem ursprünglichen Objekt zurückgegeben, das das Kontextmenü und den angeklickten Menüpunkt ausgelöst hat. Möglicherweise müssen Sie den Dom mit jQuery durchlaufen , um aus den Ereigniszielen etwas Sinnvolles zu finden. Dies sollte jedoch eine gute Ebene der Basisfunktionalität bieten.

Hier ist ein Beispiel, um Informationen für das ausgewählte Element und die ausgewählte Aktion zurückzugeben:

$("#myTable").contextMenu({
    menuSelector: "#contextMenu",
    menuSelected: function (invokedOn, selectedMenu) {
        var msg = "You selected the menu item '" + 
                  selectedMenu.text() +
                  "' on the value '" + 
                  invokedOn.text() + "'";
        alert(msg);
    }
});

Bildschirmfoto:

Screenshot des Kontextmenüs

Update Hinweis:

Diese Antwort wurde erheblich aktualisiert, indem sie in eine jQuery-Erweiterungsmethode eingeschlossen wurde. Wenn Sie mein Original sehen möchten, können Sie den Post-Verlauf anzeigen, aber ich glaube, dass diese endgültige Version viel bessere Codierungspraktiken verwendet.

Bonus-Funktion :

Wenn Sie bei der Entwicklung von Funktionen einige nützliche Funktionen für Poweruser oder sich selbst hinzufügen möchten, können Sie das Kontextmenü umgehen, basierend auf den Tastenkombinationen, die beim Klicken mit der rechten Maustaste gedrückt werden. Wenn Sie beispielsweise zulassen möchten, dass das ursprüngliche Browser-Kontextmenü beim Halten angezeigt wird Ctrl, können Sie dies als erste Zeile des contextMenu-Handlers hinzufügen:

// return native menu if pressing control
if (e.ctrlKey) return;

1
Wie können wir verhindern, dass das Menü eine horizontale Symbolleiste erstellt, wenn Sie am Ende des rechten / linken Bildschirms klicken? Angenommen, wir haben zu nahe am rechten Ende des Bildschirms geklickt und wenn das Menü geöffnet wird, wird es links geöffnet und erkennt, dass es sich in der Nähe des Bildschirmende befindet und in eine andere Richtung geöffnet werden sollte. Mit intelligentem Hinzufügen von Pull-Left oder Pull-Right meine ich?
Django

1
@ Simon, fertig! Das Problem war, dass jedes Element, das an das contextMenuPlugin übergeben wurde, automatisch einen eigenen Klick-Handler im Menü-Popup hinzufügte. Sobald auf das Element geklickt wurde, wurde jeder Ereignishandler hintereinander ausgelöst. Um dies zu beheben, wird der Handler jetzt dynamisch zum Zeitpunkt der Behandlung des Rechtsklickereignisses hinzugefügt, sodass immer nur ein einziges Element für die Behandlung des Menüklicks verantwortlich ist.
KyleMit

1
@ KyleMit: Danke, ich klaue das für mein Projekt. Ich bemerkte jedoch ein kleines Problem. Die Berechnungen für die obere und linke Position basieren auf der Höhe und Breite des Fensters - dies ist in Ordnung, solange kein Bildlauf durchgeführt wird. Wenn Sie der Tabelle weitere Zeilen hinzufügen, damit gescrollt wird, werden Sie feststellen, dass für Zeilen, die anfänglich unten angezeigt werden, das Kontextmenü über dem Cursor angezeigt wird, auch wenn unten nach unten gescrollt ist, wenn Sie nach unten gescrollt haben ein bisschen. Das Update ist einfach - ersetzen Sie einfach $ (Fenster) .height () durch $ (Dokument) .height ()
Sadhir

1
@ Sadhir, danke für deinen Kommentar. Es sieht so documentaus, als ob es einige Probleme gibt, da das Dokument immer noch viel Platz hat, wenn es nicht nach unten gescrollt wird, sodass das Menü am unteren Rand des Fensters herunterfällt. Hier ist ein Beispiel für dieses Problem in der Geige . Es sieht so aus, als müssten Sie dies berücksichtigen $(window).scrollTop(). Ich werde meine Antwort aktualisieren.
KyleMit

2
Nochmals vielen Dank @KyleMit, das alle Probleme beim Scrollen behebt. Übrigens, in Bezug auf die Kommentare dazu, dass dies in Firefox nicht funktioniert - es scheint ein Fehler in Firefox zu sein, bei dem das Klickereignis auf ein Dokument mit der rechten Maustaste ausgelöst wird. Das Kontextmenü wird also sofort mit der rechten Maustaste ausgeblendet. Die Verwendung von $ ('body'). on ('click' .. anstelle von $ (document) .click () scheint das Problem für mich gelöst zu haben
Sadhir

8

Einige Änderungen an KyleMits Code hinzugefügt :

  • Der Handler "on document click" wurde von "foreach" verschoben.
  • "foreach" entfernt, einfach Ereignis zur Auswahl hinzufügen
  • Menü im "Dokumentkontextmenü" ausblenden
  • Ereignisse passieren

    $("#myTable tbody td").contextMenu({
    menuSelector: "#contextMenu",
    menuSelected: function (invokedOn, selectedMenu) {
        var msg = "You selected the menu item '" + selectedMenu.text() +
            "' on the value '" + invokedOn.text() + "'";
        alert(msg);
    },
    onMenuShow: function(invokedOn) {
        var tr = invokedOn.closest("tr");
        $(tr).addClass("warning");
    },
    onMenuHide: function(invokedOn) {
        var tr = invokedOn.closest("tr");
        $(tr).removeClass("warning");
    } });
    

http://jsfiddle.net/dmitry_far/cgqft4k3/


1
Tolle Ergänzungen! Ich liebe die Idee, einen visuellen Indikator hinzuzufügen onMenuShow. Beachten Sie auch, dass Sie am Ende $.fn.contextMenu = function (settings) {}aufrufen sollten, return this;da zu erwarten ist, dass jQuery-Methoden normalerweise verkettbar sind.
KyleMit

-3

Ich fand dieses einfache und funktionierende Kontextmenü. Ich verwende diese Bibliothek http://swisnl.github.io/jQuery-contextMenu/index.html . Ich hoffe es hilft

Tabelle:

<table id="ppmpsupplies" class="table table-bordered table-hover" cellspacing="0" width="100%">
          <thead>
            <tr>
              <th>Code</th>
              <th>General Description</th>
              <th>Unit</th>
              <th>Quantity</th>
              <th>Estimated Budget</th>
              <th>Mode of Procurement</th>                 

            </tr>
          </thead>
          <tbody>
            <?php foreach($items as $item){?>
            <tr>
             <td><?php echo $item->id;?></td>
             <td><?php echo $item->description;?></td>
             <td><?php echo $item->unit;?></td>
             <td><?php echo $item->quantity;?></td>
             <td><?php echo $item->budget;?></td>
             <td><?php echo $item->mode;?></td>                     
          </tr>
          <?php }?>

        </tbody>
        <tfoot>
          <td colspan="3"></td>
          <td>Total</td>
          <td></td>
        </tfoot>
      </table>

Kontextmenü:

    "edit": {
        name: "Edit",
        icon: "fa-pencil-square-o",
        callback: function(item, id) {

        return true;
        }
        },
"delete": {
        name: "Delete",
        icon: "fa-trash-o",
        callback: function(item, id) {

        return true;
        }
        },
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.