如何为网页添加自定义右键菜单?

IT技术 javascript contextmenu right-click
2021-01-24 20:36:10

我想向我的 Web 应用程序添加自定义右键单击菜单。这可以在不使用任何预建库的情况下完成吗?如果是这样,如何显示不使用 3rd 方 JavaScript 库的简单自定义右键单击菜单?

我的目标是类似于 Google Docs 的功能。它允许用户右键单击并向用户显示他们自己的菜单。

注意: 我想学习如何制作我自己的东西而不是使用别人已经制作的东西,因为大多数时候,那些 3rd 方库的功能很臃肿,而我只想要我需要的功能,所以我希望它完全手工制作我。

6个回答

回答您的问题 - 使用contextmenu事件,如下所示:

if (document.addEventListener) {
  document.addEventListener('contextmenu', function(e) {
    alert("You've tried to open context menu"); //here you draw your own menu
    e.preventDefault();
  }, false);
} else {
  document.attachEvent('oncontextmenu', function() {
    alert("You've tried to open context menu");
    window.event.returnValue = false;
  });
}
<body>
  Lorem ipsum...
</body>

但是您应该问问自己,您是否真的想覆盖默认的右键单击行为 - 这取决于您正在开发的应用程序。


JSFIDDLE

“但你应该问问自己,你真的想覆盖默认的右键单击行为吗” ——这个解决方案与@MohamedIqzas ( stackoverflow.com/a/16481062/264097 )下面的答案中的实现有一个重要的区别. 在下面的答案中,默认上下文菜单仅针对某个 HTML 元素被抑制,而不是针对整个文档。这使得它的侵入性小得多。
2021-03-11 20:36:10
在 Opera 11.01、Firefox 3.6.13、Chrome 9、Safari 5(所有 4 个通过 addEventListener)和 IE 8(attachEvent)上测试。
2021-03-15 20:36:10
您刚刚解释了如何禁用右键单击菜单。如何创建我们自己的菜单?
2021-03-22 20:36:10
@shanehoban 当您查看代码时,您会看到这一行e.preventDefault();这就是不显示常规菜单的原因。您可以做的是创建一些条件逻辑来检查是否在右键单击时按下键然后不调用e.preventDefault()-然后您将获得常规浏览器菜单。
2021-03-24 20:36:10
@Shashwat 您知道用户单击的位置,并且您没有原始菜单。在那个地方创建一个容器并在那里显示你的菜单。
2021-04-09 20:36:10

对我非常有用。为了像我这样的人,期待菜单的绘制,我把我用来制作右键菜单的代码放在这里:

$(document).ready(function() {


  if ($("#test").addEventListener) {
    $("#test").addEventListener('contextmenu', function(e) {
      alert("You've tried to open context menu"); //here you draw your own menu
      e.preventDefault();
    }, false);
  } else {

    //document.getElementById("test").attachEvent('oncontextmenu', function() {
    //$(".test").bind('contextmenu', function() {
    $('body').on('contextmenu', 'a.test', function() {


      //alert("contextmenu"+event);
      document.getElementById("rmenu").className = "show";
      document.getElementById("rmenu").style.top = mouseY(event) + 'px';
      document.getElementById("rmenu").style.left = mouseX(event) + 'px';

      window.event.returnValue = false;


    });
  }

});

// this is from another SO post...  
$(document).bind("click", function(event) {
  document.getElementById("rmenu").className = "hide";
});



function mouseX(evt) {
  if (evt.pageX) {
    return evt.pageX;
  } else if (evt.clientX) {
    return evt.clientX + (document.documentElement.scrollLeft ?
      document.documentElement.scrollLeft :
      document.body.scrollLeft);
  } else {
    return null;
  }
}

function mouseY(evt) {
  if (evt.pageY) {
    return evt.pageY;
  } else if (evt.clientY) {
    return evt.clientY + (document.documentElement.scrollTop ?
      document.documentElement.scrollTop :
      document.body.scrollTop);
  } else {
    return null;
  }
}
.show {
  z-index: 1000;
  position: absolute;
  background-color: #C0C0C0;
  border: 1px solid blue;
  padding: 2px;
  display: block;
  margin: 0;
  list-style-type: none;
  list-style: none;
}

.hide {
  display: none;
}

.show li {
  list-style: none;
}

.show a {
  border: 0 !important;
  text-decoration: none;
}

.show a:hover {
  text-decoration: underline !important;
}
<!-- jQuery should be at least version 1.7 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="contextmenu.js"></script>
<link rel="stylesheet" href="contextmenu.css" />


<div id="test1">
  <a href="www.google.com" class="test">Google</a>
  <a href="www.google.com" class="test">Link 2</a>
  <a href="www.google.com" class="test">Link 3</a>
  <a href="www.google.com" class="test">Link 4</a>
</div>

<!-- initially hidden right-click menu -->
<div class="hide" id="rmenu">
  <ul>
    <li>
      <a href="http://www.google.com">Google</a>
    </li>

    <li>
      <a href="http://localhost:8080/login">Localhost</a>
    </li>

    <li>
      <a href="C:\">C</a>
    </li>
  </ul>
</div>

