后台管理系统带关闭的选项卡(多标签页)功能 适配bootstrap3和4 Bootstrap Hover Dropdown

眼看着是不是很熟悉,其实基本大部门后台管理系统都有这个功能,使用iframe实现展示标签页面。

主要功能:标签页点击,添加标签页,向左滚动标签页,向右滚动标签页,刷新当前标签页,关闭当前标签页,关闭其他标签页,关闭全部标签页,标签页删除

今天我们就来破析下具体的实现原理:

一、标签页点击:点击标签,要将当前标签置为选中,将其他标签设置为非选中,当前标签如果在非正常标签区域内还需要对标签页进行位置适配。

分三种情况:

1.标签页在正常区域内

点击后

2.标签区域左侧标签页出现一半(需要将当前标签页的左侧与标签区域左侧对齐)

点击后

3.标签区域右侧标签页出现一半(需要将当前标签页的右侧与标签区域右侧对齐)

 点击后

 

二、添加标签页:向标签区域添加标签页(根据某种条件判断是否存在此标签页),将此标签页设置为选中,将其他标签设置为非选中,如果新标签页位置超出标签区域还要进行位置匹配。

分三种情况:

1.新标签页处于正常标签区域:

 

 2.新标签页超出了正常标签区域(需要将当前标签页的右侧与标签区域右侧对齐):

 

三、向左滚动标签页:计算规则语言上不太好表述,可以关注下面具体代码。

四、向右滚动标签页:计算规则语言上不太好表述,可以关注下面具体代码。

五、标签页删除:根据当前标签页获取下一个选中标签页,设置下一个选中标签页,移除当前标签页及iframe。

六、刷新当前标签页:获取当前选中标签页,重新加载对应iframe的src进行iframe刷新,具体可关注代码。

七、关闭当前标签页:触发当前标签页的删除事件。

八、关闭其他标签页:移除其他标签页及iframe(第一个标签页及iframe不可移除),然后触发当前标签页的点击事件。

九、关闭全部标签页:移除所有标签页(第一个标签页及iframe不可移除),然后触发第一个标签页的点击事件。

下面附上具体的代码:

1.页面代码:tabspage.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>后台管理系统多标签页</title>
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link href="lib/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
    <!--<link href="lib/bootstrap4/dist/css/bootstrap.min.css" rel="stylesheet" />-->
    <style type="text/css">
        body {
            font-family: 'Source Sans Pro','Helvetica Neue',Helvetica,Arial,sans-serif;
            font-size: 14px;
            font-weight: 400;
            background-color: #f6f6f6;
            color: #666;
        }

        body, div, ul, li {
            margin: 0;
            padding: 0;
        }

        a {
            color: #3c8dbc;
        }
        a:hover,
        a:active,
        a:focus {
            outline: none;
            text-decoration: none;
            color: #72afd2;
        }

        .sidebar {
            height: 50px;
        }
        .sidebar ul li {
            float: left;
            display: block;
            padding: 0 10px 0 10px;
            max-width: 80px;
        }
    </style>
    <link href="css/tabspage.css" rel="stylesheet" />
