xmlplus组件设计系列之树(Tree)(9)


Posted in Javascript onMay 02, 2017

树形组件是一种具有层级结构的组件,广泛应用于各种场景。本章会实现一个简单的树形组件,尽管功能有限,但你可以通过扩展它来实现自己所需要的树形组件。

xmlplus组件设计系列之树(Tree)(9)

数据源

树形组件的数据源可以是 JSON 格式的数据对象,也可以是具有 XML 结构的数据或者是其它的具有层级结构的数据。本章将采用具有如下 JSON 格式的数据对象。

var data = {
 name: 'My Tree',
 children: [
 { name: 'hello' },
 { name: 'world' },
 {
  name: 'child folder',
  children: [
  { name: 'alice' }
  ]
 }
 ]
};

上述数据源中,name 值会作为树结点的名称显示,含 children 的数组代表节点的子级。

递归结构的设计

由 HTML 中的列表元素 ul 以及 li 组合而成对象具有天然的树形结构,我们不妨采用它们作为构建树形组件的基本元素。树形组件的最外层必然是一个 ul 元素,所以我们可以暂时定义树形组件如下:

Tree: {
 xml: `<ul id='tree'>
   <Item id='item'/>
   </ul>`
}

这里的未定义的组件 Item 是一个需要递归定义的子项组件,它会根据提供的数据递归地生成子孙对象。下面是一种可能的设计:

Item: {
 xml: `<li id='item'>
   <div id='content'>
    <span id='neme'/><span id='expand'/>
   </div>
   <ul id='entries'/>
   </li>`,
 map: { defer: "entries" }
}

注意,上面的 neme 对象是用于显示 name 属性的。expand 对象用于展开或者关闭子级对象 entries。子级对象 entries 被设置为需要延迟实例化,只有当用户点击 expand 对象展开子级时,该对象才会实例化。

数据的加载与渲染

如上一节所述,我们设定了子级对象 entries 需要延迟实例化。所以,在给子项 Item 提供的数据设置接口不应该立马对 entries 实例化。下面我们先给出数据接口函数。

Item: {
 // css, xml, map 项同上
 fun: function (sys, items, opts) {
  var data;
  function val(value) {
   data = value;
   sys.neme.text(data.name);
   data.children && data.children.length && sys.expand.show().text(" [+]");
  }
  return { val: val };
 }
}

该接口函数 val 只是设置了当前节点相关的内容。下面我们来侦听 expand 对象的点击事件,并适时地完成组件对象 entries 的实例化。

Item: {
 // css, xml, map 项同上
 fun: function (sys, items, opts) {
  var data, open;
  sys.expand.on("click", function () {
   open = !open;
   sys.expand.text(open ? " [-]" : " [+]");
   open ? (sys.entries.show() && load()) : sys.entries.hide();
  });
  function load() {
   if ( sys.entries.children().length == 0 )
    for ( var item of data.children )
    sys.add.before("Item").value().val(item);
  }
  function val(value) {
   data = value;
   sys.neme.text(data.name);
   data.children && data.children.length && sys.expand.show().text(" [+]");
  }
  return { val: val };
 }
}

上述代码中包含一个 open 参数,该参数记录了当前节点的是否处于展开状态以供相关的侦听器使用。

动态添加节点

现在我们对上述组件进行一个小的扩展,使得它支持动态添加树节点的功能。首先,我们在对象 entries 的子级添加一个触发按钮,并命名为 add。

Item: {
 xml: "<li id='item'>
   <div id='content'>
    <span id='neme'/><span id='expand'/>
   </div>
   <ul id='entries'>
    <li id='add'>+</li>
   </ul>
   </li>",
 map: { defer: "entries" }
}

其次,需要侦听 add 对象的点击事件,在侦听器中完成对象的添加。

Item: {
 // css, xml, map 项同前
 fun: function (sys, items, opts) {
  var data, open;
  sys.item.on("click", "//*[@id='add']", function () {
   var stuff = {name: 'new stuff'};
   data.children.push(stuff);
   sys.add.before("Item").value().val(stuff);
  });
  // 其余代码同前
 }
}

这里需要注意,对 add 对象的侦听不能采取直接式的侦听:sys.add.on("click",...),而应该使用代理的方式,否则会报错。因为其父级属于延迟实例化的组件,在 entries 对象未实例化之间,add 对象并不可见。

完整的树形组件

综合以上的内容,现在给出一个完整版本的树形组件:

Tree: {
 css: `#tree { font-family: Menlo, Consolas, monospace; color: #444; }
   #tree, #tree ul { padding-left: 1em; line-height: 1.5em; list-style-type: dot; }`,
 xml: `<ul id='tree'>
   <Item id='item'/>
   </ul>`,
 fun: function (sys, items, opts) {
  return items.item;
 }
},
Item: {
 css: "#item { cursor: pointer; }",
 xml: `<li id='item'>
   <div id='content'>
    <span id='neme'/><span id='expand'/>
   </div>
   <ul id='entries'>
    <li id='add'>+</li>
   </ul>
   </li>`,
 map: { defer: "entries" },
 fun: function (sys, items, opts) {
  var data, open;
  sys.item.on("click", "//*[@id='add']", function () {
   var stuff = {name: 'new stuff'};
   data.children.push(stuff);
   sys.add.before("Item").value().val(stuff);
  });
  sys.expand.on("click", function () {
   open = !open;
   sys.expand.text(open ? " [-]" : " [+]");
   open ? (sys.entries.show() && load()) : sys.entries.hide();
  });
  function load() {
   if ( sys.entries.children().length == 1 )
    for ( var item of data.children )
    sys.add.before("Item").value().val(item);
  }
  function val(value) {
   data = value;
   sys.neme.text(data.name);
   data.children && data.children.length && sys.expand.show().text(" [+]");
  }
  return { val: val };
 }
}

