Home  >  Article  >  Web Front-end  >  Several forms of tree structure menus in JavaScript_javascript skills

Several forms of tree structure menus in JavaScript_javascript skills

WBOY
WBOYOriginal
2016-05-16 18:27:44990browse
1. Suspended layer tree (Tree)
This tree structure implements a function similar to breadcrumb navigation. It listens to the mouse movement event of the node, and then displays the child nodes below or to the right of the node, and so on. Recursively displays the child nodes of child nodes.

User homepage blog settings article album message comment system
There are a few small issues to pay attention to here. Firstly, this kind of tree structure is the absolute positioning of the suspended layer. When creating the layer, it must be placed directly on the body. Below, this is done to ensure that any layer can be covered in IE, because there are hidden rules for things like stacking context in IE. In addition, of course there is a select Can you cover me? An old question, here is to add an iframe element behind each floating layer. Of course, the menu at the same level only generates one iframe element. If the menu has several levels, it will generate several iframe masks. Then when the menu is displayed and hidden, it will be displayed and hidden at the same time. Hide iframe.

However, this kind of menu is not suitable for the front-end, because currently it only supports dynamically adding menu nodes in scripts, and cannot obtain menu nodes from existing html elements. For the sake of SEO and other front-end navigation, we usually dynamically add them in the background For output, if the menu has multiple levels, it is recommended not to exceed 2 levels. Customers will be too lazy to look at too many levels, but it is still very good to have a breadcrumb navigation display.

menu.js
Copy code The code is as follows:

