Simple Tree in Javascript : Tree : GUI Components JAVASCRIPT DHTML TUTORIALS


JAVASCRIPT DHTML TUTORIALS » GUI Components » Tree »

 

Simple Tree in Javascript




<html>
<head>
<title>:: Tree Sample ::</title>

  <style type="text/css">
  body
{
  padding: 0;
  margin: 0;
}
.tree ul, .tree li
{
  list-style-type: none;
  margin: 0 0 0 2px;
  padding: 0;
  display: block;
}
.tree ul ul
{
  margin-left: 16px;
}
.tree a.selected
{
  background-color: lavender;
}
.tree .collapsed ul, .tree .collapsed span
{
  display: none;
}
.tree span
{
  display: block;
  margin: 0 0 0 16px;
  padding: 0;
  color: Gray;
  cursor: default;
  font-size: smaller;
}
.tree img
{
  border: none;
  text-align: left;
  vertical-align: middle;
  margin-right: 2px;
}
.tree img.plusminus
{
  width: 9px;
  height: 9px;
}
.tree a, .tree a:link, .tree a:visited, .tree a:active
{
  font-size: 10pt;
  color: navy;
  font-family: Verdana;
  text-decoration: none;
  text-align: left;
  margin: 0 2px 0 2px;
}
.tree a:hover
{
  text-decoration: underline;
  color: Blue;
}

  </style>
<script type="text/javascript">
/*
    _______________________            ______________________
    XML DOM Tree Component             Browsers support:
             Version 1.5                -> Internet Explorer
                                        -> Mozilla
    _____________________________       -> Opera
    Features:                           -> Firefox
     -> Server Side Independency        -> Konqueror
     -> Cross Browser Support
     -> Dynamic Loading
     -> XML Source
     -> Easy Customization





                              ______________________________
                                  Serghei Egoricev (c) 2006
                                    egoricev [at] gmail.com
*/

// CSS import

Tree = function() {}
/*
  Use double click for navigate, single click for expand
*/
Tree.useDblClicks = true// NOT IMPLEMENTED
Tree.saveNodesStateInCookies = true;
/*
  CSS classes
*/
Tree.expandedClassName = "";
Tree.collapsedClassName = "collapsed";
Tree.selectedClassName = "selected";
Tree.plusMinusClassName = "plusminus";
Tree.treeClass = "tree";

/*
  Images
*/
Tree.collapsedImage = "treeimg/collapsed.gif";
Tree.expandedImage = "treeimg/expanded.gif";
Tree.noChildrenImage = "treeimg/treenochild.gif";

/*
  Xml Attributes
*/
Tree.xmlCaption = "caption";
Tree.xmlUrl = "url";
Tree.xmlTarget = "target";
Tree.xmlRetreiveUrl = "retreiveUrl";
Tree.xmlIcon = "icon";
Tree.xmlExpanded = "expanded";

/*
  Text for loading
*/
Tree.loadingText = "Loading ...";

/*
  Private members
*/
Tree.obj = null;
Tree.instanceCount = 0;
Tree.instancePrefix = "alder";
Tree.cookiePrefix = "alder";
Tree.dwnldQueue = new Array;
Tree.dwnldCheckTimeout = 100;

