2010-12-30 44 views
72

Tôi đã xem một ví dụ trực tuyến hiển thị cách tùy chỉnh giao diện menu ngữ cảnh nhấp chuột phải của jstree (sử dụng plugin contextmenu).Cấu hình trình đơn ngữ cảnh nhấp chuột phải jstree cho các loại nút khác nhau

Ví dụ: cho phép người dùng xóa "tài liệu" chứ không phải "thư mục" (bằng cách ẩn tùy chọn "xóa" khỏi trình đơn ngữ cảnh cho thư mục).

Bây giờ tôi không thể tìm thấy ví dụ đó. ai đó có thể chỉ cho tôi phương hướng đúng không? Chính thức documentation không thực sự hữu ích.

Edit:

Vì tôi muốn các menu ngữ cảnh mặc định chỉ với một hoặc hai thay đổi nhỏ, tôi muốn không tái tạo lại toàn bộ menu (mặc dù tất nhiên tôi sẽ nếu đó là cách duy nhất). Những gì tôi muốn làm là một cái gì đó như thế này:

"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'); 
      } 
     } 
    } 
} 

nhưng nó không hoạt động - mục tạo luôn bị tắt (cảnh báo không bao giờ xuất hiện).

Trả lời

120

Plugin contextmenu đã có hỗ trợ cho việc này. Từ các tài liệu liên quan đến bạn:

items: hy vọng một đối tượng hoặc một chức năng, mà phải trả lại một đối tượng. Nếu một hàm được sử dụng nó được kích hoạt trong bối cảnh của cây và nhận được một đối số - nút đã được nhấp chuột phải.

Vì vậy, thay vì cung cấp cho contextmenu một đối tượng được mã hóa cứng để hoạt động, bạn có thể cung cấp chức năng sau. Nó kiểm tra các yếu tố đó được nhấp cho một lớp có tên là "thư mục", và loại bỏ các "xóa" mục trình đơn bằng cách xóa nó từ đối tượng:

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; 
} 

Lưu ý rằng ở trên sẽ ẩn tùy chọn xóa hoàn toàn, nhưng plugin cũng cho phép bạn hiển thị một mục trong khi tắt hành vi của nó, bằng cách thêm _disabled: true vào mục có liên quan. Trong trường hợp này, bạn có thể sử dụng items.deleteItem._disabled = true trong câu lệnh if để thay thế.

nên được rõ ràng, nhưng hãy nhớ để khởi plugin với customMenu chức năng thay vì những gì bạn đã từng:

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

Edit: Nếu bạn không muốn menu để được tái tạo trên mỗi nhấp chuột phải, bạn có thể đặt logic trong trình xử lý hành động để tự xóa mục menu.

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

Chỉnh sửa lần nữa: Sau khi xem xét mã nguồn jsTree, nó trông giống như ContextMenu đang được tái tạo mỗi khi nó được hiển thị anyway (xem các chức năng show()parse()), vì vậy tôi không thấy vấn đề với giải pháp đầu tiên của tôi.

Tuy nhiên, tôi thực hiện như ký hiệu bạn đang đề xuất, với hàm làm giá trị cho _disabled.Một con đường tiềm năng để khám phá là quấn hàm parse() của bạn với hàm của riêng bạn để đánh giá hàm tại disabled: function() {...} và lưu trữ kết quả trong _disabled, trước khi gọi số parse() gốc.

Sẽ không khó để sửa đổi mã nguồn trực tiếp. Dòng 2867 của phiên bản 1.0-rc1 là phù hợp nhất:

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

Bạn chỉ có thể thêm một dòng trước này để kiểm tra $.isFunction(val._disabled), và nếu như vậy, val._disabled = val._disabled(). Sau đó gửi cho người tạo dưới dạng bản vá :)

+0

Cảm ơn. Tôi nghĩ rằng tôi đã từng thấy một giải pháp liên quan đến việc thay đổi chỉ những gì cần thay đổi từ mặc định (thay vì tạo lại toàn bộ menu từ đầu). Tôi sẽ chấp nhận câu trả lời này nếu không có giải pháp nào tốt hơn trước khi tiền thưởng hết hạn. – MGOwen

+0

@MGOwen, khái niệm I * am * sửa đổi "mặc định", nhưng có đúng là đối tượng được tạo lại mỗi khi hàm được gọi. Tuy nhiên, mặc định cần được nhân bản trước, nếu không bản thân mặc định sẽ được sửa đổi (và bạn sẽ cần logic phức tạp hơn để hoàn nguyên nó về trạng thái ban đầu). Một thay thế tôi có thể nghĩ là di chuyển 'var items' sang bên ngoài của hàm để nó chỉ được tạo một lần và trả về một số mục từ hàm, ví dụ: 'return {renameItem: items.renameItem};' hoặc 'return {renameItem: items.renameItem, deleteItem: items.deleteItem};' –

