//初始化数据源里的数据以便后面的快速检索
MzTreeView.prototype.dataFormat = function()
{
  var a = new Array();
  for (var id in this.nodes) a[a.length] = id;
  this.names = a.join(this._d + this._d);
  this.totalNode = a.length; a = null;
};//在数据源检索所需的数据节点
//id  客户端节点对应的id
MzTreeView.prototype.load = function(id)
{
  var node = this.node[id], d = this.divider, _d = this._d;
  var sid = node.sourceIndex.substr(node.sourceIndex.indexOf(d) + d.length);
  var reg = new RegExp("(^|"+_d+")"+ sid +d+"[^"+_d+d +"]+("+_d+"|$)", "g");
  var cns = this.names.match(reg), tcn = this.node[id].childNodes; if (cns){
  reg = new RegExp(_d, "g"); for (var i=0; i<cns.length; i++)
  tcn[tcn.length] = this.nodeInit(cns[i].replace(reg, ""), id); }
  node.isLoad = true;
};//初始化节点信息, 根据 this.nodes 数据源生成节点的详细信息
//sourceIndex 数据源中的父子节点组合的字符串 0_1
//parentId    当前树节点在客户端的父节点的 id
MzTreeView.prototype.nodeInit = function(sourceIndex, parentId)
{
  this.index++;
  var source= this.nodes[sourceIndex], d = this.divider;
  var text  = this.getAttribute(source, "text");
  var hint  = this.getAttribute(source, "hint");
  var sid   = sourceIndex.substr(sourceIndex.indexOf(d) + d.length);
  this.node[this.index] =
  {
    "id"    : this.index,
    "text"  : text,
    "hint"  : hint ? hint : text,
    "icon"  : this.getAttribute(source, "icon"),
    "path"  : this.node[parentId].path + d + this.index,
    "isLoad": false,
    "isExpand": false,
    "parentId": parentId,
    "parentNode": this.node[parentId],
    "sourceIndex" : sourceIndex,
    "childAppend" : ""
  };
     this.nodes[sourceIndex] = "index:"+ this.index +";"+ source;
     this.node[this.index].hasChild = this.names.indexOf(this._d + sid + d)>-1;
  if(this.node[this.index].hasChild)  this.node[this.index].childNodes = [];
  return this.node[this.index];
};//从XML格式字符串里提取信息
//source 数据源里的节点信息字符串(以后可以扩展对XML的支持)
//name   要提取的属性名
MzTreeView.prototype.getAttribute = function(source, name)
{
  var reg = new RegExp("(^|;|\\s)"+ name +"\\s*:\\s*([^;]*)(\\s|;|$)", "i");
  if (reg.test(source)) return RegExp.$2.replace(/[\x0f]/g, ";"); return "";
};//根据节点的详细信息生成HTML
//node   树在客户端的节点对象
//AtEnd  布尔值  当前要转换的这个节点是否为父节点的子节点集中的最后一项
MzTreeView.prototype.nodeToHTML = function(node, AtEnd)
{
  var source = this.nodes[node.sourceIndex];
  var target = this.getAttribute(source, "target");
  var data = this.getAttribute(source, "data");
  var url  = this.getAttribute(source, "url");
  if(!url) url = this.url;
  if(data) url += (url.indexOf("?")==-1?"?":"&") + data;
  if(!target) target = this.target;  var id   = node.id;
  var HCN  = node.hasChild, isRoot = node.parentId=="0";
  if(isRoot && node.icon=="") node.icon = "root";
  if(node.icon=="" || typeof(this.icons[node.icon])=="undefined")
    node.icon = HCN ? "folder" : "file";
  node.iconExpand  = AtEnd ? "└" : "├";  var HTML = "<DIV noWrap='True'><NOBR>";
  if(!isRoot)
  {
    node.childAppend = node.parentNode.childAppend + (AtEnd ? " " : "│");
    if(this.wordLine)
    {
      HTML += "<SPAN>"+ node.parentNode.childAppend + (AtEnd ? "└" : "├") +"</SPAN>";
      if(HCN) HTML += "<SPAN class='pm' id='"+ this.name +"_expand_"+ id +"'>+</SPAN>";
    }
    else
    {
      node.iconExpand  = HCN ? AtEnd ? "PM2" : "PM1" : AtEnd ? "L2" : "L1";
      HTML += "<SPAN>"+ this.word2image(node.parentNode.childAppend) +"<IMG "+
        "align='absmiddle' id='"+ this.name +"_expand_"+ id +"' "+
        "src='"+ this.icons[node.iconExpand].src +"' style='cursor: "+ (!node.hasChild ? "":
        (this.navigator=="msie"||this.navigator=="opera"? "hand" : "pointer")) +"'></SPAN>";
    }
  }
  HTML += "<IMG "+
    "align='absMiddle' "+
    "id='"+ this.name +"_icon_"+ id +"' "+
    "src='"+ this.icons[node.icon].src +"'><A "+
    "class='MzTreeview' hideFocus "+
    "id='"+ this.name +"_link_"+ id +"' "+
    "href='"+ url +"' "+
    "target='"+ target +"' "+
    "title='"+ node.hint +"' "+
    "onfocus=\""+ this.name +".focusLink('"+ id +"')\" "+
    "onclick=\"return "+ this.name +".nodeClick('"+ id +"')\">"+ node.text +
  "</A></NOBR></DIV>";
  if(isRoot && node.text=="") HTML = "";  HTML = "\r\n<SPAN id='"+ this.name +"_tree_"+ id +"'>"+ HTML 
  HTML +="<SPAN style='DISPLAY: none'></SPAN></SPAN>";
  return HTML;
};//在使用图片的时候对 node.childAppend 的转换
MzTreeView.prototype.word2image = function(word)
{
  var str = "";
  for(var i=0; i<word.length; i++)
  {
    var img = "";
    switch (word.charAt(i))
    {
      case "│" : img = "L4"; break;
      case "└" : img = "L2"; break;
      case " " : img = "empty"; break;
      case "├" : img = "L1"; break;
      case "─" : img = "L3"; break;
      case "┌" : img = "L0"; break;
    }
    if(img!="")
      str += "<IMG align='absMiddle' src='"+ this.icons[img].src +"' height='20'>";
  }
  return str;
}
//将某个节点下的所有子节点转化成详细的<HTML>元素表达
//id 树的客户端节点 id
MzTreeView.prototype.buildNode = function(id)
{
  if(this.node[id].hasChild)
  {
    var tcn = this.node[id].childNodes, str = "";
    for (var i=0; i<tcn.length; i++)
      str += this.nodeToHTML(tcn[i], i==tcn.length-1);
    var temp = this.getElementById(this.name +"_tree_"+ id).childNodes;
    temp[temp.length-1].innerHTML = str;
  }
};//聚集到客户端生成的某个节点上
//id  客户端树节点的id
MzTreeView.prototype.focusClientNode      = function(id)
{
  if(!this.currentNode) this.currentNode=this.node["0"];  var a = this.getElementById(this.name +"_link_"+ id); if(a){ a.focus();
  var link = this.getElementById(this.name +"_link_"+ this.currentNode.id);
  if(link)with(link.style){color="";   backgroundColor="";}
  with(a.style){color = this.colors.highLightText;
  backgroundColor = this.colors.highLight;}
  this.currentNode= this.node[id];}
};//焦点聚集到树里的节点链接时的处理
//id 客户端节点 id
MzTreeView.prototype.focusLink= function(id)
{
  if(this.currentNode && this.currentNode.id==id) return;
  this.focusClientNode(id);
};//点击展开树节点的对应方法
MzTreeView.prototype.expand   = function(id, sureExpand)
{
  var node  = this.node[id];
  if (sureExpand && node.isExpand) return;
  if (!node.hasChild) return;
  var area  = this.getElementById(this.name +"_tree_"+ id);
  if (area)   area = area.childNodes[area.childNodes.length-1];
  if (area)
  {
    var icon  = this.icons[node.icon];
    var iconE = this.iconsExpand[node.icon];
    var Bool  = node.isExpand = sureExpand || area.style.display == "none";
    var img   = this.getElementById(this.name +"_icon_"+ id);
    if (img)  img.src = !Bool ? icon.src :typeof(iconE)=="undefined" ? icon.src : iconE.src;
    var exp   = this.icons[node.iconExpand];
    var expE  = this.iconsExpand[node.iconExpand];
    var expand= this.getElementById(this.name +"_expand_"+ id);
    if (expand)
    {
      if(this.wordLine) expand.innerHTML = !Bool ? "+"  : "-";
      else expand.src = !Bool ? exp.src : typeof(expE) =="undefined" ? exp.src  : expE.src;
    }
    if(!Bool && this.currentNode.path.indexOf(node.path)==0 && this.currentNode.id!=id)
    {
      try{this.getElementById(this.name +"_link_"+ id).click();}
      catch(e){this.focusClientNode(id);}
    }
    area.style.display = !Bool ? "none" : "block";//(this.navigator=="netscape" ? "block" : "");
    if(!node.isLoad)
    {
      this.load(id);
      if(node.id=="0") return;

解决方案 »

  1.   


          //当子节点过多时, 给用户一个正在加载的提示语句
          if(node.hasChild && node.childNodes.length>200)
          {
            setTimeout(this.name +".buildNode('"+ id +"')", 1);
            var temp = this.getElementById(this.name +"_tree_"+ id).childNodes;
            temp[temp.length-1].innerHTML = "<DIV noWrap><NOBR><SPAN>"+ (this.wordLine ?
            node.childAppend +"└" : this.word2image(node.childAppend +"└")) +"</SPAN>"+
            "<IMG border='0' height='16' align='absmiddle' src='"+this.icons["file"].src+"'>"+
            "<A style='background-Color: "+ this.colors.highLight +"; color: "+
            this.colors.highLightText +"; font-size: 9pt'>请稍候...</A></NOBR></DIV>";
          }
          else this.buildNode(id);
        }
      }
    };//节点链接单击事件处理方法
    //id 客户端树节点的 id
    MzTreeView.prototype.nodeClick = function(id)
    {
      var source = this.nodes[this.node[id].sourceIndex];
      eval(this.getAttribute(source, "method"));
      return !(!this.getAttribute(source, "url") && this.url=="#");
    };//为配合系统初始聚集某节点而写的函数, 得到某节点在数据源里的路径
    //sourceId 数据源里的节点 id
    MzTreeView.prototype.getPath= function(sourceId)
    {
      
    Array.prototype.indexOf = function(item)
      {
        for(var i=0; i<this.length; i++)
        {
          if(this[i]==item) return i;
        }
        return -1;
      };
      var _d = this._d, d = this.divider;
      var A = new Array(), id=sourceId; A[0] = id;
      while(id!="0" && id!="")
      {
        var str = "(^|"+_d+")([^"+_d+d+"]+"+d+ id +")("+_d+"|$)";
        if (new RegExp(str).test(this.names))
        {
          id = RegExp.$2.substring(0, RegExp.$2.indexOf(d));
          if(A.indexOf(id)>-1) break;
          A[A.length] = id;
        }
        else break;
      }
      return A.reverse();
    };//在源代码里指定 MzTreeView 初始聚集到某个节点
    //sourceId 节点在数据源里的 id
    MzTreeView.prototype.focus = function(sourceId, defer)
    {
      if (!defer)
      {
        setTimeout(this.name +".focus('"+ sourceId +"', true)", 100);
        return;
      }
      var path = this.getPath(sourceId);
      if(path[0]!="0")
      {
        alert("节点 "+ sourceId +" 没有正确的挂靠有效树节点上!\r\n"+
          "节点 id 序列 = "+ path.join(this.divider));
        return;
      }
      var root = this.node["0"], len = path.length;
      for(var i=1; i<len; i++)
      {
        if(root.hasChild)
        {
          var sourceIndex= path[i-1] + this.divider + path[i];
          for (var k=0; k<root.childNodes.length; k++)
          {
            if (root.childNodes[k].sourceIndex == sourceIndex)
            {
              root = root.childNodes[k];
              if(i<len - 1) this.expand(root.id, true);
              else this.focusClientNode(root.id);
              break;
            }
          }
        }
      }
    };//树的单击事件处理函数
    MzTreeView.prototype.clickHandle = function(e)
    {
      e = window.event || e; e = e.srcElement || e.target;
      //alert(e.tagName)
      switch(e.tagName)
      {
        case "IMG" :
          if(e.id)
          {
            if(e.id.indexOf(this.name +"_icon_")==0)
              this.focusClientNode(e.id.substr(e.id.lastIndexOf("_") + 1));
            else if (e.id.indexOf(this.name +"_expand_")==0)
              this.expand(e.id.substr(e.id.lastIndexOf("_") + 1));
          }
          break;
        case "A" :
          if(e.id) this.focusClientNode(e.id.substr(e.id.lastIndexOf("_") + 1));
          break;
        case "SPAN" :
          if(e.className=="pm")
            this.expand(e.id.substr(e.id.lastIndexOf("_") + 1));
          break;
        default :
          if(this.navigator=="netscape") e = e.parentNode;
          if(e.tagName=="SPAN" && e.className=="pm")
            this.expand(e.id.substr(e.id.lastIndexOf("_") + 1));
          break;
      }
    };//MzTreeView 双击事件的处理函数
    MzTreeView.prototype.dblClickHandle = function(e)
    {
      e = window.event || e; e = e.srcElement || e.target;
      if((e.tagName=="A" || e.tagName=="IMG")&& e.id)
      {
        var id = e.id.substr(e.id.lastIndexOf("_") + 1);
        if(this.node[id].hasChild) this.expand(id);
      }
    };//回到树当前节点的父层节点
    MzTreeView.prototype.upperNode = function()
    {
      if(!this.currentNode) return;
      if(this.currentNode.id=="0" || this.currentNode.parentId=="0") return;
      if (this.currentNode.hasChild && this.currentNode.isExpand)
        this.expand(this.currentNode.id, false);
      else this.focusClientNode(this.currentNode.parentId);
    };//展开当前节点并
    MzTreeView.prototype.lowerNode = function()
    {
      if (!this.currentNode) this.currentNode = this.node["0"];
      if (this.currentNode.hasChild)
      {
        if (this.currentNode.isExpand)
          this.focusClientNode(this.currentNode.childNodes[0].id);
        else this.expand(this.currentNode.id, true);
      }
    }//聚集到树当前节点的上一节点
    MzTreeView.prototype.pervNode = function()
    {
      if(!this.currentNode) return; var e = this.currentNode;
      if(e.id=="0") return; var a = this.node[e.parentId].childNodes;
      for(var i=0; i<a.length; i++){if(a[i].id==e.id){if(i>0){e=a[i-1];
      while(e.hasChild){this.expand(e.id, true);
      e = e.childNodes[e.childNodes.length - 1];}
      this.focusClientNode(e.id); return;} else {
      this.focusClientNode(e.parentId); return;}}}
    };//聚集到树当前节点的下一节点
    MzTreeView.prototype.nextNode = function()
    {
      var e = this.currentNode; if(!e) e = this.node["0"];
      if (e.hasChild){this.expand(e.id, true);
      this.focusClientNode(e.childNodes[0].id); return;}
      while(typeof(e.parentId)!="undefined"){
      var a = this.node[e.parentId].childNodes;
      for(var i=0; i<a.length; i++){ if(a[i].id==e.id){
      if(i<a.length-1){this.focusClientNode(a[i+1].id); return;}
      else e = this.node[e.parentId];}}}
    };//展开树的所有节点
    MzTreeView.prototype.expandAll = function()
    {
      if(this.totalNode>500) if(
        confirm("您是否要停止展开全部节点?\r\n\r\n节点过多!展开很耗时")) return;
      if(this.node["0"].childNodes.length==0) return;
      var e = this.node["0"].childNodes[0];
      var isdo = t = false;
      while(e.id != "0")
      {
        var p = this.node[e.parentId].childNodes, pn = p.length;
        if(p[pn-1].id==e.id && (isdo || !e.hasChild)){e=this.node[e.parentId]; isdo = true;}
        else
        {
          if(e.hasChild && !isdo)
          {
            this.expand(e.id, true), t = false;
            for(var i=0; i<e.childNodes.length; i++)
            {
              if(e.childNodes[i].hasChild){e = e.childNodes[i]; t = true; break;}
            }
            if(!t) isdo = true;
          }
          else
          {
            isdo = false;
            for(var i=0; i<pn; i++)
            {
              if(p[i].id==e.id) {e = p[i+1]; break;}
            }
          }
        }
      }
    };//本树将要用动的图片的字义及预载函数
    //path 图片存放的路径名
    MzTreeView.prototype.setIconPath  = function(path)
    {
      var k = 0, d = new Date().getTime();
      for(var i in this.icons)
      {
        var tmp = this.icons[i];
        this.icons[i] = new Image();
        this.icons[i].src = path + tmp;
        if(k==9 && (new Date().getTime()-d)>20)
          this.wordLine = true; k++;
      }
      for(var i in this.iconsExpand)
      {
        var tmp = this.iconsExpand[i];
        this.iconsExpand[i]=new Image();
        this.iconsExpand[i].src = path + tmp;
      }
    };//设置树的默认链接
    //url 默认链接  若不设置, 其初始值为 #
    MzTreeView.prototype.setURL     = function(url){this.url = url;};//设置树的默认的目标框架名 target
    //target 目标框架名  若不设置, 其初始值为 _self
    MzTreeView.prototype.setTarget  = function(target){this.target = target;};
    // -->
      

  2.   

    一个简单的示例:<script language="JavaScript"
      src="http://www.meizz.com/Web/Plugs/MzTreeView10.js"></script>
    <base href="http://www.meizz.com/Web/">
    <style>
    A.MzTreeview
    {
      font-size: 9pt;
      padding-left: 3px;
    }
    </style>
    <script language="JavaScript">
      var tree = new MzTreeView("tree");  tree.icons["property"] = "property.gif";
      tree.icons["css"] = "collection.gif";
      tree.icons["book"]  = "book.gif";
      tree.iconsExpand["book"] = "bookopen.gif"; //展开时对应的图片  tree.setIconPath("http://www.meizz.com/Icons/TreeView/"); //可用相对路径  tree.nodes["0_1"] = "text:WEB 编程";
      tree.nodes["1_100"] = "text:代码示例; data:id=100"; 
      tree.nodes["1_200"] = "text:梅花雪脚本控件集; data:id=200";
      tree.nodes["1_310"] = "text:CSS; icon:css; data:id=310"; 
      tree.nodes["1_320"] = "text:DHTML; data:id=320"; 
      tree.nodes["1_300"] = "text:HTML; data:id=300"; 
      tree.nodes["1_400"] = "text:JavaScript; icon:book; data:id=400";
      tree.nodes["320_322"] = "text:属性; icon: property; data:id=322"; 
      tree.nodes["320_323"] = "text:方法; data:id=323"; 
      tree.nodes["320_324"] = "text:事件; icon:event; data:id=324"; 
      tree.nodes["320_325"] = "text:集合; data:id=325"; 
      tree.nodes["400_407"] = "text:对象; data:id=407"; 
      tree.nodes["400_406"] = "text:方法; data:id=406"; 
      tree.nodes["400_408"] = "text:运算符; data:id=408"; 
      tree.nodes["400_409"] = "text:属性; data:id=409"; 
      tree.nodes["407_1140"] = "text:Date; url:Article.asp; data:id=140";
      tree.nodes["406_1127"] = "text:toString; url:Article.asp; data:id=127";
      tree.nodes["408_1239"] = "text:||; url:Article.asp; data:id=239";
      tree.nodes["409_1163"] = "text:E;  url:Article.asp; data:id=163";  tree.setURL("Catalog.asp");
      tree.setTarget("MzMain");
      document.write(tree.toString());    //亦可用 obj.innerHTML = tree.toString();
    </script>
      

  3.   

    好啊,用用看,meizz大哥的一定是好东西
      

  4.   

    这棵树本来是有 checkbox 和 右键菜单的, 但我想这两样东西不常用, 且与项目的耦合性比较高, 所以我就没有加在JS代码里了. 至少树节点的拖曳我想在以后的版本再发布, :)
      

  5.   

    http://www.meizz.com/Web/Demo/MzTreeView10.htm打不开!
      

  6.   

    其实这tree的很多方法不是tree应该有的,如browserCheck,getElementById可以抽象到其它地方,结构是否应该可以考虑更优化一些?另事件的处理上clickHandle,dbClickHandle方式不是太好,是否应该再改善?纯属个人意见,^_^
      

  7.   

    提几个意见参考,是我在编写树时的问题
    1.树的快捷键不准确,参考下window的树
    2.封装加入树的事件,在调用时重写它
    3.加入树的编辑功能
    4.点击数后的动作不一定都是连接,也可能是调用一个函数
    5.似乎没有add,remove等方法,收起接点好象也没
      

  8.   

    嗯, 多谢阿信的提醒, 本来 browserCheck, getElementById 这两个东东我是写在别人类里的, 这棵树在写代码的时候是很多的类集合起来的, BaseClass BrowserCheck XStringLoader TreeView 大致有这么四个类, 只是我想既然它单独作为一个控件形式来发布, 这些东西就合到一起吧.  至于 clickHandle 和 dblClickHandle 这两个东西的启用, 我只是想减少每个节点的HTML字符串长, 加快树的展示, :)  谢谢阿信!
      

  9.   

    meizz(梅花雪)!你好啊!我MSN与QQ都加了你多多向你学习。
      

  10.   

    TO ttyp:
        对点击树的动作是一个连接, 我已经开放了接口的, 比如说你在节点里不设置 url 而设置 method
        Tree.nodes["1_2"] = "text:text; mdthod:alert('haha')";
        Tree.nodes["1_2"] = "text:text; mdthod: yourFunction()";
    这样设置之后点击这个链接的时候就会执行你的代码, 而不触发链接了.    对于树的编辑功能我是这么想的: 要编辑, 那就肯定会牵涉到数据的保存, 下面的 add 和 remove 操作一样也会牵涉到数据保存, 要保存数据那就得回传服务器等操作, 会使树更进一步复杂, 当然要做编辑那绝对没有问题, 我只是想单纯地作一棵一次加载数据比较好用的树.    对于 remove 可以这样扩充:
    MzTreeView.prototype.removeNode = function(id)
    {
      var d = this.divider, _d = this._d
      var reg = new RegExp("(^|"+ _d +")([^"+d+"]+"+d+id+")("+ _d +"|$)", "g");
      var a = this.names.match(reg);
      if (a)
      {
        for(var i=0; i<a.length; i++)
        {
          var sourceNode = this.nodes[a[i].replace(new RegExp(_d, "g"), "")];
          var clientId = this.getAttribute(sourceNode, "index");
          if(clientId)
          {
            this.names = this.names.replace(a[i], "");
            this.node[this.node[clientId].parentId].isLoad = false;
            this.node[this.node[clientId].parentId].childNodes.length = 0;
            this.expand(this.node[this.node[clientId].parentId].id, false);
          }
          else this.names = this.names.replace(a[i], "");
        }
      }
    }
      

  11.   

    有时间还可以实现指定xml数据源,展开一个接点下的所有接点,展开过多时用confirm不用alert
      

  12.   

    TO ttyp: 绑定XML文件, 其实我已经实现了的, 只是没有放在这个版本里来, 而对于展开一个节点下节点过多的提示我是用 "正在加载..." 作为提示的.
      

  13.   

    TO: ygjwjj(塔克拉玛干---胡杨林)  不好意思我没有说明, 用方向虽会展开节点的, 但是你可以使用 {TAB} 和 shift+{TAB} 键进行跳转
      

  14.   

    对于树的编辑功能--->add,remove和edit这些单独放到一个页面去对数据库操作就好了,一次性加载树,这样是否要简单些
      

  15.   

    tree.nodes["409_1163"] = "text:E;  url:Article.asp; data:id=163";如果 url 带有冒号“:”会不会出问题?
      

  16.   

    口水ing,梅老大,最终版么?
      

  17.   

    佩服啊,对这么无聊的javascript也这么有耐心,真是什么人都有(我没有恶意)
      

  18.   

    MzTreeView 1.0 兼容 IE5.0 Firefox1.0 Opera7 Netscape7 Mozilla1.7
    对于 Opera Netscape Mozilla 由于我没有安装更低版本的浏览器, 所以我没有测试, 不过我想它兼容的版本应该比我列出来的更低些 :)
      

  19.   

    说实话, 网页脚本语言(JavaScript JScript VBScript)一向来不被传统的C/S编程人员所重视, 因为这些脚本语言与他们的C系列B系列的强语言来说, "这些脚本语言入门太容易了, 太没有技术可言了", 所以总有很多人认为会写脚本语言的人不能被称作程序员, 据我所知在新浪网招聘里招这些写脚本的人就不叫程序员, 而叫做美工. 美工... 连最基本的名份都没有给写脚本的人, 就如同古代的三妻四妾的妾一般, 不过人家好歹也是妾, 而我们做"美工"的相对于那些正统的程序员来说, 只能被称之为丫環, 永远只是个可有可无的陪衬.
        在这里我无心挑起C/S与B/S之争, 这个论题在各个论坛里都论过了, 论烂了, 我只是想表达一下做B/S 项目开发人员的Browser 端开发的人的一种悲衷, 一种无奈.
        我之所以要写这棵树, 我就是想减轻一点Browser 开发人员的辛苦, 对脚本的能力之弱的痛苦, 对各种浏览器的兼容的痛苦. 对于 Browser coder 真是太辛苦了, MS他们在根本上就已经削弱了脚本的能力, 就已经限制住了这批人的想象空间, 还要受到"正统的"程序员的轻视(至少在工资上就差了不只一个档次), 但是这些脚本 coder 们做的是什么苦力活你们知不知道呀, 他们做为一个脚本 coder要学多少门知识才能胜任? HTML CSS 脚本(JavaScript VBScript) DHTML 还有写代码的工具(比如DW FP EditPlus) 还有图片处理(PS) 还得拥有美学细胞.
        我最近还会推出一批网页脚本控件, 如 ComboBox MenuBar PageControl PopupMenu 等, 为了我们辛苦的 Script Coder. 最后我还想推出一个 JavaScript Application Manager (JSAM) 以整体框架的形式解决B/S开发中最辛苦的工作(这个框架的创始人是万常华).
      

  20.   

    学习并强烈支持梅老大。我个人感觉 B/S 模式的编程易学难精, 其实脚本深入了之后,相比只会简单拖放控件使用 VB, delphi 作快速开发的人来讲技术要求更高的多。我也是无意挑起 B/S , C/S 之争,说到底不管是 C/S, B/S, 深入了之后都是没有止境的。如果你一直满足于使用高级 IDE 拖放出“产品”,那么你永远只能生产玩具一样的“产品”。
      

  21.   

    我最近还会推出一批网页脚本控件, 如 ComboBox MenuBar PageControl PopupMenu 等, 为了我们辛苦的 Script Coder. 最后我还想推出一个 JavaScript Application Manager (JSAM) 以整体框架的形式解决B/S开发中最辛苦的工作(这个框架的创始人是万常华).
    ---------------------------------------------------------
    期待