为不同的节点类型配置jstree右键单击contextmenu

MGO*_*wen 80 jquery contextmenu jstree

我在网上看到了一个示例,展示了如何自定义jstree右键单击上下文菜单的外观(使用contextmenu插件).

例如,允许我的用户删除"文档"而不删除"文件夹"(通过隐藏文件夹的上下文菜单中的"删除"选项).

现在我找不到那个例子.谁能指出我正确的方向?官方文档没有真正帮助.

编辑:

由于我希望默认的上下文菜单只有一个或两个小的更改,我宁愿不重新创建整个菜单(当然,如果这是唯一的方法我会的).我想做的是这样的事情:

"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'); 
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但它不起作用 - 创建项目始终被禁用(警报永远不会出现).

Dav*_*ang 137

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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,上面将完全隐藏删除选项,但插件还允许您通过添加_disabled: true到相关项目来显示项目,同时禁用其行为.在这种情况下,您可以items.deleteItem._disabled = trueif语句中使用.

应该是显而易见的,但请记住使用customMenu函数而不是之前的函数初始化插件:

$("#tree").jstree({plugins: ["contextmenu"], contextmenu: {items: customMenu}});
//                                                                    ^
// ___________________________________________________________________|
Run Code Online (Sandbox Code Playgroud)

编辑:如果您不希望每次右键单击都重新创建菜单,则可以将逻辑放在删除菜单项本身的操作处理程序中.

"label": "Delete",
"action": function (obj) {
    if ($(this._get_node(obj)).hasClass("folder") return; // cancel action
}
Run Code Online (Sandbox Code Playgroud)

再次编辑:在查看jsTree源代码之后,看起来每次显示上下文菜单时都会重新创建(请参阅show()parse()函数),所以我没有看到第一个解决方案的问题.

但是,我确实喜欢你建议的符号,功能作为值_disabled.探索的潜在途径是在调用原始parse()函数之前,使用您自己的函数来计算函数disabled: function () {...}并将结果存储在_disabled其中parse().

要么直接修改源代码也不难.版本1.0-rc1的第2867行是相关的:

str += "<li class='" + (val._class || "") + (val._disabled ? " jstree-contextmenu-disabled " : "") + "'><ins ";
Run Code Online (Sandbox Code Playgroud)

您可以在此检查之前添加一行$.isFunction(val._disabled),如果是,则val._disabled = val._disabled().然后将其作为补丁提交给创作者:)

  • 在 jstree 3.0.8 中: `if ($(node).hasClass("folder"))` 不起作用。但这确实是: `if (node.children.length &gt; 0) { items.deleteItem._disabled = true; }` (2认同)

小智 18

使用不同的节点类型实现:

$('#jstree').jstree({
    'contextmenu' : {
        'items' : customMenu
    },
    'plugins' : ['contextmenu', 'types'],
    'types' : {
        '#' : { /* options */ },
        'level_1' : { /* options */ },
        'level_2' : { /* options */ }
        // etc...
    }
});
Run Code Online (Sandbox Code Playgroud)

和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;
}
Run Code Online (Sandbox Code Playgroud)

工作得很漂亮.


小智 12

清除一切.

而不是这个:

$("#xxx").jstree({
    'plugins' : 'contextmenu',
    'contextmenu' : {
        'items' : { ... bla bla bla ...}
    }
});
Run Code Online (Sandbox Code Playgroud)

用这个:

$("#xxx").jstree({
    'plugins' : 'contextmenu',
    'contextmenu' : {
        'items' : customMenu
    }
});
Run Code Online (Sandbox Code Playgroud)


Jea*_*ete 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},
Run Code Online (Sandbox Code Playgroud)