contextmenu
Plugin ma już wsparcia dla tego produktu. Z dokumentacji, do której linkowałeś:
items
: Oczekuje obiektu lub funkcji, która powinna zwrócić obiekt . Jeśli funkcja jest używana, jest uruchamiana w kontekście drzewa i otrzymuje jeden argument - węzeł, który został kliknięty prawym przyciskiem myszy.
Więc zamiast dawać contextmenu
zakodowany obiekt do pracy, możesz podać następującą funkcję. Sprawdza kliknięty element pod kątem klasy o nazwie „folder” i usuwa element menu „usuń”, usuwając go z obiektu:
function customMenu(node) {
var items = {
renameItem: {
label: "Rename",
action: function () {...}
},
deleteItem: {
label: "Delete",
action: function () {...}
}
};
if ($(node).hasClass("folder")) {
delete items.deleteItem;
}
return items;
}
Zwróć uwagę, że powyższe całkowicie ukryje opcję usuwania, ale wtyczka umożliwia również pokazanie elementu podczas wyłączania jego zachowania, poprzez dodanie _disabled: true
do odpowiedniego elementu. W takim przypadku możesz zamiast tego użyć items.deleteItem._disabled = true
w if
instrukcji.
Powinno to być oczywiste, ale pamiętaj, aby zainicjować wtyczkę customMenu
funkcją zamiast tego, co miałeś wcześniej:
$("#tree").jstree({plugins: ["contextmenu"], contextmenu: {items: customMenu}});
// ^
// ___________________________________________________________________|
Edycja: Jeśli nie chcesz, aby menu było odtwarzane po każdym kliknięciu prawym przyciskiem myszy, możesz umieścić logikę w programie obsługi akcji dla samego elementu menu usuwania.
"label": "Delete",
"action": function (obj) {
if ($(this._get_node(obj)).hasClass("folder") return;
}
Edytuj ponownie: po obejrzeniu kodu źródłowego jsTree wygląda na to, że menu kontekstowe jest odtwarzane za każdym razem, gdy jest wyświetlane (zobacz funkcje show()
i parse()
), więc nie widzę problemu z moim pierwszym rozwiązaniem.
Jednak podoba mi się notacja, którą proponujesz, z funkcją jako wartością _disabled
. Potencjalną ścieżką do zbadania jest zawinięcie ich parse()
funkcji w swoją własną, która ocenia funkcję disabled: function () {...}
i zapisuje wynik _disabled
, przed wywołaniem oryginału parse()
.
Bezpośrednia modyfikacja ich kodu źródłowego również nie będzie trudna. Wiersz 2867 wersji 1.0-rc1 jest odpowiedni:
str += "<li class='" + (val._class || "") + (val._disabled ? " jstree-contextmenu-disabled " : "") + "'><ins "
Możesz po prostu dodać linię przed tą, która sprawdza $.isFunction(val._disabled)
, a jeśli tak, to val._disabled = val._disabled()
. Następnie prześlij go twórcom jako łatkę :)
var items
poza funkcję, aby została utworzona tylko raz, i zwrócenie wyboru elementów z funkcji, np.return {renameItem: items.renameItem};
Lubreturn {renameItem: items.renameItem, deleteItem: items.deleteItem};
<a>
, że kliknięty element jest przechowywany w$.vakata.context.tgt
. Więc spróbuj spojrzeć w górę$.vakata.context.tgt.attr("rel")
.if ($(node).hasClass("folder"))
nie działało. ale tak sięif (node.children.length > 0) { items.deleteItem._disabled = true; }
Zaimplementowano z różnymi typami węzłów:
$('#jstree').jstree({ 'contextmenu' : { 'items' : customMenu }, 'plugins' : ['contextmenu', 'types'], 'types' : { '#' : { /* options */ }, 'level_1' : { /* options */ }, 'level_2' : { /* options */ } // etc... } });
Oraz funkcja 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; }
Działa pięknie.
źródło
type
atrybucie, a nie klasie CSS uzyskanej za pomocą jQuery.'action': function () { /* action */ }
do drugiego fragmentu kodu ? Co zrobić, jeśli chcesz korzystać z „normalnych” funkcji i elementów menu, ale po prostu usunąć jedną z nich (np. Usuń Usuń, ale zachowaj Zmień nazwę i Utwórz)? Moim zdaniem tak naprawdę o to pytał OP. Z pewnością nie musisz ponownie pisać funkcji takich jak Zmień nazwę i Utwórz, jeśli usuniesz inny element, taki jak Usuń?items
liście obiektów, a następnie określasz, które z tych elementów usunąć dla danej funkcjinode.type
na końcucustomMenu
funkcji. Gdy użytkownik kliknie dany węzełtype
, menu kontekstowe wyświetli listę wszystkich elementów pomniejszonych o wszystkie usunięte w warunku na końcucustomMenu
funkcji. Nie piszesz ponownie żadnej funkcjonalności (chyba że jstree zmieniło się od czasu tej odpowiedzi trzy lata temu, w którym to przypadku może nie mieć już zastosowania).Aby wszystko wyczyścić.
Zamiast tego:
$("#xxx").jstree({ 'plugins' : 'contextmenu', 'contextmenu' : { 'items' : { ... bla bla bla ...} } });
Użyj tego:
$("#xxx").jstree({ 'plugins' : 'contextmenu', 'contextmenu' : { 'items' : customMenu } });
źródło
Nieco inaczej dostosowałem sugerowane rozwiązanie do pracy z typami, być może może to pomóc komuś innemu:
Gdzie # {$ id_arr [$ k]} jest odniesieniem do kontenera div ... w moim przypadku używam wielu drzew, więc cały ten kod będzie wyjściem do przeglądarki, ale masz pomysł .. Zasadniczo chcę wszystko opcje menu kontekstowego, ale tylko „Utwórz” i „Wklej” w węźle Dysk. Oczywiście z poprawnymi powiązaniami z tymi operacjami później:
<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},
źródło
Btw: Jeśli chcesz tylko usunąć opcje z istniejącego menu kontekstowego - to zadziałało dla mnie:
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; }
źródło
Możesz zmodyfikować kod @ Box9, aby dopasować go do wymagań dynamicznego wyłączania menu kontekstowego jako:
function customMenu(node) { ............ ................ // Disable the "delete" menu item // Original // delete items.deleteItem; if ( node[0].attributes.yyz.value == 'notdelete' ) { items.deleteItem._disabled = true; } }
Musisz dodać jeden atrybut „xyz” w swoich danych XML lub JSOn
źródło
od jsTree 3.0.9 musiałem użyć czegoś takiego jak
var currentNode = treeElem.jstree('get_node', node, true); if (currentNode.hasClass("folder")) { // Delete the "delete" menu item delete items.deleteItem; }
ponieważ dostarczony
node
obiekt nie jest obiektem jQuery.źródło
Odpowiedź Davida wydaje się dobra i skuteczna. Znalazłem inną odmianę rozwiązania, w której można użyć atrybutu a_attr do rozróżnienia różnych węzłów i na tej podstawie można wygenerować różne menu kontekstowe.
W poniższym przykładzie użyłem dwóch typów węzłów Folder i Pliki. Użyłem też różnych ikon, używając glifów. W przypadku węzła typu pliku można uzyskać tylko menu kontekstowe, aby zmienić nazwę i usunąć. W przypadku folderu dostępne są wszystkie opcje, utwórz plik, utwórz folder, zmień nazwę, usuń.
Aby uzyskać pełny fragment kodu, możesz wyświetlić 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); } } });
Początkowe dane json są jak poniżej, gdzie typ węzła jest wymieniony w 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'} }, ];
Jako część menu kontekstowego, aby utworzyć plik i folder, użyj podobnego kodu poniżej, jak akcja na pliku.
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); }
jako akcja folderu:
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); }
źródło