O contextmenu
plugin já tem suporte para isso. Da documentação que você vinculou a:
items
: Espera um objeto ou uma função, que deve retornar um objeto . Se uma função for usada, ela será acionada no contexto da árvore e receberá um argumento - o nó que foi clicado com o botão direito.
Portanto, em vez de fornecer contextmenu
um objeto embutido em código para trabalhar, você pode fornecer a seguinte função. Ele verifica o elemento que foi clicado para uma classe chamada "pasta" e remove o item de menu "excluir" excluindo-o do objeto:
function customMenu(node) {
var items = {
renameItem: {
label: "Rename",
action: function () {...}
},
deleteItem: {
label: "Delete",
action: function () {...}
}
};
if ($(node).hasClass("folder")) {
delete items.deleteItem;
}
return items;
}
Observe que o acima irá ocultar a opção de exclusão completamente, mas o plugin também permite que você mostre um item enquanto desativa seu comportamento, adicionando _disabled: true
ao item relevante. Nesse caso, você pode usar items.deleteItem._disabled = true
dentro da if
instrução.
Deve ser óbvio, mas lembre-se de inicializar o plug-in com a customMenu
função em vez do que você tinha anteriormente:
$("#tree").jstree({plugins: ["contextmenu"], contextmenu: {items: customMenu}});
// ^
// ___________________________________________________________________|
Editar: se não quiser que o menu seja recriado a cada clique com o botão direito, você pode colocar a lógica no manipulador de ação para o próprio item de menu de exclusão.
"label": "Delete",
"action": function (obj) {
if ($(this._get_node(obj)).hasClass("folder") return;
}
Edite novamente: depois de examinar o código-fonte do jsTree, parece que o menu de contexto está sendo recriado toda vez que é mostrado (veja as funções show()
e parse()
), portanto, não vejo nenhum problema com minha primeira solução.
No entanto, gosto da notação que você está sugerindo, com uma função como o valor de _disabled
. Um caminho potencial a explorar é envolver a parse()
função deles com a sua própria, que avalia a função em disabled: function () {...}
e armazena o resultado em _disabled
, antes de chamar o original parse()
.
Também não será difícil modificar seu código-fonte diretamente. A linha 2867 da versão 1.0-rc1 é a relevante:
str += "<li class='" + (val._class || "") + (val._disabled ? " jstree-contextmenu-disabled " : "") + "'><ins "
Você pode simplesmente adicionar uma linha antes desta que verifica $.isFunction(val._disabled)
e, em caso afirmativo val._disabled = val._disabled()
,. Em seguida, envie-o aos criadores como um patch :)
var items
para fora da função para que seja criada apenas uma vez e retornar uma seleção de itens da função, por exemplo,return {renameItem: items.renameItem};
oureturn {renameItem: items.renameItem, deleteItem: items.deleteItem};
<a>
elemento que foi clicado está armazenado em$.vakata.context.tgt
. Portanto, tente olhar para cima$.vakata.context.tgt.attr("rel")
.if ($(node).hasClass("folder"))
não funcionou. mas isso fez:if (node.children.length > 0) { items.deleteItem._disabled = true; }
Implementado com diferentes tipos de nós:
$('#jstree').jstree({ 'contextmenu' : { 'items' : customMenu }, 'plugins' : ['contextmenu', 'types'], 'types' : { '#' : { /* options */ }, 'level_1' : { /* options */ }, 'level_2' : { /* options */ } // etc... } });
E a função 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; }
Funciona lindamente.
fonte
type
atributo em vez de uma classe CSS obtida usando jQuery.'action': function () { /* action */ }
no segundo trecho? E se você quiser usar a funcionalidade "normal" e os itens de menu, mas simplesmente remover um deles (por exemplo, remover Excluir, mas manter Renomear e Criar)? Na minha opinião, era isso que o OP estava perguntando de qualquer maneira. Certamente você não precisa reescrever a funcionalidade para coisas como Renomear e Criar se remover outro item como Excluir?items
lista de objetos e, em seguida, especificar quais desses itens remover para um determinadonode.type
no final dacustomMenu
função. Quando o usuário clica em um nó de dadotype
, o menu de contexto irá listar todos os itens menos os removidos na condicional no final dacustomMenu
função. Você não está reescrevendo nenhuma funcionalidade (a menos que jstree tenha mudado desde esta resposta, três anos atrás, caso em que pode não ser mais relevante).Para limpar tudo.
Em vez disso:
$("#xxx").jstree({ 'plugins' : 'contextmenu', 'contextmenu' : { 'items' : { ... bla bla bla ...} } });
Usa isto:
$("#xxx").jstree({ 'plugins' : 'contextmenu', 'contextmenu' : { 'items' : customMenu } });
fonte
Eu adaptei a solução sugerida para trabalhar com tipos de forma um pouco diferente, embora talvez possa ajudar alguém:
Onde # {$ id_arr [$ k]} é a referência para o contêiner div ... no meu caso, eu uso muitas árvores, então todo esse código será a saída para o navegador, mas você entendeu. Basicamente, eu quero todos as opções do menu de contexto, mas apenas 'Criar' e 'Colar' no nó Drive. Obviamente, com as ligações corretas a essas operações mais tarde:
<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},
fonte
Aliás: se você deseja apenas remover opções do menu de contexto existente - funcionou para mim:
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; }
fonte
Você pode modificar o código @ Box9 de acordo com seus requisitos de desativação dinâmica do menu de contexto como:
function customMenu(node) { ............ ................ // Disable the "delete" menu item // Original // delete items.deleteItem; if ( node[0].attributes.yyz.value == 'notdelete' ) { items.deleteItem._disabled = true; } }
Você precisa adicionar um atributo "xyz" em seus dados XML ou JSOn
fonte
a partir do jsTree 3.0.9 eu precisava usar algo como
var currentNode = treeElem.jstree('get_node', node, true); if (currentNode.hasClass("folder")) { // Delete the "delete" menu item delete items.deleteItem; }
porque o
node
objeto fornecido não é um objeto jQuery.fonte
A resposta de David parece boa e eficiente. Eu encontrei outra variação da solução onde você pode usar o atributo a_attr para diferenciar nós diferentes e com base nisso você pode gerar um menu de contexto diferente.
No exemplo a seguir, usei dois tipos de nós: Pasta e Arquivos. Também usei ícones diferentes usando o glifo. Para nó de tipo de arquivo, você só pode obter o menu de contexto para renomear e remover. Para Pasta, todas as opções estão lá, criar arquivo, criar pasta, renomear, remover.
Para um snippet de código completo, você pode ver 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); } } });
Os dados json iniciais foram os abaixo, onde o tipo de nó é mencionado em 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'} }, ];
Como parte do item de menu contect para criar um arquivo e uma pasta, use o código semelhante abaixo, como ação de arquivo.
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); }
como ação de pasta:
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); }
fonte