javascript evento - Haciendo menús personalizados con el botón derecho del mouse para mi aplicación web




click jquery (7)

Tengo algunos sitios web como google-docs y map-quest que tienen menús desplegables personalizados cuando haces clic derecho. De alguna manera, anulan el comportamiento del navegador del menú desplegable, y ahora estoy seguro de cómo lo hacen. Encontré un plugin jQuery que hace esto, pero aún tengo curiosidad por algunas cosas:

  • ¿Como funciona esto? ¿Se está anulando el menú desplegable del navegador o se simuló el efecto? ¿Si es así, cómo?
  • ¿Qué abstrae el complemento? ¿Qué está pasando detrás de escena?
  • ¿Es esta la única forma de lograr este efecto?

Vea varios menús contextuales personalizados en acción


Answers

Sé que esto también es bastante viejo. Hace poco tuve la necesidad de crear un menú contextual que inserte en otros sitios que tienen diferentes propiedades en función del elemento al que se hace clic.

Es bastante duro, y hay formas probables de lograr esto. Utiliza el menú de contexto de jQuery Biblioteca ubicada aquí

Disfruté crearlo y aunque ustedes podrían sacarle provecho.

Aquí está el fiddle . Espero que pueda ayudar a alguien allá afuera.

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

Puede ver este tutorial: http://www.youtube.com/watch?v=iDyEfKWCzhg Asegúrese de que el menú contextual esté oculto al principio y tenga una posición de absoluta. Esto asegurará que no habrá un menú contextual múltiple y una creación inútil del menú contextual. El enlace a la página se coloca en la descripción del video de YouTube.

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

Como dijo Adrian, los complementos funcionarán de la misma manera. Hay tres partes básicas que vas a necesitar:

1: Manejador de eventos para 'contextmenu' evento 'contextmenu' :

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

Aquí, puede vincular el controlador de eventos a cualquier selector para el que desee mostrar un menú. Elegí el documento completo.

2: controlador de eventos para 'click' evento 'click' (para cerrar el menú personalizado):

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

3: CSS para controlar la posición del menú:

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

Lo importante con CSS es incluir el z-index y la position: absolute

No sería demasiado difícil envolver todo esto en un hábil plugin jQuery.

Puede ver una demostración simple aquí: http://jsfiddle.net/andrewwhitaker/fELma/


El menú de contexto del navegador está siendo anulado. No hay forma de aumentar el menú contextual nativo en ningún navegador importante.

Como el complemento está creando su propio menú, la única parte que realmente se está abstrayendo es el evento del menú contextual del navegador. El complemento crea un menú html basado en su configuración, luego coloca ese contenido en la ubicación de su clic.

Sí, esta es la única forma de crear un menú contextual personalizado. Obviamente, los diferentes complementos hacen las cosas un poco diferentes, pero todos anularán el evento del navegador y colocarán su propio menú basado en html en el lugar correcto.


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


Sé que esta pregunta es muy antigua, pero me ocurrió el mismo problema y lo resolví yo mismo, así que estoy respondiendo por si alguien encuentra esto en Google como lo hice yo. Basé mi solución en la de @ Andrew, pero básicamente modifiqué todo después.

EDITAR : viendo lo popular que ha sido últimamente, decidí actualizar también los estilos para que se vea más como 2014 y menos como Windows 95. Solucioné los errores @Quantico y @Trengot detectados, por lo que ahora es una respuesta más sólida.

EDIT 2 : Lo configuré con StackSnippets ya que son una nueva característica realmente genial. Dejo la buena jsfiddle aquí como referencia (haga clic en el 4º panel para ver cómo funcionan).

Nuevo fragmento de pila:

// 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  -->
Right click me

Nota: es posible que vea algunos pequeños errores (menú desplegable lejos del cursor, etc.), asegúrese de que funciona en jsfiddle , ya que es más similar a su página web que StackSnippets.


Podemos referirnos a la especificación y eso es excelente y más preciso, pero la mayoría de los casos también se pueden explicar de una manera más comprensible con las siguientes afirmaciones:

  • + operadores + y - trabajan solo con valores primitivos. Más específicamente, + (adición) funciona con cadenas o números, y + (unario) y - (resta y unario) solo funciona con números.
  • Todas las funciones u operadores nativos que esperan un valor primitivo como argumento, primero convertirán ese argumento al tipo primitivo deseado. Se realiza con valueOf o toString , que están disponibles en cualquier objeto. Esa es la razón por la que tales funciones u operadores no generan errores cuando se invocan en objetos.

Entonces podemos decir que:

  • [] + [] es igual que String([]) + String([]) que es igual que '' + '' . Mencioné anteriormente que + (adición) también es válido para números, pero no hay una representación de número válida de una matriz en JavaScript, así que en su lugar se usa la adición de cadenas.
  • [] + {} es igual que String([]) + String({}) que es igual que '' + '[object Object]'
  • {} + [] . Esta merece más explicación (ver respuesta de Ventero). En ese caso, las llaves no se tratan como un objeto sino como un bloque vacío, por lo que resulta igual a +[] . Unary + solo funciona con números, por lo que la implementación intenta obtener un número de [] . En primer lugar, intenta valueOf que en el caso de matrices devuelve el mismo objeto, y luego intenta el último recurso: la conversión de un resultado de toString a un número. Podemos escribirlo como +Number(String([])) que es igual a +Number('') que es igual a +0 .
  • Array(16).join("wat" - 1) resta - funciona solo con números, por lo que es lo mismo que: Array(16).join(Number("wat") - 1) , como "wat" no puede ser convertido a un número válido. Recibimos NaN y cualquier operación aritmética en los resultados de NaN con NaN , por lo que tenemos: Array(16).join(NaN) .




javascript jquery jquery-plugins contextmenu right-click