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

发布时间 - 2026-01-11 00:56:10    点击率:

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

数据源

树形组件的数据源可以是 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。这里有详尽的入门文档可供参考。

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


# xmlplus  # 组件  # tree  # xmlplus组件设计系列之网格(DataGrid)(10)  # xmlplus组件设计系列之路由(ViewStack)(7)  # xmlplus组件设计系列之分隔框(DividedBox)(8)  # 值得分享和收藏的xmlplus组件学习教程  # 递归  # 是一个  # 是一种  # 你可以  # 才会  # 但在  # 我们可以  # 可以通过  # 而成  # 或者是  # 你对  # 可供  # 应用于  # 拖动  # 报错  # 会比  # 但你  # 定了  # 设置为  # 它会 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 如何用手机制作网站和网页,手机移动端的网站能制作成中英双语的吗?  Laravel如何实现多表关联模型定义_Laravel多对多关系及中间表数据存取【方法】  Laravel Session怎么存储_Laravel Session驱动配置详解  如何打造高效商业网站?建站目的决定转化率  如何在阿里云虚拟主机上快速搭建个人网站?  音响网站制作视频教程,隆霸音响官方网站?  在线制作视频的网站有哪些,电脑如何制作视频短片?  如何快速搭建高效WAP手机网站?  郑州企业网站制作公司,郑州招聘网站有哪些?  ChatGPT常用指令模板大全 新手快速上手的万能Prompt合集  Laravel怎么在Controller之外的地方验证数据  高性能网站服务器配置指南:安全稳定与高效建站核心方案  Laravel如何与Inertia.js和Vue/React构建现代单页应用  如何快速搭建高效香港服务器网站?  Laravel如何优雅地处理服务层_在Laravel中使用Service层和Repository层  Android利用动画实现背景逐渐变暗  rsync同步时出现rsync: failed to set times on “xxxx”: Operation not permitted  实例解析Array和String方法  jquery插件bootstrapValidator表单验证详解  移动端脚本框架Hammer.js  VIVO手机上del键无效OnKeyListener不响应的原因及解决方法  今日头条AI怎样推荐抢票工具_今日头条AI抢票工具推荐算法与筛选【技巧】  javascript如何操作浏览器历史记录_怎样实现无刷新导航  JavaScript实现Fly Bird小游戏  网站制作价目表怎么做,珍爱网婚介费用多少?  HTML5空格在Angular项目里怎么处理_Angular中空格的渲染问题【详解】  学生网站制作软件,一个12岁的学生写小说,应该去什么样的网站?  Laravel如何实现数据导出到PDF_Laravel使用snappy生成网页快照PDF【方案】  Laravel如何使用Telescope进行调试?(安装和使用教程)  Laravel Seeder填充数据教程_Laravel模型工厂Factory使用  Linux安全能力提升路径_长期防护思维说明【指导】  Laravel如何部署到服务器_线上部署Laravel项目的完整流程与步骤  Laravel如何监控和管理失败的队列任务_Laravel失败任务处理与监控  Laravel事件监听器怎么写_Laravel Event和Listener使用教程  Laravel N+1查询问题如何解决_Eloquent预加载(Eager Loading)优化数据库查询  js代码实现下拉菜单【推荐】  Laravel如何使用Gate和Policy进行权限控制_Laravel权限判定与策略规则配置  购物网站制作费用多少,开办网上购物网站,需要办理哪些手续?  大学网站设计制作软件有哪些,如何将网站制作成自己app?  nodejs redis 发布订阅机制封装实现方法及实例代码  香港服务器部署网站为何提示未备案?  怎么制作网站设计模板图片,有电商商品详情页面的免费模板素材网站推荐吗?  ,怎么在广州志愿者网站注册?  如何在搬瓦工VPS快速搭建网站?  微信小程序 配置文件详细介绍  Laravel Octane如何提升性能_使用Laravel Octane加速你的应用  网页设计与网站制作内容,怎样注册网站?  Laravel如何配置.env文件管理环境变量_Laravel环境变量使用与安全管理  Laravel怎么使用Session存储数据_Laravel会话管理与自定义驱动配置【详解】  Laravel模型事件有哪些_Laravel Model Event生命周期详解