</head>
<body>
    <div class="tab-iframes">
        <div class="sidebar">
            <ul class="sidebar-menu">
                <li><a data-href="home/homepage.html">主页</a></li>
                <li><a data-href="home/homepage1.html">主页1</a></li>
                <li><a data-href="home/homepage2.html">主页2</a></li>
                <li><a data-href="home/homepage3.html">主页3</a></li>
                <li><a data-href="home/homepage4.html">主页4</a></li>
                <li><a data-href="home/homepage5.html">主页5</a></li>
                <li><a data-href="home/homepage6.html">主页6</a></li>
                <li><a data-href="home/homepage7.html">主页7</a></li>
                <li><a data-href="home/homepage8.html">主页8</a></li>
                <li><a data-href="home/homepage9.html">主页9</a></li>
                <li><a data-href="home/homepage10.html">主页10</a></li>
                <li><a data-href="home/homepage11.html">主页11</a></li>
                <li><a data-href="home/homepage12.html">主页12</a></li>
                <li><a data-href="home/homepage13.html">主页13</a></li>
                <li><a data-href="home/homepage14.html">主页14</a></li>
                <li><a data-href="home/homepage15.html">主页15</a></li>
                <li><a data-href="home/homepage16.html">主页16</a></li>
                <li><a data-href="home/homepage17.html">主页17</a></li>
                <li><a data-href="home/homepage18.html">主页18</a></li>
                <li><a data-href="home/homepage19.html">主页19</a></li>
                <li><a data-href="home/homepage20.html">主页20</a></li>
                <li><a data-href="https://www.baidu.com">百度</a></li>
            </ul>
        </div>

        <!-- 页面标签 -->
        <div class="main-tabs">
            <div class="tabs-control tabs-prev" tabs-event="leftPage"><i class="glyphicon glyphicon-backward"></i></div>
            <div class="tabs-control tabs-next" tabs-event="rightPage"><i class="glyphicon glyphicon glyphicon-forward"></i></div>
            <div class="tabs-control tabs-refresh" tabs-event="refresh" title="刷新"><i class="glyphicon glyphicon-refresh"></i></div>
            <div class="tabs-control tabs-down">
                <div id="conTabsDropdown" class="dropdown-toggle" data-toggle="dropdown" data-display="static" data-hover="dropdown" data-delay="500" aria-haspopup="true" aria-expanded="false">
                    <i class="glyphicon glyphicon-menu-down"></i>
                </div>
                <ul class="dropdown-menu dropdown-menu-right" aria-labelledby="conTabsDropdown" role="menu" x-placement="bottom-end">
                    <li tabs-event="closeThisTabs"><a href="#">关闭当前标签页</a></li>
                    <li tabs-event="closeOtherTabs"><a href="#">关闭其它标签页</a></li>
                    <li tabs-event="closeAllTabs"><a href="#">关闭全部标签页</a></li>
                </ul>
            </div>
            <div class="tabs-page">
                <ul class="tabs-nav">
                    <li data-url="home/homepage.html" class="tabs-this"><i class="glyphicon glyphicon-home"></i><i class="glyphicon glyphicon-remove"></i></li>
                </ul>
            </div>
        </div>
        <!-- 主体内容 -->
        <div class="main-iframes">
            <div class="iframe-item iframe-show"><iframe src="home/homepage.html" frameborder="0" class="tabs-iframe"></iframe></div>
        </div>
    </div>
    <script src="lib/jquery/dist/jquery.min.js"></script>
    <script src="lib/bootstrap/js/bootstrap.min.js"></script>
    <!--<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="lib/bootstrap4/dist/js/bootstrap.min.js"></script>-->
    <script src="lib/bootstrap-hover-dropdown/bootstrap-hover-dropdown.min.js"></script>
    <script src="js/tabspage.js"></script>
</body>
</html>  
View Code

2.css样式代码:tabspage.css

.main-tabs {
    position: absolute;
    top: 50px;
    right: 0;
    left: 0;
    z-index: 1001;
    height: 40px;
    line-height: 40px;
    padding: 0 120px 0 40px;
    background-color: #fff;
}
.main-tabs .tabs-control {
    position: absolute;
    top: 0;
    width: 40px;
    height: 100%;
    text-align: center;
    cursor: pointer;
    transition: all .3s;
    -webkit-transition: all .3s;
    box-sizing: border-box;
    border-left: 1px solid #f6f6f6;
    font-size: 16px;
    font-style: normal;
    -webkit-font-smoothing: antialiased;
}
.main-tabs .tabs-control:hover {
    background-color: #f6f6f6
}
.main-tabs .tabs-prev {
    left: 0;
    border-left: none;
    border-right: 1px solid #f6f6f6;
}
.main-tabs .tabs-next {
    right: 80px;
}
.main-tabs .tabs-refresh {
    right: 40px;
}
.main-tabs .tabs-down {
    right: 0;
}
        
/* 解决bootstrap4 dropdown 小三角问题 */
.main-tabs .tabs-down .dropdown-toggle::after {
    display: none !important;
}