@Matt 我在 Firefox 58 上遇到了同样的问题。这篇文章描述了一个对我有用的解决方案:stackoverflow.com/a/40545465/2922675您禁用文档的事件传播并在 window 对象上注册您的上下文菜单处理程序. 这是一个调整后的小提琴:jsfiddle.net/jjgkLe3g/1
2021-03-11 20:36:10
@Adelphia - 任何非本地且非您自己创建的内容都是第三方。jQuery真的不是因为额外的东西而变得臃肿。没有达到它会减慢任何事情的程度。它非常有用,并且jQuery在此答案中使用的相同内容可以轻松转换为标准JavaScript命令。它可能不是 100% 符合原始问题中的请求,但它肯定 95% 符合它。
2021-03-13 20:36:10
@TheDukeOfMarshallשלם 我没有要求任何人为我编写代码,只是为了解释如何去做。所选答案完美地做到了这一点。
2021-03-20 20:36:10
@Schism后缀mouseY(event)mouseX(event)px,使其按预期:http://jsfiddle.net/a6w7n64o/
2021-03-31 20:36:10
在 Firefox 39 中,默认上下文菜单仍然显示在自定义菜单的顶部。此外,自定义菜单在显示后立即消失。
2021-04-07 20:36:10

一些漂亮的 CSS 和一些没有外部库的非标准 html 标签的组合可以产生很好的结果 (JSFiddle)

HTML

<menu id="ctxMenu">
    <menu title="File">
        <menu title="Save"></menu>
        <menu title="Save As"></menu>
        <menu title="Open"></menu>
    </menu>
    <menu title="Edit">
        <menu title="Cut"></menu>
        <menu title="Copy"></menu>
        <menu title="Paste"></menu>
    </menu>
</menu>

注意:菜单标签不存在,我自己编的(你可以用任何东西)

CSS

#ctxMenu{
    display:none;
    z-index:100;
}
menu {
    position:absolute;
    display:block;
    left:0px;
    top:0px;
    height:20px;
    width:20px;
    padding:0;
    margin:0;
    border:1px solid;
    background-color:white;
    font-weight:normal;
    white-space:nowrap;
}
menu:hover{
    background-color:#eef;
    font-weight:bold;
}
menu:hover > menu{
    display:block;
}
menu > menu{
    display:none;
    position:relative;
    top:-20px;
    left:100%;
    width:55px;
}
menu[title]:before{
    content:attr(title);
}
menu:not([title]):before{
    content:"\2630";
}

JavaScript的只是在这个例子中,我个人将其删除Windows上的持久菜单

var notepad = document.getElementById("notepad");
notepad.addEventListener("contextmenu",function(event){
    event.preventDefault();
    var ctxMenu = document.getElementById("ctxMenu");
    ctxMenu.style.display = "block";
    ctxMenu.style.left = (event.pageX - 10)+"px";
    ctxMenu.style.top = (event.pageY - 10)+"px";
},false);
notepad.addEventListener("click",function(event){
    var ctxMenu = document.getElementById("ctxMenu");
    ctxMenu.style.display = "";
    ctxMenu.style.left = "";
    ctxMenu.style.top = "";
},false);

另外请注意,您可以可能修改menu > menu{left:100%;},以menu > menu{right:100%;}用于从右翼扩大到左边的菜单。您需要在某处添加边距或其他内容

您可能希望return false;在处理“contextmenu”事件的函数末尾添加以支持更多浏览器(-versions)。
2021-03-22 20:36:10
如果我的目的是让菜单项显示一个指向 URL 的弹出窗口,那么将菜单项 HTML 标签包装在 <a> 标签中是实现这一目标的方法,编写使弹出窗口的 JS 代码更常见吗?点击时发生?很好的例子。这正是我正在寻找的:从基本构建块构建的上下文菜单。我认为这非常有用,即使我最终使用了一个库,因为我从中得到了理解。
2021-03-22 20:36:10
@PhilippeCarphin 你可以使用任何你想要的标签。锚标签可能更有意义。此外,这menu[title]:before是将标题注入标签的魔法,因此您不必使用它,并且可以根据需要填写标签中的内容
2021-03-26 20:36:10

根据此处和其他“流程”中的答案,我制作了一个看起来像 Google Chrome 的版本,带有 css3 过渡。 JS Fiddle

让我们开始吧,因为我们在这个页面上有上面的js,我们可以担心css和布局。我们将使用的布局是一个<a>元素,带有一个<img>元素或字体很棒的图标 ( <i class="fa fa-flag"></i>) 和 a<span>来显示键盘快捷键。所以这是结构:

<a href="#" onclick="doSomething()">
  <img src="path/to/image.gif" />
  This is a menu option
  <span>Ctrl + K</span>
</a>

我们将把它们放在一个 div 中,并在右键单击时显示该 div。让我们像在 Google Chrome 中一样设计它们,好吗?

#menu a {
  display: block;
  color: #555;
  text-decoration: no[...]

现在我们将从接受的答案中添加代码,并获取光标的 X 和 Y 值。为此,我们将使用e.clientXe.clientY我们正在使用客户端,因此必须修复菜单 div。

var i = document.getElementById("menu").style;
if (document.addEventListener) {
  document.addEventListener('contextmenu', function(e) {
    var posX = e.clientX;
    var posY = e.client[...]

就是这样!只需添加 css 过渡以淡入淡出,就完成了!

这让我免于头疼!如果 OP 正在寻找一种在整个网页上使用的简单方法,而不仅仅是一个 div,那么这应该是可以接受的答案:)
2021-03-13 20:36:10
当您右键单击页面的右侧部分时,菜单的某些部分会隐藏。
2021-04-10 20:36:10

您可以尝试通过将以下内容添加到您的正文标签来简单地阻止上下文菜单:

<body oncontextmenu="return false;">

这将阻止对上下文菜单的所有访问(不仅从鼠标右键,而且从键盘)。

PS,您可以将此添加到要禁用上下文菜单的任何标签

例如:

<div class="mydiv" oncontextmenu="return false;">

将仅禁用该特定 div 中的上下文菜单