jQuery 使用 Bootstrap 3 下拉菜单作为上下文菜单

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18666601/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 22:10:54  来源:igfitidea点击:

Use Bootstrap 3 dropdown menu as context menu

jquerytwitter-bootstraptwitter-bootstrap-3

提问by MojoDK

Using Bootstrap 3, how can I place the dropdown menu at the cursor and open it from code?

使用 Bootstrap 3,如何将下拉菜单放在光标处并从代码中打开它?

I need to use it on a table as a context menu for its rows.

我需要在表上使用它作为其行的上下文菜单。

回答by KyleMit

I just wanted to improve on letiagoalvesgreat answer with a couple more suggestions.
Here's a walkthrough on how to add a context menu to any html element.

我只是想通过更多的建议来改进letiagoalves 的好答案。
这是有关如何向任何 html 元素添加上下文菜单的演练。

Let's start off with a working demo in jsFiddle

让我们从 jsFiddle 中的一个工作演示开始

Markup:

标记:

First, let's add a menu from the bootstrap dropdown control. Add it anywhere to your HTML, preferably at the root level of the body. The .dropdown-menuclass will set display:noneso it's initially invisible.
It should look like this:

首先,让我们从引导下拉控件中添加一个菜单。将它添加到 HTML 的任何位置,最好是在正文的根级别。该.dropdown-menu课程将设置display:none所以它的最初不可见的。
它应该是这样的:

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

Extension Settings:

扩展设置:

To keep our design modular, we'll add our JavaScript code as a jQuery extension called contextMenu.

为了保持我们的设计模块化,我们将我们的 JavaScript 代码添加为名为 .js 的 jQuery 扩展contextMenu

When we call $.contextMenu, we'll pass in a settings object with 2 properties:

当我们调用 时$.contextMenu,我们将传入一个具有 2 个属性的设置对象:

  1. menuSelectortakes the jQuery selector of the menu we created earlier in HTML.
  2. menuSelectedwill be called when the context menu action is clicked.
  1. menuSelector采用我们之前在 HTML 中创建的菜单的 jQuery 选择器。
  2. menuSelected单击上下文菜单操作时将调用。
$("#myTable").contextMenu({
    menuSelector: "#contextMenu",
    menuSelected: function (invokedOn, selectedMenu) {
        // context menu clicked
    });
});

Plugin Template:

插件模板:

Based off the jQuery boilerplate plugin template, we'll use an Immediately-Invoked Function Expressionso we don't muddle up the global namespace. Since we have dependencies on jQuery and need access to the window, we'll pass them in as variables so we can survive minification. It will look like this:

基于jQuery 样板插件模板,我们将使用立即调用的函数表达式,这样我们就不会混淆全局命名空间。由于我们依赖于 jQuery 并且需要访问窗口,我们将它们作为变量传递,以便我们可以在缩小后继续存在。它看起来像这样:

(function($, window){

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

})(jQuery, window);

Okay, no more plumbing. Here's the meat of the function:

好了,不用下水管了。这是函数的主要内容:

Handle Right Click Events:

处理右键单击事件:

We'll handle the contextmenumouse event on the object that called the extension. When the event fires, we'll grab the dropdown menu that we added in the beginning. We'll locate it by using the selector string passed in by the settings when we initialized the function. We'll modify the menu by doing the following:

我们将处理contextmenu调用扩展的对象上的鼠标事件。当事件触发时,我们将获取我们在开始时添加的下拉菜单。我们将使用初始化函数时由设置传入的选择器字符串来定位它。我们将通过执行以下操作来修改菜单:

  • We'll grab the e.targetproperty and store it as a data attribute called invokedOn, so we can later identify the element that raised the context menu.
  • We'll toggle the menu's display to visible using .show()
  • We'll position the element using .css().
    • We need to make sure it's positionis set to absolute.
    • Then we'll set the left and top location using the pageXand pageYproperties of the event.
  • Finally, to prevent the right click action from opening up it's own menu, we'll return falseto stop the javascript from handling anything else.
  • 我们将获取该e.target属性并将其存储为名为 的数据属性invokedOn,以便我们稍后可以识别引发上下文菜单的元素。
  • 我们将使用 .show()
  • 我们将使用 定位元素.css()
    • 我们需要确保它position被设置为absolute.
    • 然后我们将使用事件的pageXpageY属性设置左侧和顶部位置。
  • 最后,为了防止右键单击操作打开它自己的菜单,我们将return false阻止 javascript 处理其他任何事情。

It will look like this:

它看起来像这样:

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

    return false;
});

Fix Menu Edge Cases:

修复菜单边缘情况:

This will open the menu to the bottom right of the cursor that opened it. However, if the cursor is to the far right of the screen, the menu should open to the left. Likewise, if the cursor is on the bottom, the menu should open to the top. It's also important to differentiate between the bottom of the window, which contains the physical frame, and the bottom of the documentwhich represents the entire html DOM and can scroll far past the window.

这将打开打开它的光标右下角的菜单。但是,如果光标位于屏幕最右侧,则菜单应向左打开。同样,如果光标在底部,菜单应该打开到顶部。区分包含物理框架的 底部和代表整个 html DOM 并且可以滚动超过窗口的底部window也很重要document

To accomplish this, we'll set the location using the following functions:

为此,我们将使用以下函数设置位置:

We'll call them like this:

我们会这样称呼它们:

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

Which will call this function to return the appropriate position:

这将调用此函数以返回适当的位置:

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
}

Bind Click Events on the Menu Element:

绑定菜单元素上的点击事件:

After we display the context menu, we need to add an event handler to listen for click events on it. We'll remove any other bindings that might have already been added so that we won't fire the same event twice. These can occur anytime the menu was opened, but nothing was selected due to clicking off. Then we can add a new binding on the clickevent where we'll handle the logic in the next section.

显示上下文菜单后,我们需要添加一个事件处理程序来侦听其上的点击事件。我们将删除可能已经添加的任何其他绑定,以便我们不会两次触发相同的事件。这些可以在菜单打开的任何时候发生,但由于单击关闭而未选择任何内容。然后我们可以在click事件上添加一个新的绑定,我们将在下一节中处理逻辑。

As valepu noted, we don't want to register clicks on anything other than menu items, so we setup a delegated handlerby passing a selector into the onfunction which will "filter the descendants of the selected elements that trigger the event".

正如valepu 所指出的,我们不想注册对菜单项以外的任何东西的点击,因此我们通过将选择器传递给函数来设置一个委托处理程序,该on函数将“过滤触发事件的选定元素的后代”。

So far, the function should look like this:

到目前为止,该函数应如下所示:

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

Handle Menu Clicks

处理菜单点击

Once we know a click has occurred on the menu, we'll do the following things: We'll hide the menu from the screen with .hide(). Next, we want to save the element on which the menu was originally invoked as well as the selection from the current menu. Finally, we'll fire the function option that was passed into the extension by using .call()on the property and passing in the event targets as arguments.

一旦我们知道在菜单上发生了点击,我们将执行以下操作: 我们将使用 将菜单从屏幕上隐藏.hide()。接下来,我们要保存最初调用菜单的元素以及当前菜单中的选择。最后,我们将通过.call()在属性上使用并传入事件目标作为参数来触发传递到扩展中的函数选项。

$menu.hide();

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

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

Hide When Clicked Off:

单击关闭时隐藏:

Finally, as with most context menus, we want to close the menu when a user clicks off of it as well. To do so, we'll listen for any click events on the body and close the context menu if it's open like this:

最后,与大多数上下文菜单一样,我们也希望在用户单击时关闭菜单。为此,我们将监听 body 上的任何点击事件,如果它像这样打开,则关闭上下文菜单:

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

Note: Thanks to Sadhir's comment, Firefox linux triggers the click event on documentduring a right click, so you have to setup the listener on body.

注意感谢 Sadhir 的评论,Firefox linuxdocument在右键单击期间触发 click 事件,因此您必须在body.

Example Syntax:

示例语法:

The extension will return with the original object that raised the context menu and the menu item that was clicked. You may have to traverse the domusing jQuery to find something meaningful from the event targets, but this should provide a good layer of base functionality.

扩展将返回引发上下文菜单的原始对象和单击的菜单项。您可能必须使用 jQuery遍历 dom以从事件目标中找到有意义的内容,但这应该提供一个很好的基础功能层。

Here's a example to return info for the item and action selected:

以下是返回所选项目和操作的信息的示例:

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

Screenshot:

截屏:

Context Menu Screenshot

上下文菜单截图

Update Note:

更新说明:

This answer has been updated substantially by wrapping it in a jQuery extension method. If you'd like to see my original, you can view the post history, but I believe this final version utilizes much better coding practices.

该答案已通过将其包装在 jQuery 扩展方法中进行了大幅更新。如果你想看我的原版,你可以查看帖子历史,但我相信这个最终版本利用了更好的编码实践。

Bonus Feature:

奖励功能

If you want to add some nice functionality for powerusers or yourself in developing features, you can bypass the context menu based on any key combinations being held when your right click. For example, if you wanted to allow the original browser context menu to display when holding Ctrl, you could add this as the first line of the contextMenu handler:

如果您想在开发功能时为高级用户或您自己添加一些不错的功能,您可以根据右键单击时按住的任何组合键绕过上下文菜单。例如,如果您希望在按住 时显示原始浏览器上下文菜单Ctrl,则可以将其添加为 contextMenu 处理程序的第一行:

// return native menu if pressing control
if (e.ctrlKey) return;

回答by letiagoalves

It is possible. I made you a working demo to give a good start.

有可能的。我为您制作了一个工作演示,以提供一个良好的开端。

Working demo(Right click on any table row to see it in action)

工作演示(右键单击任何表格行以查看其运行情况)

First create your dropdown menu, hide it and change its positionto absolute:

首先创建您的下拉菜单,隐藏它并将其更改positionabsolute

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

Then bind a contextmenuevent to your table rows so it shows dropdown/context menu and position it at the cursor:

然后将contextmenu事件绑定到您的表格行,以便它显示下拉/上下文菜单并将其定位在光标处:

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

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

Then when user select an option hide dropdown/context menu:

然后当用户选择一个选项隐藏下拉/上下文菜单时:

$contextMenu.on("click", "a", function() {
   $contextMenu.hide();
});

回答by Far Dmitry

Added some modifications to KyleMit's code:

KyleMit的代码添加了一些修改:

  • moved "on document click" handler from "foreach"
  • removed "foreach", just add event to selector
  • hide menu on "document context menu"
  • passing events

    $("#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");
    } });
    
  • 从“foreach”移动“on document click”处理程序
  • 删除了“foreach”,只需将事件添加到选择器
  • 隐藏“文档上下文菜单”上的菜单
  • 传递事件

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

http://jsfiddle.net/dmitry_far/cgqft4k3/

回答by Larigyn

I found this simple and working context menu. I'm using this library http://swisnl.github.io/jQuery-contextMenu/index.html. Hope it helps

我发现了这个简单且有效的上下文菜单。我正在使用这个库http://swisnl.github.io/jQuery-contextMenu/index.html。希望能帮助到你

table:

桌子:

<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;
        }
        },