/*
** Author : Jonllen
** Create : 2009-12-13
** Update : 2010-05-08
** SVN : 152
** WebSite: http://www.jonllen.com/
*/
var Menu = function (container) {
this.container = container;
return this;
}
Menu.prototype = {
list : new Array(),
active : new Array(),
iframes : new Array(),
settings : {
id : null,
parentId : 0,
name : null,
url : null,
level : 1,
parent : null,
children : null,
css : null,
element : null
},
push : function (item) {
var list = Object.prototype.toString.apply(item) === '[object Array]' ? item : [item];
for( var i=0; i< list.length; i ) {
var settings = list[i];
for( p in this.settings) {
if( !settings.hasOwnProperty(p) ) settings[p] = this.settings[p];
}
this.list.push(settings);
}
return this;
},
getChlid : function (id) {
var list = new Array();
for( var i=0;i < this.list.length; i )
{
var item = this.list[i];
if( item.parentId == id)
{
list.push(item);
}
}
return list;
},
render : function (container) {
var _this = this;
var menuElem = container || this.container;
for( var i=0;i < this.list.length; i )
{
var item = this.list[i];
if ( item.parentId != 0 ) continue;
var itemElem = document.createElement('div');
itemElem.innerHTML = '' item.name '';
itemElem.className = 'item';
if ( item.css ) itemElem.className = ' ' item.css;
var disabled = (' ' item.css ' ').indexOf(' disabled ')!=-1;
if ( disabled ) {
itemElem.childNodes[0].disabled = true;
itemElem.childNodes[0].className = 'disabled';
itemElem.childNodes[0].removeAttribute('href');
}
if ( (' ' item.css ' ').indexOf(' hidden ')!=-1 ) {
itemElem.style.display = 'none';
}
itemElem.menu = item;
itemElem.menu.children = this.getChlid(item.id);
itemElem.onmouseover = function (e){
_this.renderChlid(this);
};
menuElem.appendChild(itemElem);
}
document.onclick = function (e){
e = window.event || e;
var target = e.target || e.srcElement;
if (!target.menu) {
var self = _this;
for( var i=1;i<_this.active.length;i ) {
var item = _this.active[i];
var menuElem = document.getElementById('menu' item.id);
if ( menuElem !=null)
menuElem.style.display = 'none';
}
for(var j=1;j<_this.iframes.length;j ){
_this.iframes[j].style.display = 'none';
}
}
};
},
renderChlid : function (target){
var self = this;
var item = target.menu;
var activeItem = self.active[item.level];
while(activeItem) {
var activeItemElem = activeItem.element;
if ( activeItemElem!= null ) activeItemElem.style.display = 'none';
activeItem = self.active[activeItem.level 1];
}
self.active[item.level] = item;
var level = item.level;
while(this.iframes[level]) {
this.iframes[level].style.display = 'none';
level ;
}
var childElem = document.getElementById('menu' item.id);
if (childElem==null) {
var hasChild = false;
for( var j=0;jif( (' ' item.children[j].css ' ').indexOf(' hidden ') == -1) {
hasChild = true;
break;
}
}
if( hasChild) {
var xy = self.elemOffset(target);
var x = xy.x;
var y = target.offsetHeight xy.y;
if ( item.level >= 2 )
{
x = target.offsetWidth - 1;
y -= target.offsetHeight;
}
childElem = document.createElement('div');
childElem.id = 'menu' item.id;
childElem.className = 'child';
childElem.style.position = 'absolute';
childElem.style.left = x 'px';
childElem.style.top = y 'px';
childElem.style.zIndex = 1000 item.level;
for( var i=0;i < item.children.length; i )
{
var childItem = item.children[i];
var childItemElem = document.createElement('a');
var disabled = (' ' childItem.css ' ').indexOf('disabled')!=-1;
if ( disabled ) {
childItemElem.disabled = true;
childItemElem.className = ' ' childItem.css;
}else {
childItemElem.href = childItem.url;
}
if ( (' ' childItem.css ' ').indexOf(' hidden ')!=-1 ) {
childItemElem.style.display = 'none';
}
childItemElem.innerHTML = childItem.name;
childItemElem.menu = childItem;
childItemElem.menu.children = self.getChlid(childItem.id);
var hasChild = false;
for( var j=0;jif( (' ' childItemElem.menu.children[j].css ' ').indexOf(' hidden ') == -1) {
hasChild = true;
break;
}
}
if( hasChild ) {
childItemElem.className = ' hasChild';
}
childItemElem.onmouseover = function (e) {
self.renderChlid(this)
};
childElem.appendChild(childItemElem);
}
document.body.insertBefore(childElem,document.body.childNodes[0]);
item.element = childElem;
}
}
if( childElem!=null) {
var iframeElem = this.iframes[item.level];
if ( iframeElem == null) {
iframeElem = document.createElement('iframe');
iframeElem.scrolling = 'no';
iframeElem.frameBorder = 0;
iframeElem.style.cssText = 'position:absolute; overflow:hidden;';
document.body.insertBefore(iframeElem,document.body.childNodes[0]);
this.iframes[item.level]=iframeElem;
}
childElem.style.display = 'block';
iframeElem.width = childElem.offsetWidth;
iframeElem.height = childElem.offsetHeight;
iframeElem.style.left = parseInt(childElem.style.left) 'px';
iframeElem.style.top = parseInt(childElem.style.top) 'px';
iframeElem.style.display = 'block';
}
},
elemOffset : function(elem){
if( elem==null) return {x:0,y:0};
var t = elem.offsetTop;
var l = elem.offsetLeft;
while( elem = elem.offsetParent) {
t = elem.offsetTop;
l = elem.offsetLeft;
}
return {x : l,y : t};
}
};

演示地址 http://demo.jb51.net/js/tree_json/menu.htm
打包下载地址

2.右键菜单树(ContextMenu)
自定义右键菜单(ContextMenu)和悬浮层树(Tree)其实现上都大同小异,都是在脚本里动态添加节点,然后在生成一个绝对定位层,只不过右键菜单树(ContextMenu)触发的事件不一样。另外右键菜单还需要提供一个动态添加菜单项功能,以实现右击不同的元素可以显示不同的右键菜单,我这里提供一种"回调函数",使用见如下代码:
ContextMenu回调函数
复制代码 代码如下:

//ContextMenu
var contextmenu = new ContextMenu(...{ container : document.getElementById('treemenu') });
contextmenu.push( ...{ html : 'Powered By: Jonllen', css : 'disabled'});
contextmenu.push( ...{ html : '', css : 'line'});
contextmenu.push( ...{ html : '刷新(R)', href : 'javascript:location.reload();'});
for(var i=0;icontextmenu.push(...{
id : menu[i].id,
level : menu[i].level,
parentId : menu[i].parentId,
html : menu[i].name,
href : menu[i].url
});
}
contextmenu.render();
//原有回调函数
var contextmenuOnShow = contextmenu.onShow;
//设置新的回调函数
contextmenu.onShow = function (target, _this)...{
var item = target.treemenu || target.parentNode.treemenu;
if( item ) ...{
var html = '添加' item.html '“子节点' (item.children.length 1) '”';
_this.push( ...{
html : html,
click : function (e)...{
item.expand = false;
var newItem = ...{
id : item.id '0' (item.children.length 1),
level : item.level 1,
parentId : item.id,
html : item.html '子节点' (item.children.length 1),
href : '#',
css : 'item',
createExpand : true
};
item.children.push(newItem);
treemenu.list.push(newItem);
treemenu.renderChild(item);
},
clickClose : true,
index : 1,
type : 'dynamic'
});
_this.push( ...{
html : '删除节点“' item.html '”',
click : function (e)...{
if( confirm('是否确认删除节点“' item.html '”?'))
treemenu.remove(item);
},
clickClose : true,
index : 2,
type : 'dynamic'
});
}
contextmenuOnShow(target, _this);
};

So how to implement the "callback function"? In fact, it is very simple. When the function reaches a certain line of code, it runs a preset "callback function". It is a bit like an event mechanism, like binding multiple window.onload events. Since there may be bound functions before, record the previous functions first. , then set the newly bound function, and then call the previously bound function. The code shown above realizes that if the right-click element is a treemenu node, the add and delete treemenu node menu will be added in the right-click. The effect is shown in the node tree (TreeMenu) example below.
We need to pay attention to the scope in the callback function. The this pointer points to the current callback function object, not the context in which the callback function is run. However, we can also use the call method to run the callback function in the current this context. Here I adopt the method of passing 2 parameters to the callback function, so that the callback function can easily obtain this object and other variables. This is commonly used in the Ajax Callback function.
The custom right-click menu (ContextMenu) is only suitable for shortcut operations of some auxiliary functions. For example, if there are some OA systems with complex business functions, I will also use it in conjunction with the node tree (TreeMenu) below. If possible, try not to use the right-click menu. Firstly, users need to be trained in the habit of right-clicking. Secondly, customizing the right-click menu loses some functions of the original right-click menu, such as viewing source files.
Here is the right-click menu area.
Right-click me and you can see the properties.
You can also select me and right-click to copy.
Can you cover me?
ContextMenu.js
Copy code The code is as follows:

