Code Monkey home page Code Monkey logo

jquery-ui-contextmenu's Introduction

jquery.ui-contextmenu GitHub version Build Status Selenium Test Status

A jQuery plugin that provides a context menu (based on the standard jQueryUI menu widget).

  • Define menus from <ul> element or definition list (i.e. [{title: "Paste", cmd: "paste"}, ...]).
  • Themable using jQuery ThemeRoller.
  • Supports delegation (i.e. can be bound to elements that don't exist at the time the context menu is initialized).
  • Optional support for touch devices.

Status

The latest release is available at npm Registry:

$ npm install ui-contextmenu

GitHub version See also the Change Log.

Demo

Live demo page:
sample

Tutorial

First, include dependencies:

  • jQuery 1.7+ (1.10 or later recommended)
  • jQuery UI 1.9+ (at least core, widget, menu), 1.11+ recommended
  • One of the ThemeRoller CSS themes or a custom one
  • jquery.ui-contextmenu.js (also available as CDN on jsdelivr or cdnjs)
<head>
    <link href="//code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css" 
        type="text/css" rel="stylesheet" />
    <script src="//code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
    <script src="//code.jquery.com/ui/1.11.2/jquery-ui.min.js" type="text/javascript"></script>
    <script src="assets/jquery.ui-contextmenu.min.js"></script>

Assume we have some HTML elements that we want to attach a popup menu to:

<div id="container">
    <div class="hasmenu">AAA</div>
    <div class="hasmenu">BBB</div>
    <div class="hasmenu">CCC</div>
</div>

Now we can enable a context menu like so:

$("#container").contextmenu({
	delegate: ".hasmenu",
	menu: [
		{title: "Copy", cmd: "copy", uiIcon: "ui-icon-copy"},
		{title: "----"},
		{title: "More", children: [
			{title: "Sub 1", cmd: "sub1"},
			{title: "Sub 2", cmd: "sub1"}
			]}
		],
	select: function(event, ui) {
		alert("select " + ui.cmd + " on " + ui.target.text());
	}
});

The delegate option defines a CSS selector, which is evaluated for all elements inside the context element (#container in our example).
In order to attach menus to all matching elements on the page that have class="hasmenu", we may use document as context:

$(document).contextmenu({
    delegate: ".hasmenu",
    ...
});

Note: only one contextmenu widget instance can be bound to one element. See the Howto below for a solution to this problem.

The menu options may contain a (nested) array of entry defiitions. Following a list of available properties:

action
Type: Function, default: n.a.
Optional callback that will be executed when the entry is selected.
addClass
Type: String, default: ""
Additional class name(s) to be added to the entries <li> element. Separate multiple class names with a space.
Custom CSS may be applied like .ui-menu .my-class { color: red; }.
cmd
Type: String, default: ""
Optional identifier associated with the menu entry. It can later be accessed in the select event as ui.cmd.
data
Type: Object, default: {}
Optional hash of additional properties that will be added to the entry's data attribute.
It can later be accessed in the select event as ui.item.data().
disabled
Type: Boolean, default: false
Pass true to disable the entry.
title
Type: String, default: ""
The displayed name of the menu entry. Use dashes ("---") to define a separator.
uiIcon
Type: String, default: ""
If defined, an icon is added to the menu entry. For example passing "ui-icon-copy" will generate this element: <span class='ui-icon ui-icon-copy' />.
See also <Icon Overview.

Instead of handling all menu commands in the select event, it is also possible to attach callbacks directly to menu entries:

$(document).contextmenu({
    delegate: ".hasmenu",
    menu: [
        {title: "Copy", uiIcon: "ui-icon-copy", action: function(event, ui){
                alert("Copy " + ui.target.text());
             }
         },
        ...
});

Initialize menu from an existing <ul> element

In this case menu must point to the markup:

$(document).contextmenu({
    delegate: ".hasmenu",
    menu: "#options",
    select: function(event, ui) {
    	...
    }
});

We also have to provide some HTML markup that defines the context menu structure, see jQueryUI menu for details:

<ul id="options" class="ui-helper-hidden">
    <li data-command="copy"><span class="ui-icon ui-icon-copy"></span>Copy</li>
    <li data-command="paste" class="ui-state-disabled">Paste</li>
    <li>----</li>
    <li>More
        <ul>
            <li data-command="sub1">Sub 1</li>
            <li data-command="sub2">Sub 2</li>
        </ul>
    </li>
</ul>

Note: until and including jQuery UI 1.10 the use of anchors (<a>) in menu items was required:

<ul id="options" class="ui-helper-hidden">
    <li data-command="copy"><a href="#"><span class="ui-icon ui-icon-copy"></span>Copy</a>
    ...
</ul>

Modify the menu depending on the context

Often we need to modify the menu before it is displayed, in order to reflect the current context. This can be done in the beforeOpen event:

$(document).contextmenu({
    delegate: ".hasmenu",
    menu: [
        {title: "Cut", cmd: "cut", uiIcon: "ui-icon-scissors"},
        {title: "Copy", cmd: "copy", uiIcon: "ui-icon-copy"},
        {title: "Paste", cmd: "paste", uiIcon: "ui-icon-clipboard", disabled: true },
        ...
        ],
    beforeOpen: function(event, ui) {
        var $menu = ui.menu,
            $target = ui.target,
            extraData = ui.extraData; // optionally passed when menu was opened by call to open()

        // Optionally return false, to prevent opening the menu
//      return false;

        // En/disable single entries
        $(document).contextmenu("enableEntry", "paste", false);
        // Show/hide single entries
        $(document).contextmenu("showEntry", "cut", false);
        // Redefine the title of single entries
        $(document).contextmenu("setEntry", "copy", "Copy '" + $target.text() + "'")
        // Redefine all attributes of single entries
        $(document).contextmenu("setEntry", "cut", {title: "Cuty", uiIcon: "ui-icon-heart", disabled: true});
        // Redefine the whole menu
        $(document).contextmenu("replaceMenu", [{title: "aaa"}, {title: "bbb"}, ...]);
        // Redefine the whole menu from another HTML definition
        $(document).contextmenu("replaceMenu", "#options2");
    },
    ...
});

API documentation

Options

addClass
Type: String, default: "ui-contextmenu"
This class is added to the outer ul element.
autoFocus
Type: Boolean, default: false
Set keyboard focus to first menu entry on open.
autoTrigger
Type: Boolean, default: true
Set `false` to prevent opening on a browser's `contextmenu` event, which is normally triggered by a mouse rightclick.
The menu can still be opened by calling the `open()` method.
delegate
Type: String
A selector to filter the elements that trigger the context menu.
hide
Type: Boolean | Number | String | Object, default: { effect: "fadeOut", duration: "fast" }
Effect applied when hiding the popup.
See sample for possible option values.
ignoreParentSelect
Type: Boolean, default: true
If true, a click on a menu item that contains a sub-menu, will not trigger the select event.
menu
Type: Object[] | String | jQuery
jQuery object or selector of HTML markup that defines the context menu structure (see jQueryUI menu for details).
If an array of objects is passed, it will be used to generate
such markup on the fly.
position
Type: Object | Function,
default: {my: "left top", at: "center", of: event, collision: "fit"}
Define position where popup opens. A simple position may be passed.
Also a function may be specified, to recalculate position every time:
    $("#container").contextmenu({
        position: function(event, ui){
            return {my: "left top", at: "left bottom", of: ui.target};
        }, ...
preventContextMenuForPopup
Type: Boolean, default: false
Prevent that a right click inside an open popup menu will open the browser's system context menu.
preventSelect
Type: Boolean, default: false
Prevent accidental text selection of potential menu targets on doubleclick or drag.
show
Type: Boolean | Number | String | Object, default: { effect: "slideDown", duration: "fast"}
Effect applied when showing the popup.
See sample for possible option values.
taphold
Type: Boolean, default: false
Open menu on taphold events, which is especially useful for touch devices (but may require external plugins to generate taphold events).
tooltip
Type: String, optional
Add a title attribute to the menu markup, which will be displayed as tooltip by most browser (or external plugins).
uiMenuOptions
Type: Object, default: {}
Custom options passed to UI Menu, when the widget is created.
Especially useful to tweak the position of submenus.

Methods

close()
Close context menu if open.
Call like $(...).contextmenu("close");.
enableEntry(cmd, flag)
Enable or disable the entry. `flag` defaults to `true`
Call like $(...).contextmenu("enableEntry", "paste", false);.
getMenu()
Return the jQuery object for the menu's UL element.
isOpen()
Return true if popup is visible.
open(target[, extraData])
Open context menu on a specific target (target (as a jQuery object) must match the options.delegate filter).
Call like $(...).contextmenu("open", $(target)[, extraData]);. Optional `extraData` will be available in event handlers as ui.extraData.
replaceMenu(menu)
Replace the whole menu definition.
Call like $(...).contextmenu("replaceMenu", "#menu2");. or $(...).contextmenu("replaceMenu", [{title: "aaa"}, {title: "bbb"}, ...]);.
setEntry(cmd, data)
Redefine menu entry (title or all of it).
`data` may be a title string or a menu definition object.
Call like $(...).contextmenu("setEntry", "paste", "Paste link");.
showEntry(cmd, flag)
Show or hide the entry. `flag` defaults to `true`
Call like $(...).contextmenu("showEntry", "paste", false);.

Events

jquery-contextmenu exposes events from jQueryUI menu: blur, create, focus, select. However, since the event.target parameter contains the menu item, we additionally pass the element that was right-clicked in ui.target.

Events may be handled by passing a handler callback option:

$("#container").contextmenu({
    [...]
    select: function(event, ui) {
        alert("select " + ui.cmd + " on " + ui.target.text());
    }
});

Alternatively a handler may be bound, so this is equivalent:

$("#container").bind("contextmenuselect", function(event, ui) {
    alert("select " + ui.cmd + " on " + ui.target.text());
});
beforeOpen(event, ui)
Triggered just before the popup menu is opened.
Return false to prevent opening.
This is also a good place to modify the menu (i.e. hiding, disabling, or renaming entries, or replace the menu altogether).
blur(event, ui)
Triggered when the menu loses focus (original jQuery UI Menu event).
close(event)
Triggered when the menu is closed.
create(event, ui)
Triggered when the contextmenu widget is created.
createMenu(event, ui)
Triggered when the popup menu is created (original jQuery UI Menu `create` event).
focus(event, ui)
Triggered when a menu gains focus or when any menu item is activated (original jQuery UI Menu event).
open(event)
Triggered when the menu is opened.
select(event, ui)
Triggered when a menu item is selected.
ui.cmd contains the command id. Return false to prevent closing the menu.

Tips and Tricks

[Howto] Add right-aligned shortcut hints

Simply add a tag of your choice to the title (for example <kbd>)

$(document).contextmenu({
    delegate: ".hasmenu",
    menu: [
        {title: "Edit title<kbd>[F2]</kbd>", cmd: "rename"}, 
        {title: "Copy <kbd>[Ctrl+C]</kbd>", cmd: "copy"}, ...
        ],

and make it right aligned via CSS:

.ui-menu kbd {
    float: right;
}

[Howto] Enable keyboard control

In order open a context menu with the keyboard, make sure the target elements are tabbable, for example by adding a tabindex="0" attribute. Also make sure the autoFocus: true option is set. This will allow to Use Tab and the Windows Menu keys.

[Howto] Modify the menu using an asynchronous request

$(document).contextmenu({
    ...
    beforeOpen: function(event, ui) {
        // Immediate menu changes
        $(document).contextmenu("setEntry", "test", "(loading...)");
        // Menu opens, then we submit a request and wait for the resonse
        $.ajax({
            ...
        }).done(function(data) {
            // Modify the menu from the ajax response. The menu will be updated
            // while open
            $(document).contextmenu("setEntry", "test", {
                title: "New entry", cmd: "test", 
                children: [ ... ]
                });
        });
    },

Alternatively we can delay the opening until the response arrives:

$(document).contextmenu({
    ...
    beforeOpen: function(event, ui) {
        var dfd = new $.Deferred();

        $.ajax({
            ...
        }).done(function(data) {
            // Modify the menu from the ajax response. The menu will be opened
            // afterwards
            $(document).contextmenu("setEntry", "test", {
                title: "New entry", cmd: "test", 
                children: [ ... ]
                });
            dfd.resolve(); // Notify about finished response
        });

        // Return a promise to delay opening until an async response becomes
        // available
        ui.result = dfd.promise();
    },

[Howto] Bind different contextmenus to the same DOM element

This is especially useful if we want to bind contextmenus for different selectors to the document element, in order to make them global:

$(document).contextmenu({
    delegate: ".hasmenu",
    menu: ...,
    select: function(event, ui) {
        alert("select contextmenu 1" + ui.cmd + " on " + ui.target.text());
    }
});

Another call to $(document).contextmenu({...}) would destroy the previous instance, because the jQuery Widget Factory only allows one instance per element.

The soulution is to create new widget with another name but identical functionality:

// 1. Create and register another widget that inherits directly from 
//    jquery-ui-contextmenu:
$.widget("moogle.contextmenu2", $.moogle.contextmenu, {});
// 2. Now we can bind this new widget to the same DOM element without
//    destroying a previous widget.
$(document).contextmenu2({
    delegate: ".hasmenu2",
    menu: ...,
    select: function(event, ui) {
        alert("select contextmenu2" + ui.cmd + " on " + ui.target.text());
    }
});

Credits

Thanks to all contributors.

Browser Status Matrix

Selenium Test Status

jquery-ui-contextmenu's People

Contributors

a-sassermann avatar abzainuddin avatar francisrath avatar gvas avatar jdaltemus avatar johnarcher avatar mar10 avatar topolm avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.