Membuat menu konteks klik kanan khusus untuk aplikasi web saya

127

Saya memiliki beberapa situs web seperti google-docs dan map-quest yang memiliki menu drop down kustom ketika Anda mengklik kanan. Entah bagaimana mereka mengesampingkan perilaku menu drop-down browser, dan saya sekarang yakin persis bagaimana mereka melakukannya. Saya menemukan plugin jQuery yang melakukan ini, tetapi saya masih penasaran tentang beberapa hal:

  • Bagaimana cara kerjanya? Apakah menu drop-down browser benar-benar diganti, atau apakah efeknya hanya disimulasikan? Jika ya, bagaimana caranya?
  • Apa yang dilakukan abstrak plugin? Apa yang terjadi di balik layar?
  • Apakah ini satu-satunya cara untuk mencapai efek ini?

gambar menu konteks kustom

Lihat beberapa menu konteks kustom beraksi

Gordon Gustafson
sumber

Jawaban:

219

Saya tahu pertanyaan ini sudah sangat lama, tetapi baru saja muncul dengan masalah yang sama dan menyelesaikannya sendiri, jadi saya menjawab seandainya ada yang menemukan ini melalui google seperti yang saya lakukan. Saya mendasarkan solusi saya pada @ Andrew's, tetapi pada dasarnya memodifikasi semuanya sesudahnya.

EDIT : melihat betapa populernya ini akhir-akhir ini, saya memutuskan untuk memperbarui juga gaya agar terlihat lebih seperti 2014 dan kurang seperti windows 95. Saya memperbaiki bug @ Quantico dan @Trengot terlihat jadi sekarang ini jawaban yang lebih solid.

EDIT 2 : Saya mengaturnya dengan StackSnippets karena mereka adalah fitur baru yang sangat keren. Saya meninggalkan jsfiddle yang baik di sini untuk referensi pemikiran (klik pada panel 4 untuk melihat mereka bekerja).

Cuplikan Stack Baru:

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

Catatan: Anda mungkin melihat beberapa bug kecil (dropdown jauh dari kursor, dll), pastikan itu berfungsi di jsfiddle , karena itu lebih mirip dengan halaman web Anda daripada StackSnippets.

Francisco Presencia
sumber
1
Saya pikir Anda mungkin memiliki masalah dengan mousedown. Ini dapat menyebabkan kondisi balapan, karena mengklik pada item menu memicu klik yang merupakan mouse dan mouse ke atas.
Quantico
2
Terima kasih @ Quantico, itu benar dan sekarang harus diperbaiki, baik dalam kode dan di jsfiddle. Ada masalah lain? Sidenote: wow, 170 suntingan sebelumnya ke jsfiddle, pasti menjadi populer.
Francisco Presencia
1
Saat menggunakan biola baru, jika Anda popup muncul transparan jika Anda menggunakan elemen html lain pada halaman. EDIT: Menambahkan warna latar belakang ke css memecahkannya.
Holloway
1
Masalah kecil lainnya: jika Anda mengklik kanan di suatu tempat saat menu terlihat, itu berkedip sebelum ditampilkan. Saya merasa harus menyembunyikan (seperti default) atau menyembunyikan dan kemudian muncul di posisi baru.
Holloway
@ChetanJoshi sepertinya harus bekerja pada IE11 menurut MDN: developer.mozilla.org/en-US/docs/Web/Events/… Apakah Anda melihat ada kesalahan?
Francisco Presencia
63

Seperti yang dikatakan Adrian, plugin akan bekerja dengan cara yang sama. Ada tiga bagian dasar yang akan Anda butuhkan:

1: Penangan 'contextmenu'acara untuk acara:

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

Di sini, Anda dapat mengikat pengendali acara ke pemilih mana pun yang ingin Anda tampilkan menu. Saya telah memilih seluruh dokumen.

2: Pengatur 'click'acara untuk acara (untuk menutup menu khusus):

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

3: CSS untuk mengontrol posisi menu:

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

Yang penting dengan CSS adalah memasukkan z-indexdanposition: absolute

Tidak akan terlalu sulit untuk membungkus semua ini dalam plugin jQuery yang apik.

Anda dapat melihat demo sederhana di sini: http://jsfiddle.net/andrewwhitaker/fELma/