/**//*
** Author : Jonllen
** Create : 2010-05-01
** Update : 2010-05-09
** SVN : 153
** WebSite: http://www.jonllen.com/
*/
var ContextMenu = function (settings) ...{
for( p in this.settings)
...{
if( !settings.hasOwnProperty(p) ) settings[p] = this.settings[p];
}
this.settings = settings;
this.settings.menu = document.createElement('div');
this.settings.menu.className = this.settings.css;
this.settings.menu.style.cssText = 'position:absolute;display:none;';
document.body.insertBefore(this.settings.menu,document.body.childNodes[0]);
return this;
}
ContextMenu.prototype = ...{
list : new Array(),
active : new Array(),
iframes : new Array(),
settings : ...{
menu : null,
excursionX : 0,
excursionY : 0,
css : 'contextmenu',
container : null,
locked : false
},
item : ...{
id : null,
level : 1,
parentId : 0,
html : '',
title : '',
href : 'javascript:;',
target : '_self',
css : null,
element : null,
childElement : null,
parent : null,
children : null,
type : 'static',
click : null,
clickClose : false
},
push : function (item) ...{
var list = Object.prototype.toString.apply(item) === '[object Array]' ? item : [item];
for( var i=0; i< list.length; i ) ...{
var _item = list[i];
for( p in this.item) ...{
if( !_item.hasOwnProperty(p) ) _item[p] = this.item[p];
}
_item.element = null;
if( _item.name ) _item.html = _item.name;
if( _item.url ) _item.href = _item.url;
if( _item.type == 'static') ...{
this.list.push(_item);
}else ...{
if(this.dynamic == null) this.dynamic = new Array();
this.dynamic.push(_item);
}
}
return this;
},
bind : function ()...{
var _this = this;
for( var i=0; this.dynamic && i...{
var item = this.dynamic[i];
var itemElem = document.createElement('div');
itemElem.title = item.title;
itemElem.innerHTML = '' item.html '';
itemElem.className = 'item ' (item.css?' ' item.css:'');
item.element = itemElem;
if( item.click ) ...{
(function (item)...{
item.element.childNodes[0].onclick = function (e)...{
if( item.clickClose) _this.hidden();
return item.click(e);
};
})(item);
}
itemElem.contextmenu = item;
itemElem.onmouseover = function (e)...{ _this.hidden(item.level);};
var index = item.index || 0;
if( index >= this.settings.menu.childNodes.length)
index = this.settings.menu.childNodes.length - 1;
if( index < 0 )
this.settings.menu.appendChild(itemElem);
else
this.settings.menu.insertBefore(itemElem, this.settings.menu.childNodes[index]);
}
},
render : function ( container ) ...{
var _this = this;
container = container || this.settings.container;
this.settings.menu.innerHTML = '';
for( var i=0;i < this.list.length; i )
...{
var item = this.list[i];
if ( item.parentId != 0 ) continue;
var itemElem = document.createElement('div');
itemElem.title = item.title;
itemElem.innerHTML = '' item.html '';
itemElem.className = 'item ' (item.css?' ' item.css:'');
var disabled = _this.hasClass(itemElem, 'disabled');
if ( disabled ) ...{
itemElem.childNodes[0].disabled = true;
itemElem.childNodes[0].className = 'disabled';
itemElem.childNodes[0].removeAttribute('href');
}
if ( _this.hasClass(itemElem, 'hidden') ) ...{
itemElem.style.display = 'none';
}
if( item.click ) ...{
(function (item)...{
item.element.childNodes[0].onclick = function (e)...{
if( item.clickClose) _this.hidden();
return item.click(e);
};
})(item);
}
itemElem.contextmenu = item;
itemElem.contextmenu.children = this.getChlid(item.id);
if( itemElem.contextmenu.children.length > 0 )
itemElem.childNodes[0].className = ' hasChild';
itemElem.onmouseover = function (e)...{ _this.renderChlid(this);};
this.settings.menu.appendChild(itemElem);
}
this.active[0] = ...{ element : _this.settings.menu };
this.settings.menu.contextmenu = _this;
container.oncontextmenu = function (e)...{
e = window.event || e;
var target = e.target || e.srcElement;
if( e.preventDefault)
e.preventDefault();
var mouseCoords = _this.mouseCoords(e);
_this.settings.menu.style.left = mouseCoords.x _this.settings.excursionX 'px';
_this.settings.menu.style.top = mouseCoords.y _this.settings.excursionY 'px';
_this.hidden();
_this.show(0, target);
return false;
};
this.addEvent(document, 'click', function (e)...{
e = window.event || e;
var target = e.target || e.srcElement;
var isContextMenu = !!target.contextmenu;
if( isContextMenu == false) ...{
var parent = target.parentNode;
while( parent!=null) ...{
if( parent.contextmenu) ...{
isContextMenu = true;
break;
}
parent = parent.parentNode;
}
}
if (isContextMenu == false) ...{
_this.hidden();
}
});
},
renderChlid : function ( target )...{
if(this.settings.locked) return;
var contextmenu = target.contextmenu;
var currentLevel = contextmenu.level;
this.hidden(currentLevel);
var hasChild = false;
for( var j=0;jif( (' ' contextmenu.children[j].css ' ').indexOf(' hidden ') == -1) ...{
hasChild = true;
break;
}
}
if( !hasChild) return;
var childElem = contextmenu.element;
if (childElem == null) ...{
childElem = document.createElement('div');
childElem.className = this.settings.css;
childElem.style.position = 'absolute';
childElem.style.zIndex = 1000 contextmenu.level;
var _this = this;
for( var i=0;i < contextmenu.children.length; i )
...{
var childItem = contextmenu.children[i];
var childItemElem = document.createElement('div');
childItemElem.title = childItem.title;
childItemElem.innerHTML = '' childItem.html '';
childItemElem.className = 'item' (childItem.css?' ' childItem.css : '');
var disabled = this.hasClass(childItemElem, 'disabled');
if ( disabled ) ...{
childItemElem.childNodes[0].disabled = true;
childItemElem.childNodes[0].removeAttribute('href');
}
if ( this.hasClass(childItemElem, 'hidden') ) ...{
childItemElem.style.display = 'none';
}
if( childItem.click ) ...{
(function (childItem)...{
childItem.element.childNodes[0].onclick = function (e)...{
if( childItem.clickClose) _this.hidden();
return childItem.click(e);
};
})(childItem);
}
childItem.parent = contextmenu;
childItemElem.contextmenu = childItem;
childItemElem.contextmenu.children = this.getChlid(childItem.id);
var hasChild = false;
for( var j=0; jif( (' ' childItemElem.contextmenu.children[j].css ' ').indexOf(' hidden ') == -1) ...{
hasChild = true;
break;
}
}
if( hasChild ) ...{
childItemElem.childNodes[0].className = ' hasChild';
}
childItemElem.onmouseover = function (e)...{ _this.renderChlid(this);};
childElem.appendChild(childItemElem);
}
document.body.insertBefore(childElem,document.body.childNodes[0]);
contextmenu.element = childElem;
}
this.active[currentLevel] = contextmenu;
var xy = this.elemOffset(target);
var x = xy.x target.offsetWidth this.settings.excursionX;
var y = xy.y this.settings.excursionY;
childElem.style.left = x 'px';
childElem.style.top = y 'px';
childElem.style.display = 'block';
this.show(currentLevel);
},
getChlid : function (id) ...{
var list = new Array();
for( var i=0;i < this.list.length; i )
...{
var item = this.list[i];
if( item.parentId == id)
...{
list.push(item);
}
}
return list;
},
show : function (level, target) ...{
if(this.settings.locked) return;
level = level || 0;
var item = this.active[level];
if ( level == 0 ) ...{
for( var i=0;this.dynamic && i < this.dynamic.length; i )
...{
var dynamicItemElem = this.dynamic[i].element;
if( dynamicItemElem !=null) dynamicItemElem.parentNode.removeChild(dynamicItemElem);
}
if (this.dynamic) this.dynamic.length = 0;
this.onShow(target, this);
}
var menuElem = item.element;
menuElem.style.display = 'block';
var iframeElem = this.iframes[level];
if ( iframeElem == null) ...{
iframeElem = document.createElement('iframe');
iframeElem.scrolling = 'no';
iframeElem.frameBorder = 0;
iframeElem.style.cssText = 'position:absolute; overflow:hidden;';
document.body.insertBefore(iframeElem,document.body.childNodes[0]);
this.iframes.push(iframeElem);
}
iframeElem.width = menuElem.offsetWidth;
iframeElem.height = menuElem.offsetHeight;
var menuElemOffset = this.elemOffset(menuElem);
iframeElem.style.left = menuElemOffset.x 'px';
iframeElem.style.top = menuElemOffset.y 'px';
iframeElem.style.display = 'block';
},
onShow : function (target, _this) ...{
if( target.nodeType == 1 && target.tagName == 'A' && target.innerHTML.indexOf('.rar') != -1 )...{
//解压文件
_this.push( ...{
html : '解压缩到“' target.innerHTML.substring(0,target.innerHTML.lastIndexOf('.')) '\”...',
click : function (e)...{
e = e || window.event;
var srcElement = e.srcElement || e.target;
srcElement.className = 'on';
srcElement.innerHTML = '解压缩到“' target.innerHTML.substring(0,target.innerHTML.lastIndexOf('.')) '\”...';
var url = '/Ajax/FileZip.aspx?mode=unzip&files=' target.href.substring(target.href.replace('//','xx').indexOf('/'));
if( typeof Ajax == 'undefined') return;
Ajax.get(url, function (data, _this)...{
_this.settings.locked = true;
eval(data);
if( rs.success ) ...{
location.reload();
}else...{
alert(rs.error);
_this.hidden();
}
}, _this);
srcElement.onclick = null;
_this.settings.locked = true;
},
clickClose : false,
index : 2,
type : 'dynamic'
});
}
else if( target.nodeType == 1 && target.title.indexOf('添加到') == 0) ...{
//添加单个压缩文件
_this.push( ...{
html : target.title,
title : target.title,
click : function (e)...{
var index = target.href.indexOf('?path=');
if( index != -1)...{
var fullName = target.href.substring(index '?path='.length);
}else ...{
var fullName = target.href.substring(target.href.replace('//','xx').indexOf('/'));
}
e = e || window.event;
var srcElement = e.srcElement || e.target;
srcElement.className = 'on';
srcElement.innerHTML = '正在添加到“' fullName.substring(fullName.lastIndexOf('/') 1) '.rar”...';
var url = '/Ajax/FileZip.aspx?mode=zip&files=' fullName;
if( typeof Ajax == 'undefined') return;
Ajax.get(url, function (data, _this)...{
_this.settings.locked = true;
eval(data);
if( rs.success ) ...{
location.reload();
}else...{
alert(rs.error);
_this.hidden();
}
}, _this);
srcElement.onclick = null;
_this.settings.locked = true;
},
clickClose : false,
index : 2,
type : 'dynamic',
css : 'on'
});
}else ...{
//添加多个压缩文件
var fileName = '';
var files = new Array();
var ids = document.getElementsByName('ids');
for( var i=0; iif( !ids[i].checked) continue;
var file = ids[i].value;
files.push(file);
if( files.length == 1) ...{
fileName = file.substring(file.lastIndexOf('/') 1) '.rar';
}
}
if( files.length > 0 )...{
_this.push( ...{
html : '添加' files.length '个文件到压缩包“' fileName '”',
click : function (e)...{
e = e || window.event;
var srcElement = e.srcElement || e.target;
srcElement.className = 'on';
srcElement.innerHTML = '正在添加到“' fileName '”...';
var url = '/Ajax/FileZip.aspx?mode=zip&files=' files.join('|');
if( typeof Ajax == 'undefined') return;
Ajax.get(url, function (data, _this)...{
_this.settings.locked = true;
eval(data);
if( rs.success ) ...{
location.reload();
}else...{
alert(rs.error);
_this.hidden();
}
}, _this);
srcElement.onclick = null;
_this.settings.locked = true;
},
clickClose : false,
index : 2,
type : 'dynamic'
});
}
}
if( target.nodeType == 1 && target.tagName == 'A') ...{
_this.push( ...{
html : '属性“' target.innerHTML '”',
href : target.href,
click : function (e)...{
prompt('属性“' target.innerHTML '”',target.href);
return false;
},
clickClose : true,
index : 3,
type : 'dynamic'
});
}
var selection = window.getSelection ? window.getSelection().toString() : document.selection.createRange().text;
if( selection ) ...{
_this.push( ...{
html : '复制“' (selection.length > 15 ? selection.substring(0,12) '...' : selection) '”',
title : '复制“' selection '”',
click : function (e) ...{
if(window.clipboardData) ...{
window.clipboardData.clearData();
window.clipboardData.setData("Text", selection);
}else ...{
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var clip = Components.classes['@mozilla.org/widget/clipboard;1'].createInstance(Components.interfaces.nsIClipboard);
var trans = Components.classes['@mozilla.org/widget/transferable;1'].createInstance(Components.interfaces.nsITransferable);
if (!clip || !trans) return;
trans.addDataFlavor('text/unicode');
var len = new Object();
var str = Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString);
str.data = selection;
trans.setTransferData("text/unicode",str,selection.length*2);
var clipid=Components.interfaces.nsIClipboard;
if (!clip) return false;
clip.setData(trans,null,clipid.kGlobalClipboard);
}
},
clickClose : true,
index : 0,
type : 'dynamic'
});
}
_this.bind();
},
hidden : function (level) ...{
level = level || 0;
for( var i = level; ivar item = this.active[i];
var iframeElem = this.iframes[i];
if ( iframeElem !=null)
iframeElem.style.display = 'none';
if(this.settings.locked) return;
var menuElem = item.element;
if ( menuElem !=null)
menuElem.style.display = 'none';
}
this.onHidden(level);
},
onHidden : function (level) ...{
},
hasClass : function (elem, name)
...{
return !!elem && (' ' elem.className ' ').indexOf(' ' name ' ') != -1;
},
elemOffset : function(elem)...{
var left = 0;
var top = 0;
while (elem.offsetParent)...{
left = elem.offsetLeft;
top = elem.offsetTop;
elem = elem.offsetParent;
}
left = elem.offsetLeft;
top = elem.offsetTop;
return ...{x:left, y:top};
},
mouseCoords : function (e)...{
if (e.pageX && e.pageY) ...{
return ...{
x: e.pageX,
y: e.pageY
};
}
var d = (document.documentElement && document.documentElement.scrollTop) ? document.documentElement : document.body;
return ...{
x: e.clientX d.scrollLeft,
y: e.clientY d.scrollTop
};
},
addEvent : function(target,eventType,func)...{
if(target.attachEvent)
...{
target.attachEvent("on" eventType, func);
}else if(target.addEventListener)
...{
target.addEventListener(eventType == 'mousewheel' ? 'DOMMouseScroll' : eventType, func, false);
}
return this;
},
removeEvent : function(target,eventType,func)...{
if(target.detachEvent)
...{
target.detachEvent("on" eventType, func);
}else if(target.removeEventListener)
...{
target.removeEventListener(eventType == 'mousewheel' ? 'DOMMouseScroll' : eventType, func, false);
}
return this;
}
}

