眼看着是不是很熟悉,其实基本大部门后台管理系统都有这个功能,使用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>
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; }
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);
bootstrap-hover-dropdown 有点问题暂时还在适配中。
写的代码有点丑陋,大家凑合着看吧。
支持开源代码,也一直为此不懈努力着!
有什么问题欢迎咨询。