Javascript 实现滑动效果目录树

初学前端,因为写项目接触了饿了么的框架,它里面提供了树型组件,但无奈有时候可操控行并不是很高,于是,抱着学习加实用的态度,打算自己写一个。不多废话,直接上码。

以下为css代码:

.treeNode
{
    position : relative ;
    margin-left : 20px ;
}
.treeNodeName:hover
{
    background : azure ;
    cursor : pointer
}
.treeNodeContentDisplay
{
    position : relative ;
    overflow : hidden
}
.treeNodeContentHidden
{
    position : relative ;
    overflow : hidden
}

其中treeNodeContentDisplay和treeNodeContentHidden这两个类名主要是为了方面标记树节点的展开和收起这两个状态。为了方便控制,所有元素均为相对定位。其中overflow:hidden是这里面的关键,它保证了滑动效果的正常(点开挤下去,收起又自动上来)。其实节点里的子元素是正常的,收起不显示是因为将包裹的外层元素高度设为了0,因为有溢出隐藏,所以就看不到了。如果没有溢出隐藏,那么虽然外包元素高度为0,但是里面的内容还是看的见,就很坑的会显示出来,然后下面的元素挤上来,就是重载一块儿了。

接下来就是整个树渲染实现部分了:

"user strict";
(function(global)
{
   let makeTree = function(dom , data)
   {
      for(let i = 0 ; i < data.length ; ++i)
      {
         let treeNode = document.createElement("div");
         treeNode.setAttribute("class" , "treeNode");
         let nameNode = document.createElement("div");
         nameNode.textContent = data[i]["name"];
         nameNode.setAttribute("class" , "treeNodeName");
         if(data[i]["action"])
         {
            for(let j = 0 ; j < data[i]["action"].length ; ++j)
            {
               for(let key in data[i]["action"][j])
               {
                  nameNode.addEventListener(key , data[i]["action"][j][key]);
               }
            }
         }
         let contentNode = document.createElement("div");
         contentNode.setAttribute("class" , "treeNodeContentHidden");
         contentNode.style.height = "0px";
         if(data[i]["children"])
         {
            if(0 != data[i]["children"].length)
            {
               makeTree(contentNode , data[i]["children"]);
            }
         }
         treeNode.appendChild(nameNode);
         treeNode.appendChild(contentNode);
         dom.appendChild(treeNode);
      }
   };

   let bindEvent = function(dom)
   {
      let childrenNodes = dom.children;
      for(let i = 0 ; i < childrenNodes.length ; ++i)
      {
         if("treeNode" == childrenNodes[i].getAttribute("class"))
         {
            let nameNode = childrenNodes[i].children[0];
            let contentNode = childrenNodes[i].children[1];
            nameNode.addEventListener("click" , function(){
               let self = this;
               if("treeNodeContentHidden" == contentNode.getAttribute("class"))
               {
                  if(self.hiddenInterval)
                  {
                     if(null != self.hiddenInterval)
                     {
                        clearInterval(self.hiddenInterval);
                     }
                  }
                  self.displayInterval = setInterval(function(){
                     let totalHeight = 0;
                     for(let j = 0 ; j < contentNode.children.length ; ++j)
                     {
                        totalHeight += contentNode.children[j].clientHeight;
                     }
                     if(totalHeight > contentNode.clientHeight)
                     {
                        contentNode.style.height = (contentNode.clientHeight + 1) + "px";
                     }
                     else
                     {
                        contentNode.style.height = "";
                        clearInterval(self.displayInterval);
                     }
                  } , 4);
                  contentNode.setAttribute("class" , "treeNodeContentDisplay");
               }
               else
               {
                  if(self.displayInterval)
                  {
                     if(null != self.displayInterval)
                     {
                        clearInterval(self.displayInterval);
                     }
                  }
                  self.hiddenInterval = setInterval(function(){
                     if(0 < contentNode.clientHeight)
                     {
                        contentNode.style.height = (contentNode.clientHeight - 1) + "px";
                     }
                     else
                     {
                        contentNode.style.height = "0px";
                        clearInterval(self.hiddenInterval);
                     }
                  } , 4);
                  contentNode.setAttribute("class" , "treeNodeContentHidden");
               }
            });
            bindEvent(contentNode);
         }
      }
   };

   function DirectoryTree()
   {

   }

   DirectoryTree.prototype.createTree = function(dom , data)
   {
      dom.style.marginLeft = "-20px";
      dom.style.position = "relative";
      makeTree(dom , data);
      bindEvent(dom);
   };

   global.DirectoryTree = new DirectoryTree();
})(window);

