JavaScript DHTML/GUI Components/Tree Table

Материал из Web эксперт
Перейти к: навигация, поиск

Tree table Demo

<html>
<head>
<title>Document sans titre</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<!-- extendedArray.js -->
<script type="text/javascript">
function array_contains(obj)
{
  for (var i = 0; i < this.length; i++)
  {
     if (this[i] == obj) return i;
  }
  return -1;
}
Array.prototype.contains = array_contains;
function array_remove(obj)
{
    var index = this.contains(obj);
    if(index > -1)
        this.splice(index, 1);
}
Array.prototype.remove = array_remove;
// using this function ensure that you won"t add an element which already exists in the array
function array_add(obj)
{
    var index = this.contains(obj);
    if(index == -1)
        this.push(obj);
}
Array.prototype.add = array_add;
function array_to_string()
{
    var result = "";
  for (var i = 0; i < this.length; i++)
  {
     result += this[i] + " ; ";
     //log(this[i]);
  }
  return result;
}
Array.prototype.to_string = array_to_string;
</script>
<!-- treetable.js -->
<script type="text/javascript">
var open_nodes = new Array();
// ICONS
var FOLDER_CLSD_PIC = "treeTableImages/icon_arrowfolderclosed1_sml.gif";
var FOLDER_OPEN_PIC = "treeTableImages/icon_arrowfolderopen2_sml.gif";
var DOC_PIC = "treeTableImages/icon_doc_sml.gif";
// highlighting
var normalColor = "#E6F2FF";
var highlightColor = "#C6D2DF";
// Regular Expressions
// specify how the id are encoded to represent the path of the tree
// ususally we just need to change TREE_PATH_SEP
var RE_PATH = "[0-9]+"; 
var TREE_PATH_SEP  = ".";
// This method should be called when a click occurs on the folder icon (or something equivalent!)
// e is the event and elm is the element on which the event occured
function toggleRows(e, elm)
{
    // first we check if we moved the mouse during the click as it signifies that it is a dnd and not a click
  if(mouseMoved(e))
    return;
    var toggledRow = find_ancestor("tr", elm);
    var id = toggledRow.id;  // the id of the row we are toggling (it contains the path)
    var name = toggledRow.getAttribute("name");
    var rows = find_ancestor("table", elm).getElementsByTagName("TR");
    // regular expression representing the id of the children of toggledRow
    var idToggledRE = id.slice(0, id.length) + RE_PATH;
    if(open_nodes.contains(name) > -1) // the element was opened -> closing
    {
        elm.style.backgroundImage = "url("+FOLDER_CLSD_PIC+")";
        for (var i = 0; i < rows.length; i++)
        {
          var currentRow = rows[i];
          if (matchStart(currentRow.id, idToggledRE, false)) // if currentRow is a child of toggledRow
          {
              currentRow.style.display = "none";
          }
      }
      open_nodes.remove(name);
    }
    else // opening
    {
        // trick to avoid a problem of display after a restore when a folder become a doc as he is empty
        if(elm.getAttribute("class") != "folder"){
            open_nodes.remove(name);
            return;
        }
        elm.style.backgroundImage = "url("+FOLDER_OPEN_PIC+")";
        for (var i = 0; i < rows.length; i++)
        {
          var currentRow = rows[i];
          var currentIconLink = currentRow.getElementsByTagName("A")[0];
          if (matchStart(currentRow.id, idToggledRE, true)) // if currentRow is a child of toggledRow
          {
              if (document.all)
                  currentRow.style.display = "block"; //IE4+ specific code
               else
                  currentRow.style.display = "table-row";
                  
               // this is just to be sure that we have the right icon (maybe not necessary)
               if(currentIconLink.getAttribute("class") != "folder")
                  currentIconLink.style.backgroundImage = "url("+DOC_PIC+")";
               
               // reopen the rows which where already opened 
               if (open_nodes.contains(currentRow.getAttribute("name")) > -1)
               {
                  open_nodes.remove(currentRow.getAttribute("name"));
                  toggleRows(null, currentIconLink);
               }
          }
      }
      open_nodes.add(name);
    }
    // ignore the selectRow event as it was a toggling event
    ignoreSelectRowEvt = true;
}
// return the first element of the collection with an attribute "name" correponding to name
function findElementByName(collection, name)
{
    for (var i = 0; i < collection.length; i++)
    {
        if(collection[i].getAttribute("name") == name){
            return collection[i];
        }
    }
}
// pattern is a string containing a regular expression without the "/" at the beginning and the end
// returns true if target begin with pattern, false else. Moreover if matchDirectChildrenOnly=true
// we return false if the target is not a direct child.
function matchStart(target, pattern, matchDirectChildrenOnly)
{
   var patternObj = eval("/^"+pattern+"/");
   if (!target.match(patternObj)) return false;
   if (!matchDirectChildrenOnly) return true;
   var extendedPattern = eval("/^"+pattern+"["+TREE_PATH_SEP+"]"+RE_PATH+"/");
   if (target.match(extendedPattern)) return false;
   return true;
}
function collapseAllRows()
{
   var rows = document.getElementsByTagName("TR");
   var pattern = eval("/^[0-9]+["+TREE_PATH_SEP+"]"+RE_PATH+"/");
   var patternFirstLevel = eval("/^"+RE_PATH+"["+TREE_PATH_SEP+"]$/");
   for (var j = 0; j < rows.length; j++)
   {
      var r = rows[j];
      if (r.id.match(pattern))
      {
         r.style.display = "none";
         if(r.getElementsByTagName("A")[0].getAttribute("class")=="folder")
            r.getElementsByTagName("A")[0].style.backgroundImage = "url("+FOLDER_CLSD_PIC+")";
    else 
      r.getElementsByTagName("A")[0].style.backgroundImage = "url("+DOC_PIC+")";
      }else if (r.id.match(patternFirstLevel))
      {
         r.getElementsByTagName("A")[0].style.backgroundImage = "url("+FOLDER_CLSD_PIC+")";
      }
   }
   open_nodes = new Array();
}
function openAllRows()
{
   var rows = document.getElementsByTagName("TR");
   var pattern = eval("/^"+RE_PATH+"["+TREE_PATH_SEP+"]/");
   var patternFirstLevel = eval("/^"+RE_PATH+"["+TREE_PATH_SEP+"]$/");
   var firstLevelRows = new Array();
   open_nodes = new Array();
   for (var i = 0; i < rows.length; i++)
   {
      var r = rows[i];
      if (r.id.match(patternFirstLevel))
      {
         firstLevelRows.push(r);
      }
      else if (r.id.match(pattern))
      {
         open_nodes.add(r.getAttribute("name"));
      }
   }
   for (var j = 0; j < firstLevelRows.length; j++)
      toggleRows(null,firstLevelRows[j].getElementsByTagName("A")[0]);
}
// restore the state of the tree depending on open_nodes
// take all the nodes of first level and for each reopen or close it depending on 
// the open_nodes list. Moreover we call toggleRows to restore the state of the children nodes.
function restore(){
  var rows = document.getElementsByTagName("TR");
     var pattern = eval("/^"+RE_PATH+"["+TREE_PATH_SEP+"]$/");
  for (var j = 0; j < rows.length; j++)
  {
     var r = rows[j];
     if (r.id.match(pattern)) // first level 
     {
      // as toggleRows() will check open_nodes to know wheter it has to open or close the node, 
      // we have to do the opposite before because we just want to restore the state and not to really toggle it
        if (open_nodes.contains(r.getAttribute("name")) > -1)
           open_nodes.remove(r.getAttribute("name"));
        else
           open_nodes.add(r.getAttribute("name"));
        toggleRows(null, r.getElementsByTagName("A")[0]);
     }
  }
}
// This method should be used with an onclick event for your tables rows (TR)
// in order to have them visually selected
var selectedRow;
var ignoreSelectRowEvt = false; // set this variable to true if you want to ignore the next selectRow event
function selectRow(row)
{
    if(ignoreSelectRowEvt){
        ignoreSelectRowEvt = false;
        return;
    }
    if(selectedRow)
        selectedRow.style.backgroundColor = normalColor;
    
    // if we are deselecting
    if(selectedRow && selectedRow.id == row.id)
    {
        selectedRow = null;
    }else{
        selectedRow = row;
        row.style.backgroundColor = highlightColor;
    }
}
</script>
<!-- utils.js -->
<script type="text/javascript">
// return the first ancestor tag (tagAnc) of obj
function find_ancestor(tagAnc, obj)
{
   var exit = false;
   var parent = obj;
   while (!exit)
   {
      parent = parent.parentNode;
      if (parent.tagName == tagAnc.toLowerCase() || parent.tagName == tagAnc.toUpperCase())
         return parent;
      if (parent.tagName == "HTML" || parent.tagName == "html")
         return null;
   }
}
function log(txt, divName)
{
   if (!divName) var divName = "console";
   $(divName).innerHTML += "<br>" + txt;
}
var clicX = 0;
var clicY = 0;
function storeMouseXY(e)
{
   if (!e) var e = window.event; 
  if (e.pageX || e.pageY)
  {
    clicX = e.pageX;
    clicY = e.pageY;
  }
  else if (e.clientX || e.clientY)
  {
    clicX = e.clientX + document.body.scrollLeft;
    clicY = e.clientY + document.body.scrollTop;
  }
}
// return true if the mouse moved more than 3 pixels in one direction between the beginning
// of the event and the end of the envent
// WARNING : in order to use this method you should use onmousedown="storeMouseXY(event); in the 
// element where is called mouseMoved...
function mouseMoved(e)
{
  if(e){
      var oldx = clicX, oldy = clicY;
      storeMouseXY(e);
      if(Math.abs(clicX-oldx) > 3 || Math.abs(clicY-oldy) > 3)
          return true;
    }
  return false;
}
// get the id of the obj, i.e. the corresponding id in the db
function getDB_ID(obj)
{
   // look for a parent TR
   var parentTR;
   if (obj.tagName == "tr" || obj.tagName == "TR")
      parentTR = obj;
   else
      parentTR = find_ancestor("tr", obj);
  return parentTR.id;
   // look for a child with name ops
   var children = parentTR.getElementsByTagName("td");
   var ops = null;
   for (var i = 0; i < children.length; i++)
   {
      if (children[i].getAttribute("name") && children[i].getAttribute("name").toUpperCase() == "OPS")
      {
         ops = children[i];
         break;
      }
   }
   return ops.firstChild.value;
}
</script> 
<style media="all" rel="Stylesheet" type="text/css">
.folder { background: url(../treeTableImages/icon_arrowfolderclosed1_sml.gif)  no-repeat; float: left; height: 15px; width: 33px; padding-right: 3px; ! important}
.doc { background: url(../treeTableImages/icon_doc_sml.gif) no-repeat; float: left; height: 15px; width: 15px; padding-right: 3px; margin-left: 0px; ! important}
.tier0 {
  margin-left: 0;
}
.tier1 {
  margin-left: 1.5em;
}
.tier2 {
  margin-left: 3em;
}
.tier3 {
  margin-left: 4.5em;
}
.tier4 {
  margin-left: 6em;
}
.tier5 {
  margin-left: 7.5em;
}
.tier6 {
  margin-left: 9em;
}
.tier7 {
  margin-left: 10.5em;
}
.tier8 {
  margin-left: 12em;
}
.tier9 {
  margin-left: 13.5em;
}
</style>
</head>
<body onload="collapseAllRows();">
<table width="100%" border="1">
  <tr id="0." name="r1"> 
    <td>
    <div class="tier0">
      <a href="#" onclick="toggleRows(event, this)" onmousedown="storeMouseXY(event); return false;" class="folder"></a>
    </div>
  </td>
    <td>a1</td>
    <td>a2</td>
    <td>a3</td>
    <td>a4</td>
  </tr>
  <tr id="0.0." name="r2"> 
    <td><div class="tier1"><a href="#" onclick="toggleRows(event, this)" onmousedown="storeMouseXY(event); return false;" class="folder"></a></div></td>
    <td>b1</td>
    <td>b2</td>
    <td>b3</td>
    <td>b4</td>
  </tr>
  <tr id="0.0.0." name="r3"> 
    <td><div class="tier2"><a href="#" onclick="toggleRows(event, this)" onmousedown="storeMouseXY(event); return false;" class="folder"></a></div></td>
    <td>c1</td>
    <td>c2</td>
    <td>c3</td>
    <td>c4</td>
  </tr>
  <tr id="0.0.0.0." name="r4"> 
    <td><div class="tier3"><a href="#" onclick="toggleRows(event, this)" onmousedown="storeMouseXY(event); return false;" class="doc"></a></div></td>
    <td>d1</td>
    <td>d2</td>
    <td>d3</td>
    <td>d4</td>
  </tr>
</table>
</body>
</html>


<A href="http://www.wbex.ru/Code/JavaScriptDownload/treetable.zip">treetable.zip( 10 k)</a>