.main-tabs .tabs-down .dropdown-menu {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 1000;
    float: left;
    min-width: 160px;
    padding: 0;
    margin: 0;
    font-size: 14px;
    color: #212529;
    text-align: left;
    list-style: none;
    background-color: #fff;
    -webkit-background-clip: padding-box;
    background-clip: padding-box;
    border: 1px solid rgba(0,0,0,.15);
    border-radius: 4px;
    -webkit-box-shadow: 0 6px 12px rgba(0,0,0,.175);
    box-shadow: 0 6px 12px rgba(0,0,0,.175);
    font-size: 14px;
    list-style: none;
    transform: none;
}
.main-tabs .tabs-down .dropdown-menu-right {
    right: 0;
    left: auto;
}
.main-tabs .tabs-down .dropdown-menu>li {
    display: block;
    width: 100%;
    clear: both;
    text-align: inherit;
    white-space: nowrap;
    background-color: transparent;
    border: 0;
}
.main-tabs .tabs-down .dropdown-menu > li > a {
    display: block;
    padding: 3px 0 3px 30px;
    clear: both;
    font-size: 14px;
    font-weight: 400;
    line-height: 30px;
    color: #333;
    white-space: nowrap;
}

.dropdown-menu > li > a:focus,
.dropdown-menu > li > a:hover {
    color: #262626;
    text-decoration: none;
    background-color: #f5f5f5;
}

a:focus, a:hover {
    color: #23527c;
    text-decoration: underline;
}
a:active, a:hover {
    outline: 0;
}

.main-tabs .tabs-page {
    left: 0;
    margin: 0;
    overflow: hidden;
    text-align: left !important;
}

.main-tabs .tabs-page .tabs-nav {
    position: relative;
    left: 0;
    height: 40px;
    border: none;
    white-space: nowrap;
    font-size: 0;
    transition: all .2s;
    -webkit-transition: all .2s;
}
.main-tabs .tabs-page .tabs-nav li {
    position: relative;
    padding-left: 10px;
    padding-right: 40px;
    text-align: center;
    cursor: pointer;
    min-width: 0;
    line-height: 40px;
    max-width: 160px;
    text-overflow: ellipsis;
    overflow: hidden;
    border-right: 1px solid #f6f6f6;
    vertical-align: top;
    display: inline-block;
    font-size: 14px;
    -webkit-transition: all .2s;
}
.main-tabs .tabs-page .tabs-nav li:after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 0;
    height: 2px;
    border-radius: 0;
    background-color: #292B34;
    transition: all .3s;
    -webkit-transition: all .3s
}
.main-tabs .tabs-page .tabs-nav li:first-child {
    padding-right: 15px;
}
.main-tabs .tabs-page .tabs-nav li.tabs-this,
.main-tabs .tabs-page .tabs-nav li:hover {
    background-color: #f6f6f6;
}


.main-tabs .tabs-page .tabs-nav li.tabs-this:after {
    width: 100%;
    border: none;
    height: 2px;
    background-color: #292B34
}
.tabs-page .tabs-page .tabs-nav .tabs-this {
    color: #000;
}
.main-tabs .tabs-page .tabs-nav li:hover:after {
    width: 100%
}
.main-tabs .tabs-page .tabs-nav li .glyphicon-remove {
    position: absolute;
    right: 8px;
    top: 50%;
    margin: -7px 0 0;
    width: 16px;
    height: 16px;
    line-height: 16px;
    border-radius: 50%;
    font-size: 12px;
    text-align: center;
    color: #c2c2c2;
    font-size: 16px;
    font-style: normal;
}
.main-tabs .tabs-page .tabs-nav li .glyphicon-remove:hover {
    background-color: #FF5722;
    color: #fff;
    border-radius: 10px;
}
.main-tabs .tabs-page .tabs-nav li:first-child .glyphicon-remove {
    display: none;
}