该部分代码实现了树的渲染和事件的响应,内部方法makeTree和bindEvent通过递归的方式将整棵树渲染出来并绑定相关事件。其中,整个树的滑动效果是通过定时器来进行的,当然,为了不浪费资源,我们需要在动画结束后关闭定时器,这里我们将定时器的信息保存成dom对象的一个属性,方便后面使用。但是这里需要注意,因为定时器内this指向会发生改变(指向window),所以在点击方法内将指向dom的this保存到self。对于外部,通过立即函数将DirectoryTree实例添加至window对象,使其成为一个全局的对象,方便调用。定义createTree在DirectoryTree原型链上供外部调用,createTree需要两个参数,一个为放置树的dom对象,该对象可以为任意,只要能有子元素的标签都可以;另一个为treeData,为树的信息结构,表现如下:

let treeData = [
   {
           name : "ddd" ,
           action : [
               {mouseover : function(){console.log(1)}} ,
            {click : function(){console.log(2)}}
           ] ,
           children : [
               {
                   name : "ccc" ,
                   children : [
                       {
                           name : "sgf" ,
                           children : []
                       } ,
                       {
                           name : "dffgsf" ,
                           children : []
                       } ,
                       {
                           name : "dfgh" ,
                           children : []
                       }
                   ]
               } ,
               {
                   name : "gsdf" ,
                   children : [
                       {
                           name : "4352" ,
                           children : [
                               {
                                   name : "dfads" ,
                                   children : []
                               }
                           ]
                       }
                   ]
               } ,
               {
                   name : "fda" ,
                   children : []
               }
           ]
       } ,
       {
           name : "lalala" ,
           children : [
               {
                   name : "dfldkjfl"
               } ,
               {
                   name : "dfl"
               } ,
               {
                   name : "ldsfkjl"
               }
           ]
       }
];

其中,name为树的节点名,是必须项;action为节点的响应事件,为一个数组,可以有多个事件响应,每个响应为一个key值为事件,值为响应方法的对象。该参数不是必须的;children为该节点下的子节点,为一个数组,元素为节点信息,该项不是必须项。

以下为html测试部分:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>DirectoryTreeTest</title>
    <link rel = "stylesheet" href = "./DirectoryTree.css" />
    <script type = "text/javascript" src = "./DirectoryTree.js"></script>
</head>
<body>
    <div id = "tree"></div>
    <script>
       let treeData = [
          {
                name : "ddd" ,
                action : [
                    {mouseover : function(){console.log(1)}} ,
                   {click : function(){console.log(2)}}
                ] ,
                children : [
                    {
                        name : "ccc" ,
                        children : [
                            {
                                name : "sgf" ,
                                children : []
                            } ,
                            {
                                name : "dffgsf" ,
                                children : []
                            } ,
                            {
                                name : "dfgh" ,
                                children : []
                            }
                        ]
                    } ,
                    {
                        name : "gsdf" ,
                        children : [
                            {
                                name : "4352" ,
                                children : [
                                    {
                                        name : "dfads" ,
                                        children : []
                                    }
                                ]
                            }
                        ]
                    } ,
                    {
                        name : "fda" ,
                        children : []
                    }
                ]
            } ,
            {
               name : "lalala" ,
                children : [
                    {
                       name : "dfldkjfl"
                    } ,
                    {
                       name : "dfl"
                    } ,
                    {
                       name : "ldsfkjl"
                    }
                ]
            }
       ];
       DirectoryTree.createTree(document.querySelector("#tree") , treeData);
    </script>
</body>
</html>
初学初写,望加指正
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值