XUL tree - Adding nodes with a context menu / edit node names example/tutorial
The XUL language is very powerful. Powerful enough to forget traditional means of doing
web applications with HTML and using the many widget available with XUL.
There are many Javascript-Tree implementations out there, but all are either not
cross-browser compliant or are slow etc. Not so with native XUL trees. This example
shows how to add nodes by right-clicking on a node or item and selecting the context-menu
"Add node". A prompt shows up where you can enter the name, and after clicking on OK, the
node is added. Furthermore the name of the node can be edited by double-clicking on the
node. In combination with AJAX this is a very powerful tool:
AJAX/Javascript Form GET Request
AJAX/Javascript Form POST Request
AJAX/Javascript XML Processing Example/Tutorial
UPDATE: There is another implementation of the same available at: XUL Tree Edit/Add NEW Tutorial
tree.xul
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="edittree.css" type="text/css"?>
<window id="treeImage" title="Tree With Images"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script>
<![CDATA[
var currentid;
var currentobj;
function addnode() {
var ti = currentobj.parentNode.parentNode;
mylabel = prompt("Name of node:", "name");
if(!mylabel) { return; }
if (ti.getAttribute("container") != "true") {
ti.setAttribute("container","true");
ti.setAttribute("open","true");
}
var i = 0;
var foundchild = 0;
for (i=0; i < ti.childNodes.length; i++) {
if (ti.childNodes[i].nodeName == "treechildren") {
var ti = ti.childNodes[i];
var titem = document.createElement("treeitem");
var trow = document.createElement("treerow");
var tcell = document.createElement("treecell");
tcell.setAttribute("label", mylabel);
trow.appendChild(tcell);
titem.appendChild(trow);
ti.appendChild(titem);
foundchild = 1;
}
}
if (foundchild == 0) {
var ti = currentobj.parentNode.parentNode;
var tch = document.createElement("treechildren");
var titem = document.createElement("treeitem");
var trow = document.createElement("treerow");
var tcell = document.createElement("treecell");
tcell.setAttribute("label", mylabel);
trow.appendChild(tcell);
titem.appendChild(trow);
tch.appendChild(titem);
ti.appendChild(tch);
}
}
]]>
</script>
<popupset label="zzzo">
<popup id="clipmenu" >
<menuitem label="Add Node" oncommand="addnode();"/>
</popup>
</popupset>
<tree id="thetree" flex="1" width="350" height="200" >
<treecols>
<treecol id="name" label="Name" primary="true" flex="1"/>
</treecols>
<treechildren>
<treeitem container="true" open="true">
<treerow>
<treecell label="1111"/>
</treerow>
<treechildren>
<treeitem container="true" open="true">
<treerow>
<treecell label="2222"/>
</treerow>
</treeitem>
<treeitem container="true" open="true">
<treerow>
<treecell label="3333"/>
</treerow>
<treechildren id="child">
<treeitem container="true" open="true">
<treerow>
<treecell label="4444"/>
</treerow>
</treeitem>
<treeitem container="true" open="true">
<treerow>
<treecell label="5555"/>
</treerow>
</treeitem>
</treechildren>
</treeitem>
</treechildren>
</treeitem>
<treeitem container="true" open="true">
<treerow>
<treecell label="6666"/>
</treerow>
<treechildren>
<treeitem container="true" open="true">
<treerow>
<treecell label="7777"/>
</treerow>
</treeitem>
</treechildren>
</treeitem>
</treechildren>
</tree>
</window>
edittree.css
tree
{
-moz-binding: url("edittree.xml#edittree");
-moz-user-focus: normal !important;
-moz-user-select: text;
}
textbox {
min-height: 1.8em;
-moz-user-focus: normal !important;
-moz-user-select: text !important;
}
tree[editing] > treechildren:-moz-tree-row(selected)
{
background-color: transparent;
border: none;
}
edittree.xml
Based on the example posted at xulplanet.com
<?xml version="1.0"?>
<bindings id="treeEditBindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="edittree" extends="chrome://global/content/bindings/tree.xml#tree">
<content>
<children includes="treecols"/>
<xul:stack flex="1">
<xul:treerows class="tree-rows" flex="1">
<children/>
</xul:treerows>
<xul:textbox ileattr="text" left="0" top="0" hidden="true"/>
</xul:stack>
</content>
<implementation>
<field name="_editOriginalValue">0</field>
<field name="_editRow">-1</field>
<field name="_editCol">null</field>
<field name="onAccept">null</field>
<method name="setEditMode">
<parameter name="x"/>
<parameter name="y"/>
<parameter name="val"/>
<body>
<![CDATA[
var txt = document.getAnonymousElementByAttribute(this, "ileattr", "text");
if (val){
if (x < 0) return;
var originalValue = this.view.getCellText(x,y);
var cellnode = this.getCellNodeAt(x,y);
if (!(cellnode || this.view.isEditable(x,y))) return;
if (this._editRow >= 0) this._assignValueToCell(txt.value,true);
if (cellnode && cellnode.getAttribute("readonly")) return;
txt.removeAttribute("hidden");
var treeBox = this.treeBoxObject;
var outx = {}, outy = {}, outwidth = {}, outheight = {};
var coords = treeBox.getCoordsForCellItem(x,y,"text",outx,outy,outwidth,outheight);
this._editRow = x;
this._editCol = y;
txt.setAttribute("left",outx.value-3);
txt.setAttribute("top",outy.value-3);
txt.setAttribute("height",outheight.value);
var coords = treeBox.getCoordsForCellItem(x,y,"cell",outx,outy,outwidth,outheight);
txt.setAttribute("width",outwidth.value - outy.value);
this._editOriginalValue = originalValue;
if (cellnode) cellnode.setAttribute("label","");
this.view.setCellText(x,y,"");
txt.value = originalValue;
txt.select();
this.setAttribute("editing","true");
txt.addEventListener("keydown", this.fieldKeyDown, false);
txt.addEventListener("blur", this.fieldChange, true);
}
else {
this.removeAttribute("editing");
txt.setAttribute("hidden","true");
txt.removeEventListener("keydown", this.fieldKeyDown, false);
txt.removeEventListener("blur", this.fieldChange, true);
txt.blur();
}
]]>
</body>
</method>
<method name="getCellNodeAt">
<parameter name="row"/>
<parameter name="col"/>
<body>
var view;
try {
view = this.contentView;
} catch (ex){}
if (view){
var elem = view.getItemAtIndex(row);
if (elem){
var pos = ((document.getElementById(col).ordinal - 1) >> 1);
return elem.firstChild.childNodes[pos];
}
}
return null;
</body>
</method>
<method name="fieldKeyDown">
<parameter name="aEvent"/>
<body>
<![CDATA[
var tree = aEvent.target;
while (tree && tree.tagName != "tree") tree = tree.parentNode;
if (aEvent.keyCode == 13){
tree._assignValueToCell(this.value,true);
}
if (aEvent.keyCode == 27){
tree._assignValueToCell(tree._editOriginalValue,false);
}
aEvent.preventBubble();
]]>
</body>
</method>
<method name="_assignValueToCell">
<parameter name="value"/>
<parameter name="acceptMode"/>
<body>
<![CDATA[
if (this._editRow == -1) return;
if (acceptMode && this.onAccept &&
this.onAccept(this._editRow,this._editCol,this._editOriginalValue,value))
return;
var cellnode = this.getCellNodeAt(this._editRow,this._editCol);
if (cellnode) cellnode.setAttribute("label",value);
this.view.setCellText(this._editRow,this._editCol,value);
this.treeBoxObject.invalidateCell(this._editRow,this._editCol);
this._editRow = -1;
this._editCol = null;
this.setEditMode("normal");
]]>
</body>
</method>
<method name="fieldChange">
<parameter name="aEvent"/>
<body>
<![CDATA[
var tree = aEvent.target;
while (tree && tree.tagName != "tree") tree = tree.parentNode;
tree._assignValueToCell(this.value,true);
]]>
</body>
</method>
</implementation>
<handlers>
<handler event="dblclick">
var treeBox = this.treeBoxObject;
var row = {};
var col = {};
var obj = {};
treeBox.getCellAt(event.clientX,event.clientY,row,col,obj);
this.setEditMode(row.value,col.value,true);
</handler>
<handler event="click" button="2">
var treeBox = this.treeBoxObject;
var row = {};
var col = {};
var obj = {};
treeBox.getCellAt(event.clientX,event.clientY,row,col,obj);
var cellnode = this.getCellNodeAt(row.value,col.value);
currentid = cellnode.id;
currentobj = cellnode;
document.getElementById('clipmenu').showPopup(
document.documentElement, event.clientX,
event.clientY, "bottomleft", "topleft");
</handler>
</handlers>
</binding>
</bindings>
Last-Modified: Sat, 04 Feb 2006 16:02:58 GMT