/* 标签 iframe */
.main-iframes {
    position: fixed;
    overflow: hidden;
    overflow-y: auto;
    top: 90px;
    left: 0;
    bottom: 0;
    right: 0;
    width: auto;
    box-sizing: border-box;
    box-sizing: inherit;
    z-index: 1000;
    -webkit-transition: all .3s;
    background-color: #eee;
}
.main-iframes .iframe-item {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    overflow: hidden;
    display: none;
}
.iframe-show {
    display: block !important;
}
.tabs-iframe {
    position: absolute;
    width: 100%;
    height: 100%;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
}
View Code

3.js代码:tabspage.js

+function ($) {
    'use strict';

    var Selector = {
        mainTabs: '.main-tabs',
        tabsPage: '.tabs-page',
        tabsNav: '.tabs-nav',
        tabsNavLi: '.tabs-nav>li',
        tabClose: '.glyphicon-remove',
        mainIframes: '.main-iframes',
        iframeItem: '.iframe-item',
        tabsIframe: '.tabs-iframe',
    };

    var ClassName = {
        tabsThis: 'tabs-this',
        iframeItem: 'iframe-item',
        iframeShow: 'iframe-show',
        tabsIframe: 'tabs-iframe',
        tabClose: 'glyphicon-remove',
    };

    var Func = {
        addTabIframe: function (data) {
            var tabUl = $(Selector.tabsPage).children(Selector.tabsNav),
                tabUlLi = tabUl.children("li"),
                pageIndex = tabUlLi.length,
                title = data.title || "新标签页";

            //选项卡-tab
            var li = $('<li data-url="' + (data.url || "") + '"><span>' + title + '</span></li>'),
                remove = $('<i class="glyphicon glyphicon-remove"></i>');
            remove.on("click", Event.tabDelete);
            li.append(remove);
            tabUl.append(li);

            //选项卡-iframe
            $(Selector.mainIframes).append(['<div class="' + ClassName.iframeItem + '">', '<iframe src="' + (data.url || "") + '" frameborder="0" class="' + ClassName.tabsIframe + '"></iframe>', "</div>"].join(""));

            //设置当前选中选项
            Func.setTabThis(pageIndex);

            Func.rollPage(pageIndex); //tabs自适应
        },
        addTabExist: function (pageIndex) {
            //设置当前选中选项
            Func.setTabThis(pageIndex);
            Event.refresh();
            Func.rollPage(pageIndex); //tabs自适应
        },
        getTabExist: function (url) {
            var tabUl = $(Selector.tabsPage).children(Selector.tabsNav),
                tabUlLi = tabUl.children("li"),
                isExist = false,
                pageIndex = 0;

            tabUlLi.each(function (i) {
                var dataUrl = $(this).attr("data-url");
                if (dataUrl === url) {
                    isExist = true;
                    pageIndex = i;
                }
            });
            var data = { isExist: isExist, pageIndex: pageIndex };
            return data;
        },
        setTabThis: function (pageIndex) {
            //设置当前选中选项
            var tabUl = $(Selector.tabsPage).children(Selector.tabsNav),
                tabUlLi = tabUl.children("li"),
                iframeItem = $(Selector.mainIframes).children(Selector.iframeItem),
                tabsThis = ClassName.tabsThis,
                iframeShow = ClassName.iframeShow;

            tabUlLi.eq(pageIndex).addClass(tabsThis).siblings().removeClass(tabsThis)
            iframeItem.eq(pageIndex).addClass(iframeShow).siblings().removeClass(iframeShow);
        },
        pageIndex: function () {
            var tabsNavLi = $(Selector.tabsNavLi),
                tabsThis = ClassName.tabsThis;

            //获取当前选中项
            var index = 0;
            tabsNavLi.each(function (i) {
                var isThis = $(this).hasClass(tabsThis);
                if (isThis) {
                    index = i;
                }
            });
            //console.log(index);
            return index;
        },
        rollPage: function (pageIndex) {
            var pageIndex = pageIndex || Func.pageIndex();
            var ul = $(Selector.tabsNav),
                li = ul.children("li"),
                ulOuter = (ul.prop("scrollWidth"), ul.outerWidth()),
                ulLeft = parseFloat(ul.css("left"));

            var pageLi = li.eq(pageIndex);
            if (pageLi[0]) {
                var liLeft = pageLi.position().left; //相对于父元素的位置偏移
                var liOuter = pageLi.outerWidth();
                //console.log("pageIndex:" + pageIndex + " liLeft:" + liLeft + " liOuter:" + liOuter + " ulLeft:" + ulLeft + " ulOuter:" + ulOuter + "   " + (ulOuter - liLeft));
                //liLeft:647.578125 liOuter:86.7969 ulLeft:0 ulOuter:662   14.421875
                var lLeft = Math.round(liLeft + ulLeft);
                if (lLeft <= 0) {
                    ul.css("left", -liLeft);
                    return false;
                }
                else {
                    if (lLeft >= ulOuter) {
                        //console.log(1);
                        //ul 需要往左移动的长度
                        var ulLeftW = lLeft - ulOuter + liOuter;
                        ul.css("left", ulLeft - ulLeftW);
                        return false;
                    } else {
                        var liInUl = ulOuter - lLeft;
                        if (liInUl <= liOuter) {
                            //console.log(1);
                            //ul 需要往左移动的长度
                            var ulLeftW = liOuter - liInUl;
                            ul.css("left", ulLeft - ulLeftW);
                            return false;
                        }
                    }
                }
            }
        },
        tabAuto: function () {
            var tabsPage = $(Selector.tabsPage),
                ul = tabsPage.children(Selector.tabsNav);

            if (ul.prop("scrollWidth") > ul.outerWidth() + 1) {
                tabsPage.attr("overflow", "");
            } else {
                tabsPage.removeAttr("overflow");
            }
        },
        getIframeUrl: function (index) {
            var pageIndex = index || Func.pageIndex(),
                url = $(Selector.tabsNavLi).eq(pageIndex).attr("data-url");
            return url;
        },
    };
    var Event = {
        //tab-pages 新增事件
        tabAdd: function (data) {
            var tabExist = Func.getTabExist(data.url);
            var isExist = tabExist.isExist;//是否存在当前标签
            var pageIndex = tabExist.pageIndex;

            //不存在 新增tab、iframe
            if (!isExist) {
                //添加新的选项卡
                Func.addTabIframe(data);
            } else {
                //设置当前选中选项
                Func.addTabExist(pageIndex);
            }
            Func.tabAuto();
        },
        //tab-pages 点击事件
        tabClick: function () {
            //console.log("点击了选项卡");
            var r = $(this),
                pageIndex = r.index(),
                d = r.find("a");

            Func.setTabThis(pageIndex);

            Func.rollPage(pageIndex); //tabs自适应
            //"javascript:;" !== d.attr("href") && "_blank" === d.attr("target") || (, )
        },
        //tab-pages 删除事件
        tabDelete: function () {
            var currentLi = $(this).parent(),
                currentIndex = currentLi.index(),
                tabsThis = ClassName.tabsThis,
                currentIframe = $(Selector.mainIframes).children(Selector.iframeItem).eq(currentIndex);

            //如果删除的是当前选中项 需要先处理下一个选中项 然后再移除
            var pageIndex = currentIndex;
            var isThis = currentLi.hasClass(tabsThis);
            if (isThis) {
                var next = currentLi.next();
                if (next[0]) {
                    pageIndex = next.index();
                } else {
                    var prev = currentLi.prev();
                    if (prev[0]) {
                        pageIndex = prev.index();
                    }
                }

                //设置新的当前选中项
                Func.setTabThis(pageIndex);
            }
            currentLi.remove();//移除 tab
            currentIframe.remove(); //移除 iframe

            //当前选中项 index
            pageIndex = (pageIndex > currentIndex ? pageIndex : currentIndex) - 1;
            Func.rollPage(pageIndex);//tabs 自适应
            setTimeout(function () { Func.tabAuto() }, 50);
        },
        leftPage: function () {
            //console.log("出发了向左事件");
            var ul = $(Selector.tabsNav),
                li = ul.children("li"),
                ulOuter = (ul.prop("scrollWidth"), ul.outerWidth()),
                ulLeft = parseFloat(ul.css("left"));

            //ulLeft不可能>0,即只能<= 0
            if (ulLeft < 0) {
                var r = -ulLeft - ulOuter;
                li.each(function (e, t) {
                    var n = $(t),
                        liLeft = n.position().left;
                    //console.log(l)
                    if (liLeft >= r) {
                        ul.css("left", -liLeft);
                        return false;
                    }
                });
            }
            else
                return false;
        },
        rightPage: function () {
            //console.log("出发了向右事件");
            var ul = $(Selector.tabsNav),
                li = ul.children("li"),
                ulOuter = (ul.prop("scrollWidth"), ul.outerWidth()),
                ulLeft = parseFloat(ul.css("left"));

            li.each(function (e, t) {
                var n = $(t),
                    liLeft = n.position().left,
                    liOuter = n.outerWidth();

                var lLeft = Math.round(liLeft + ulLeft + liOuter);
                if (lLeft > ulOuter) {
                    var ulOuter2 = 2 * ulOuter;
                    if (lLeft >= ulOuter2) {
                        var liTemp = lLeft - ulOuter2;
                        if (liTemp <= liOuter) {
                            //console.log(1);
                            //ul 需要往左移动的长度
                            var ulLeftW = ulOuter + liTemp;
                            ul.css("left", ulLeft - ulLeftW);
                            return false;
                        }
                    } else {
                        var liCount = li.length;
                        var index = n.index() + 1; //li
                        var liTemp = ulOuter2 - lLeft;
                        if (index == liCount) {
                            //console.log(0);
                            //ul 需要往左移动的长度
                            var ulLeftW = ulOuter - liTemp;
                            ul.css("left", ulLeft - ulLeftW);
                            return false;
                        }
                    }
                }
            })
        },
        //关闭当前标签
        closeThisTabs: function () {
            //console.log("出发了关闭事件");
            var pageIndex = Func.pageIndex();
            //console.log(pageIndex);
            $(Selector.tabsNavLi).eq(pageIndex).find(Selector.tabClose).trigger("click")
        },
        //关闭其他标签
        closeOtherTabs: function () {
            var iframeItem = $(Selector.mainIframes).find(Selector.iframeItem);
            var pageIndex = Func.pageIndex();
            $(Selector.tabsNavLi).each(function (i) {
                var e = $(this);
                if (i != 0 && i != pageIndex) {
                    e.remove();
                    iframeItem.eq(i).remove();
                }
            });
            $(Selector.tabsNavLi).eq(Func.pageIndex()).trigger("click");
        },
        //关闭所有标签tabs
        closeAllTabs: function () {
            $(Selector.tabsNavLi + ":gt(0)").remove();
            $(Selector.mainIframes).find(".iframe-item:gt(0)").remove();
            $(Selector.tabsNavLi).eq(0).trigger("click");
        },
        //刷新iframe
        refresh: function () {
            var pageIndex = Func.pageIndex();
            var iframeItem = $(Selector.mainIframes).children(Selector.iframeItem);
            var iframe = iframeItem.eq(pageIndex).find(Selector.tabsIframe);

            //iframe[0].window.location.reload(true);
            //解决 iframe 跨域 刷新问题
            var url = Func.getIframeUrl(pageIndex);
            var newUrl = url + "?t=" + new Date().getTime();
            iframe[0].src = newUrl;
        },
    };

    //菜单点击事件
    $("body").on("click", "*[data-href]", function () {
        var e = $(this),
            t = e.attr("data-href"),
            i = e.attr("data-text");
        var data = { url: t, title: i || e.text() }
        Event.tabAdd(data);
    });
    //按钮事件
    $("body").on("click", "*[tabs-event]", function () {
        var e = $(this),
            t = e.attr("tabs-event");
        Event[t] && Event[t].call(this, e);
    });
    //绑定 pageTabs 点击事件
    $(Selector.mainTabs).on("click", Selector.tabsNavLi, Event.tabClick);

}(jQuery);
View Code

bootstrap-hover-dropdown 有点问题暂时还在适配中。

 写的代码有点丑陋,大家凑合着看吧。

支持开源代码,也一直为此不懈努力着!

有什么问题欢迎咨询。

原文地址:https://www.cnblogs.com/wsk198726/p/12692384.html