javascript-TreeView父子联动效果保持节点状态一致


Posted in Javascript onAugust 12, 2007

我们大部分都用过TreeView控件,对这个控件的评价也是各式各样的,但是我觉得不论如何它是一个免费的开源的控件,所以我还是在用它。在刚接触ASP.NET的时候,记得需要做一个分配权限的权限树,当时只知道有这个树,经过一天的研究对其服务器端的行为基本以及搞清楚了,但是由于当时的js水平有限,所以对客户端的代码很畏惧,基本没有看过。
当时有这样一个要求:如果一个节点被选中则该节点的所有子节点都要选中,如果该节点的所有子节点取消选择则该节点也要取消选择(节点一致性),相反一样。
还有一个要求就是:如果可以实现三态树则更好(这个有点难,本文中不予讨论)。本文将详细介绍前面要求1。
        首先我们要庆幸微软的这个TreeView控件是开源的,不论是客户端代码还是服务器端代码您都可以轻松获得,可以去微软的网站上下载。在网上也见过一些讲述该问题的文章,他们大多是使用一个TreeView之外的js实现,我个人认为从面向对象的观点,该功能属于TreeView,所以没有理由将它分离出去,因此今天我将修改TreeView.htc来实现上面的功能。要想获得该文件(TreeView.htc)可以去微软的网站上下载之。
说实在的该功能曾经也困扰了我很久,一直想解决这个问题,但是一直没有时间来研究TreeView.htc中的代码,今天终于下决心搞定它。
        我们知道在TreeView的oncheck中需要激发该事件处理函数,这个函数很容易找到,可以在TreeView生成的客户度脚本中找到,代码片断如下:
oncheck="javascript: if (this.clickedNodeIndex != null) this.queueEvent('oncheck', this.clickedNodeIndex)"
        由此我们可以去htc(以后说的htc都指的是TreeView.htc),找到doCheckboxClick方法,只要看名字就知道它是干什么的(命名实在太重要了,否则在3000行代码中找某个函数真的很累)。
        该方法是当用户点击CheckBox的使用要处理的函数,函数如下所示:
        function doCheckboxClick(el){
            var checked = private_getAttribute(el,"checked")
            el.checked = !checked;
            var evt = createEventObject();
            evt.treeNodeIndex = getNodeIndex(el);
            g_nodeClicked = el;
            _tvevtCheck.fire(evt);
            setNodeState(el,el.checked);// maybe need el only,but I think it's safly
        }
        其中第一行、第二行以及最后一行是我添加的,第一二行是为了在页面回传以后checked属性无效时所做的修改,按照原来的方法在提交后el.checked是undefined,所以导致无法正确同步节点,如果读者有兴趣可以试一试。setNodeState函数,从名字上可以看出该函数是用来设置节点状态的,它将当前你选择的节点作为参数传递到函数内部。如注释中所说实际上你可以只传递el进去,而无需再传一个布尔值,不过我想这样传递可能更安全,不过没有关系,你觉得不爽可以修改:。
        下面看看setNodeState的函数体,该函数由两个方法组成,一个是设置所有孩子节点的状态,一个是设置该节点的父节点状态。代码如下:
        function  setNodeState(el,state){
            _setChildNode(el,state);
            _setParentNode(el,state);
        }
        为了可以设置当前节点的所有孩子节点是否和父节点(本节点)状态一至我们需要函数_setChildNode,同理为了使节点的父节点和该节点状态一至我们需要_setParentNode函数。两个函数都是递归函数,将会遍历所有的子节点和所有的父节点以及兄弟节点(为什么要遍历兄弟节点稍候再说),下面我们先看看遍历孩子节点的代码,代码如下所示:
        function  _setChildNode(el,state){
            var childNodes = el.children;
            if(childNodes.length > 0){// if has childs 
                for(var i = 0 ;i<=childNodes.length-1;i++){
                    private_setAttribute(childNodes[i],"Checked",state);
                    _saveCheckState(childNodes[i]);
                    _setChildNode(childNodes[i],state);
                }
            }
        }
        该函数先获得当前节点的所有子节点,这里可以直接使用TreeView提供的函数getChildren方法,而我使用的使html的原始方法。如果该节点存在子节点则遍历所有子节点,同时设置子节点的状态和当前节点的状态一致,_saveCheckState函数的作用稍候会介绍。private_setAttribute方法为TreeView提供的一个内部设置属性的方法(js没有private关键字,这里只是做说明而已),该方法将设置每一个节点的Checked属性为当前这个节点的状态。
        下面是如何设置父节点状态的代码:
         function  _setParentNode(el,state){
            var parentNode = el.parentElement;
            if(parentNode){
                if(!_checkSiblingdNode(el)){
                    private_setAttribute(parentNode,"Checked",state);
                    _saveCheckState(parentNode);
                    _setParentNode(parentNode,state);
                }
            }
        }
        该函数先获得其父节点,如果父节点存在则检查当前节点的兄弟节点,上面有提到了需要检查兄弟节点,这里检查兄弟节点的目的是:父节点的状态不是由当前节点一个节点控制的(这种情况只存在于当前节点没有兄弟节点的情况下),如果有其他兄弟是选中的,那么父节点是不能被取消了,这样将导致兄弟节点和父子节点不一致不一致。该函数里面也调用了_saveCheckState函数,在后面将介绍之。