Andrew Whitaker
sumber
Saya pikir menu konteks ini akan lebih berguna jika tetap terbuka ketika pengguna mengklik di dalamnya (tetapi ditutup ketika pengguna mengklik di luarnya). Mungkinkah itu diubah agar bekerja seperti ini?
Anderson Green
2
Anda akan melihat event.targetbagian dalam klik yang mengikat pada document. Jika tidak ada di dalam menu konteks, sembunyikan menu: jsfiddle.net/fELma/286
Andrew Whitaker
2
Saya memodifikasinya sedikit (sehingga mencegah beberapa menu muncul sekaligus): jsfiddle.net/fELma/287
Anderson Green
Saya mencoba membuat menu konteks klik kanan radial (seperti yang ada di sini: pushing-pixels.org/wp-content/uploads/2012/07/… ). Ini adalah awal yang baik dalam memahami bagaimana cara melakukannya, terima kasih!
Boris
@AndrewWhitaker jawaban Anda mengatakan itu akan diterapkan pada seluruh dokumen. Bagaimana jika saya ingin itu diterapkan ke kontrol tertentu, misalnya, sebuah tombol (dengan anggapan id sebagai tombol1) ..?
Tk1993
8

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

Isa tanpa batas
sumber
4

di sini adalah contoh untuk menu konteks klik kanan dalam javascript: Klik Kanan Menu Konteks

Kode javasScript mentah digunakan untuk fungsionalitas menu konteks. Tolong periksa ini, harap ini akan membantu Anda.

Kode Langsung:

(function() {
  
  "use strict";


  /*********************************************** Context Menu Function Only ********************************/
  function clickInsideElement( e, className ) {
    var el = e.srcElement || e.target;
    if ( el.classList.contains(className) ) {
      return el;
    } else {
      while ( el = el.parentNode ) {
        if ( el.classList && el.classList.contains(className) ) {
          return el;
        }
      }
    }
    return false;
  }

  function getPosition(e) {
    var posx = 0, posy = 0;
    if (!e) var e = window.event;
    if (e.pageX || e.pageY) {
      posx = e.pageX;
      posy = e.pageY;
    } else if (e.clientX || e.clientY) {
      posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
      posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
    }
    return {
      x: posx,
      y: posy
    }
  }

  // Your Menu Class Name
  var taskItemClassName = "thumb";
  var contextMenuClassName = "context-menu",contextMenuItemClassName = "context-menu__item",contextMenuLinkClassName = "context-menu__link", contextMenuActive = "context-menu--active";
  var taskItemInContext, clickCoords, clickCoordsX, clickCoordsY, menu = document.querySelector("#context-menu"), menuItems = menu.querySelectorAll(".context-menu__item");
  var menuState = 0, menuWidth, menuHeight, menuPosition, menuPositionX, menuPositionY, windowWidth, windowHeight;

  function initMenuFunction() {
    contextListener();
    clickListener();
    keyupListener();
    resizeListener();
  }

  /**
   * Listens for contextmenu events.
   */
  function contextListener() {
    document.addEventListener( "contextmenu", function(e) {
      taskItemInContext = clickInsideElement( e, taskItemClassName );

      if ( taskItemInContext ) {
        e.preventDefault();
        toggleMenuOn();
        positionMenu(e);
      } else {
        taskItemInContext = null;
        toggleMenuOff();
      }
    });
  }

  /**
   * Listens for click events.
   */
  function clickListener() {
    document.addEventListener( "click", function(e) {
      var clickeElIsLink = clickInsideElement( e, contextMenuLinkClassName );

      if ( clickeElIsLink ) {
        e.preventDefault();
        menuItemListener( clickeElIsLink );
      } else {
        var button = e.which || e.button;
        if ( button === 1 ) {
          toggleMenuOff();
        }
      }
    });
  }

  /**
   * Listens for keyup events.
   */
  function keyupListener() {
    window.onkeyup = function(e) {
      if ( e.keyCode === 27 ) {
        toggleMenuOff();
      }
    }
  }

  /**
   * Window resize event listener
   */
  function resizeListener() {
    window.onresize = function(e) {
      toggleMenuOff();
    };
  }

  /**
   * Turns the custom context menu on.
   */
  function toggleMenuOn() {
    if ( menuState !== 1 ) {
      menuState = 1;
      menu.classList.add( contextMenuActive );
    }
  }

  /**
   * Turns the custom context menu off.
   */
  function toggleMenuOff() {
    if ( menuState !== 0 ) {
      menuState = 0;
      menu.classList.remove( contextMenuActive );
    }
  }

  function positionMenu(e) {
    clickCoords = getPosition(e);
    clickCoordsX = clickCoords.x;
    clickCoordsY = clickCoords.y;
    menuWidth = menu.offsetWidth + 4;
    menuHeight = menu.offsetHeight + 4;

    windowWidth = window.innerWidth;
    windowHeight = window.innerHeight;

    if ( (windowWidth - clickCoordsX) < menuWidth ) {
      menu.style.left = (windowWidth - menuWidth)-0 + "px";
    } else {
      menu.style.left = clickCoordsX-0 + "px";
    }

    // menu.style.top = clickCoordsY + "px";

    if ( Math.abs(windowHeight - clickCoordsY) < menuHeight ) {
      menu.style.top = (windowHeight - menuHeight)-0 + "px";
    } else {
      menu.style.top = clickCoordsY-0 + "px";
    }
  }


  function menuItemListener( link ) {
    var menuSelectedPhotoId = taskItemInContext.getAttribute("data-id");
    console.log('Your Selected Photo: '+menuSelectedPhotoId)
    var moveToAlbumSelectedId = link.getAttribute("data-action");
    if(moveToAlbumSelectedId == 'remove'){
      console.log('You Clicked the remove button')
    }else if(moveToAlbumSelectedId && moveToAlbumSelectedId.length > 7){
      console.log('Clicked Album Name: '+moveToAlbumSelectedId);
    }
    toggleMenuOff();
  }
  initMenuFunction();

})();
/* For Body Padding and content */
body { padding-top: 70px; }
li a { text-decoration: none !important; }

