All complete code for this article – Github

It is a common requirement for the front-end development to transform the hierarchical structure returned from the background into a visible tree graph, and to be able to fold and expand the hierarchical operation. This article to implement a tree – style tree diagram component.

The demo implementation

The principle of

The core is to recursively construct a tree of DOM+ events to delegate to collapsed/expanded ICONS based on the data

  1. Construct tree-like DOM data structure according to data recursion:

    [{"name": "Animal animal"."open": true."children": [{"name": "Aquatic animals"."children": [{"name": "Sea animals"}]}]Copy the code

    Each array of data is one<ul>Tag, item in the array is one<em>Tag along with<li>The label. In each item, the children encountered is a new one<ul>Tag, recursive implementation

    The final structure is as follows:

    <ul class="level level0">
        <li>
            <a href="#" class="title">Animals, animals</a>
    
            <em class="icon open"></em>
            <ul class="level level1" style="Display: block">
                <li>
                    <a href="#" class="title">Aquatic animals</a>
    
                    <em class="icon open"></em>
                    <ul class="level level2" style="display: block;">.</ul>
                </li>
            </ul>
        </li>
    </ul>
    Copy the code
  2. Event delegate to collapsed/expanded icon Collapse/expanded icon uses event delegate to delegate to the outermost container

Complete demo code:

<style>
    .container {
        box-sizing: border-box;
        margin: 20px auto;
        padding: 10px;
        width: 600px;
        border: 1px dashed #AAA;
        -webkit-user-select: none;
    }

    .level {
        display: none;
        font-size: 14px;
        margin-left: 10px;
    }

    .level.level0 {
        display: block;
        margin-left: 0;
    }

    .level li {
        position: relative;
        padding-left: 15px;
        line-height: 30px;
    }

    .level li .icon {
        position: absolute;
        left: 0;
        top: 9px;
        box-sizing: border-box;
        width: 12px;
        height: 12px;
        line-height: 8px;
        text-align: center;
        border: 1px solid #AAA;
        background: #EEE;
        cursor: pointer;
    }

    .level li .icon:after {
        display: block;
        content: "+";
        font-size: 12px;
        font-style: normal;
    }

    .level li .icon.open:after {
        content: "-";
    }

    .level li .title {
        color: # 000;
    }
</style>
<body>
<div class="container" id="container1">
    <ul class="level level0">
    </ul>
</div>
<script src="./ztree.js"></script>
</body>
Copy the code

ztree.js

//ztree.js
let zTreeModule = (function () {
    let container = document.querySelector('.container'),
        levelBox = document.querySelector('.container>ul');

    // Get data from the server
    const queryData = () = > {
        return new Promise(resolve= > {
            let xhr = new XMLHttpRequest;
            xhr.open('GET'.'./data.json');
            xhr.onreadystatechange = () = > {
                if (xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)) {
                    let data = JSON.parse(xhr.responseText); resolve(data); }}; xhr.send(null);
        });
    };

    // Data binding
    const bindHTML = data= > {
        let n = 0;
        const gethtml = data= > {
            let str =  ` ` ;
            n++;// Level = ++ for each level
            data.forEach(item= > {
                let {
                    name,
                    children,
                    open
                } = item;

                str +=  `<li>
                    <a href="#" class="title">${name}</a>
                    ${children && children.length>0?` 
                        <em class="icon ${open?'open':' '}"></em>
                        <ul class="level level${n}" style="display:${open?'block':'none'}">
                            ${gethtml(children)}
                        </ul>
                     `: ` ` }
                </li>` ;
            });
            n--;// return a layer on the
            return str;
        };
        levelBox.innerHTML = gethtml(data);
    };

    // Implement click collapse switch based on event delegate
    const handle = () = > {
        container.addEventListener('click'.function (ev) {
            let target = ev.target;
            if (target.tagName === 'EM') {
                let ulBox = target.nextElementSibling,
                    isOpen = target.classList.contains('open');
                if(! ulBox)return;
                if (isOpen) {
                    ulBox.style.display = 'none';
                    target.classList.remove('open');
                    return;
                }
                ulBox.style.display = 'block';
                target.classList.add('open'); }}); };return {
        async init() {
            let data = await queryData();
            bindHTML(data);
            handle();
        }
    };
})();
zTreeModule.init();
Copy the code

Package as plug-in

code

All complete code for this article – Github

(function () {
    // Plugin core :OOP pattern that allows for the creation of separate instances for efficient management of private properties and public methods
    class zTree {
        constructor(element, options) {
            // init params
            let self = this,
                len = arguments.length,
                config = {// Declare the default parameters
                    element: null.data: null.callback: function () {}};// Two different calls can be made by judging the length of the argument
            if (len === 0) throw new Error( ` element or options are required! ` );
            if (len === 1) options = element;
            if(! options ||typeofoptions ! = ="object") throw new TypeError( `options must be an object! ` );
            if (len === 2) options.element = element;
            // Merge the configuration
            config = Object.assign(config, options);

            // Check parameter format
            // verify the format of the transmitted information
            let {
                element: element2,
                data,
                callback
            } = config;
            if(! element2 || element2.nodeType ! = =1) throw new TypeError( ` element must be a DOM element object ` );
            if (!Array.isArray(data)) throw new TypeError( ` data must be an array! ` );
            if (typeofcallback ! = ="function") throw new TypeError( ` callback must be an function! ` );

            //mount to instance
            // Mount config to the current instance
            self.element = element2;
            self.data = data;
            self.callback = callback;

            self.n = 0;

            // init
            self.init();
        }
        init() {
            let self = this;
            self.element.innerHTML =  ` <ul class="level level0">
                ${self.html(self.data)}
            </ul> ` ;
            self.handle();
        }
        // dynamic creation of DOM structure
        // Create a DOM structure recursively
        html(data) {
            let self = this,
                str =  ` ` ;
            self.n++;
            data.forEach(item= > {
                let {
                    name,
                    children,
                    open
                } = item;
                str +=  `<li>
                    <a href="#" class="title">${name}</a>
                    ${children && children.length>0?` 
                        <em class="icon ${open?'open':' '}"></em>
                        <ul class="level level${self.n}" style="display:${open?'block':'none'}">
                            ${self.html(children)}
                        </ul>
                     ` : ` ` }
                </li>` ;
            });
            self.n--;
            return str;
        }
        // achieve specific functions
        // Use event delegate for event binding
        handle() {
            let self = this;
            self.element.addEventListener('click'.function (ev) {
                let target = ev.target;
                if (target.tagName === 'EM') {
                    let ulBox = target.nextElementSibling,
                        isOpen = target.classList.contains('open');
                    if(! ulBox)return;
                    if (isOpen) {
                        ulBox.style.display = 'none';
                        target.classList.remove('open');
                        self.callback(self, target, ulBox);
                        return;
                    }
                    ulBox.style.display = 'block';
                    target.classList.add('open'); self.callback(self, target, ulBox); }}); }}/ / API
    if (typeof window! = ="undefined") {
        window.zTree = zTree;
    }
    Two import specifications are supported
    if (typeof module= = ="object" && typeof module.exports === "object") {
        module.exports = zTree;
    }
})();
Copy the code

use

Introduction:

<script src="dist/tree.min.js"></script>
Copy the code

Commonjs and ES6Module specifications are also supported

Supports custom configuration: options:

element:[dom]
data:[array]
callback: [function->self/em/ul]
Copy the code

Two ways of use:

new zTree([element],[options])
new zTree([options])
Copy the code

Such as:

new zTree(container1, {
   data
});
new zTree({
    element: container2,
    data,
    callback(self, em, ul) {
            console.log(self, em, ul); }});Copy the code

example

You can see the complete example code in the repository example folder

See example -gitPage

<div class="container" id="container1"></div>
<div class="container" id="container2"></div>
<script>
      / /...
    (async function () {
        const data = await queryData();

        let container1 = document.querySelector('#container1'),
            container2 = document.querySelector('#container2');
        new zTree(container1, {
            //data:data
            data
        });
        new zTree({
            element: container2,
            data,
            callback(self, em, ul) {
                console.log(self, em, ul);
            }
        });
    })();
</script>
Copy the code

All complete code for this article – Github