/*
  Interval handler. Ckecks for new nodes loaded.
  Adds loaded nodes to the tree.
*/
Tree.checkLoad = function ()
{
  var i, httpReq;
  for (i = 0; i<Tree.dwnldQueue.length; i++)
    if ((httpReq = Tree.dwnldQueue[i][0]).readyState == /*COMPLETED*/)
    {
      var node = Tree.dwnldQueue[i][1];
      // unqueue loaded item
      Tree.dwnldQueue.splice(i, 1);
      Tree.appendLoadedNode(httpReq, node);
      if (Tree.saveNodesStateInCookies)
        Tree.openAllSaved(Tree.getId(node));
    // if
  // will call next time, not all nodes were loaded
  if (Tree.dwnldQueue.length != 0)
    window.setTimeout(Tree.checkLoad, Tree.dwnldCheckTimeout);
}

/*
  Adds loaded node to tree.
*/
Tree.appendLoadedNode = function (httpReq, node)
{
  // create DomDocument from loaded text
  var xmlDoc = Tree.loadXml(httpReq.responseText);
  // create tree nodes from xml loaded
  var newNode = Tree.convertXml2NodeList(xmlDoc.documentElement);
  // Add loading error handling here must be added
  Tree.appendNode(node, newNode);
}

/*
  Event handler when node is clicked.
  Navigates node link, and makes node selected.
*/
Tree.NodeClick = function (event)
{
  var node = event.srcElement /*IE*/ || event.target /*DOM*/;
  // <li><a><img> - <img> is capturing the event
  while (node.tagName != "A")
    node = node.parentNode;
  node.blur();
  node = node.parentNode;
  Tree.obj = Tree.getObj(node);
  Tree.expandNode(node);
  Tree.selectNode(node);
}

/*
  Event handler when plus/minus icon is clicked.
  Desides whenever node should be expanded or collapsed.
*/
Tree.ExpandCollapseNode = function (event)
{
  var anchorClicked = event.srcElement /*IE*/ || event.target /*DOM*/;
  // <li><a><img> - <img> is capturing the event
  while (anchorClicked.tagName != "A")
    anchorClicked  = anchorClicked.parentNode;
  anchorClicked.blur();
  var node = anchorClicked.parentNode;
  // node has no children, and cannot be expanded or collapsed
  if (node.empty)
    return;
  Tree.obj = Tree.getObj(node);
  if (Tree.isNodeCollapsed(node))
    Tree.expandNode(node);
  else
    Tree.collapseNode(node);
  // cancelling the event to prevent navigation.
  if (event.preventDefault == undefined)
  // IE
    event.cancelBubble = true;
    event.returnValue = false;
  // if
  else
  // DOM
    event.preventDefault();
    event.cancelBubble = true;
  // else
}

/*
  Determines if specified node is selected.
*/
Tree.isNodeSelected = function (node)
{
  return (node.isSelected == true|| (Tree.obj.selectedNode == node);
}

/*
  Determines if specified node is expanded.
*/
Tree.isNodeExpanded = function (node)
{
  return (Tree.expandedClassName == node.className|| (node.expanded == true);
}

/*
  Determines if specified node is collapsed.
*/
Tree.isNodeCollapsed = function (node)
{
  return (Tree.collapsedClassName == node.className|| (node.collapsed == true);
}

/*
  Determines if node currently selected is at same
  level as node specified (has same root).
*/
Tree.isSelectedNodeAtSameLevel = function (node)
{
  if (Tree.obj.selectedNode == null// no node currently selected
    return false;
  var i, currentNode, children = node.parentNode.childNodes; // all nodes at same level (li->ul->childNodes)
  for (i = 0; i < children.length; i++)
    if ((currentNode = children[i]) != node && Tree.isNodeSelected(currentNode))
      return true;
  return false;
}

/*
  Mark node as selected and unmark prevoiusly selected.
  Node is marked with attribute and <a> is marked with css style
  to avoid mark <li> twise with css style expanded and selected.
*/
Tree.selectNode = function (node)
{
  if (Tree.isNodeSelected(node)) // already marked
    return;
  if (Tree.obj.selectedNode != null)
  {// unmark previously selected node.
    Tree.obj.selectedNode.isSelected = false;
    // remove css style from anchor
    Tree.getNodeAnchor(Tree.obj.selectedNode).className = "";
  // if
  // collapse selected node if at same level
  if (Tree.isSelectedNodeAtSameLevel(node))
    Tree.collapseNode(Tree.obj.selectedNode);
  // mark node as selected
  Tree.obj.selectedNode = node;
  node.isSelected = true;
  Tree.getNodeAnchor(node).className = Tree.selectedClassName;
}

/*
  Expand collapsed node. Loads children nodes if needed.
*/
Tree.expandNode = function (node, avoidSaving)
{
  if (node.empty)
    return;
  Tree.getNodeImage(node).src = Tree.expandedImage;
  node.className = Tree.expandedClassName;
  node.expanded = true;
  node.collapsed = false;
  if (Tree.areChildrenNotLoaded(node))
    Tree.loadChildren(node);
  if (Tree.saveNodesStateInCookies && !avoidSaving)
    Tree.saveOpenedNode(node);
}

/*
  Collapse expanded node.
*/
Tree.collapseNode = function (node, avoidSaving)
{
  if (node.empty)
    return;
  Tree.getNodeImage(node).src = Tree.collapsedImage;
  node.className = Tree.collapsedClassName;
  node.collapsed = true;
  node.expanded = false;
  if (Tree.saveNodesStateInCookies && !avoidSaving)
    Tree.saveClosedNode(node);
}

/*
  Returns plus/minus <img> for node specified.
*/
Tree.getNodeImage = function (node)
{
  return node.getElementsByTagName("IMG")[0];
}

/*
  Returns retreiveUrl for node specified.
*/
Tree.getNodeRetreiveUrl = function (node)
{
  return node.getElementsByTagName("A")[0].href;
}

/*
  Returns node link <a> element (<li><a><img></a><a>)
*/
Tree.getNodeAnchor = function (node)
{
  return node.getElementsByTagName("A")[1];
}

/*
  Cancel loading children nodes.
*/
Tree.CancelLoad = function (event)

  var i, node = event.srcElement /*IE*/ || event.target /*DOM*/;
  while (node.tagName != "LI")
    node = node.parentNode;
  // search node in queue
  for (i = 0; i<Tree.dwnldQueue.length; i++)
    if (Tree.dwnldQueue[i][1== node)
    {
      // remove from queue
      Tree.dwnldQueue.splice(i, 1);
      // collapse node
      Tree.collapseNode(node);
    // if
}

/*
  Loads text from url specified and returns it as result.
*/
Tree.loadUrl = function (url, async)
{
  // create request object
  var httpReq = window.XMLHttpRequest ? new XMLHttpRequest() new ActiveXObject("Microsoft.XMLHTTP");
  // prepare request
  httpReq.open("GET" /* method */, url /* url */, async == true /* async */null /* login */null /* password */);
  // send request
  httpReq.send(null);
  return async == true? httpReq : httpReq.responseText;
}

/*
  Creates XmlDom document from xml text string.
*/
Tree.loadXml = function (xmlString)
{
  var xmlDoc;
  if (window.DOMParser/*Mozilla*/
    xmlDoc = new DOMParser().parseFromString(xmlString, "text/xml");
  else
  {
    if (document.implementation && document.implementation.createDocument)
      xmlDoc = document.implementation.createDocument("",""null)/*Konqueror*/
    else
      xmlDoc = new ActiveXObject("Microsoft.XmlDom")/*IE*/
    
    xmlDoc.async = false;
    xmlDoc.loadXML(xmlString);
  // else
  return xmlDoc;
}

/*
  Determines if children are loaded for node specified.
*/
Tree.areChildrenNotLoaded = function (node)
{
  return Tree.getNodeSpan(node!= null;
}

/*
  Finds loading span for node.
*/
Tree.getNodeSpan = function (node)
{
  var span = node.getElementsByTagName("SPAN");
  return (span.length > && (span = span[0]).parentNode == node? span : null;
}

/*
  Enqueue load of children nodes for node specified.
*/
Tree.loadChildren = function (node)
{
  // get url with children
  var url = Tree.getNodeRetreiveUrl(node);
  // retreive xml text from url
  var httpReq = Tree.loadUrl(url, true);
  // enqueue node loading
  if (Tree.dwnldQueue.push(new Array (httpReq, node)) == 1)
    window.setTimeout(Tree.checkLoad, Tree.dwnldCheckTimeout);
}

/*
  Creates HTML nodes list from XML nodes.
*/
Tree.convertXml2NodeList = function (xmlElement)
{
  var ul = document.createElement("UL");
  var i, node, children = xmlElement.childNodes;
  var index = 0;
  for (i = 0; i<children.length; i++)
    if ((node = children[i]).nodeType == /* ELEMENT_NODE */)
      ul.appendChild(Tree.convertXml2Node(node)).nodeIndex = index++;
  return ul;
}

/*
  Adds event handler
*/
Tree.addEvent = function (obj, fn, ev)
{
  if (ev == undefinedev = "click"// defaulting event to onclick
  if (obj.addEventListener)
    obj.addEventListener(ev, fn, false);
  else
    if (obj.attachEvent)
      obj.attachEvent("on"+ev, fn);
    else
      obj.onclick = fn;
}

/*
  Determines if xml node has child nodes inside.
*/
Tree.hasXmlNodeChildren = function (xmlElement)
{
  var i, children = xmlElement.childNodes;
  for (i = 0; i<children.length; i++)
    if ((node = children[i]).nodeType == /* ELEMENT_NODE */)
      return true;
  return false;
}

/*
  Appends newly created node to node specified.
  Simply replace loading <span> at new node.
*/
Tree.appendNode = function (node, newNode)
{
  node.replaceChild(newNode, Tree.getNodeSpan(node));
}

/*
  Creates tree object. Loads it content from url specified.
*/
Tree.prototype.Create = function (url, obj)
{
  var div = document.createElement("DIV");
  div.id = Tree.instancePrefix + Tree.instanceCount++;
  div.className = Tree.treeClass;
  var xml = Tree.loadUrl(url, false);
  var xmlDoc = Tree.loadXml(xml);
  var newNode = Tree.convertXml2NodeList(xmlDoc.documentElement);
  div.appendChild(newNode);
  if (obj != undefined)
  {
    if (obj.appendChild// is node
      obj.appendChild(div);
    else if (document.getElementById(obj)) // is node id
      document.getElementById(obj).appendChild(div);
  // if
  else
    document.body.appendChild(div);
  if (Tree.saveNodesStateInCookies)
    Tree.openAllSaved(div.id);
}

/*
  Creates HTML tree node (<li>) from xml element.
*/
Tree.convertXml2Node = function (xmlElement)
{
  var li = document.createElement("LI");
  var a1 = document.createElement("A");
  var a2 = document.createElement("A");
  var i1 = document.createElement("IMG");
  var i2 = document.createElement("IMG");
  var hasChildNodes = Tree.hasXmlNodeChildren(xmlElement);
  var retreiveUrl = xmlElement.getAttribute(Tree.xmlRetreiveUrl);
  
  // plus/minus icon
  i1.className = Tree.plusMinusClassName;
  a1.appendChild(i1);
  Tree.addEvent(a1, Tree.ExpandCollapseNode);
  
  // plus/minus link
  a1.href = retreiveUrl != null && retreiveUrl.length != ? retreiveUrl : "about:blank";
  li.appendChild(a1);
  
  // node icon
  i2.src = xmlElement.getAttribute(Tree.xmlIcon);
  a2.appendChild(i2);
  
  // node link
  a2.href = xmlElement.getAttribute(Tree.xmlUrl);
  a2.target = xmlElement.getAttribute(Tree.xmlTarget);
  a2.title = xmlElement.getAttribute(Tree.xmlCaption);
  a2.appendChild(document.createTextNode(xmlElement.getAttribute(Tree.xmlCaption)));
  Tree.addEvent(a2, Tree.NodeClick);

  li.appendChild(a2);
  
  // loading span
  if (!hasChildNodes && retreiveUrl != null && retreiveUrl.length != 0)
  {
    var span = document.createElement("SPAN");
    span.innerHTML = Tree.loadingText;
    Tree.addEvent(span, Tree.CancelLoad);
    li.appendChild(span);
  // if
  
  // add children
  if (hasChildNodes)
    li.appendChild(Tree.convertXml2NodeList(xmlElement));
  if (hasChildNodes || retreiveUrl != null && retreiveUrl.length != 0)
  {
    if (xmlElement.getAttribute(Tree.xmlExpanded))
      Tree.expandNode(li, true);
    else
      Tree.collapseNode(li, true);
  // if
  else
  {
    i1.src = Tree.noChildrenImage; // no children
    li.empty = true;
  // else

  return li;
}

/*
  Retreives current tree object.
*/
Tree.getObj = function (node)
{
  var obj = node;
  while (obj != null && obj.tagName != "DIV")
    obj = obj.parentNode;
  return obj;
}

Tree.getId = function (node)
{
  var obj = Tree.getObj(node);
  if (obj)
    return obj.id;
  return "";
}

/*
  Retreives unique id for tree node.
*/
Tree.getNodeId = function (node)
{
  var id = "";
  var obj = node;
  while (obj != null && obj.tagName != "DIV")
  {
    if (obj.tagName == "LI" && obj.nodeIndex != null)
      id = "_" + obj.nodeIndex + id;
    obj = obj.parentNode;
  // while
//  if (obj != null && obj.tagName == "DIV")
//    id = obj.id + "_" + id;
  return id;
}

/*
  Saves node as opened for reload.
*/
Tree.saveOpenedNode = function (node)
{
  var treeId = Tree.getId(node);
  var state = Tree.getAllNodesSavedState(treeId);
  var nodeState = Tree.getNodeId(node",";
  if (state.indexOf(nodeState== -1)
  {
    state += nodeState;
    Tree.setAllNodesSavedState(treeId, state);
  // if
}

/*
  Saves node as closed for reload.
*/
Tree.saveClosedNode = function (node)
{
  var treeId = Tree.getId(node);
  var state = Tree.getAllNodesSavedState(treeId);
  state = state.replace(new RegExp(Tree.getNodeId(node",""g")"");
  Tree.setAllNodesSavedState(treeId, state);
}

Tree.getAllNodesSavedState = function (treeId)
{
  var state = Tree.getCookie(Tree.cookiePrefix + "_" + treeId);
  return state == null "" : state;
}

Tree.setAllNodesSavedState = function (treeId, state)
{
  Tree.setCookie(Tree.cookiePrefix + "_" + treeId, state);
}

/*
  Enques list of all opened nodes
*/
Tree.openAllSaved = function(treeId)
{
  var nodes = Tree.getAllNodesSavedState(treeId).split(",");
  var i;
  for (i=0; i<nodes.length; i++)
  {
    var node = Tree.getNodeById(treeId, nodes[i]);
    if (node && Tree.isNodeCollapsed(node))
      Tree.expandNode(node);
  // for
}

Tree.getNodeById = function(treeId, nodeId)
{
  var node = document.getElementById(treeId);
  if (!node)
    return null;
  var path = nodeId.split("_");
  var i;
  for (i=1; i<path.length; i++)
  {
    if (node != null)
    {
      node = node.firstChild;
      while (node != null && node.tagName != "UL")
        node = node.nextSibling;
    // if
    if (node != null)
      node = node.childNodes[path[i]];
    else
      break;
  // for
  return node;
}

Tree.setCookie = function(sName, sValue)
{
  document.cookie = sName + "=" + escape(sValue";";
}

Tree.getCookie = function(sName)
{
  var a = document.cookie.split("; ");
  for (var i=0; i < a.length; i++)
  {
    var aa = a[i].split("=");
    if (sName == aa[0]) 
      return unescape(aa[1]);
  // for
  return null;
}

</script>
</head>
<body>
<div id="tree"></div>
<hr/>
<script type="text/javascript">
new Tree().Create("tree.xml""tree");
new Tree().Create("tree.xml");
</script>
</body>
</html>
           
       

Download : Download nav_SimpleTree.zip nav_SimpleTree.zip


-

Leave a Comment / Note


 
Verification is used to prevent unwanted posts (spam). .

Follow Navioo On Twitter

JAVASCRIPT DHTML TUTORIALS

 Navioo GUI Components
» Tree