自定义dhtmlxGrid表头菜单

本文介绍如何在dhtmlxGrid中自定义表头菜单,通过修改示例代码实现表头右键菜单的功能扩展,如列的显示与隐藏、排序及列属性更改等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

dhtmlxGrid可以定义表头菜单以及表格右键菜单,表格右键菜单可以自定义,但是表头菜单只能使用其提供的菜单。dhtmlxGrid默认的表头菜单可以决定表格中每一列是否在表格中显示,并没有提供更多的扩展,如果我想自定义表头菜单,该怎么做呢?本文就是基于自定义表格菜单,说说我的实现方式。 以下是dhtmlxGrid的表头菜单效果:

其功能过于单一,以下是表格右键菜单效果:

如果能够像表格菜单一样自定义表头菜单,那会是一件非常有意义的事情,因为dhtmlxGrid菜单都是一些针对行和单元格的操作,没有提过针对列的操作,比如我可能需要在某一列上实现该列的显示与隐藏、排序、改变列属性以及在该列右边添加一新的列,等等。 如何实现表头菜单的自定义呢?可不可将表格右键菜单移到表头上去呢? 首先,来看看context menu的实现方式,下面代码来自dhtmlxGrid Samples中的Context menu例子源码:

function onButtonClick(menuitemId, type) {
    var data = mygrid.contextID.split("_");
    //rowId_colInd;
    mygrid.setRowTextStyle(data[0], "color:" + menuitemId.split("_")[1]);
    return true;
}
menu = new dhtmlXMenuObject();
menu.setIconsPath("../common/images/");
menu.renderAsContextMenu();
menu.attachEvent("onClick", onButtonClick);
menu.loadXML("../common/_context.xml");
mygrid = new dhtmlXGridObject('gridbox');
mygrid.setImagePath("../../codebase/imgs/");
mygrid.setHeader("Author,Title");
mygrid.setInitWidths("250,250");
mygrid.enableAutoWidth(true);
mygrid.setColAlign("left,left");
mygrid.setColTypes("ro,link");
mygrid.setColSorting("str,str");
mygrid.enableContextMenu(menu);
mygrid.init();
mygrid.setSkin("dhx_skyblue");
mygrid.loadXML("../common/grid_links.xml");

上面代码创建了一个menu并将其作为context menu附件到grid上面去,下面为最关键的两行行代码:

menu.renderAsContextMenu();
mygrid.enableContextMenu(menu);

上面对于context menu提供的方法太少,这时候可以看看dhtmlxMenu api,看看有没有设置context menu生效位置的方法(指定context menu在哪片区域有效)。在dhtmlxMenu API Methods里没有找到需要的方法,这时候在官网论坛搜搜,也许可以找到点什么。 在论坛里可以找到一个例子,大致代码如下:

function onButtonClick(menuitemId, type) {
    var data = mygrid.contextID.split("_");
    //rowId_colInd;
    mygrid.setRowTextStyle(data[0], "color:" + menuitemId.split("_")[1]);
    return true;
}
menu = new dhtmlXMenuObject();
menu.setIconsPath("../common/images/");
menu.attachEvent("onClick", onButtonClick);
menu.loadXML("../common/_context.xml");

mygrid = new dhtmlXGridObject('gridbox');
mygrid.setImagePath("../../codebase/imgs/");
mygrid.setHeader("Author,Title");
mygrid.setInitWidths("250,250");
mygrid.enableAutoWidth(true);
mygrid.setColAlign("left,left");
mygrid.setColTypes("ro,link");
mygrid.setColSorting("str,str");
//mygrid.enableContextMenu(menu); //使其失效
mygrid.init();
mygrid.setSkin("dhx_skyblue");
mygrid.loadXML("../common/grid_links.xml");

mygrid.hdr.id = "header_id";
var header_row = mygrid.hdr.rows[1];
for ( var i = 0; i < header_row.cells.length; i++) {
   header_row.cells[i].id = "context_zone_" + i;
}
menu.addContextZone("header_id");

上面最关键的代码在最后几行,给dhtmlxGrid表头设置了一个id,然后调用menu的addContextZone()方法指定centext的有效区域。视乎这就是我们所需要的,但是你执行以上代码你会发现onButtonClick方法里mygrid.contextID会报错,原因是mygrid没有contextID属性(在context menu中通过该属性可以获知鼠标焦点在哪一行,但是现在在表头上强加了该menu,所以并不存在该属性了)。 剩下的问题是需要解决,菜单单击事件了。我们可以在表头的contextmenu事件处罚的时候获取鼠标焦点,并将自定义的菜单在该位置显示,该方法如下:

dhtmlxEvent(mygrid.hdr, "contextmenu", function(ev) {
	ev = ev || event;
	var el = ev.target || ev.srcElement;
	var zel = el;
	while (zel.tagName != "TABLE")
		zel = zel.parentNode;
	var grid = zel.grid;
	if (!grid)
		return;
	grid.setActive();

	el = grid.getFirstParentOfType(el, "TD")

	if ((grid) && (!grid._colInMove)) {
		grid.resized = null;
		if ((!grid._mCols) || (grid._mCols[el._cellIndex] == "true"))
			colId = el._cellIndex + 1;//获得表头右键菜单焦点所在列索引
	}

	function mouseCoords(ev) {
		if (ev.pageX || ev.pageY) {
			return {
				x : ev.pageX,
				y : ev.pageY
			};
		}
		var d = _isIE && document.compatMode != "BackCompat" ? 
	            document.documentElement: document.body;
		return {
			x : ev.clientX + d.scrollLeft - d.clientLeft,
			y : ev.clientY + d.scrollTop - d.clientTop
		};
	}

	var coords = mouseCoords(ev);
	menu.addContextZone("header_id");
	menu.showContextMenu(coords.x, coords.y);//强制显示
	return true;
});

在上面的代码里,我们获得表头右键菜单焦点所在列索引,将其值赋给colId,然后在菜单单击事件的时候添加一新的列并将colId重置:

function onButtonClick(menuitemId, type, e) {
	mygrid.insertColumn(colId, "12", "ed", 80);
	colId = 0;
	return true;
}

然后,需要禁止掉表格数据区域的菜单显示:

mygrid.attachEvent("onBeforeContextMenu", function(rid, cid, e) {
	return false;//禁止数据区域菜单
});

最后的最后,最后的代码如下:

var mygrid, colId;

function onButtonClick(menuitemId, type, e) {
	mygrid.insertColumn(colId, "12", "ed", 80);
	colId = 0;
	return true;
}

menu = new dhtmlXMenuObject();
menu.setIconsPath("../common/images/");
menu.renderAsContextMenu();
menu.attachEvent("onClick", onButtonClick);
menu.loadXML("../common/_context.xml");
menu.attachEvent("onBeforeContextMenu", function(zoneId, e) {
	var hdr = document.getElementById(zoneId)
	return true;
});

