71 votes

Utiliser le menu déroulant de Bootstrap 3 comme menu contextuel

En utilisant Bootstrap 3, comment puis-je placer le menu déroulant au niveau du curseur et l'ouvrir à partir du code ?

J'ai besoin de l'utiliser sur un tableau comme un menu contextuel pour ses lignes.

0 votes

J'ai fait un plugin de menu contextuel pour bootstrap. Regardez ça github.com/sydcanem/bootstrap-contextmenu .

156voto

KyleMit Points 6937

Je voulais juste améliorer letiagoalves excellente réponse avec quelques suggestions supplémentaires.
Voici comment ajouter un menu contextuel à n'importe quel élément html.

Commençons par une démonstration fonctionnelle dans jsFiddle.

Markup :

Tout d'abord, ajoutons un menu à partir de l'onglet contrôle de liste déroulante bootstrap . Ajoutez-le n'importe où dans votre HTML, de préférence au niveau de la racine du corps. Le site .dropdown-menu va définir display:none donc c'est initialement invisible.
Il devrait ressembler à ceci :

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

Paramètres de l'extension :

Pour garder notre conception modulaire, nous ajouterons notre code JavaScript comme une extension jQuery appelée contextMenu .

Quand nous appelons $.contextMenu nous allons passer un objet de paramètres avec 2 propriétés :

  1. menuSelector prend le sélecteur jQuery du menu que nous avons créé plus tôt en HTML.
  2. menuSelected sera appelé lorsque l'action du menu contextuel sera cliquée.

    $("#myTable").contextMenu({ menuSelector: "#contextMenu", menuSelected: function (invokedOn, selectedMenu) { // context menu clicked }); });

Modèle de plugin :

Sur la base du Modèle de plugin jQuery boilerplate nous utiliserons un Expression de la fonction immédiatement déclenchée pour ne pas embrouiller l'espace de nom global. Puisque nous avons des dépendances sur jQuery et que nous avons besoin d'accéder à la fenêtre, nous allons les passer en tant que variables afin de survivre à la minification. Cela ressemblera à ceci :

(function($, window){

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

})(jQuery, window);

Ok, plus de plomberie. Voici l'essentiel de la fonction :

Gérer les événements de clic droit :

Nous allons nous occuper de la contextmenu événement souris sur l'objet qui a appelé l'extension. Lorsque l'événement se déclenche, nous allons saisir le menu déroulant que nous avons ajouté au début. Nous allons le localiser en utilisant la chaîne de sélection passée par les paramètres lorsque nous avons initialisé la fonction. Nous allons modifier le menu en faisant ce qui suit :

  • On va prendre le e.target et le stocker comme un attribut de données appelé invokedOn afin de pouvoir identifier ultérieurement l'élément qui a fait apparaître le menu contextuel.
  • Nous allons basculer l'affichage du menu sur visible en utilisant .show()
  • Nous allons positionner l'élément en utilisant .css() .
    • Nous devons nous assurer que c'est position est réglé sur absolute .
    • Ensuite, nous définirons l'emplacement de la gauche et du haut à l'aide de l'élément pageX y pageY les propriétés de l'événement.
  • Enfin, pour empêcher l'action du clic droit d'ouvrir son propre menu, nous allons return false pour empêcher le javascript de gérer autre chose.

Cela ressemblera à ceci :

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

    return false;
});

Corriger les cas de contournement du menu :

Cela ouvrira le menu en bas à droite du curseur qui l'a ouvert. Toutefois, si le curseur se trouve à la position à l'extrême droite de l'écran le menu devrait s'ouvrir sur la gauche. De même, si le curseur se trouve en bas, le menu doit s'ouvrir vers le haut. Il est également important de faire la différence entre le menu le bas de la window qui contient le cadre physique, et le bas de l'écran. document qui représente l'ensemble du DOM du html et peut défiler bien au-delà de la fenêtre.

Pour ce faire, nous allons définir l'emplacement en utilisant les fonctions suivantes :

On va les appeler comme ça :

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

qui appellera cette fonction pour retourner la position appropriée :

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
}

Lier les événements de clic sur l'élément de menu :

Après avoir affiché le menu contextuel, nous devons ajouter un gestionnaire d'événements pour écouter les événements de clics sur celui-ci. Nous supprimerons toute autre liaison qui aurait déjà été ajoutée afin de ne pas déclencher deux fois le même événement. Ces événements peuvent se produire chaque fois que le menu a été ouvert, mais que rien n'a été sélectionné en raison d'un clic. Ensuite, nous pouvons ajouter une nouvelle liaison sur l'élément click où nous allons gérer la logique dans la section suivante.

Comme valepu a noté nous ne voulons pas enregistrer les clics sur d'autres éléments que les éléments de menu. gestionnaire délégué en passant un sélecteur dans la fonction on qui va "filtrer les descendants des éléments sélectionnés qui déclenchent l'événement".

Jusqu'à présent, la fonction devrait ressembler à ceci :

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

Gérer les clics sur les menus

Une fois que nous savons qu'un clic a eu lieu sur le menu, nous allons faire les choses suivantes : Nous allons cacher le menu de l'écran avec .hide() . Ensuite, nous voulons enregistrer l'élément sur lequel le menu a été invoqué à l'origine, ainsi que la sélection du menu actuel. Enfin, nous allons lancer l'option de fonction qui a été passée dans l'extension en utilisant la commande .call() sur la propriété et en passant les cibles de l'événement comme arguments.

$menu.hide();

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

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

Hide When Clicked Off :

Enfin, comme pour la plupart des menus contextuels, nous souhaitons également fermer le menu lorsqu'un utilisateur clique dessus. Pour ce faire, nous allons écouter tous les événements de clics sur le corps et fermer le menu contextuel s'il est ouvert comme ceci :

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

Note : Merci au commentaire de Sadhir Firefox linux déclenche l'événement clic sur document lors d'un clic droit, vous devez donc configurer l'écouteur sur body .

Exemple Syntaxe :

L'extension retournera avec l'objet original qui a soulevé le menu contextuel et l'élément de menu qui a été cliqué. Vous devrez peut-être traverser le dom en utilisant jQuery pour trouver quelque chose de significatif à partir des cibles des événements, mais cela devrait fournir une bonne couche de fonctionnalité de base.

Voici un exemple pour renvoyer des informations sur l'élément et l'action sélectionnés :

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

Capture d'écran :

Context Menu Screenshot

Note de mise à jour :

Cette réponse a été mise à jour de manière substantielle en l'enveloppant dans une méthode d'extension jQuery. Si vous voulez voir l'original, vous pouvez consulter l'historique des messages, mais je pense que cette version finale utilise de bien meilleures pratiques de codage.

Fonction bonus :

Si vous souhaitez ajouter une fonctionnalité intéressante pour les utilisateurs expérimentés ou pour vous-même en développant des fonctionnalités, vous pouvez contourner le menu contextuel en fonction de toute combinaison de touches maintenue lors du clic droit. Par exemple, si vous souhaitez que le menu contextuel du navigateur original s'affiche lorsque vous maintenez la touche Ctrl vous pourriez ajouter ceci comme première ligne du gestionnaire du menu contextuel :

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

1 votes

Comment éviter que le menu ne crée une barre de défilement horizontale s'il est cliqué près de l'extrémité droite/gauche de l'écran ? Par exemple, si nous avons cliqué trop près de l'extrémité droite de l'écran et que le menu s'ouvre, il s'ouvre à gauche en détectant qu'il est près de l'extrémité de l'écran et qu'il devrait s'ouvrir dans l'autre direction. J'entends par là que l'on peut ajouter intelligemment la fonction "pull-left" ou "pull-right" ?

0 votes

Beau travail. Cela fonctionnera-t-il pour les lignes ajoutées dynamiquement ?

0 votes

Toute suggestion sur la façon de faire fonctionner cela sur plusieurs éléments plutôt que sur l'ensemble du tableau - c'est-à-dire les éléments <li> afin que seuls ceux-là soient cliquables. Si vous l'appliquez à chaque <tr> par exemple, la fonction de rappel pour l'élément sélectionné dans le menu est appelée plusieurs fois - jsfiddle.net/HLqTQ/1

93voto

letiagoalves Points 5411

C'est possible. Je vous ai fait une démo fonctionnelle pour vous donner un bon départ.

Démonstration de travail (Cliquez avec le bouton droit de la souris sur n'importe quelle ligne du tableau pour le voir en action)

Créez tout d'abord votre menu déroulant, cachez-le et modifiez son position à absolute :

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

Puis lier un contextmenu aux lignes de votre tableau pour qu'il affiche le menu déroulant/contexte et le positionne au niveau du curseur :

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

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

Ensuite, lorsque l'utilisateur sélectionne une option, le menu déroulant/contexte est masqué :

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

5 votes

+1, Bonne réponse. Ma seule suggestion serait que, typiquement, les menus contextuels disparaissent pour tout clic n'importe où sur la page. J'ai ajouté à votre exemple dans ma réponse ci-dessous.

0 votes

Merci :) J'ai remarqué cela, mais j'ai choisi de ne pas supposer plus que ce que le PO a demandé, bien qu'il le veuille probablement.

1 votes

À l'intérieur du callback que vous pouvez utiliser : var $row = $(this) ou si vous voulez seulement son id, il n'est pas nécessaire de créer un objet jQuery, il suffit de faire var id = this.id

10voto

James Points 138

J'ai fait un plugin de menu contextuel pour bootstrap. Regardez ça https://github.com/sydcanem/bootstrap-contextmenu .

9voto

Far Dmitry Points 91

Ajout de quelques modifications à KyleMit Le code de l'entreprise :

  • déplacement du gestionnaire "on document click" depuis "foreach".
  • suppression de "foreach", ajout d'un événement au sélecteur
  • cacher le menu sur "menu contextuel du document".
  • événements passagers

    $("#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/

-3voto

Larigyn Points 115

J'ai trouvé ce menu contextuel simple et fonctionnel. J'utilise cette bibliothèque http://swisnl.github.io/jQuery-contextMenu/index.html . J'espère que cela vous aidera

table :

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

Menu contextuel :

    "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;
        }
        },

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X