+0

Tôi thích cái cuối cùng đặc biệt, nơi bạn sửa đổi nguồn jstree. Tôi đã thử nó và nó hoạt động, chức năng được gán cho "_disabled" (trong ví dụ của tôi) chạy.Nhưng, nó không giúp được gì bởi vì tôi không thể truy cập vào nút (ít nhất tôi cũng cần thuộc tính rel để lọc các nút theo kiểu nút) từ bên trong phạm vi của hàm. Tôi đã thử kiểm tra các biến tôi có thể vượt qua từ mã nguồn jstree nhưng không thể tìm thấy nút. Bất kỳ ý tưởng? – MGOwen

12

Để xóa mọi thứ.

Thay vì điều này:

$("#xxx").jstree({ 
    'plugins' : 'contextmenu', 
    'contextmenu' : { 
     'items' : { ... bla bla bla ...} 
    } 
}); 

Sử dụng này:

$("#xxx").jstree({ 
    'plugins' : 'contextmenu', 
    'contextmenu' : { 
     'items' : customMenu 
    } 
}); 
1

Bạn có thể sửa đổi @ đang Box9 như cho phù hợp với yêu cầu của bạn về vô hiệu hóa năng động của menu ngữ cảnh như:

function customMenu(node) { 

    ............ 
    ................ 
    // Disable the "delete" menu item 
    // Original // delete items.deleteItem; 
    if (node[0].attributes.yyz.value == 'notdelete' ) { 


     items.deleteItem._disabled = true; 
    } 

} 

Bạn cần thêm một thuộc tính "xyz" vào dữ liệu XML hoặc JSOn

4

Tôi đã điều chỉnh giải pháp được đề xuất để làm việc với các loại hơi khác nhau, có lẽ nó có thể giúp người khác:

Trường hợp # {$ id_arr [$ k]} là tham chiếu đến vùng chứa div ... trong trường hợp của tôi, tôi sử dụng nhiều cây để tất cả mã này sẽ là đầu ra cho trình duyệt, nhưng bạn có ý tưởng .. Về cơ bản tôi muốn tất cả các tùy chọn menu ngữ cảnh nhưng chỉ 'Tạo' và 'Dán' trên nút Ổ đĩa. Rõ ràng với các ràng buộc chính xác cho những hoạt động sau:

<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}, 
0

như của jsTree 3.0.9 tôi cần phải sử dụng một cái gì đó giống như

var currentNode = treeElem.jstree('get_node', node, true); 
if (currentNode.hasClass("folder")) { 
    // Delete the "delete" menu item 
    delete items.deleteItem; 
} 

vì đối tượng node được cung cấp không phải là một đối tượng jQuery.

16

thực hiện với các loại nút khác nhau:

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

Và chức năng 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; 
} 

trình đẹp mắt.

+1

Tôi thích câu trả lời này vì nó dựa vào thuộc tính 'type' hơn là một lớp CSS thu được bằng cách sử dụng jQuery. –

+0

Bạn đang đặt mã nào bên trong ''action': function() {/ * action * /}' trong đoạn thứ hai? Điều gì sẽ xảy ra nếu bạn muốn sử dụng các mục menu và chức năng "bình thường", nhưng chỉ cần xóa một trong số chúng (ví dụ: xóa Xóa nhưng giữ Đổi tên và Tạo)? Theo quan điểm của tôi, đó thực sự là những gì OP được yêu cầu anyway. Chắc chắn bạn không cần phải viết lại chức năng cho những thứ như Đổi tên và Tạo nếu bạn xóa một mục khác như Xóa? – Andy

+0

Tôi không chắc tôi hiểu câu hỏi của bạn. Bạn định nghĩa tất cả các chức năng cho menu ngữ cảnh đầy đủ (ví dụ: Xóa, Đổi tên và Tạo) trong danh sách các đối tượng 'items', sau đó bạn chỉ định mục nào trong số các mục này để loại bỏ cho một' node.type' đã cho ở cuối của hàm 'customMenu'. Khi người dùng nhấp vào một nút của 'loại' đã cho, trình đơn ngữ cảnh sẽ liệt kê tất cả các mục trừ đi bất kỳ phần tử nào bị loại bỏ trong phần cuối của hàm' customMenu'. Bạn không viết lại bất kỳ chức năng nào (trừ khi jstree đã thay đổi kể từ câu trả lời này ba năm trước, trong trường hợp này nó có thể không còn liên quan nữa). – stacked

Các vấn đề liên quan