在实际应用中的树形组件会比这里的功能更丰富些,你可以在上述代码的基础上进一步的改进,比如添加些 ICON 图标、让子项成为可拖动的等等。但在改进过程中尽量避免代码的复杂化,适当地剥离些代码出来封装成组件是非常有必要的。

本系列文章基于 xmlplus 框架。如果你对 xmlplus 没有多少了解,可以访问 www.xmlplus.cn。这里有详尽的入门文档可供参考。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
window.addEventListener来解决让一个js事件执行多个函数
Dec 26 Javascript
jquery 滚动条事件简单实例
Jul 12 Javascript
jQuery之选项卡的简单实现
Feb 28 Javascript
jquery如何扑捉回车键触发的事件
Apr 24 Javascript
剖析Node.js异步编程中的回调与代码设计模式
Feb 16 Javascript
纯js的右下角弹窗实例
Mar 12 Javascript
React操作真实DOM实现动态吸底部的示例
Oct 23 Javascript
JS非行间样式获取函数的实例代码
Jun 05 Javascript
JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【圆形情况】
Dec 13 Javascript
vue-router的钩子函数用法实例分析
Oct 26 Javascript
vue项目配置同一局域网可使用ip访问的操作
Oct 23 Javascript
基于ajax实现上传图片代码示例解析
Dec 03 Javascript
详解Vue2.X的路由管理记录之 钩子函数(切割流水线)
May 02 #Javascript
令按钮悬浮在(手机)页面底部的实现方法
May 02 #Javascript
Vue2.0表单校验组件vee-validate的使用详解
May 02 #Javascript
ES6学习教程之对象的扩展详解
May 02 #Javascript
Javascript ES6中数据类型Symbol的使用详解
May 02 #Javascript
Bootstrap实现基于carousel.js框架的轮播图效果
May 02 #Javascript
Vue2.x中的父子组件相互通信的实现方法
May 02 #Javascript
You might like
推荐几部必看的DC动画电影
2020/03/03 欧美动漫
thinkphp使用phpmailer发送邮件的方法
2014/11/24 PHP
php实现按指定大小等比缩放生成上传图片缩略图的方法
2014/12/15 PHP
PHP 网站修改默认访问文件的nginx配置
2017/05/27 PHP
基于JQuery的一句话搞定手风琴菜单
2012/09/14 Javascript
正则表达式中特殊符号及正则表达式的几种方法总结(replace,test,search)
2013/11/26 Javascript
jquery uploadify 在FF下无效的解决办法
2014/09/26 Javascript
jQuery实现根据类型自动显示和隐藏表单
2015/03/18 Javascript
js 开发之autocomplete=&quot;off&quot;在chrom中失效的解决办法
2017/09/28 Javascript
Vue自定义过滤器格式化数字三位加一逗号实现代码
2018/03/23 Javascript
红黑树的插入详解及Javascript实现方法示例
2018/03/26 Javascript
在ES5与ES6环境下处理函数默认参数的实现方法
2018/05/13 Javascript
JS实现仿微信支付弹窗功能
2018/06/25 Javascript
Vue.js实现表格渲染的方法
2018/09/07 Javascript
微信小程序车牌号码模拟键盘输入功能的实现代码
2018/11/11 Javascript
Javascript实现时间倒计时功能
2018/11/17 Javascript
JavaScript基于遍历操作实现对象深拷贝功能示例
2019/03/05 Javascript
JS实现简单省市二级联动
2019/11/27 Javascript
[03:46]DOTA2英雄基础教程 维萨吉
2013/12/11 DOTA
[01:19:23]2018DOTA2亚洲邀请赛 4.5 淘汰赛 Mineski vs VG 第二场
2018/04/06 DOTA
Python中的自省(反射)详解
2015/06/02 Python
Android模拟器无法启动,报错:Cannot set up guest memory ‘android_arm’ Invalid argument的解决方法
2016/07/01 Python
Python冲顶大会 快来答题!
2018/01/17 Python
Python3.4学习笔记之常用操作符,条件分支和循环用法示例
2019/03/01 Python
Python Flask框架扩展操作示例
2019/05/03 Python
python使用pandas处理excel文件转为csv文件的方法示例
2019/07/18 Python
python-opencv获取二值图像轮廓及中心点坐标的代码
2019/08/27 Python
Python编程快速上手——PDF文件操作案例分析
2020/02/28 Python
Python使用GitPython操作Git版本库的方法
2020/02/29 Python
德国网上花店:Valentins
2018/08/15 全球购物
介绍一下Java的安全机制
2012/06/28 面试题
大学活动邀请函
2014/01/28 职场文书
2015年毕业生实习评语
2015/03/25 职场文书
宝宝满月祝酒词
2015/08/10 职场文书
党员反腐倡廉学习心得体会
2015/08/15 职场文书
python turtle绘图命令及案例
2021/11/23 Python