下面的代码描述了如何检查兄弟节点的状态,代码如下:
        function _checkSiblingdNode(el){
            var parentNode = el.parentElement;// you can use getParentTreeNode function in this htc,but I like this usage.
            for(var i = 0;i<=parentNode.children.length-1;i++){
                if(el != parentNode.children[i]){
                    if(private_getAttribute(parentNode.children[i],"Checked")){
                        return true;
                    }
                }
            }
            return false;
        }
        正如注释所述,你可以使用getParentTreeNode方法来获得节点的父节点,但是我比较喜欢使用原始的方法:。这里将排除自己(if(el!=parentNode.children[i]))。
        有了上面的代码我们就可以做到客户端无刷新的父子节点一致的选择,那么我们现在来介绍一下_saveCheckState函数,如果没有该函数页面提交以后,刚才除了手工点击的节点以外其他的节点都不能保存状态。所以该函数的作用就是要保存所有节点的状态(主要是自动选择的节点),保证在回传之后树的状态依然存在。下面的代码描述了_saveCheckState函数,代码如下:
        function _saveCheckState(el){
            if(getNodeIndex(el))
                queueEvent('oncheck', getNodeIndex(el));
        }
        该方法首先检查当前节点的index是否有效,如果有效则保存状态。这里需要说一下queueEvent方法做了什么,代码我就不帖了,该函数的实际意义是将客户端的操作保存在一个名为__TreeView1_State__的隐藏域中,这样在回传的时候服务器端会根据该隐藏域,初始化树的状态。其中TreeView1是我在测试系统中TreeView的名字。
        最后的问题就是部署的问题了,因为修改了htc,所以在部署的时候需要替换原来的htc文件。
        如果有需要修改之后的TreeView.htc(格式化之后的),可以在CSDN上改我留言留下email。我的CSDN帐号是cuike519。
        希望微软可以早日将这个功能添加到TreeView控件中,最好也能实现多态的树结构。请浏览blog的相关评论,我会在评论中更新文章!

Javascript 相关文章推荐
CodeMirror2 IE7/IE8 下面未知运行时错误的解决方法
Mar 29 Javascript
JS的参数传递示例介绍
Feb 08 Javascript
JavaScript获取页面上被选中文字的方法技巧
Mar 13 Javascript
js+HTML5实现canvas多种颜色渐变效果的方法
Jun 05 Javascript
Bootstrap CSS组件之输入框组
Dec 17 Javascript
基于jquery实现多选下拉列表
Aug 02 jQuery
Vue的土著指令和自定义指令实例详解
Feb 04 Javascript
基于axios封装fetch方法及调用实例
Feb 05 Javascript
JS自定义右键菜单实现代码解析
Jul 16 Javascript
简单了解three.js 着色器材质
Aug 03 Javascript
vue浏览器返回监听的具体步骤
Feb 03 Vue.js
node.js如何自定义实现一个EventEmitter
Jul 16 Javascript
TopList标签和JavaScript结合两例
Aug 12 #Javascript
Javascript-Mozilla和IE中的一个函数直接量的问题分析
Aug 12 #Javascript
IE和Mozilla的兼容性汇总event
Aug 12 #Javascript
收藏Javascript中常用的55个经典技巧
Aug 12 #Javascript
JavaScript-世界上误解最深的语言分析
Aug 12 #Javascript
用javascript实现兼容IE7的类库 IE7_0_9.zip提供下载
Aug 08 #Javascript
alixixi runcode.asp的代码不错的应用
Aug 08 #Javascript
You might like
一个MYSQL操作类
2006/11/16 PHP
php单例模式实现(对象只被创建一次)
2012/12/05 PHP
WordPress中用于获取文章作者与分类信息的方法整理
2015/12/17 PHP
yii权限控制的方法(三种方法)
2015/12/28 PHP
jQuery学习4 浏览器的事件模型
2010/02/07 Javascript
jQuery方法简洁实现隔行换色及toggleClass的使用
2013/03/15 Javascript
js使用ajax读博客rss示例
2014/05/06 Javascript
JavaScript计算两个日期时间段内日期的方法
2015/03/16 Javascript
14款经典网页图片和文字特效的jQuery插件-前端开发必备
2015/08/25 Javascript
js变量提升深入理解
2016/09/16 Javascript
简单三步实现报表页面集成天气
2016/12/15 Javascript
requirejs按需加载angularjs文件实例
2017/06/08 Javascript
javascript input输入框模糊提示功能的实现
2017/09/25 Javascript
Vue的事件响应式进度条组件实例详解
2018/02/04 Javascript
JS获取并处理php数组的方法实例分析
2018/09/04 Javascript
vue基础之事件简写、事件对象、冒泡、默认行为、键盘事件实例分析
2019/03/11 Javascript
javascript触发模拟鼠标点击事件
2019/06/26 Javascript
浅谈vue项目利用Hbuilder打包成APP流程,以及遇到的坑
2020/09/12 Javascript
Vue3为什么这么快
2020/09/23 Javascript
Openlayers显示地理位置坐标的方法
2020/09/28 Javascript
ant design的table组件实现全选功能以及自定义分页
2020/11/17 Javascript
初学Python函数的笔记整理
2015/04/07 Python
浅析Python中yield关键词的作用与用法
2016/11/29 Python
基于Django filter中用contains和icontains的区别(详解)
2017/12/12 Python
Python读取Word(.docx)正文信息的方法
2018/03/15 Python
Python 实现字符串中指定位置插入一个字符
2018/05/02 Python
Pycharm+django2.2+python3.6+MySQL实现简单的考试报名系统
2019/09/05 Python
Python 实现自动导入缺失的库
2019/10/29 Python
Python实现非正太分布的异常值检测方式
2019/12/09 Python
Python bisect模块原理及常见实例
2020/06/17 Python
关于 HTML5 的七个传说小结
2012/04/12 HTML / CSS
电大自我鉴定
2013/10/27 职场文书
决心书范文
2014/03/11 职场文书
2015年底工作总结范文
2015/05/15 职场文书
创业计划书之溜冰场
2019/10/25 职场文书
python中数组和列表的简单实例
2022/03/25 Python