/* Thumbnail only */
.thumb {
  margin-bottom: 30px;
}
.thumb:hover a, .thumb:active a, .thumb:focus a {
  border: 1px solid purple;
}

/************** For Context menu ***********/
/* context menu */
.context-menu {  display: none;  position: absolute;  z-index: 9999;  padding: 12px 0;  width: 200px;  background-color: #fff;  border: solid 1px #dfdfdf;  box-shadow: 1px 1px 2px #cfcfcf;  }
.context-menu--active {  display: block;  }

.context-menu__items { list-style: none;  margin: 0;  padding: 0;  }
.context-menu__item { display: block;  margin-bottom: 4px;  }
.context-menu__item:last-child {  margin-bottom: 0;  }
.context-menu__link {  display: block;  padding: 4px 12px;  color: #0066aa;  text-decoration: none;  }
.context-menu__link:hover {  color: #fff;  background-color: #0066aa;  }
.context-menu__items ul {  position: absolute;  white-space: nowrap;  z-index: 1;  left: -99999em;}
.context-menu__items > li:hover > ul {  left: auto;  padding-top: 5px  ;  min-width: 100%;  }
.context-menu__items > li li ul {  border-left:1px solid #fff;}
.context-menu__items > li li:hover > ul {  left: 100%;  top: -1px;  }
.context-menu__item ul { background-color: #ffffff; padding: 7px 11px;  list-style-type: none;  text-decoration: none; margin-left: 40px; }
.page-media .context-menu__items ul li { display: block; }
/************** For Context menu ***********/
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<body>



    <!-- Page Content -->
    <div class="container">

        <div class="row">

            <div class="col-lg-12">
                <h1 class="page-header">Thumbnail Gallery <small>(Right click to see the context menu)</small></h1>
            </div>

            <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                <a class="thumbnail" href="#">
                    <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                </a>
            </div>
            <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                <a class="thumbnail" href="#">
                    <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                </a>
            </div>
            <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                <a class="thumbnail" href="#">
                    <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                </a>
            </div>
            <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                <a class="thumbnail" href="#">
                    <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                </a>
            </div>
            <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                <a class="thumbnail" href="#">
                    <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                </a>
            </div>
            <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                <a class="thumbnail" href="#">
                    <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                </a>
            </div>
            <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                <a class="thumbnail" href="#">
                    <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                </a>
            </div>
            <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                <a class="thumbnail" href="#">
                    <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                </a>
            </div>

        </div>

        <hr>


    </div>
    <!-- /.container -->


    <!-- / The Context Menu -->
    <nav id="context-menu" class="context-menu">
        <ul class="context-menu__items">
            <li class="context-menu__item">
                <a href="#" class="context-menu__link" data-action="Delete This Photo"><i class="fa fa-empire"></i> Delete This Photo</a>
            </li>
            <li class="context-menu__item">
                <a href="#" class="context-menu__link" data-action="Photo Option 2"><i class="fa fa-envira"></i> Photo Option 2</a>
            </li>
            <li class="context-menu__item">
                <a href="#" class="context-menu__link" data-action="Photo Option 3"><i class="fa fa-first-order"></i> Photo Option 3</a>
            </li>
            <li class="context-menu__item">
                <a href="#" class="context-menu__link" data-action="Photo Option 4"><i class="fa fa-gitlab"></i> Photo Option 4</a>
            </li>
            <li class="context-menu__item">
                <a href="#" class="context-menu__link" data-action="Photo Option 5"><i class="fa fa-ioxhost"></i> Photo Option 5</a>
            </li>
            <li class="context-menu__item">
                <a href="#" class="context-menu__link"><i class="fa fa-arrow-right"></i> Add Photo to</a>
                <ul>
                    <li><a href="#!" class="context-menu__link" data-action="album-one"><i class="fa fa-camera-retro"></i> Album One</a></li>
                    <li><a href="#!" class="context-menu__link" data-action="album-two"><i class="fa fa-camera-retro"></i> Album Two</a></li>
                    <li><a href="#!" class="context-menu__link" data-action="album-three"><i class="fa fa-camera-retro"></i> Album Three</a></li>
                    <li><a href="#!" class="context-menu__link" data-action="album-four"><i class="fa fa-camera-retro"></i> Album Four</a></li>
                </ul>
            </li>
        </ul>
    </nav>

    <!-- End # Context Menu -->


</body>

Robin Hossain
sumber
Pekerjaan bagus menggunakan vanilla JS dan lay out yang bersih!
kurt
3

Menu konteks browser sedang diganti. Tidak ada cara untuk menambah menu konteks asli di browser utama apa pun.

Karena plugin membuat menu sendiri, satu-satunya bagian yang benar-benar disarikan adalah acara menu konteks browser. Plugin membuat menu html berdasarkan konfigurasi Anda, lalu menempatkan konten itu di lokasi klik Anda.

Ya, ini adalah satu-satunya cara untuk membuat menu konteks kustom. Jelas, plugin yang berbeda melakukan hal yang sedikit berbeda, tetapi mereka semua akan menimpa acara browser dan menempatkan menu berbasis html mereka sendiri di tempat yang benar.

Adrian Gonzales
sumber
2
Hanya untuk menyebutkan bahwa Firefox sekarang menambahkan dukungan untuk 'menu konteks' asli HTML5 (dinyatakan melalui markup). Sekarang tersedia di Firefox 8 beta. ( developer.mozilla.org/en/Firefox_8_for_developers ).
poshaughnessy
2

Anda dapat menonton tutorial ini: http://www.youtube.com/watch?v=iDyEfKWCzhg Pastikan menu konteks disembunyikan pada awalnya dan memiliki posisi absolut. Ini akan memastikan bahwa tidak akan ada banyak menu konteks dan pembuatan menu konteks yang tidak berguna. Tautan ke halaman ditempatkan dalam deskripsi video YouTube.

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

Saya tahu ini agak tua juga. Baru-baru ini saya perlu membuat menu konteks yang saya masukkan ke situs lain yang memiliki properti berbeda berdasarkan elemen yang diklik.

Ini agak kasar, dan ada kemungkinan cara yang lebih baik untuk mencapai ini. Itu menggunakan menu konteks jQuery Library yang terletak di sini

Saya senang menciptakannya dan meskipun kalian mungkin bisa menggunakannya.

Ini biola . Saya berharap semoga dapat membantu seseorang di luar sana.

$(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()
      };
    }
  });
});
Cody Fidler
sumber
0

Saya memiliki implementasi yang bagus dan mudah menggunakan bootstrap sebagai berikut.

<select class="custom-select" id="list" multiple></select>

<div class="dropdown-menu" id="menu-right-click" style=>
    <h6 class="dropdown-header">Actions</h6>
    <a class="dropdown-item" href="" onclick="option1();">Option 1</a>
    <a class="dropdown-item" href="" onclick="option2();">Option 2</a>
</div>

<script>
    $("#menu-right-click").hide();

    $(document).on("contextmenu", "#list", function (e) {
        $("#menu-right-click")
            .css({
                position: 'absolute',
                left: e.pageX,
                top: e.pageY,
                display: 'block'
            })
        return false;
    });

    function option1() {
        // something you want...
        $("#menu-right-click").hide();
    }

    function option2() {
        // something else 
        $("#menu-right-click").hide();
    }
</script>
Alexandre Crivellaro
sumber