El contextmenu
complemento ya tiene soporte para esto. De la documentación a la que vinculó:
items
: Espera un objeto o una función, que debería devolver un objeto . Si se usa una función, se activa en el contexto del árbol y recibe un argumento: el nodo en el que se hizo clic con el botón derecho.
Entonces, en lugar de proporcionar contextmenu
un objeto codificado de forma rígida para trabajar, puede proporcionar la siguiente función. Comprueba el elemento en el que se hizo clic para una clase llamada "carpeta" y elimina el elemento del menú "eliminar" eliminándolo del 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;
}
Tenga en cuenta que lo anterior ocultará la opción de eliminación por completo, pero el complemento también le permite mostrar un elemento mientras deshabilita su comportamiento, agregando _disabled: true
el elemento relevante. En este caso, puede usar items.deleteItem._disabled = true
dentro de la if
declaración.
Debería ser obvio, pero recuerde inicializar el complemento con la customMenu
función en lugar de lo que tenía anteriormente:
$("#tree").jstree({plugins: ["contextmenu"], contextmenu: {items: customMenu}});
// ^
// ___________________________________________________________________|
Editar: si no desea que el menú se vuelva a crear con cada clic derecho, puede poner la lógica en el controlador de acciones para el elemento del menú de eliminación.
"label": "Delete",
"action": function (obj) {
if ($(this._get_node(obj)).hasClass("folder") return;
}
Edite de nuevo: después de mirar el código fuente de jsTree, parece que el menú contextual se vuelve a crear cada vez que se muestra de todos modos (consulte las funciones show()
y parse()
), por lo que no veo ningún problema con mi primera solución.
Sin embargo, me gusta la notación que está sugiriendo, con una función como valor para _disabled
. Una ruta potencial para explorar es envolver su parse()
función con la suya propia que evalúa la función disabled: function () {...}
y almacena el resultado en _disabled
, antes de llamar al original parse()
.
Tampoco será difícil modificar su código fuente directamente. La línea 2867 de la versión 1.0-rc1 es la relevante:
str += "<li class='" + (val._class || "") + (val._disabled ? " jstree-contextmenu-disabled " : "") + "'><ins "
Simplemente puede agregar una línea antes de esta que verifique $.isFunction(val._disabled)
, y si es así val._disabled = val._disabled()
,. Luego envíelo a los creadores como parche :)
var items
fuera de la función para que se cree solo una vez y devolver una selección de elementos de la función, por ejemplo,return {renameItem: items.renameItem};
oreturn {renameItem: items.renameItem, deleteItem: items.deleteItem};
<a>
elemento en el que se hizo clic está almacenado en$.vakata.context.tgt
. Así que intenta mirar hacia arriba$.vakata.context.tgt.attr("rel")
.if ($(node).hasClass("folder"))
no funcionó. pero esto hizo:if (node.children.length > 0) { items.deleteItem._disabled = true; }
Implementado con diferentes tipos de nodos:
$('#jstree').jstree({ 'contextmenu' : { 'items' : customMenu }, 'plugins' : ['contextmenu', 'types'], 'types' : { '#' : { /* options */ }, 'level_1' : { /* options */ }, 'level_2' : { /* options */ } // etc... } });
Y la función 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 maravillosamente.
fuente
type
atributo en lugar de en una clase CSS obtenida con jQuery.'action': function () { /* action */ }
en el segundo fragmento? ¿Qué sucede si desea utilizar la funcionalidad "normal" y los elementos del menú, pero simplemente elimine uno de ellos (por ejemplo, elimine Eliminar pero conserve Renombrar y Crear)? En mi opinión, eso es realmente lo que pedía el OP de todos modos. Seguramente no necesita volver a escribir la funcionalidad para cosas como Cambiar nombre y Crear si elimina otro elemento como ¿Eliminar?items
lista de objetos, luego especifica cuál de estos elementos eliminar para un determinadonode.type
al final de lacustomMenu
función. Cuando el usuario hace clic en un nodo de dadotype
, el menú contextual enumerará todos los elementos menos los eliminados en el condicional al final de lacustomMenu
función. No está reescribiendo ninguna funcionalidad (a menos que jstree haya cambiado desde esta respuesta hace tres años, en cuyo caso puede que ya no sea relevante).Para despejar todo.
En lugar de esto:
$("#xxx").jstree({ 'plugins' : 'contextmenu', 'contextmenu' : { 'items' : { ... bla bla bla ...} } });
Utilizar esta:
$("#xxx").jstree({ 'plugins' : 'contextmenu', 'contextmenu' : { 'items' : customMenu } });
fuente
Sin embargo, he adaptado la solución sugerida para trabajar con tipos de manera un poco diferente, tal vez pueda ayudar a otra persona:
Donde # {$ id_arr [$ k]} es la referencia al contenedor div ... en mi caso, uso muchos árboles, por lo que todo este código será la salida para el navegador, pero entiendes la idea ... Básicamente, quiero todos las opciones del menú contextual, pero solo 'Crear' y 'Pegar' en el nodo Unidad. Obviamente, con los enlaces correctos a esas operaciones más adelante:
<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},
fuente
Por cierto: si solo desea eliminar opciones del menú contextual existente, esto funcionó para mí:
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; }
fuente
Puede modificar el código @ Box9 para que se adapte a sus requisitos de desactivación dinámica del menú contextual como:
function customMenu(node) { ............ ................ // Disable the "delete" menu item // Original // delete items.deleteItem; if ( node[0].attributes.yyz.value == 'notdelete' ) { items.deleteItem._disabled = true; } }
Necesita agregar un atributo "xyz" en sus datos XML o JSOn
fuente
a partir de jsTree 3.0.9 necesitaba usar algo como
var currentNode = treeElem.jstree('get_node', node, true); if (currentNode.hasClass("folder")) { // Delete the "delete" menu item delete items.deleteItem; }
porque el
node
objeto que se proporciona no es un objeto jQuery.fuente
La respuesta de David parece fina y eficiente. Encontré otra variación de la solución en la que puede usar el atributo a_attr para diferenciar diferentes nodos y, en función de eso, puede generar un menú contextual diferente.
En el siguiente ejemplo, he usado dos tipos de nodos Carpeta y Archivos. También he usado diferentes íconos usando glyphicon. Para el nodo de tipo de archivo, solo puede obtener el menú contextual para cambiar el nombre y eliminarlo. Para Carpeta, todas las opciones están ahí, crear archivo, crear carpeta, renombrar, eliminar.
Para obtener un fragmento de código completo, puede 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); } } });
Los datos iniciales de json han sido los siguientes, donde se menciona el tipo de nodo dentro de 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 del elemento del menú contextual para crear un archivo y una carpeta, use un código similar a continuación, como acción de archivo.
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 acción de carpeta:
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); }
fuente