Demo addresshttp://demo.jb51.net/js/tree_json/ContextMenu.htm

3. Node Tree (TreeMenu)
Node tree (TreeMenu) is the most used in our actual projects. The famous MzTreeVew of Meihuaxue on the Internet has been optimized for large amounts of data and is very efficient. But I don’t really like to adopt things. Since I don’t understand some things or why they do what they do, I want to try to “invent the wheel” myself. Of course, the function is definitely not as powerful as MzTreeVew. I did not do an efficiency test when the amount of data was large. I borrowed the pictures from MzTreeVew first.

Infinite-level node tree

To achieve infinite-level functions, if there are no tricks, it seems that the only way is recursion. However, it should be noted that there must be a correct condition to determine the return to avoid an infinite loop. In terms of data storage structure, generally our database stores id, name, and parentId fields. This structure is still stored in the tree structure. When expanding a tree node, we need to obtain all its child nodes based on the id and save them. , to avoid repeated traversal for the second time.

Hierarchical relationship structure

What I want to say here is that the HTML presented has a hierarchical relationship, and each tree node object has a hierarchical relationship. The HTML hierarchical relationship shows that the elements of the child nodes must be the child nodes of the elements of the parent node. Originally I thought this was not necessary. Later I found that only by doing this can the state of the child nodes be maintained. For example, if I click on a first-level node, I only need Expand all second-level nodes, and the status of third-level or fourth-level nodes does not need to be changed. The HTML structure is easy to implement with this hierarchical relationship support. Corresponding to this is the tree node object, which stores parent node objects, child node collection objects, reference elements, etc. to facilitate recursive calls. This information is attached to the corresponding DOM element.