mygrid = new dhtmlXGridObject('gridbox');
mygrid.setImagePath("../codebase/imgs/");
mygrid.setHeader("Sales,Book Title,Author,Price,In Store,Shipping,Bestseller,
          Date of Publication");
mygrid.setInitWidths("50,150,100,80,80,80,80,200");
mygrid.setColAlign("right,left,left,right,center,left,center,center");
mygrid.setColTypes("dyn,edtxt,ed,price,ch,co,ra,ro");

mygrid.init();
mygrid.setSkin("dhx_skyblue");
//mygrid.enableHeaderMenu();
mygrid.enableColumnMove(true);
mygrid.enableContextMenu(menu);
dhtmlxEvent(mygrid.hdr, "contextmenu", function(ev) {
	ev = ev || event;
	var el = ev.target || ev.srcElement;
	var zel = el;
	while (zel.tagName != "TABLE")
		zel = zel.parentNode;
	var grid = zel.grid;
	if (!grid)
		return;
	grid.setActive();

	el = grid.getFirstParentOfType(el, "TD")

	if ((grid) && (!grid._colInMove)) {
		grid.resized = null;
		if ((!grid._mCols) || (grid._mCols[el._cellIndex] == "true"))
                            //获得表头右键菜单焦点所在列索引
			colId = el._cellIndex + 1;
	}

	function mouseCoords(ev) {
		if (ev.pageX || ev.pageY) {
			return {
				x : ev.pageX,
				y : ev.pageY
			};
		}
		var d = _isIE && document.compatMode != "BackCompat" ? 
                         document.documentElement: document.body;
		return {
			x : ev.clientX + d.scrollLeft - d.clientLeft,
			y : ev.clientY + d.scrollTop - d.clientTop
		};
	}

	var coords = mouseCoords(ev);
	menu.addContextZone("header_id");
	menu.showContextMenu(coords.x, coords.y);//强制显示
	return true;
});

mygrid.attachEvent("onBeforeContextMenu", function(rid, cid, e) {
	return false;//禁止数据区域菜单
});

mygrid.loadXML("../common/grid_ml_16_rows_columns_manipulations.xml");

mygrid.hdr.id = "header_id";
var header_row = mygrid.hdr.rows[1];
for ( var i = 0; i < header_row.cells.length; i++) {
	header_row.cells[i].id = "context_zone_" + i;
}

效果图如下;

dhtmlXTree进行一个小的扩展 需求1: 动态生成树形菜单,每个节点都有各自的URL地址,单击不同的节点框架页的右侧跳转到该节点所对应的URL。(框架页说明:左边是树形菜单;右边是显示页面相应信息的页面) 分析: dhtmlXTree提供了很好的添加,删除节点的方法,故选择dhtmlXTree。 但是dhtmlXTree不能满足"每个节点都有各自的URL地址,单击不同的节点框架页的右侧跳转到该节点所对应的URL"这点需求,因次想到了对dhtmlXTree进行一个小的扩展,即在其节点对象原有属性的基础上,再添加两个扩展属性。具体操作如下: 1、找到定义节点对象的那个函数(或方法) function dhtmlXTreeItemObject(itemId,itemText,parentObject,treeObject,actionHandler,mode) 修改为 function dhtmlXTreeItemObject(itemId,itemText,parentObject,treeObject,actionHandler,mode,url,target) 并在方法体中添加赋值语句:this.itemURL=url;this.itemTarget=target; 2、然后修改所有与dhtmlXTreeItemObject有关(直接或者间接相关)的方法: _attachChildNode,insertNewItem,insertNewChild,insertNewNext,_recreateBranch,_parseXMLTree 注:_parseXMLTree方法是与loadXML,loadXMLString相关的。 在这些方法中生成节点的语句中添加相应的参数和语句,以支持新添加的属性itemURL,itemTarget。 需求2: 为dhtmlXTree树上的每一个节点添加右键菜单。附:在树上的节点上点右键时才会生成菜单,空白区域单击时不会生成菜单。 分析: 1、用 dhtmlXTree + dhtmlxmenu 实现。 2、 用dhtmlxmenu生成菜单的部分代码: var menu = new dhtmlXMenuObject(); menu.setImagePath("imgs/"); menu.setIconsPath("images/"); menu.renderAsContextMenu(); menu.loadXML("dhtmlxmenu.xml?e="+new Date().getTime()); menu.addContextZone("treeboxbox_tree"); menu.addContextZone方法是为了把菜单添加到指定区域。 3、dhtmlXTreeObject.prototype._createItem方法是构造树形菜单上元素的具体实现方法。看这个方法的具体操作,可以发现它为每一个节点构建了一个table,节点的内容(即名字)放置在一个span中。 4、考虑到dhtmlxmenu实在指定的区域构建菜单,所以可以为dhtmlXTree树上的每一个节点添加右键事件,在这个右键事件里获得该节点对象所对应的区域,然后在这个区域内构建Menu菜单。 难点和解决方案: 1、怎样获得dhtmlXTree树上的每一个节点对象所对应的区域?(dhtmlXTreeObject.prototype._createItem方法没有为这个节点的span设置id) 解决方法: 在dhtmlXTreeObject.prototype._createItem方法中添加一个为span设置id的语句: 即: 在itemObject.span=document.createElement('span'); itemObject.span.className="standartTreeRow"; 后,新添加一句 itemObject.span.id="treeNode_"+itemObject.id;//为这个span新增一个Id属性 2、为dhtmlXTree树上的每一个节点添加右键事件,在这个右键事件里获得该节点对象所对应的区域,然后在这个区域内构建Menu菜单。 解决方法: a、为dhtmlXTree树上的每一个节点添加右键事件: tree.setOnRightClickHandler(treeOnRegihtClick);//右键事件 b、构建Menu菜单: var menu = new dhtmlXMenuObject(); function treeOnRegihtClick(id){ alert("右键 "+" span.id:"+tree.getItem(id).span.id); menu.setImagePath("imgs/"); menu.setIconsPath("images/"); menu.renderAsContextMenu(); menu.loadXML("dhtmlxmenu.xml?e="+new Date().getTime()); menu.addContextZone(tree.getItem(id).span.id);alert("width:"+tree.getItem(id).span.clientWidth); //var X=tree.getItem(id).span.getBoundingClientRect().left; //var Y=tree.getItem(id).span.getBoundingClientRect().top; var X=document.getElementById('mouseXPosition').value;//获得鼠标的横坐标位置 var Y=document.getElementById('mouseYPosition').value;//获得鼠标的纵坐标位置 menu.showContextMenu(X,Y);//调用showContextMenu方法显示菜单 说明:如果这儿不加上这条语句的话,第一次点击右键时只能生成菜单,但是显示不出菜单,下次点击右键Menu菜单才能弹出。 //menu._showContextMenu(X,Y,tree.getItem(id).span.id); } c、用 javascript 获取当页面上鼠标(光标)位置 <script type="text/javascript"> // 说明:获取鼠标位置 function mousePosition(ev){ if(ev.pageX || ev.pageY){ return {x:ev.pageX, y:ev.pageY}; } return { x:ev.clientX + document.body.scrollLeft - document.body.clientLeft, y:ev.clientY + document.body.scrollTop - document.body.clientTop }; } document.onmousemove = mouseMove; function mouseMove(ev){ ev = ev || window.event; var mousePos = mousePosition(ev); document.getElementById('mouseXPosition').value = mousePos.x; document.getElementById('mouseYPosition').value = mousePos.y; } </script> 页面上放置两个隐藏域存放鼠标的位置:<input type="hidden" id=mouseXPosition><input type="hidden" id=mouseYPosition>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值