博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
选项卡页
阅读量:4636 次
发布时间:2019-06-09

本文共 17810 字,大约阅读时间需要 59 分钟。

目标

选项卡用于显示已经打开过的页面.在这些页面之间切换.

制作js插件类,实现基础的选项卡页功能.选项卡的显示,关闭,移动,选项卡页的缓存.

使用document.createDocumentFragment缓存已打开页面,替代iframe方案

图示1

图示2

html结构

由选项卡容器(.cachetabs)和显示容器两部分,主要功能实现在选项卡,显示容器只是加载页面.

选项卡容器html结构如下.由按钮和选项卡导航区域组成,按钮含前进,后退,关闭按钮组.选项卡框是个带横向滚动条的div

1 
2
3
4
5
功能 6
7
定位当前页 8
关闭全部 9
关闭其它10
11

功能特点

  1. 加载新页面:缓存当前页面后,加载新页面,同时增加一个选项卡.如果选项卡标题有相同的,则在选项卡标题加上(n)
  2. 选项卡容器上加上主题类,可使用不同颜色主题.变化部位在于活动选项卡颜色和底边框颜色
  3. 导航按钮:选项卡超出可视范围后,使用前进后退按钮滚动选项卡框
  4. 点击选项卡时,如果靠近选项卡框的两端,则调整该选项卡到中间位置
  5. 关闭所有选项卡.关闭除活动选项卡外的.定位当前活动选项卡,当其不在可视范围内时.
  6. 当前活动页面不缓存,当页面从非活动转活动时,其对应缓存会删除
  7. 缓存页面使用document.createDocumentFragment,加入其中的DOM会从当前文档中脱离.用以替代经典的iframe方法,
  8. 经过测试,一个填写过的表单页面在放入文档片段对象之后,再取出来时,其状态不变.

使用

// 调用方式// 配置let tabscfg = {  // 显示容器的ID  screenId: 'cachetabs_mainbox',  // 选项卡容器的ID  cachetabsId: 'cachetabs1'};// 实例化let cachetabs = new $.cacheTabs(tabscfg);// 载入页面 html:页面dom,menutitle:选项卡标题cachetabs.load(html, tabtitle)

类与样式

