Usar el menú desplegable Bootstrap 3 como menú contextual


Usando Bootstrap 3, ¿cómo puedo colocar el menú desplegable en el cursor y abrirlo desde el código?

Necesito usarlo en una tabla como un menú contextual para sus filas.

Author: letiagoalves, 2013-09-07

4 answers

Es posible. Te hice una demostración de trabajo para dar un buen comienzo.

Demostración de trabajo (Haga clic derecho en cualquier fila de la tabla para verla en acción)

Primero crea tu menú desplegable, escóndelo y cambia su position a absolute:

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

Luego enlaza un evento contextmenu a las filas de la tabla para que muestre el menú desplegable / contextual y colócalo en el cursor:

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

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

Luego, cuando el usuario seleccione una opción ocultar desplegable / contexto menú:

$contextMenu.on("click", "a", function() {
   $contextMenu.hide();
});
 86
Author: letiagoalves,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-06 21:41:42

Solo quería mejorar letiagoalves gran respuesta con un par de sugerencias más.
Aquí hay un tutorial sobre cómo agregar un menú contextual a cualquier elemento html.

Comencemos con una demostración en jsFiddle

Marcado:

Primero, vamos a añadir un menú desde el control desplegable bootstrap. Agréguelo en cualquier lugar a su HTML, preferiblemente en el nivel raíz del cuerpo. La clase .dropdown-menu establecerá display:none por lo que es inicialmente invisible.
Debería verse así:

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

Configuración de la extensión:

Para mantener nuestro diseño modular, agregaremos nuestro código JavaScript como una extensión jQuery llamada contextMenu.

Cuando llamemos a $.contextMenu, pasaremos un objeto settings con 2 propiedades:

  1. menuSelector toma el selector jQuery del menú que creamos anteriormente en HTML.
  2. menuSelected se llamará cuando se haga clic en la acción del menú contextual.
$("#myTable").contextMenu({
    menuSelector: "#contextMenu",
    menuSelected: function (invokedOn, selectedMenu) {
        // context menu clicked
    });
});

Plugin Plantilla:

Basado en la plantilla de plugin jQuery boilerplate, usaremos una Expresión de Función Inmediatamente Invocada para no confundir el espacio de nombres global. Dado que tenemos dependencias en jQuery y necesitamos acceso a la ventana, las pasaremos como variables para que podamos sobrevivir a la minificación. Se verá así:

(function($, window){

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

})(jQuery, window);
Vale, no más cañerías. Aquí está la carne de la función:

Manejar Eventos de Clic derecho:

Nos encargaremos de la contextmenu evento del ratón en el objeto que llamó a la extensión. Cuando se inicie el evento, tomaremos el menú desplegable que agregamos al principio. Lo localizaremos usando la cadena de selector pasada por la configuración cuando inicializamos la función. Modificaremos el menú haciendo lo siguiente:

  • Tomaremos la propiedad e.target y la almacenaremos como un atributo de datos llamado invokedOn, para que luego podamos identificar el elemento que generó el menú contextual.
  • Vamos a alternar la pantalla del menú a visible usando .show()
  • Colocaremos el elemento usando .css().
    • Tenemos que asegurarnos de que es position está establecido en absolute.
    • Luego estableceremos la ubicación izquierda y superior usando las propiedades pageX y pageY del evento.
  • Finalmente, para evitar que la acción de clic derecho abra su propio menú, return false detendremos que el javascript maneje cualquier otra cosa.

Se verá como esto:

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

    return false;
});

Corregir Casos de Borde de Menú:

Esto abrirá el menú en la parte inferior derecha del cursor que lo abrió. Sin embargo, si el cursor está a la a la derecha de la pantalla, el menú debe abrirse a la izquierda. Del mismo modo, si el cursor está en la parte inferior, el menú debe abrirse hacia la parte superior. También es importante diferenciar entre la parte inferior de la window, que contiene el marco físico, y la parte inferior de la document que representa todo el html DOM y puede desplazarse más allá de la ventana.

Para lograr esto, estableceremos la ubicación usando las siguientes funciones:

Los llamaremos así: {[39]]}

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

Que llamará a esta función para devolver la posición apropiada:

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
}

Enlazar Eventos de clic en el elemento de Menú:

Después de mostrar el menú contextual, necesitamos agregar un controlador de eventos para escuchar los eventos de clic en él. Eliminaremos cualquier otro enlace que ya se haya añadido para que no disparemos el mismo evento dos veces. Estos pueden ocurrir en cualquier momento en que se abrió el menú, pero no se seleccionó nada debido a hacer clic en off. Luego podemos agregar un nuevo enlace en el evento click donde manejaremos la lógica en la siguiente sección.

Como valepu señaló , no queremos registrar clics en nada que no sean elementos de menú, por lo que configuramos un controlador delegado pasando un selector a la función on que " filtrará los descendientes de los elementos que desencadenan el evento".

Hasta ahora, la función debería verse así:

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

Manejar los clics del menú

Una vez que sepamos que se ha producido un clic en el menú, haremos lo siguiente: Ocultaremos el menú de la pantalla con .hide(). A continuación, queremos guardar el elemento en el que se invocó originalmente el menú, así como la selección del menú actual. Finalmente, activaremos la opción de función que se pasó a la extensión usando .call() en la propiedad y pasando los destinos de evento como argumentos.

$menu.hide();

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

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

Ocultar Cuando Se Hace Clic En Off:

Finalmente, como con la mayoría de los menús contextuales, queremos cerrar el menú cuando un usuario hace clic fuera de él también. Para hacerlo, escucharemos cualquier evento de clic en el cuerpo y cerraremos el menú contextual si está abierto de esta manera:

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

Nota: Gracias al comentario de Sadhir, Firefox linux activa el evento click en document durante un clic derecho, por lo que tiene que configurar el oyente en body.

Sintaxis de ejemplo:

La extensión regresará con el objeto original que levantó el menú contextual y el elemento de menú en el que se hizo clic. Es posible que tenga que recorrer el dom utilizando jQuery para encontrar algo significativo de los destinos de eventos, pero esto debería proporcionar una buena capa de funcionalidad base.

Aquí hay un ejemplo para devolver información para el elemento y la acción seleccionado:

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

Captura de pantalla:

Captura de Pantalla del Menú Contextual

Nota de actualización:

Esta respuesta se ha actualizado sustancialmente envolviéndola en un método de extensión jQuery. Si desea ver mi original, puede ver el historial de publicaciones, pero creo que esta versión final utiliza prácticas de codificación mucho mejores.

Función de bonificación :

Si desea agregar alguna funcionalidad agradable para powerusers o usted mismo en el desarrollo de características, puede omitir el menú contextual basado en cualquier combinación de teclas que se mantenga cuando haga clic con el botón derecho. Por ejemplo, si desea permitir que se muestre el menú contextual original del navegador cuando mantenga presionada Ctrl, podría agregar esto como la primera línea del controlador ContextMenu:

// return native menu if pressing control
if (e.ctrlKey) return;
 134
Author: KyleMit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:24

Se han añadido algunas modificaciones al código de KyleMit:

  • moved" on document click "handler from" foreach "
  • eliminado "foreach", solo tiene que añadir evento al selector
  • ocultar el menú en "menú contextual del documento"
  • Eventos de paso

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

 6
Author: Far Dmitry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:24

Encontré este menú contextual simple y funcional. Estoy usando esta biblioteca http://swisnl.github.io/jQuery-contextMenu/index.html . Espero que ayude

Cuadro:

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

Contextmenu:

    "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;
        }
        },
 -3
Author: Larigyn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-22 03:43:34