📜 ⬆️ ⬇️

Context menu in javascript: small but powerful

You probably have seen javascript implementation of context menus on the basis of popular libraries such as jQuery and prototype. So they must have come across their main shortcomings: the inconvenience of the API, a large amount of code, demanding resources, the love of generating a huge amount of html code. At one point, these problems overcame my laziness, and I decided to fight them by setting the following tasks:

Context menu

UPD : placed the project in google code, use, develop:
svn checkout js-cmenu.googlecode.com/svn/trunk js-cmenu-read-only


Functional


There is a submenu. Their nesting is theoretically unlimited.
Menu items can be made inaccessible (disabled = true), invisible (visible = false), you can dynamically change the caption, icon and add new menu items and submenus.
It works correctly at different boundaries of the screen areas, the situation is worked out when the menu is in a scrolling diva (the scroll with the item that caused the menu).
Radio menu: select one of the menu items.
Several options for building and further behavior of the menu.

For reasons of lack of necessity, the following functions were removed: creating a menu by ajax request, calling the right button (does not work everywhere), horizontal menu (it was extremely rarely used).
')

How it works


There is a global collection where all-all menus (linear list) are collected. The menu itself is an object containing information about its behavior and state, as well as menu items. A menu item may contain submenus.

To display a menu on a page, you must point to some element of the page (I usually use pictures or links), and also indicate the event of this element on which the menu will be called if it (the event) is different from the click.

One menu can be drawn into many elements on the page. This menu will not multiply, but nevertheless will know where it was called from (this is essentially in the handler).

I tried to make the code as simple and clear as possible so that adding new functions and removing unnecessary things would not be difficult. For example, the implementation of the radio menu required five or six lines and 10 minutes of time. Modify the code as you want, but in the comments specify the link of the author.

Creation and Calling Examples


Menus can be created in different ways, depending on the degree of perversion of the menu that you want to end up with. The simplest thing is to pass an array of actions to the menu constructor.
var x = $.cmenu.getMenu([
new menuItem('', 'arrow_left',function(){history.back();}),
new menuItem('!', 'arrow_right',function(){history.forward();}),
new menuItem('','arrow_refresh',function(){location.href=location.href;})
]);
$('.callMenu').bindMenu(x);
$('#main_link').bindMenu(x);

At the exit, get a link to the finished menu. Which can be bind anywhere. Already zabindili! No, I'm lying. It can be even simpler: bind an array of actions to an element.
$('.callMenu').bindMenu([
new menuItem('', 'arrow_left',function(){history.back();}),
new menuItem('!', 'arrow_right',function(){history.forward();}),
new menuItem('','arrow_refresh',function(){location.href=location.href;})
]);

I completely forgot, it can be even simpler: instead of the actions, set an array of parameters of these actions.
$('.callMenu').bindMenu([
['', 'arrow_left',function(){history.back();}],
['!', 'arrow_right',function(){history.forward();}],
['','arrow_refresh',function(){location.href=location.href;}]
]);

this is equivalent to calling the action constructor
menuItem = function(caption,icon,execute,submenu)

or just setting the action via json
{
caption:'Caption',
icon:' undefined, , ',
visible:true,
disabled:false,
execute:function(){},
submenu:{-, , — }
}

This will cover the bulk of the tasks. But what if we need a menu that dynamically changes depending on external factors? I thought a lot about it, tried various implementations, and as a result I settled alone: ​​we passed a function to the menu constructor. This function will be called whenever you need to show the menu.
Attention: it is important!
To optimize the menu as a whole, this function works rather strangely. It receives as a single parameter the menu object at its full disposal. It must return either a lie (this will mean that the menu does not require redrawing), or truth, or an array of actions. But you can not return an array of actions, but simply write it into the “a” member of the menu object - menu.a = [array of actions], this is equivalent.
Often the menu depends not only on the state of the environment, but also on what element of the menu has caused. For this menu has a member caller. It contains a link to the DOM element that called the menu. For a submenu, this item will be a link to the home item of the parent menu, so it makes sense to look at the parentMenu member that contains the link to the parent menu.

A typical function looks like this:
  menuGenerator = function (menu) {
	 if (! menu.a) {
		 // initial menu initialization
		 return true;  // need to redraw
	 }
	 if (myVarChanged ()) {// something happened in the object model
		 menu.a.doAction.disabled = myVarValue ();
		 return true;  // need to redraw
	 }
	 if (menu.caller.id = 666) {
		 menu.a.doAction.visibe = false;
		 return true;  // need to redraw
	 }
	 return false;  // everything still, no redrawing
 } 


A few words about the action object. The most important method in it is execute. This method will be called when clicking on the menu item. It takes three parameters. The first is the action object itself, the second is the menu, the third is a chain of menu calls (for complex multi-level menus can be useful).
A less important member of the action object is the submenu. There may be an array of actions, or a function-generator menu.

For radio menu behavior, set the menu.type = 'radio' property in the menu object, and two methods: set (str) and get

Take a look at an example; the radio menu theme is revealed there.

And the last. Instead of bind you can use the construction more difficult. This will avoid garbage in the house in the form of events of non-existent elements. Yes, I'm talking about inline calls. There is a $ .cmenu.getCaller (menu) method or a “overloaded” $ .cmenu.getCaller (event, menu) method in the factory-menu-class that returns a line from parameters like this:
onclick="$.cmenu.show(0,this);$.cmenu.lockHiding=true;" onmouseout="$.cmenu.lockHiding=false;"
This line can be zafigachit element.

If you know the implementation is better, please do not hide - speak out.

Source: https://habr.com/ru/post/43111/


All Articles