Menu Family: Adding A Context Menu To A TreeView
ひさびさに「実用的な」Exampleがでてきた(YUIのExampleのページへのリンクはこちら)。
内容はTreeViewのテキストノードに、ContextMenuを仕込み、ノードの追加、ラベルの編集、ノードの削除を行わせるというもの。
ContextMenuは、TreeView全体に対して1つのインスタンスを生成する、という戦略がとられていて、これは、先の例「Menu Family: Adding A Context Menu To A Table」と同様である。
以下は初期画面。
以下はノードを一つ選んで、右クリックをしたときの画面。ContextMenuが表示されている。
ここで、「子ノードの追加」、「ラベルの編集」を選ぶとラベル名を入力するためのwindowが表示される。
以下にJavascriptを含む、htmlファイルの全体を示す。
YUIのMenuをここまでやってきて、簡単なMenu機構を作るのであれば、MenuモジュールではなくTreeViewの方が見通しがよいように感じている。
以下のScriptも、比較的見通しのよいものとなっている。
特に、イベントのハンドリングは、コンストラクタへのonclick属性定義によるもので、この程度であれば、さほどの苦労なく拡張ができる。
Menu機構を考える上では「TreeView Control: Inline Editing of TreeView Node LabelsComments」と並んでよいサンプルであると思う。
<HTML> <HEAD> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <TITLE>Ajax_Sampling</TITLE> <style type="text/css"> body { margin:0; padding:0; } </style> <link rel="stylesheet" type="text/css" href="scripts/yui/fonts/fonts-min.css" /> <link rel="stylesheet" type="text/css" href="scripts/yui/reset/reset.css" /> <link rel="stylesheet" type="text/css" href="scripts/yui/menu/assets/skins/sam/menu.css" /> <link rel="stylesheet" type="text/css" href="scripts/yui/treeview/assets/skins/sam/treeview.css" /> <script type="text/javascript" src="scripts/yui/yahoo-dom-event/yahoo-dom-event.js"></script> <script type="text/javascript" src="scripts/yui/container/container_core-min.js"></script> <script type="text/javascript" src="scripts/yui/menu/menu-min.js"></script> <script type="text/javascript" src="scripts/yui/treeview/treeview.js"></script> <style type="text/css" id="defaultstyle"> h1 { font-weight: bold; margin: 0 0 1em 0; } body { padding: 1em; } p, ul { margin: 1em 0; } p em, #operainstructions li em { font-weight: bold; } #operainstructions { list-style-type: square; margin-left: 2em; } </style> <script type="text/javascript"> YAHOO.util.Event.onContentReady("mytreeview", function () { var Dom = YAHOO.util.Dom; /* Map of YAHOO.widget.TextNode instances in the TreeView instance. */ // oTextNodeMap[ラベルid]にTextNodeインスタンスを格納する var oTextNodeMap = {}; // TreeViewインスタンスを生成 var oTreeView = new YAHOO.widget.TreeView("mytreeview"); var n, oTextNode; // 3個から7個のトップレベルのノードを作成する。 for (n = 0; n < Math.floor((Math.random()*4) + 3); n++) { oTextNode = new YAHOO.widget.TextNode("label-" + n, oTreeView.getRoot(), false); // oTextNodeMap[ラベルid]にTextNodeインスタンス oTextNodeMap[oTextNode.labelElId] = oTextNode; // ブランチの作成 buildRandomTextBranch(oTextNode); } // treeViewの描画 oTreeView.draw(); //ブランチの作成;6階層までのTreeViewを再帰的に作成する。 function buildRandomTextBranch(p_oNode) { var oTextNode, i; if (p_oNode.depth < 6) { // 乱数で、0から4個までのテキストノードを生成する for (i = 0; i < Math.floor(Math.random() * 4); i++) { oTextNode = new YAHOO.widget.TextNode(p_oNode.label + "-" + i, p_oNode, false); oTextNodeMap[oTextNode.labelElId] = oTextNode; buildRandomTextBranch(oTextNode); } } } //------------------ 以下、イベントの処理 --------------------- // contextmenu(DOM)イベントで、ContextMenuインスタンスを発生・表示させた // TextNodeインスタンスを保管する。 var oCurrentTextNode = null; //oCurrentTextNode(上述)に新しいTextNodeを追加する。 function addNode() { var sLabel = window.prompt("新しいノードのラベルを入力: ", ""), oChildNode; if (sLabel && sLabel.length > 0) { //新しいNodeの追加(展開しない) oChildNode = new YAHOO.widget.TextNode(sLabel, oCurrentTextNode, false); // oCurrentTextNode(上述)をリフレッシュして展開 oCurrentTextNode.refresh(); oCurrentTextNode.expand(); //oTextNodeMapに新規保管 oTextNodeMap[oChildNode.labelElId] = oChildNode; } } //oCurrentTextNode(上述)のラベルを変更する。 function editNodeLabel() { var sLabel = window.prompt("ラベル名を入力してください: ", oCurrentTextNode.getLabelEl().innerHTML); if (sLabel && sLabel.length > 0) { oCurrentTextNode.getLabelEl().innerHTML = sLabel; } } //oCurrentTextNode(上述)のラベルを削除する。 function deleteNode() { //oTextNodeMapから除去 delete oTextNodeMap[oCurrentTextNode.labelElId]; oTreeView.removeNode(oCurrentTextNode); //drawしなおし。 oTreeView.draw(); } // contextmenuイベントのハンドラー // ContextMenuを表示させたTextNodeへの参照をoCurrentTextNodeにセットする。 function onTriggerContextMenu(p_oEvent) { var oTarget = this.contextEventTarget; // ygtvlabelはTExtNodeのラベルスタイル // http://developer.yahoo.com/yui/docs/TextNode.js.html var oTextNode = Dom.hasClass(oTarget, "ygtvlabel") ? oTarget : Dom.getAncestorByClassName(oTarget, "ygtvlabel"); // TextNodeが見つかったら、それをoCurrentTextNodeにセット。 // 見つからなかったら、キャンセル(ContextMenuは表示されない)。 if (oTextNode) { oCurrentTextNode = oTextNodeMap[oTarget.id]; } else { // Cancel the display of the ContextMenu instance. this.cancel(); } } // TreeView全体に対して、ContextManuのインスタンスを生成する。 var oContextMenu = new YAHOO.widget.ContextMenu("mytreecontextmenu", { trigger: "mytreeview", lazyload: true, itemdata: [ { text: "子ノードの追加", onclick: { fn: addNode } }, { text: "ラベルの変更", onclick: { fn: editNodeLabel } }, { text: "ノードの削除", onclick: { fn: deleteNode } } ] } ); // ContextManuのインスタンスにcontextmenuイベントをlistenさせ、 // ハンドラー(onTriggerContextMenu)を登録する。 oContextMenu.subscribe("triggerContextMenu", onTriggerContextMenu); }); </script> </HEAD> <!-- class=" yui-skin-sam"の指定が必要 --> <BODY class="yui-skin-sam"> <p> TreeViewにコンテキストメニューを表示するサンプルです。 </p> <div id="mytreeview"></div> </BODY> </HTML>