With checkbox and radio selection

The needs of actual projects are complex and changeable. Sometimes we need to provide radio single selection function, and sometimes we may need to provide checkbox multi-selection function. In order to It is also necessary to directly obtain the selected value in the background and provide checkbox and radio selection functions. Of course, we can configure and specify whether to create a checkbox or radio during instantiation. Whether each node is selected during initialization can also be set and specified. What needs to be noted here is that we cannot specify the name attribute when we directly create a checkbox or radio. In a twist, Just think of it to achieve it.
Copy code The code is as follows:

var inputTemp = document.createElement('div');
inputTemp.innerHTML = '';
var inputElem = inputTemp.childNodes[0];

Tie only Define a click event

It looks like a more complicated tree structure, but in fact I only bound a click event to the outermost container element. In addition, the linkage of clicking the checkbox is also handled in this click event, because The element's event will bubble up and trigger to the parent element, and it is easy to use the event object event to get the trigger source element, so I can get whether you clicked on the checkbox or some other element, which is very convenient. The advantage of this is that you can focus on processing one event instead of adding events to each element, which fully demonstrates the elegance and beauty of the code.

Demo effect: http://demo.jb51.net/js/tree_json/TreeMenu.htm
Package download addressJavaScript multiple tree structure menu effects
Reprinted in this article From Jinlong Blog: http://www.jonllen.com/jonllen/js/menu.aspx, please keep this statement when reprinting.
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn