自定义无限极简洁菜单展开(兼容IE8)

1.demo:单页html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>自定义简洁无限极分类</title>
    <link rel="stylesheet" type="text/css" href="js/plugins/tree-label/css/tree-label.css" />

    <style>
        .out {
            font-size: 30px;
            padding-left: 20px;
            padding-top: 20px;
        }
    </style>
</head>
<body>

<div>
    <div style="float: left; width: 20%">
        <div id="box"></div>
    </div>
    <div id="content-out" class="out" style="float: left; width: 70%;">
        <div>Joker</div>
    </div>
</div>


<script type="text/javascript" src="https://libs.baidu.com/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="js/plugins/tree-label/js/tree-label.js"></script>
<script>
    //数据格式
    var data = [
        {
            id: '1003',
            disabled: false,
            label: '自定义分类3',
            pid: null,
            children: [
                {id: '100301', disabled: false, label: '自定义分类100301', pid: '1003', children: [
                        {id: '10030101', disabled: true, label: '自定义分类10030100', pid: '100301', children: []},
                        {id: '10030102', disabled: true, label: '自定义分类10030200', pid: '100301', children: []},
                        {id: '10030103', disabled: true, label: '自定义分类10030300', pid: '100301', children: []},
                        {id: '10030104', disabled: true, label: '自定义分类10030400', pid: '100301', children: []},
                        {id: '10030105', disabled: true, label: '自定义分类10030500', pid: '100301', children: []},
                    ]},
                {id: '100302', disabled: false, label: '自定义分类100302', pid: '1003', children: []},
                {id: '100303', disabled: false, label: '自定义分类100303', pid: '1003', children: []},
                {id: '100304', disabled: false, label: '自定义分类100304', pid: '1003', children: []},
                {id: '100305', disabled: false, label: '自定义分类100305', pid: '1003', children: []},
            ]
        },
        {
            id: '1004',
            disabled: false,
            label: '自定义分类4',
            pid: null,
            children: [
                {id: '100400', disabled: false, label: '自定义分类100301', pid: '1004', children: [
                        {id: '10040101', disabled: false, label: '自定义分类10040100', pid: '100400', children: []},
                        {id: '10040102', disabled: false, label: '自定义分类10040200', pid: '100400', children: []},
                        {id: '10040103', disabled: false, label: '自定义分类10040300', pid: '100400', children: []},
                        {id: '10040104', disabled: false, label: '自定义分类10040400', pid: '100400', children: []},
                        {id: '10040105', disabled: false, label: '自定义分类10040500', pid: '100400', children: []},
                    ]},
                {id: '100401', disabled: false, label: '自定义分类100401', pid: 1004, children: []},
                {id: '100402', disabled: false, label: '自定义分类100402', pid: 1004, children: []},
                {id: '100403', disabled: false, label: '自定义分类100403', pid: 1004, children: []},
                {id: '100404', disabled: false, label: '自定义分类100404', pid: 1004, children: []},
                {id: '100405', disabled: false, label: '自定义分类100405', pid: 1004, children: []},
            ]
        },
    ];

    var param = {
        //父控件id
        id: 'box',
        //数据
        data: data,
        //展开速度
        speed: 200,
        //控件高度
        height: $(this).height(),
        //控件箭头图标
        icon: 'img/jt_right.png',
        //点击事件回调
        clickCallback: function(id, isParent){
            var content = {
                id: id,
                isParent: isParent,
                isDisabled: false
            }
            $('#content-out').append('<div>'+JSON.stringify(content)+'</div>');
        },
        //disabled点击事件回调
        disabledCallback: function(id, isParent){
            var content = {
                id: id,
                isParent: isParent,
                isDisabled: true
            }
            $('#content-out').append('<div>'+JSON.stringify(content)+'</div>');
        }
    }

    //调用
    Joker.init(param);
</script>
</body>
</html>

2. 框架css:tree-label.css

.height-joker {
    height: 20px;
    line-height: 20px;
}
.width-joker {
    height: 20px;
    line-height: 20px;
}
.text-content-joker {
    height: 100%;
    line-height: 100%;
    vertical-align: top;
    font-size: 20px;
}
.box-content-joker {
    padding: 15px 0;
}
.box-content-joker:hover {
    background-color: #ABA6AD;
}
.line-box-joker {
    padding-left: 40px;
}

.open-joker {
    transform:rotate(90deg);
    -ms-transform:rotate(90deg); 	/* IE 9 */
    -moz-transform:rotate(90deg); 	/* Firefox */
    -webkit-transform:rotate(90deg); /* Safari 和 Chrome */
    -o-transform:rotate(90deg); 	/* Opera */
}
.has-no-children-joker {
    display: none;
}
.need-hide-joker {
    display: none;
}

.none-select-joker{
    -webkit-user-select:none;
    -moz-user-select:none;
    -ms-user-select:none;
    user-select:none;
    -webkit-text-size-adjust:none
}

.disable-select-joker:after {
    content: attr(data-content);
}

.scroll-bar-joker {
    scrollbar-face-color:#C1C1C1; /*滚动条3D表面(ThreedFace)的颜色*/
    scrollbar-highlight-color:#fff; /*滚动条3D界面的亮边(ThreedHighlight)颜色*/
    scrollbar-shadow-color:#eeeeee; /*滚动条3D界面的暗边(ThreedShadow)颜色*/
    scrollbar-3dlight-color:#eeeeee; /*滚动条亮边框颜色*/
    scrollbar-arrow-color:#000; /*滚动条方向箭头的颜色 */
    scrollbar-track-color:#fff; /*滚动条的拖动区域(TrackBar)颜色*/
    scrollbar-darkshadow-color:#fff; /*滚动条暗边框(ThreedDarkShadow)颜色*/
}

/*---滚动条默认显示样式--*/
::-webkit-scrollbar-thumb{
    background-color: #C1C1C1;
    height:50px;
    outline-offset:-2px;
    outline:2px solid #fff;
    -webkit-border-radius:4px;
    border: 2px solid #fff;
}
/*---鼠标点击滚动条显示样式--*/
::-webkit-scrollbar-thumb:hover{
    background-color: #C1C1C1;
    height:50px;
    -webkit-border-radius:4px;
}
/*---滚动条大小--*/
::-webkit-scrollbar{
    width:8px;
    height:8px;
}
/*---滚动框背景样式--*/
::-webkit-scrollbar-track-piece{
    background-color:#fff;
    -webkit-border-radius:0;
}

3.框架js:tree-label.js

Joker = function () {
    function initBox() {
        var box = $('#' + id);
        box.addClass('none-select-joker line-box-joker scroll-bar-joker')
        box.css('overflow-y', 'auto');
        box.attr('onselectstart', 'return false');
        box.height(height);
        box.append(setData(data));
    }

    function setData(data, pid, level) {
        pid = getDefaultValue(pid);
        level = getDefaultValue(level, 0);
        var content = '<div class="' + isChildren(pid) + ' is-parent" p-id="' + getPid(pid) + '">';
        for (var i = 0; i < data.length; i++) {
            if (!hasValue(data[i])) {
                break;
            }
            var has_children = hasValue(data[i]['children']) && data[i]['children'].length !== 0;
            content += '<div class="box-content-joker" ' + getDisabled(data[i]) +
                ' style="margin-left: ' + level * 30 + 'px" ' +
                's-id="' + data[i]["id"] + '">\n' +
                '              <div class="height-joker" style="vertical-align: middle">\n' +
                '                  <span class="' + (has_children ? '' : 'has-no-children-joker') + '"><img class="height-joker width-joker" src="' + icon + '"></span>\n' +
                '                  <span class="disable-select-joker text-content-joker" data-content="' + data[i]['label'] + '"></span>\n' +
                '              </div>\n' +
                '          </div>';
            if (has_children) {
                content += setData(data[i]['children'], data[i]['id'], level + 1);
            }
        }
        content += '</div>';
        return content;
    }

    function initClick() {
        var isSmoothing = false;
        $('.box-content-joker').click(function () {
            if(isSmoothing){
                return;
            }
            var next = $(this).next();
            if ($(this).attr("disabled-joker")) {
                if(typeof disabledCallback === "function"){
                    if(!next.hasClass('is-parent')){
                        disabledCallback($(this).attr('s-id'), false);
                    }else {
                        disabledCallback($(this).attr('s-id'), true);
                    }
                }
                return;
            }
            if(!next.hasClass('is-parent')){
                if (typeof clickCallback === "function") {
                    clickCallback($(this).attr('s-id'), false);
                }
                return;
            }
            var useIe8 = useIE8();
            if (next.is(':hidden')) {
                //目前是隐藏的
                if (useIe8) {
                    $(this).find('img').css({filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=1)'});
                }
                $(this).find('img').addClass('open-joker');
                isSmoothing = true;
                next.slideDown(speed, function () {
                    closeChildren();
                });
            } else {
                if (useIe8) {
                    $(this).find('img.open-joker').css({filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=0)'});
                }
                $(this).find('img.open-joker').removeClass('open-joker');
                isSmoothing = true;
                next.slideUp(speed, function () {
                    closeChildren();
                });
            }

            //所有子元素关闭
            function closeChildren() {
                isSmoothing = false;
                //图标
                if (useIe8) {
                    next.find('.is-parent').prev().find('img').css({filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=0)'});
                }
                next.find('.is-parent').prev().find('img').removeClass('open-joker');
                //所有子元素
                next.find('.is-parent').hide();
            }

            if (typeof clickCallback === "function") {
                clickCallback($(this).attr('s-id'), true);
            }
        });
    }

    function getValue(param) {
        id = getDefaultValue(param['id'], 'box');
        speed = getDefaultValue(param['speed'], 100);
        height = getDefaultValue(param['height'], $(this).height());
        data = getDefaultValue(param['data']);
        icon = getDefaultValue(param['icon'], 'img/jt_right.png');
        clickCallback = getDefaultValue(param['clickCallback']);
        disabledCallback = getDefaultValue(param['disabledCallback']);
    }

    var id;//父控件id
    var data;//数据
    var speed;//展开速度
    var height;//控件高度
    var icon;//控件箭头图标
    var clickCallback;//点击事件回调
    var disabledCallback;//disabled点击事件回调
    return {
        init: function (param) {
            getValue(param);
            initBox();
            initClick();
        }
    }
}();

function hasValue(value) {
    return typeof value !== 'undefined' && value !== undefined && value != null;
}

function getDisabled(data) {
    return data['disabled'] ? 'disabled-joker=true' : '';
}

function isChildren(pid) {
    return !!pid ? 'need-hide-joker' : '';
}

function getPid(pid) {
    return !!pid ? pid : '';
}

function getDefaultValue(value, defaultValue) {
    if (hasValue(value)) {
        return value;
    }
    if (hasValue(defaultValue)) {
        return defaultValue;
    }
    return "";
}

function useIE8() {
    return $.browser.msie;
}

2021-09-08改进版

Joker = function () {
    function initBox() {
        box.height(height);
        box.append('<div id="box_content" class="none-select-joker line-box-joker scroll-bar-joker"></div>');
        var box_content = $('#box_content');
        box_content.height(height - 32);
        if (hasSearch) {
            addSearchBox();
            box_content.height(height - 85);
        }
        box_content.css('overflow-y', 'auto');
        box.attr('onselectstart', 'return false');
        box_content.append(setData(data));
    }

    function setData(data, pid, level) {
        pid = getDefaultValue(pid);
        level = getDefaultValue(level, 0);
        var content = '<div class="' + isChildren(pid) + ' '+(level===0?'':'is-parent')+'" level="'+level+'" p-id="' + getPid(pid) + '">';
        for (var i = 0; i < data.length; i++) {
            if (!hasValue(data[i])) {
                break;
            }
            var has_children = hasValue(data[i]['children']) && data[i]['children'].length !== 0;
            content += '<div class="box-content-joker '+isRoot(pid)+'" ' + getDisabled(data[i]) +
                ' level="'+level+'" cgType="'+data[i]["cgType"]+'" cgPath="'+data[i]["cgPath"]+'" style="margin-left: ' + level * 30 + 'px" ' +
                's-id="' + data[i]["id"] + '">\n' +
                '              <div class="height-joker" style="vertical-align: middle">\n' +
                '                  <span class="' + (has_children ? '' : 'has-no-children-joker') + '"><img class="img-zqm height-joker width-joker" src="' + icon + '"></span>\n' +
                '                  <span class="disable-select-joker text-content-joker" data-content="' + data[i]['label'] + '"></span>\n' +
                '              </div>\n' +
                '          </div>';
            if (has_children) {
                content += setData(data[i]['children'], data[i]['id'], level + 1);
            }
        }
        content += '</div>';
        return content;
    }

    function initSearchClick() {
        var isClicking = false;
        $('#search_btn_joker').click(function () {
            if (isClicking) {
                return;
            }
            isClicking = true;
            var search_key = $('#search_text_joker').val();
            if (!search_key) {
                return;
            }
            setSearchData(getSearchData(data, 'label', 'children', search_key))
            expandAll();
            isClicking = false;
        })
    }

    function initClick() {
        box.click(function (e) {
            var currentNode;
            if ($(e.target).hasClass('box-content-joker')) {
                currentNode = $(e.target);
            } else if (e.target.nodeName.toUpperCase() === 'IMG') {
                currentNode = $(e.target).parent().parent().parent('.box-content-joker');
            } else if (e.target.nodeName.toUpperCase() === 'SPAN') {
                currentNode = $(e.target).parent().parent('.box-content-joker');
            } else if (e.target.nodeName.toUpperCase() === 'DIV') {
                currentNode = $(e.target).parent('.box-content-joker');
            }
            if (currentNode && currentNode.hasClass('box-content-joker')) {
                addClick(currentNode);
            }
        });
    }

    function addClick(currentNode) {
        if (isSmoothing) {
            return;
        }
        $('.box-content-joker').removeClass('select-joker');
        currentNode.addClass('select-joker');
        var next = currentNode.next();
        if (currentNode.attr("disabled-joker")) {
            if (typeof disabledCallback === "function") {
                if (!next.hasClass('is-parent')) {
                    disabledCallback(currentNode.attr('s-id'), false, currentNode.find('span.text-content-joker').attr('data-content'));
                } else {
                    disabledCallback(currentNode.attr('s-id'), true, currentNode.find('span.text-content-joker').attr('data-content'));
                }
            }
            return;
        }
        if (!next.hasClass('is-parent')) {
            if (typeof clickCallback === "function") {
                clickCallback(currentNode.attr('s-id'), false, currentNode.find('span.text-content-joker').attr('data-content'));
            }
            return;
        }
        if (next.is(':hidden')) {
            //目前是隐藏的
            if (useIe8) {
                currentNode.find('img').css({filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=1)'});
            }
            currentNode.find('img').addClass('open-joker');
            isSmoothing = true;
            next.slideDown(speed, function () {
                closeChildren();
            });
        } else {
            if (useIe8) {
                currentNode.find('img.open-joker').css({filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=0)'});
            }
            currentNode.find('img.open-joker').removeClass('open-joker');
            isSmoothing = true;
            next.slideUp(speed, function () {
                closeChildren();
            });
        }

        //所有子元素关闭
        function closeChildren() {
            isSmoothing = false;
            //图标
            if (useIe8) {
                next.find('.is-parent').prev().find('img').css({filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=0)'});
            }
            next.find('.is-parent').prev().find('img').removeClass('open-joker');
            //所有子元素
            next.find('.is-parent').hide();
        }

        if (typeof clickCallback === "function") {
            clickCallback(currentNode.attr('s-id'), true, currentNode.find('span.text-content-joker').attr('data-content'));
        }
    }

    function getValue(param) {
        box = $('#' + getDefaultValue(param['id'], 'box'));
        speed = getDefaultValue(param['speed'], 100);
        hasSearch = getDefaultValue(param['hasSearch'], false);
        height = getDefaultValue(param['height'], $(this).height());
        data = getDefaultValue(param['data']);
        icon = getDefaultValue(param['icon'], 'img/jt_right.png');
        clickCallback = getDefaultValue(param['clickCallback']);
        disabledCallback = getDefaultValue(param['disabledCallback']);
    }

    function addSearchBox() {
        box.find('.row').remove();
        var searchBox = '' +
            '<div class="row" style="padding: 10px">\n' +
            '  <div class="col-lg-12">\n' +
            '    <div class="input-group group-zqm">\n' +
            '      <input type="text" id="search_text_joker" class="form-control group-zqm" placeholder="输入关键字">\n' +
            '      <span id="search_btn_joker" class="input-group-btn group-zqm">\n' +
            '        <button class="btn btn-default group-zqm" type="button"><i class="glyphicon glyphicon-search"></i></button>\n' +
            '      </span>\n' +
            '    </div>\n' +
            '  </div>\n' +
            '</div>';
        box.prepend(searchBox);
    }

    function setSearchData(new_data) {
        var box_content = $('#box_content');
        box_content.html('');
        box_content.append(setData(new_data));
    }

    //展开单个
    function expand(id, ex_self) {
        if (isSmoothing) {
            return;
        }

        closeAll();
        var currentNode = $("[s-id='"+id+"']")
        if(!currentNode){
            return;
        }

        $('.select-joker').removeClass('select-joker');
        currentNode.addClass('select-joker');

        var nodeList = [];
        nodeList = $.merge(nodeList, currentNode);
        nodeList = $.merge(nodeList, currentNode.parents('.is-parent'));
        nodeList = $.merge(nodeList, currentNode.parents('.is-root'));


        for (var i = 0; i < nodeList.length; i++) {
            var current = $(nodeList[i]);

            ex_self = !!ex_self?true:current.attr('s-id') !== currentNode.attr('s-id');

            if(current.next().hasClass('is-parent') && ex_self){
                //图标
                if (useIe8) {
                    current.find('img').css({filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=1)'});
                }
                current.find('img').addClass('open-joker');
                //所有子元素
                current.next().show();
            }
            if(current.hasClass('is-parent')){
                //图标
                if (useIe8) {
                    current.prev().find('img').css({filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=1)'});
                }
                current.prev().find('img').addClass('open-joker');
                //所有子元素
                current.show();
            }
        }
    }

    //展开所有
    function expandAll() {
        if (isSmoothing) {
            return;
        }
        //图标
        if (useIe8) {
            box.find('.is-parent').prev().find('img').css({filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=1)'});
        }
        box.find('.is-parent').prev().find('img').addClass('open-joker');
        //所有子元素
        box.find('.is-parent').show();
    }

    //关闭所有
    function closeAll() {
        if (isSmoothing) {
            return;
        }
        //图标
        if (useIe8) {
            box.find('.is-parent').prev().find('img').css({filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=0)'});
        }
        box.find('.is-parent').prev().find('img').removeClass('open-joker');
        box.find('.is-parent').hide();
        box.find('.is-root').show();
    }

    var box;//主控件
    var data;//数据
    var hasSearch;//是否展示搜索框
    var speed;//展开速度
    var height;//控件高度
    var icon;//控件箭头图标
    var isSmoothing = false;
    var clickCallback;//点击事件回调
    var disabledCallback;//disabled点击事件回调
    var useIe8 = useIE8();
    var isInit = false;
    return {
        init: function (param) {
            getValue(param);
            initBox();
            initSearchClick();
            initClick();
            isInit = true;
        },
        initHeight: function (height_new) {
            height = height_new;
        },
        setNewData: function (new_data) {
            if(!isInit){
                return null;
            }
            data = new_data;
            setSearchData(new_data)
        },
        getCurrentSelect: function(){
            if(!isInit){
                return null;
            }
            var currentNode = box.find('.select-joker');
            if(currentNode && currentNode.length !== 0){
                currentNode = $(currentNode[0]);
                return {
                    id: getDefaultValue(currentNode.attr('s-id'), ''),
                    cgPath: getDefaultValue(currentNode.attr('cgPath'), ''),
                    cgType: getDefaultValue(currentNode.attr('cgType'), ''),
                    isRoot: getDefaultValue(currentNode.hasClass('is-root'), ''),
                    isParent: currentNode.next().hasClass('is-parent'),
                    text: getDefaultValue(currentNode.find('span.text-content-joker').attr('data-content'), '')
                }
            }
            return null;
        },
        needSearchBox: function (needSearch) {
            if(!isInit){
                return;
            }
            var box_content = $('#box_content');
            if (needSearch) {
                addSearchBox();
                box_content.height(height - 85);
            } else {
                box.find('.row').remove();
                box_content.height(height - 32);
            }
        },
        expand: function (id, ex_self) {
            if(!isInit){
                return;
            }
            expand(id, ex_self);
        },
        expandAll: function () {
            if(!isInit){
                return;
            }
            expandAll();
        },
        closeAll: function () {
            if(!isInit){
                return;
            }
            closeAll();
        }
    }
}();

function hasValue(value) {
    return typeof value !== 'undefined' && value !== undefined && value != null && value !== "null";
}

function getDisabled(data) {
    return data['disabled'] ? 'disabled-joker=true' : '';
}

function isChildren(pid) {
    return !!pid ? 'need-hide-joker' : '';
}

function isRoot(pid) {
    return !pid ? 'is-root' : '';
}

function getPid(pid) {
    return !!pid ? pid : '';
}

function getDefaultValue(value, defaultValue) {
    if (hasValue(value)) {
        return value;
    }
    if (hasValue(defaultValue)) {
        return defaultValue;
    }
    return "";
}

function useIE8() {
    jQuery.browser = {};
    jQuery.browser.msie = false;
    jQuery.browser.version = 0;
    if (navigator.userAgent.match(/MSIE ([0-9]+)\./)) {
        jQuery.browser.msie = true;
        jQuery.browser.version = RegExp.$1;
    }
    return $.browser.msie;
}

function getSearchData(data, field, childrenField, searchKey) {
    var result = [];
    for (var i = 0; i < data.length; i++) {
        var node = deepClone(data[i], childrenField);
        var hasChildren = false;
        if (data[i][childrenField] && Array.isArray(data[i][childrenField])) {
            var children = getSearchData(data[i][childrenField], field, childrenField, searchKey);
            node[childrenField] = children;
            if (children.length !== 0) {
                hasChildren = true;
            }
        }
        if (hasChildren) {
            result.push(node);
        } else if (node[field] && typeof node[field] === 'string' && node[field].indexOf(searchKey) !== -1) {
            result.push(node);
        }
    }
    return result;
}

function deepClone(target, ex_field) {
    var result;
    if (typeof target === 'object') {
        if (Array.isArray(target)) {
            result = [];
            for (var i in target) {
                if (i === ex_field) {
                    continue;
                }
                result.push(deepClone(target[i]))
            }
        } else if (target === null) {
            result = null;
        } else if (target.constructor === RegExp) {
            result = target;
        } else {
            result = {};
            for (var j in target) {
                if (j === ex_field) {
                    continue;
                }
                result[j] = deepClone(target[j]);
            }
        }
    } else {
        result = target;
    }
    return result;
}

4.所用图片

下图右键即可另存为,或者直接复制地址用浏览器打开再保存图片

https://img2020.cnblogs.com/blog/1345528/202108/1345528-20210813235523540-462798512.png

5.说明

  • IE8兼容伪元素,所以文字使用伪元素展示,不易被选中
  • IE8不兼容transform属性,所以使用滤镜filter来达到图片旋转目的
  • 以上框架使用Jquery作为基础,且在1.4.2版本下通过测试

6.多个浏览器兼容

Google

Firefox

Edge

IE8

posted @ 2021-08-14 00:12  干翻苍穹  阅读(195)  评论(0)    收藏  举报