我在网上看到了一个示例,展示了如何自定义jstree右键单击上下文菜单的外观(使用contextmenu插件)。
例如,允许我的用户删除“文档”而不删除“文件夹”(通过从文件夹的上下文菜单中隐藏“删除”选项)。
现在我找不到那个例子。谁能指出我正确的方向?官方documentation并没有真正帮助。
修改
由于我希望默认上下文菜单只有一个或两个小的更改,我宁愿不重新创建整个菜单(当然,如果这是唯一的方法,我会这样做)。我想做的是这样的事情:
"contextmenu" : {
items: {
"ccp" : false,
"create" : {
// The item label
"label" : "Create",
// The function to execute upon a click
"action": function (obj) { this.create(obj); },
"_disabled": function (obj) {
alert("obj=" + obj);
return "default" != obj.attr('rel');
}
}
}
}
但它不起作用 - 只是始终禁用创建项目(警报永远不会出现)。
答案 0 :(得分:134)
contextmenu
插件已经支持此功能。从您链接到的文档:
items
:预期一个对象或一个函数,它应该返回一个对象。如果使用了一个函数,它会在树的上下文中触发并接收一个参数 - 右键单击的节点。
因此,您可以提供以下功能,而不是让contextmenu
使用硬编码对象。它检查为名为“folder”的类单击的元素,并通过从对象中删除它来删除“删除”菜单项:
function customMenu(node) {
// The default set of all items
var items = {
renameItem: { // The "rename" menu item
label: "Rename",
action: function () {...}
},
deleteItem: { // The "delete" menu item
label: "Delete",
action: function () {...}
}
};
if ($(node).hasClass("folder")) {
// Delete the "delete" menu item
delete items.deleteItem;
}
return items;
}
请注意,以上内容将完全隐藏删除选项,但该插件还允许您在禁用其行为时显示项目,方法是将_disabled: true
添加到相关项目。在这种情况下,您可以在items.deleteItem._disabled = true
语句中使用if
代替。
应该是显而易见的,但请记住使用customMenu
函数初始化插件,而不是先前使用的函数:
$("#tree").jstree({plugins: ["contextmenu"], contextmenu: {items: customMenu}});
// ^
// ___________________________________________________________________|
编辑:如果您不想在每次右键单击时重新创建菜单,则可以将逻辑放在删除菜单项本身的操作处理程序中。
"label": "Delete",
"action": function (obj) {
if ($(this._get_node(obj)).hasClass("folder") return; // cancel action
}
再次编辑:查看jsTree源代码后,看起来每次显示上下文菜单时都会重新创建(请参阅show()
和{{1}我没有看到第一个解决方案的问题。
但是,我确实喜欢你建议的符号,函数是parse()
的值。探索的潜在途径是使用您自己的_disabled
函数将parse()
函数包装起来,并在调用原始disabled: function () {...}
之前将结果存储在_disabled
中。
直接修改源代码并不困难。版本1.0-rc1的第2867行是相关的:
parse()
您只需在检查str += "<li class='" + (val._class || "") + (val._disabled ? " jstree-contextmenu-disabled " : "") + "'><ins ";
的行之前添加一行,如果是,$.isFunction(val._disabled)
。然后将其作为补丁提交给创作者:)
答案 1 :(得分:18)
使用不同的节点类型实现:
$('#jstree').jstree({
'contextmenu' : {
'items' : customMenu
},
'plugins' : ['contextmenu', 'types'],
'types' : {
'#' : { /* options */ },
'level_1' : { /* options */ },
'level_2' : { /* options */ }
// etc...
}
});
customMenu函数:
function customMenu(node)
{
var items = {
'item1' : {
'label' : 'item1',
'action' : function () { /* action */ }
},
'item2' : {
'label' : 'item2',
'action' : function () { /* action */ }
}
}
if (node.type === 'level_1') {
delete items.item2;
} else if (node.type === 'level_2') {
delete items.item1;
}
return items;
}
工作得很漂亮。
答案 2 :(得分:12)
清除一切。
而不是:
$("#xxx").jstree({
'plugins' : 'contextmenu',
'contextmenu' : {
'items' : { ... bla bla bla ...}
}
});
使用此:
$("#xxx").jstree({
'plugins' : 'contextmenu',
'contextmenu' : {
'items' : customMenu
}
});
答案 3 :(得分:5)
我已经调整了建议的解决方案,但是它可以帮助其他人:
其中#{$ id_arr [$ k]}是对div容器的引用...在我的情况下,我使用了很多树,所以所有这些代码都将是浏览器的输出,但是你明白了......基本上我想要所有上下文菜单选项,但只需要在“驱动器”节点上“创建”和“粘贴”。显然,以后对这些操作的正确绑定是:
<div id="$id_arr[$k]" class="jstree_container"></div>
</div>
</li>
<!-- JavaScript neccessary for this tree : {$value} -->
<script type="text/javascript" >
jQuery.noConflict();
jQuery(function ($) {
// This is for the context menu to bind with operations on the right clicked node
function customMenu(node) {
// The default set of all items
var control;
var items = {
createItem: {
label: "Create",
action: function (node) { return { createItem: this.create(node) }; }
},
renameItem: {
label: "Rename",
action: function (node) { return { renameItem: this.rename(node) }; }
},
deleteItem: {
label: "Delete",
action: function (node) { return { deleteItem: this.remove(node) }; },
"separator_after": true
},
copyItem: {
label: "Copy",
action: function (node) { $(node).addClass("copy"); return { copyItem: this.copy(node) }; }
},
cutItem: {
label: "Cut",
action: function (node) { $(node).addClass("cut"); return { cutItem: this.cut(node) }; }
},
pasteItem: {
label: "Paste",
action: function (node) { $(node).addClass("paste"); return { pasteItem: this.paste(node) }; }
}
};
// We go over all the selected items as the context menu only takes action on the one that is right clicked
$.jstree._reference("#{$id_arr[$k]}").get_selected(false, true).each(function (index, element) {
if ($(element).attr("id") != $(node).attr("id")) {
// Let's deselect all nodes that are unrelated to the context menu -- selected but are not the one right clicked
$("#{$id_arr[$k]}").jstree("deselect_node", '#' + $(element).attr("id"));
}
});
//if any previous click has the class for copy or cut
$("#{$id_arr[$k]}").find("li").each(function (index, element) {
if ($(element) != $(node)) {
if ($(element).hasClass("copy") || $(element).hasClass("cut")) control = 1;
}
else if ($(node).hasClass("cut") || $(node).hasClass("copy")) {
control = 0;
}
});
//only remove the class for cut or copy if the current operation is to paste
if ($(node).hasClass("paste")) {
control = 0;
// Let's loop through all elements and try to find if the paste operation was done already
$("#{$id_arr[$k]}").find("li").each(function (index, element) {
if ($(element).hasClass("copy")) $(this).removeClass("copy");
if ($(element).hasClass("cut")) $(this).removeClass("cut");
if ($(element).hasClass("paste")) $(this).removeClass("paste");
});
}
switch (control) {
//Remove the paste item from the context menu
case 0:
switch ($(node).attr("rel")) {
case "drive":
delete items.renameItem;
delete items.deleteItem;
delete items.cutItem;
delete items.copyItem;
delete items.pasteItem;
break;
case "default":
delete items.pasteItem;
break;
}
break;
//Remove the paste item from the context menu only on the node that has either copy or cut added class
case 1:
if ($(node).hasClass("cut") || $(node).hasClass("copy")) {
switch ($(node).attr("rel")) {
case "drive":
delete items.renameItem;
delete items.deleteItem;
delete items.cutItem;
delete items.copyItem;
delete items.pasteItem;
break;
case "default":
delete items.pasteItem;
break;
}
}
else //Re-enable it on the clicked node that does not have the cut or copy class
{
switch ($(node).attr("rel")) {
case "drive":
delete items.renameItem;
delete items.deleteItem;
delete items.cutItem;
delete items.copyItem;
break;
}
}
break;
//initial state don't show the paste option on any node
default: switch ($(node).attr("rel")) {
case "drive":
delete items.renameItem;
delete items.deleteItem;
delete items.cutItem;
delete items.copyItem;
delete items.pasteItem;
break;
case "default":
delete items.pasteItem;
break;
}
break;
}
return items;
$("#{$id_arr[$k]}").jstree({
// List of active plugins used
"plugins" : [ "themes","json_data", "ui", "crrm" , "hotkeys" , "types" , "dnd", "contextmenu"],
"contextmenu" : { "items" : customMenu , "select_node": true},
答案 4 :(得分:2)
顺便说一句:如果您只想从现有的上下文菜单中删除选项-这对我有用:
function customMenu(node)
{
var items = $.jstree.defaults.contextmenu.items(node);
if (node.type === 'root') {
delete items.create;
delete items.rename;
delete items.remove;
delete items.ccp;
}
return items;
}
答案 5 :(得分:1)
您可以修改@ Box9代码,以满足您对上下文菜单的动态禁用要求:
function customMenu(node) {
............
................
// Disable the "delete" menu item
// Original // delete items.deleteItem;
if ( node[0].attributes.yyz.value == 'notdelete' ) {
items.deleteItem._disabled = true;
}
}
您需要在XML或JSOn数据中添加一个属性“xyz”
答案 6 :(得分:1)
从jsTree 3.0.9开始我需要使用像
这样的东西var currentNode = treeElem.jstree('get_node', node, true);
if (currentNode.hasClass("folder")) {
// Delete the "delete" menu item
delete items.deleteItem;
}
因为提供的node
对象不是jQuery对象。
答案 7 :(得分:1)
David的回应似乎很好而有效。我发现了该解决方案的另一种变体,您可以使用a_attr属性来区分不同的节点,并以此为基础来生成不同的上下文菜单。
在下面的示例中,我使用了两种类型的节点Folder和Files。我也使用glyphicon使用了不同的图标。对于文件类型节点,您只能获取上下文菜单来重命名和删除。对于文件夹,所有选项都在那里,创建文件,创建文件夹,重命名,删除。
有关完整的代码段,您可以查看https://everyething.com/Example-of-jsTree-with-different-context-menu-for-different-node-type
$('#SimpleJSTree').jstree({
"core": {
"check_callback": true,
'data': jsondata
},
"plugins": ["contextmenu"],
"contextmenu": {
"items": function ($node) {
var tree = $("#SimpleJSTree").jstree(true);
if($node.a_attr.type === 'file')
return getFileContextMenu($node, tree);
else
return getFolderContextMenu($node, tree);
}
}
});
初始json数据如下,其中a_attr中提到了节点类型。
var jsondata = [
{ "id": "ajson1", "parent": "#", "text": "Simple root node", icon: 'glyphicon glyphicon-folder-open', "a_attr": {type:'folder'} },
{ "id": "ajson2", "parent": "#", "text": "Root node 2", icon: 'glyphicon glyphicon-folder-open', "a_attr": {type:'folder'} },
{ "id": "ajson3", "parent": "ajson2", "text": "Child 1", icon: 'glyphicon glyphicon-folder-open', "a_attr": {type:'folder'} },
{ "id": "ajson4", "parent": "ajson2", "text": "Child 2", icon: 'glyphicon glyphicon-folder-open', "a_attr": {type:'folder'} },
];
作为创建文件和文件夹的保护菜单项的一部分,请使用下面类似的代码作为文件操作。
action: function (obj) {
$node = tree.create_node($node, { text: 'New File', icon: 'glyphicon glyphicon-file', a_attr:{type:'file'} });
tree.deselect_all();
tree.select_node($node);
}
作为文件夹操作:
action: function (obj) {
$node = tree.create_node($node, { text: 'New Folder', icon:'glyphicon glyphicon-folder-open', a_attr:{type:'folder'} });
tree.deselect_all();
tree.select_node($node);
}