Avec 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 une table en tant que menu contextuel pour ses lignes.
C'est possible. Je t'ai fait une démo de travail pour bien commencer.
Démonstration de travail (Faites un clic droit sur une ligne du tableau pour la voir en action)
Créez d'abord votre menu déroulant, masquez-le et changez son position
en absolute
:
#contextMenu {
position: absolute;
display:none;
}
Ensuite, liez un événement contextmenu
à vos lignes de table afin d'afficher un menu déroulant/contextuel et positionnez-le 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;
});
Puis lorsque l'utilisateur sélectionne une option masquer le menu déroulant/contextuel:
$contextMenu.on("click", "a", function() {
$contextMenu.hide();
});
Je voulais juste améliorer letiagoalves excellente réponse avec quelques suggestions supplémentaires.
Voici une procédure pas à pas permettant d’ajouter un menu contextuel à n’importe quel élément HTML.
Tout d’abord, ajoutons un menu à partir du contrôle déroulant de démarrage . Ajoutez-le n'importe où à votre code HTML, de préférence au niveau racine du corps. La classe .dropdown-menu
Définira display:none
De sorte qu'elle est initialement invisible.
Ça devrait ressembler à ça:
<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>
Pour que notre conception reste modulaire, nous allons ajouter notre code JavaScript sous la forme d’une extension jQuery appelée contextMenu
.
Lorsque nous appelons $.contextMenu
, Nous passons dans un objet settings avec 2 propriétés:
menuSelector
prend le sélecteur jQuery du menu que nous avons créé précédemment en HTML.menuSelected
sera appelé lorsque l'utilisateur cliquera sur l'action du menu contextuel.$("#myTable").contextMenu({
menuSelector: "#contextMenu",
menuSelected: function (invokedOn, selectedMenu) {
// context menu clicked
});
});
Sur la base du modèle du plugin jQuery boilerplate , nous utiliserons une expression expression de fonction immédiatement appelée afin de ne pas embrouiller l'espace de noms global. Puisque nous avons des dépendances sur jQuery et que nous avons besoin d'accéder à la fenêtre, nous les transmettons sous forme de variables afin de pouvoir survivre à la minification. Il ressemblera à ceci:
(function($, window){
$.fn.contextMenu = function(settings) {
return this.each(function() {
// Code Goes Here
}
};
})(jQuery, window);
Nous allons gérer l'événement de souris contextmenu
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 le localiserons à l'aide de la chaîne de sélection transmise par les paramètres lors de l'initialisation de la fonction. Nous allons modifier le menu en procédant comme suit:
e.target
Et la stocker sous la forme d'un attribut de données appelé invokedOn
afin de pouvoir identifier ultérieurement l'élément qui a élevé le menu contextuel..show()
.css()
. position
est défini sur absolute
.pageX
et pageY
de l'événement.return false
Empêcher le javascript de gérer quoi que ce soit d'autre.Il 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;
});
Cela ouvrira le menu en bas à droite du curseur qui l’a ouvert. Cependant, si le curseur est sur le extrême droite de l'écran , le menu devrait s'ouvrir à gauche. De même, si le curseur est en bas, le menu devrait s’ouvrir vers le haut. Il est également important de faire la différence entre le bas du window
, qui contient le cadre physique, et le bas du document
qui représente le DOM entier HTML et peut faire défiler loin derrière la fenêtre.
Pour ce faire, nous allons définir l'emplacement à l'aide des fonctions suivantes:
Nous les appellerons 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
}
Après l'affichage du menu contextuel, nous devons ajouter un gestionnaire d'événements pour écouter les événements de clic sur celui-ci. Nous allons supprimer toutes les autres liaisons déjà ajoutées afin de ne pas déclencher le même événement deux fois. Celles-ci peuvent se produire à tout moment lorsque le menu a été ouvert, mais rien n’a été sélectionné pour la raison d’un clic. Ensuite, nous pouvons ajouter une nouvelle liaison à l’événement click
où nous allons gérer la logique dans la section suivante.
Comme notons valep , nous ne voulons pas enregistrer de clics sur autre chose que des éléments de menu. Nous avons donc configuré un gestionnaire délégué en passant un sélecteur dans le on
fonction qui "filtrera les descendants des éléments sélectionnés qui déclenchent l'événement".
Jusqu'ici, la fonction devrait ressembler à ceci:
$(settings.menuSelector)
.off('click')
.on( 'click', "a", function (e) {
//CODE IN NEXT SECTION GOES HERE
});
Lorsque nous savons qu'un clic s'est produit dans le menu, nous procédons comme suit: Nous masquerons le menu à l'écran avec .hide()
. Ensuite, nous voulons enregistrer l'élément sur lequel le menu a été appelé à l'origine, ainsi que la sélection dans le menu actuel. Enfin, nous activerons l'option de fonction qui a été transmise à l'extension en utilisant .call()
sur la propriété et en transmettant les cibles de l'événement en tant qu'arguments.
$menu.hide();
var $invokedOn = $menu.data("invokedOn");
var $selectedMenu = $(e.target);
settings.menuSelected.call($(this), $invokedOn, $selectedMenu);
Enfin, comme pour la plupart des menus contextuels, nous souhaitons également fermer le menu lorsqu'un utilisateur clique dessus. Pour ce faire, nous écouterons tous les événements de clic sur le corps et fermerons 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 click sur
document
lors d'un clic droit, vous devez donc configurer l'auditeur surbody
.
L'extension renverra avec l'objet d'origine qui a soulevé le menu contextuel et l'élément de menu sur lequel l'utilisateur a cliqué. Vous devrez peut-être traverser le dom utiliser jQuery pour trouver quelque chose de significatif à partir des cibles d'événement, mais cela devrait fournir une bonne couche de fonctionnalités 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);
}
});
Cette réponse a été sensiblement mise à jour en l'enveloppant dans une méthode d'extension jQuery. Si vous souhaitez voir mon original, vous pouvez consulter l'historique des publications, mais je pense que cette version finale utilise de bien meilleures pratiques de codage.
Bonus :
Si vous souhaitez ajouter des fonctionnalités Nice pour les utilisateurs ou pour vous-même lors du développement de fonctionnalités, vous pouvez ignorer le menu contextuel en fonction des combinaisons de touches détenues lorsque vous cliquez avec le bouton droit de la souris. Par exemple, si vous souhaitez autoriser le menu contextuel d'origine du navigateur à s'afficher lorsque vous maintenez Ctrl, vous pouvez ajouter ceci comme première ligne du gestionnaire contextMenu:
// return native menu if pressing control
if (e.ctrlKey) return;
Ajout de quelques modifications au code de KyleMit :
événements de passage
$("#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");
} });