// 缓存组件// 此缓存页面插件多用于AJAX载入片段页时.是一个显示新页面或者已缓存页面的构架.是一个iframe的替代解决方案.// 主要作用是,在一个容器中操作缓存和显示文档.每当要显示一个文档时,先将容器中当前的文档缓存到片段中,然后// 再显示新文档.这个被缓存的文档它的状态不变.将再次显示它时,从缓存中调出来.// 需要引用: JQ, JsExtFun.js$.extend({    // 创建对象 let cachetabs = new $.cacheTabs(config);    // {screenId:'显示内容的容器ID',cachetabsId:'选项卡容器ID'}    cacheTabs: function (config)    {        /*=================*         * init config         *=================*/        let self = this;        if (!config) throw '必须传入配置对象';        // cfg         let cfg = {};        // 内容显示框JQ对象        cfg.screenJQ = $('#' + config.screenId);        // 选项卡框JQ对象        cfg.tabsJQ = $('#' + config.cachetabsId);        // 活动选项卡类名        cfg.activeCls = 'active';        // 选项卡与缓存键关联属性名        cfg.cacheKey = 'cacheId';        /*====================*         * 方法 public         *====================*/        // 载入页面:缓存当前容器中的页面.将新页面显示在容器中.增加一个新选项卡        self.load = function (doms, title, closeE)        {            // 选项卡容器            let navJQ = cfg.tabsJQ.find('.cachetabs-nav');            // 如果已经存在相同title的页面,将其命名为title(n),n>=1            if (navJQ.find('.cachetabs-tab').length > 0)            {                title = titleExist(title, navJQ);            }            // 选项卡为空时,当前无页面,无需缓存.            if (navJQ.find('.cachetabs-tab').length > 0)            {                // 缓存当前DOM                let cacheId = domToCache();                // 找到当前活动的选项卡,去掉活动状态,添加缓存id,关联选项卡与缓存                navJQ.find('.' + cfg.activeCls).removeClass(cfg.activeCls).attr(cfg.cacheKey, cacheId);            }            // 新增页面的选项卡            let tab = '';            tab = String.Format(tab, cfg.activeCls, title);            // 显示容器加载新页            cfg.screenJQ.html(doms);            // 选项卡页加入新选项卡            navJQ.append(tab);            // 绑定新tab的点击事件与关闭事件            bindEventForLabelBtn(navJQ.find('.' + cfg.activeCls));            bindEventForCloseBtn(navJQ.find('.' + cfg.activeCls), closeE);            // 选项卡框滚动条移动到最后            navScroller(1);        }               /*====================*         * 方法 private         *====================*/        // 选项卡重复标题检查,如果已经存在相同title的,将其命名为title(n),n>=1        let titleExist = function (title, navJQ)        {            let i = 1;            let copytitle = title;            while (true)            {                if (navJQ.find('.cachetabs-tab[title="' + copytitle + '"]').length > 0)                {                    let copyindex = copytitle.lastIndexOf('(');                    if (copyindex > 0)                        copytitle = copytitle.substring(0, copyindex);                    copytitle = String.Format('{0}({1})', copytitle, i);                    i++;                    continue;                }                break;            }            return copytitle;        }        // 将当前页面添加到缓存 ,将对应选项卡        let domToCache = function ()        {            // 取出当前容器中的页面节点            let activeDoms = cfg.screenJQ.contents();            // 添加进缓存            let cacheId = store.add(activeDoms);            //console.log('当前页面放入缓存区成功!cacheId=' + cacheId + '内容:' + activeDoms);            return cacheId;        }        // 将缓存页面载入到显示容器中        let cacheToDom = function (cacheId)        {            let doms = store.get(cacheId);            if (doms == null)            {                //console.log('缓存页面cacheId无效,cacheId:' + cacheId);                return;            }            //console.log('缓存页面已经载入,cacheId:' + cacheId);            cfg.screenJQ.html(doms);        }        // 调整选项卡框的滚动条值,使用选项卡显示在合适的位置上        // len:滚动距离,>0 : 右滚此距离, <0 : 左滚, 0 : 滚动到最左, 1 : 到最右,        //              'left': 左滚默认距离, 'right': 右滚默认距离        let navScroller = function (len, tabJQ)        {            let navJQ = cfg.tabsJQ.find('.cachetabs-nav');            // 滚动条位置            let sPosition = navJQ.scrollLeft();            // nav宽度            let w = navJQ.width();            // nav文档长度            let swidth = navJQ[0].scrollWidth;            // 需要滚动的新位置            let toPosition = 0;            //            if (len == 0)                toPosition = 0;            else if (len == 1)                toPosition = swidth;            else if (len == 'left')                toPosition = sPosition - (w / 4);            else if (len == 'right')                toPosition = sPosition + (w / 4);            else                toPosition = sPosition + len;            // 移动滚动条, 此处无需判是否滚动到头或者尾.如果传入的滚动位置无效,则会自动设为0或最大            navJQ.scrollLeft(toPosition);            //console.log('滚动位置: ' + toPosition);            //console.log('文档长度: ' + navJQ[0].scrollWidth);        }        // 调整选项卡框的滚动条值,当指定选项卡靠近选项卡框左边或右边时,使其处于中间位置        let navScrollerByTab = function (tabJQ)        {            let navJQ = cfg.tabsJQ.find('.cachetabs-nav');            // 界限值100px,大致是一个按钮的宽度            let tagLen = 100;            // 滚动条位置            let sPosition = navJQ.scrollLeft();            // nav宽度            let w = navJQ.width();            // nav文档长度            let swidth = navJQ[0].scrollWidth;            // 离选项卡框左起位置            let tabPosition = tabJQ.position().left;            //            //console.log('滚动条位置 ' + sPosition + ' 文档宽度 ' + w + ' nav文档长度 ' + swidth + ' 离选项卡框的位置 ' + tabPosition);            if (tabPosition < tagLen || (tabPosition + 2*tagLen) > w)            {                navJQ.scrollLeft(sPosition + (tabPosition - w / 2));                //console.log('判定移动距离:' + (sPosition + (tabPosition - w / 2)));            }        }        /*=============================*         * 事件绑定         *=============================*/        // 选项卡关闭按钮: tabJQ:选项卡JQ对象,onClosing:关闭时事件.返回false取消关闭        let bindEventForCloseBtn = function (tabJQ, onClosing)        {            // 点击标签选项卡上的关闭按钮时,关闭当前页面,清除其缓存,载入上次的页面到容器中            tabJQ.find('.cachetabs-tabclose').on('click', function (event)            {                event.stopPropagation();                if (typeof onClosing == 'function')                {                    if (onClosing() == false)                        return;                }                // 当前页面:则删除当前页面,加载缓存中最后一个页面到显示容器中                if (tabJQ.hasClass(cfg.activeCls))                {                    let lastkey = store.KeyIndex.Last();                    // 如果没有缓存页了,说明关闭的是最后一个选项卡.此时删除页面即可                    if (lastkey == null)                    {                        cfg.screenJQ.empty();                    }                    else                    {                        cacheToDom(lastkey);                        // 激活缓存对应选项卡,去掉关联属性,删除该缓存.                        let navJQ = cfg.tabsJQ.find('.cachetabs-nav');                        navJQ.find(String.Format('.cachetabs-tab[{0}={1}]', cfg.cacheKey, lastkey))                            .addClass(cfg.activeCls).removeAttr(cfg.cacheKey);                        store.remove(lastkey);                    }                } else                {                    let cacheId = tabJQ.attr(cfg.cacheKey);                    // 非当前页面:直接清除缓存                    store.remove(cacheId);                }                // 删除选项卡                tabJQ.remove();            })        }        let bindEventForInit = function ()        {            bindEventForLeftRightBtn();            bindEventForGoToActiveTab();            bindEventForCloseAll();            bindEventForCloseAllWithOutActive();        }        // 选项卡点击:选项卡之间的切换        let bindEventForLabelBtn = function (tabJQ)        {            // 点击标签选项卡时,缓存当前容器中的页面,对应缓存页面加载到容器中            tabJQ.on('click', function ()            {                // 点击选项卡时,位置会相应调整,确保点击的选项卡完全显示在父级的可见区域.                navScrollerByTab(tabJQ);                // 点击的是活动页面,退出                if ($(this).hasClass(cfg.activeCls))                    return;                // 缓存当前DOM                let cacheId_current = domToCache();                // 找到当前活动的选项卡,去掉活动状态,添加缓存id,关联选项卡与缓存                $(this).parent().find('.' + cfg.activeCls).removeClass(cfg.activeCls).attr(cfg.cacheKey, cacheId_current);                // 激活点击的选项卡,获取其缓存页加载到显示容器                let cacheId = $(this).attr(cfg.cacheKey);                cacheToDom(cacheId);                // 删除其关联属性,缓存                $(this).addClass(cfg.activeCls).removeAttr(cfg.cacheKey);                store.remove(cacheId);            })        }        // 选项卡前进,后退 按钮事件绑定        let bindEventForLeftRightBtn = function ()        {            // 选项卡页向左滚动,调整选项卡框的scroll值            cfg.tabsJQ.find('.cachetabs-left').on('click', function ()            {                navScroller('left');            })            // 选项卡页向右滚动按钮,调整选项卡框的scroll值            cfg.tabsJQ.find('.cachetabs-right').on('click', function ()            {                navScroller('right');            })        }        // 定位当前选项卡        let bindEventForGoToActiveTab = function ()        {            cfg.tabsJQ.find('.cachetabs-goto-active').on('click', function ()            {                let activeTab = cfg.tabsJQ.find('.' + cfg.activeCls);                if (activeTab.length == 0) return;                navScrollerByTab(activeTab);            })        }        // 关闭所有选项卡        let bindEventForCloseAll = function ()        {            cfg.tabsJQ.find('.cachetabs-close-all').on('click', function ()            {                // 删除选项卡,删除缓存,清空显示容器                let navJQ = cfg.tabsJQ.find('.cachetabs-nav');                navJQ.empty();                store.clear();                cfg.screenJQ.empty();            })        }        // 关闭除当前外所有选项卡        let bindEventForCloseAllWithOutActive = function ()        {            cfg.tabsJQ.find('.cachetabs-close-other').on('click', function ()            {                // 删除选项卡除活动的外,删除缓存.(活动页无缓存)                let navJQ = cfg.tabsJQ.find('.cachetabs-nav');                navJQ.find('.cachetabs-tab:not(.'+ cfg.activeCls+')').remove();                store.clear();            })        }        /*==============================*         * 缓存处理类         *==============================*/        let store = new function ()        {            let self = this;            // 缓存数据            self.Dict = {};            // 缓存数据的键索引            self.KeyIndex = [];            // 添加DOM片段到缓存,然后返回缓存ID. doms:dom片段,不能是JQ对象            self.add = function (doms)            {                // 缓存索引范围 0~1023                let keycount = Object.keys(self.Dict).length;                if (keycount > 1023) return;                //                let newCacheId = '_' + Math.NextInt(keycount, 1023);                while (true)                {                    if (self.Dict.hasOwnProperty(newCacheId))                        newCacheId = '_' + Math.NextInt(keycount, 1023);                    else                        break;                }                if (!doms)                {                    self.Dict[newCacheId] = null;                } else                {                    // 建立新的文档片断对象                    let fragdom = document.createDocumentFragment();                    //console.log(fragdom);                    //fragdom.appendChild(doms);                    $(fragdom).append(doms);                    self.Dict[newCacheId] = fragdom;                }                // 缓存ID插入排序列表                self.KeyIndex.push(newCacheId);                //console.log('添加了缓存,键:' + newCacheId);                //console.log('缓存字典长度' + Object.keys(self.Dict).length);                //console.log('缓存键列表' + self.KeyIndex);                return newCacheId;            }            // 删除缓存 成功则返回true,cacheId无效返回false            self.remove = function (cacheId)            {                if (self.Dict.hasOwnProperty(cacheId))                {                    delete self.Dict[cacheId];                    self.KeyIndex.Remove(cacheId);                    //console.log('删除了缓存' + cacheId);                    //console.log('缓存字典长度' + Object.keys(self.Dict).length);                    //console.log('缓存键列表' + self.KeyIndex);                    return true;                }                //console.log('删除缓存失败,不存在的缓存id:' + cacheId);                return false;            }            // 清空缓存            self.clear = function ()            {                self.Dict = {};                self.KeyIndex = [];                //console.log('删除了全部缓存');                //console.log('缓存字典长度' + Object.keys(self.Dict).length);                //console.log('缓存键列表' + self.KeyIndex);            }            // 根据ID取缓存            self.get = function (cacheId)            {                if (self.Dict.hasOwnProperty(cacheId))                {                    //console.log('取出了缓存,键:' + cacheId);                    return self.Dict[cacheId];                }                //console.log('取出缓存无效,键不存在,键:' + cacheId);                return null;            }        }        /*==============================*         * 初始化后绑定基础事件         *==============================*/        bindEventForInit();    }})
js
.cachetabs {
display: flex; position: relative; height: 42px; line-height: 40px; border-bottom: 2px solid #007bff; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;}.cachetabs-left, .cachetabs-right {
flex: 0 1 36px; border-left: 1px solid #ced4da; border-right: 1px solid #ced4da; cursor: pointer; background-color: #fff;}.cachetabs-right {
text-align: right;} .cachetabs-left:before, .cachetabs-right:before {
content: ''; vertical-align: middle; }.cachetabs-left:before {
display: inline-block; width: 0; height: 0; border: 12px solid transparent; border-right-color: #6c757d;}.cachetabs-right:before {
display: inline-block; width: 0; height: 0; border: 12px solid transparent; border-left-color: #6c757d;}.cachetabs-left:hover, .cachetabs-right:hover, .cachetabs-tab:hover {
background-color: #e9ecef;}.cachetabs-left:active, .cachetabs-right:active, .cachetabs-tab:active {
background-color: #dee2e6;}.cachetabs-menutitle {
flex-basis: 56px; text-align: center; background-color: #fff;} .cachetabs-menutitle:after {
content: ''; display: inline-block; width: 0; height: 0; border: 5px solid transparent; border-top-color: #6c757d; margin-left: 2px; vertical-align: middle; } .cachetabs-menutitle:hover {
background-color: #e9ecef; } .cachetabs-menutitle:hover ~ .cachetabs-menugroup {
display: block; }.cachetabs-menugroup {
display: none; position: absolute; top: 40px; right: 0; width: 120px; color: #adb5bd; text-align: center; background-color: #fff; border-left: 2px solid #007bff; border-bottom: 2px solid #007bff; cursor: pointer;} .cachetabs-menugroup:hover {
display: block; }.cachetabs-close-all, .cachetabs-close-other, .cachetabs-goto-active {
display: block;}.cachetabs-goto-active {
border-bottom: 1px solid #e9ecef;} .cachetabs-close-all:hover, .cachetabs-close-other:hover, .cachetabs-goto-active:hover {
color: #495057; background-color: #e9ecef; }.cachetabs-navbox {
flex: 1 1 0; width: 0; height: 40px; overflow: hidden; background-color: #f8f9fa;}.cachetabs-nav {
position: relative; margin-right: 100px; white-space: nowrap; overflow-x: auto;}.cachetabs-tab {
display: inline-block; padding: 0 15px; height: 40px; border-right: 1px solid #ced4da; font-size: 14px; cursor: pointer;} .cachetabs-tab.active {
color: #fff; background-color: #007bff; }.cachetabs-tabclose {
display: inline-block; width: 16px; height: 16px; line-height: 16px; color: #ced4da; text-align: center; margin-left: 4px;} .cachetabs-tabclose:hover {
border-radius: 50% 50%; color: #fff; background-color: #dc3545; text-decoration: none; }.cachetabs.gray {
border-bottom: 2px solid #6c757d;} .cachetabs.gray .cachetabs-tab.active {
background-color: #6c757d; } .cachetabs.gray .cachetabs-menugroup {
border-left: 2px solid #6c757d; border-bottom: 2px solid #6c757d; }.cachetabs.green {
border-bottom: 2px solid #28a745;} .cachetabs.green .cachetabs-tab.active {
background-color: #28a745; } .cachetabs.green .cachetabs-menugroup {
border-left: 2px solid #28a745; border-bottom: 2px solid #28a745; }.cachetabs.red {
border-bottom: 2px solid #dc3545;} .cachetabs.red .cachetabs-tab.active {
background-color: #dc3545; } .cachetabs.red .cachetabs-menugroup {
border-left: 2px solid #dc3545; border-bottom: 2px solid #dc3545; }.cachetabs.yellow {
border-bottom: 2px solid #ffc107;} .cachetabs.yellow .cachetabs-tab.active {
background-color: #ffc107; } .cachetabs.yellow .cachetabs-menugroup {
border-left: 2px solid #ffc107; border-bottom: 2px solid #ffc107; }
css

 

转载于:https://www.cnblogs.com/mirrortom/p/9553407.html

你可能感兴趣的文章
日期时间工具类
查看>>
[leetcod] Clone Graph
查看>>
Remove Duplicates from Sorted Array II leetcode java
查看>>
CSS实现树形结构 + js加载数据
查看>>
P1334 瑞瑞的木板
查看>>
eclipse中folder、source folder和package的区别
查看>>
Eclipse高级使用技巧
查看>>
第2章 数字之魅——快速寻找满足条件的两个数
查看>>
study of javaserver faces lifecycle
查看>>
转 单实例的写法
查看>>
【BZOJ4254】Aerial Tramway 树形DP
查看>>
安装Node.js和npm
查看>>
预祝大家2011农历新年快乐,宏“兔”大展,心想事成~
查看>>
笔记本中美化代码的方法
查看>>
账簿与平衡段关联表
查看>>
1837Balance
查看>>
文件基本处理
查看>>
js之base64上传图片
查看>>
[转载]使用Vitamio打造自己的Android万能播放器(7)——在线播放(下载视频)...
查看>>
23期PHP基础班第四天
查看>>