JS盒子模型、图片懒加载、DOM库封装 — 3、DOM库封装

1、DOM封装-children

DOM库封装

  • css方法的封装
  • children方法的封装
  • getElementsByClassName兼容处理
  • addClass、removeClass的封装
  • js惰性编程思想
  • 使用自己的DOM库完成选项卡

html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>DOM封装-children</title>
</head>
<body>
<div id="box">
    <p>
        <span></span>
    </p>
    <p></p>
    <p></p>
    <!--以下是SPAN-->
    <!--以下是SPAN-->
    <span></span>
    <span></span>
    <span></span>
</div>
<script src="js/utils.js"></script>
<script src="js/2-1.js"></script>
</body>
</html>
复制代码

2-1.js

// console.log(box.children);//获取子节点  在IE6~8中浏览器会把注释节点当做元素节点处理
// console.log(box.childNodes);//获取所有子节点

// console.log(box.children.length);
/*兼容性:box.children 在IE6~8中浏览器会把注释节点当做元素节点处理*/

// =>children:不管在什么浏览器中都可以获取到当前元素的所有元素子节点(不包含注释)

//---------------------------------获取子节点兼容写法-------------------------------------------
// 法一:通过children获取子节点,除掉注释节点
function children(curEle) {
    var childList = curEle.children;
    childList = utils.toArray(childList);//把childList转成数组
    for (var i = 0; i < childList.length; i++) {
        var item = childList[i];
        if (item.nodeType !== 1) {
            childList.splice(i, 1);//删除当前这一项
            i--;//当前删除后索引需要--,下次从当前这个位置循环
        }
    }
    return childList;
}
console.log(children(box).length);

//----------------------------------------------------------------------------
// 法二:通过childNodes获取所有子节点,只保留元素子节点( item.nodeType === 1 )
function children(curEle) {
    var result = [],
        childList = curEle.childNodes;
    for (var i = 0; i < childList.length; i++) {
        var item = childList[i];
        item.nodeType === 1 ? result.push(item) : null;
    }
    return result;
}
console.log(children(box).length);

//-------------------------------------------获取指定元素中的所有“元素子节点”---------------------------------
//=>我们可以获取指定元素中的所有“元素子节点”,并且我们还可以指定标签名,例如:我指定标
//  签名是'span',我们把所有叫做span的元素子节点获取即可
function children(curEle, tagName) {
    var result = [],
        childList = curEle.childNodes;
    for (var i = 0; i < childList.length; i++) {
        var item = childList[i];
        item.nodeType === 1 ? result.push(item) : null;
    }
    //=>在获取的所有元素子节点中进行二次过滤
    if (typeof tagName !== 'undefined') {
        for (var k = 0; k < result.length; k++) {
            // 当前遍历元素的子节点(result[k])的标签名(tagName)
            // (得到的标签名都是大写的)和传进来的标签名(tagName,传进来的标签名也可能是大写)不相等,就删除。
            if (result[k].tagName.toLowerCase() !== tagName.toLowerCase()) {
                result.splice(k, 1);
                k--;
            }
        }
    }
    return result;
}
console.log(children(box, 'span').length);
//----------------------------------------------------------------------------
// 整合以上代码
function children(curEle, tagName) {
    var result = [],
        childList = curEle.childNodes;//获取所有子节点
    for (var i = 0; i < childList.length; i++) {//遍历获取到的子节点
        var item = childList[i];
        if (item.nodeType === 1) {// item.nodeType === 1  只保留元素子节点
            if (typeof tagName !== 'undefined') { // 传了tagNname
                if (item.tagName.toLowerCase() === tagName.toLowerCase()) {
                    result.push(item);
                }
                continue;//循环遍历,item.nodeType === 1才执行result.push(item);但是
                // item.nodeType===1,传了tagNname,continue:就不再执行到result.push(item)
                
            }
            result.push(item);//条件item.nodeType === 1成立,条件typeof tagName !== 'undefined'
            // 不成立(没传tagName),执行这一步
        }
    }
    return result;
}
console.log(children(box).length);
console.log(children(box, 'span').length);
复制代码

2、DOM封装-getElementsByClassName兼容处理之传递一个样式类

html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>DOM封装-getElementsByClassName兼容处理之传递一个样式类</title>
</head>
<body>
<div class="box1 box2">1</div>
<div class="box2 box3">2</div>
<div class="box1 box2 box3" id="div3">3</div>
<div class="box1 box3">4</div>
<div class="box3">5</div>
<div class="box2">6</div>

<script src="js/utils.js"></script>
<script src="js/3-1.js"></script>
</body>
</html>
复制代码

3-1.js

/*
 * [context].getElementsByClassName([className])
 *   在指定的上下文中,通过样式类名获取一组元素集合,IE6~8下不兼容
 */
// console.log(document.getElementsByClassName('   box1   '));
// console.log(document.getElementsByClassName('box2  box1'));//=>如果传递多个样式类名:同时具备这些样式类的元素(多个样式类之间加几个空格都无所谓,而且不计较编写的顺序)

//=>只处理传递一个样式类名的(但是首尾可能会加很多的空格)
//strClass:传递进来的样式类名,例如:'box1'
//context:限定获取的范围(上下文),不传递默认赋值为document

//=>思路:获取指定上下文中所有的标签,然后遍历这些标签,把所有CLASS中,包含传递进来的样式类的元素,都保存起来即可
function getEleByClass(strClass, context) {
    context = context || document;//不传context默认上下文为document
    var result = [],//存放最终的结果
        nodeList = context.getElementsByTagName('*');//获取指定上下文里面的所有标签
    
    strClass = strClass.replace(/^\s+|\s+$/g, '');//=>去除传递进来样式类的首尾空格

    for (var i = 0; i < nodeList.length; i++) {//循环得到的所有标签
        var item = nodeList[i];//每次遍历的标签
        //=>当前项的CLASS:item.className
        //=>传递进来的样式类:strClass
        //=>我们接下来要验证item.className字符串中是否包含传递进来的strClass样式类
        var reg = new RegExp('(^| +)' + strClass + '( +|$)')
        if (reg.test(item.className)) {
            result.push(item);
        }
    }
    return result;
}
console.log(getEleByClass('box1'));
console.log(getEleByClass('box'));
console.log(getEleByClass('  box1  '));

//验证 "box1 box2 box3"(item.className) 是否包含 "box2"(strClass)
/*
 * indexOf:虽然可以验证字符串是否包含某个字符,但是无法判断是否是包含这个样式类
 *   "box100 box2 box3".indexOf('box1') =>0
 *   但是当前样式类中并没有box1这个样式类
 */

//=> /box2/ =>只要包含box2这个字符就可以(和indexOf类似了)
//=> /\bbox2\b/ =>这个说明它是完整的单词  遇到这种字符串就不可以了'box1 box2-2 box3'  在正则中\b不仅是单词的边界,而且它会把中杠两边也作为边界  'box2-2'和/\bbox2\b/是匹配的,但是当前元素样式类是box2-2而不是box2
//=>/(^| +)box2( +|$)/  中间是box2完单词,左右两边是空格或者开始结束
// var reg = new RegExp('(^| +)' + strClass + '( +|$)');//正则要包含传递进来的形参strClass,想在一个正则中包含某个变量值属于正则里面的规则是动态设定的,字面量 /'+strClass+'/ 方式不行,字面量方式里面所有的东西都是原字符,所以只能用new 构造函数


//法二:不用正则  把获取到的item.className拆成数组循环遍历--------------------------------------------------------------------------------------------------------------------

function getEleByClass(strClass, context) {
    context = context || document;
    var result = [],
        nodeList = context.getElementsByTagName('*');
    strClass = strClass.replace(/^\s+|\s+$/g, '');

    for (var i = 0; i < nodeList.length; i++) {
        var item = nodeList[i];
        var ary = item.className.split(/ +/),//以多个空格把item.className拆成数组  'box1 box2 box3' => ['box1','box2','box3']
            flag = false;
        for (var j = 0; j < ary.length; j++) {
            if (strClass === ary[j]) {
                flag = true;
                break;
            }
        }
        flag ? result.push(item) : null;
    }
    return result;
}
console.log(getEleByClass('box1'));
console.log(getEleByClass('box'));
console.log(getEleByClass('  box1  '));
//法三:不用正则  把获取到的item.className拆成数组循环遍历  但是indexOf不兼容--------------------------------------------------------------------------------------------------------------------

function getEleByClass(strClass, context) {
    context = context || document;
    var result = [],
        nodeList = context.getElementsByTagName('*');
    strClass = strClass.replace(/^\s+|\s+$/g, '');

    for (var i = 0; i < nodeList.length; i++) {
        var item = nodeList[i];
        var ary = item.className.split(/ +/);//以多个空格把item.className拆成数组  'box1 box2 box3' => ['box1','box2','box3']
        if(ary.indexOf(strClass) > -1){//不兼容
            result.push(item);
        }
     
    }
    return result;
}
console.log(getEleByClass('box1'));
console.log(getEleByClass('box'));
console.log(getEleByClass('  box1  '));
复制代码

3、DOM封装-getElementsByClassName兼容处理之传递多个样式类名

html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>DOM封装-getElementsByClassName兼容处理之传递多个样式类名</title>
</head>
<body>
<div class="box1 box2">1</div>
<div class="box2 box3">2</div>
<div class="box1 box2 box3" id="div3">3</div>
<div class="box1 box3">4</div>
<div class="box3">5</div>
<div class="box2">6</div>

<script src="js/utils.js"></script>
<script src="js/3-2.js"></script>
</body>
</html>
复制代码

3-2.js 假设法

function getEleByClass(strClass, context) {
    context = context || document;
    var result = [],
        nodeList = context.getElementsByTagName('*');

    //=>不仅要去除首尾空格,还要把传递的多样式类名以空格(多空格)拆分成一个数组(数组中包含传递的每一个样式类名)
    strClass = strClass.replace(/^\s+|\s+$/g, '').split(/ +/);

    for (var i = 0; i < nodeList.length; i++) {
        var item = nodeList[i],
            itemClass = item.className,
            flag = true;//=>假设你传递的样式类名在ITEM中都存在

        //=>验证假设的值是真还是假:循环传递的所有样式类名,拿每一个传递的样式类名和当前标签的CLASS-NAME比较,只要有一个不在标签中,我们假设的值就是错误的
        for (var k = 0; k < strClass.length; k++) {
            var reg = new RegExp('(^| +)' + strClass[k] + '( +|$)');
            if (!reg.test(itemClass)) {
                flag = false;
                break;
            }
        }
        // console.log(nodeList[i].className)
        //=>如果FLAG为TRUE就存储
        flag ? result.push(item) : null;
    }
    return result;
}
console.log(getEleByClass(' box2 box1   '))
console.log(getEleByClass(' box2    '))
console.log(getEleByClass(' box2 box1   box3  '))
复制代码

可以通过断点看js走势:

www.cnblogs.com/mqfblog/p/5… js断点调试心得

3-3.js 排除法

function getEleByClass(strClass, context) {
   
    context = context || document;
    var nodeList = context.getElementsByTagName('*');
    console.log(nodeList);
    strClass = strClass.replace(/^ +| +$/g, '').split(/ +/);

    //=>排除法:先拿第一个样式类在所有的标签中比较,把没有当前样式类的都干掉,和第一个比较完成后,再和第二个传递进来的样式类比较,没有的接着干掉...
    nodeList = utils.toArray(nodeList);//类数组转换成数组
    for (var i = 0; i < strClass.length; i++) {
        var reg = new RegExp('(^| +)' + strClass[i] + '( +|$)');
        for (var k = 0; k < nodeList.length; k++) {
            if (!reg.test(nodeList[k].className)) {
                nodeList.splice(k, 1);
                k--;
            }
        }
    }
    return nodeList;
}

getEleByClass(' box2 box1   box3  ')
复制代码

3-4.js

假设法,做一个完善

function getEleByClass(strClass, context) {
    context = context || document;
    if ('getElementsByClassName' in document) {
        return utils.toArray(context.getElementsByClassName(strClass));
    }
    //以上是标准写法(为了保证和兼容保证统一,以上也把类数组转换成数组),以下是兼容ie6~8的写法。
    var result = [],
        nodeList = context.getElementsByTagName('*');
    strClass = strClass.replace(/^\s+|\s+$/g, '').split(/ +/);
    for (var i = 0; i < nodeList.length; i++) {
        var item = nodeList[i],
            itemClass = item.className,
            flag = true;
        for (var k = 0; k < strClass.length; k++) {
            var reg = new RegExp('(^| +)' + strClass[k] + '( +|$)');
            if (!reg.test(itemClass)) {
                flag = false;
                break;
            }
        }
        flag ? result.push(item) : null;
    }
    return result;
}
getEleByClass(' box2 box1   box3  ')
//-------------------------------------------------------------------------------------
//为了能element.queryElementsByClassName()去使用这个方法,我们需要把这个方法扩展到内置dom类的原型上,
//由于所有能操作方法的dom元素最终都会走到Node这个内置类,所以我们需要在Node这个内置类去扩展(一般项目中不这么用,
// 修改内置类这种操作很危险)
Node.prototype.queryElementsByClassName = function queryElementsByClassName() {
    if (arguments.length === 0) return [];
    var strClass = arguments[0],
        nodeList = utils.toArray(this.getElementsByTagName('*'));//这里的上下文就是this
    strClass = strClass.replace(/^ +| +$/g, '').split(/ +/);
    for (var i = 0; i < strClass.length; i++) {
        var reg = new RegExp('(^| +)' + strClass[i] + '( +|$)');
        for (var k = 0; k < nodeList.length; k++) {
            if (!reg.test(nodeList[k].className)) {
                nodeList.splice(k, 1);
                k--;
            }
        }
    }
    return nodeList;
};

document.queryElementsByClassName('box2 box1 ');//queryElementsByClassName()中的this是document,也是上下文
document.queryElementsByClassName();
box.queryElementsByClassName();//queryElementsByClassName()中的this是box,也是上下文
复制代码

4、DOM封装-基于惰性思想的高级单例模式优势和作用

//单例模式
var utils={}
//基于惰性思想的高级单例模式,(形成一个不销毁的作用域)
var utils=(function(){
    return {};
})();
复制代码

什么是js的惰性思想

//2、=>JS惰性思想:懒惰性的思想,能一次解决的,绝对不会每一次都重新的处理,属于JS性能优化的一种
var utils = (function () {//自执行函数形成一个不销毁私有作用域,有一个私有变量flag
    var flag = 'getComputedStyle' in window;//=>再给UTILS赋值的时候,我们验证了window中是否存在getComputedStyle这属性,如果存在,说明当前的浏览器是标准浏览器,反之说明是IE6~8,不需要再每次都去验证当前的浏览器是否兼容
    //用'getElementsByClassName' in document也行,ie6~8也不兼容getElementsByClassName

    function getCss(curEle, attr) {
        var value = null;
        if (flag) {
            value = window.getComputedStyle(curEle, null)[attr];
        } else {

        }
    }

    return {
        getCss: getCss
    }
})();

//1、单例模式 每执行一遍utils.getCss()方法,就要判断一次兼不兼容(不管执行多少次都要去判断这个浏览器兼不兼容,其实第一次执行的
//的时候就已经知道兼不兼容,后面不需要再去判断这个浏览器兼不兼容,直接把第一次判断出来的结果拿来用就行了)
// var utils = {
//     getCss: function (curEle, attr) {
//         var value = null;
//         if (window.getComputedStyle) {
//             value = window.getComputedStyle(curEle, null)[attr];
//         } else {
//             value = curEle.currentStyle[attr];
//         }
//         return value;
//     }
// };
console.log(utils.getCss(box, 'margin'));
console.log(utils.getCss(box, 'padding'));
//...
复制代码

所以我们把utils.js用js的惰性思想来封装:通过闭包(自执行函数形成闭包),变量保存需要获取的判断值来实现

//高级单例模式基于惰性思想的好处

//1、高级单例模式是基于惰性思想来实现性能优化,把需要做兼容处理的验证提前获取到执行一次就可以了,
//以后想用直接拿过来用就行,不需要从新做兼容判断(变量保存验证值)

// 2、单例模式中的很多方法,只暴露需要暴露的方法

var utils = (function () {
    var isCompatible = 'getElementsByClassName' in document,//ie6~8不兼容getElementsByClassName
        isSupportJSON = 'JSON' in window;//ie8及ie8以上兼容JSON  ie6~7不兼容

    //=>toArray & toJSON
    var toArray = function (classAry) {
        var ary = [];

        // // 用try catch不管浏览器兼不兼容ie6~8,都得执行一遍 ary = Array.prototype.slice.call(classAry);性能浪费
        // try{
        //     ary = Array.prototype.slice.call(classAry);
        // }catch(e){
        //     for (var i = 0; i < classAry.length; i++) {
        //         ary[ary.length] = classAry[i];
        //     }
        // }

        if (isCompatible) {//直接通过isCompatible判断兼不兼容,不需要再从新判断
            ary = Array.prototype.slice.call(classAry);
        } else {
            for (var i = 0; i < classAry.length; i++) {
                ary[ary.length] = classAry[i];
            }
        }
        return ary;
    };

    var toJSON = function (str) {
        return isSupportJSON ? JSON.parse(str) : eval('(' + str + ')');
    };

    //=>offset & winBox
    var offset = function (curEle) {
        var l = curEle.offsetLeft,
            t = curEle.offsetTop,
            p = curEle.offsetParent;
        while (p.tagName !== 'BODY') {
            // if(!/MISE 8/.test(navigator.userAgent))){
            //isCompatible === false=>ie6~8 isSupportJSON === true=>肯定不是ie6、7 
            if (isCompatible === false && isSupportJSON === true) {
                l += p.clientLeft;
                t += p.clientTop;
            }
            l += p.offsetLeft;
            t += p.offsetTop;
            p = p.offsetParent;
        }
        return {left: l, top: t};
    };

    var winBox = function (attr, value) {
        if (typeof value !== 'undefined') {
            document.documentElement[attr] = value;
            document.body[attr] = value;
            return;
        }
        return document.documentElement[attr] || document.body[attr];
    };

    //=>children
    function children(ele, attr) {
        var ary = [];
        // 'getComputedStyle' in window ? ary = toArray(ele.children) : ary = toArray(ele.childNodes);
        isCompatible ? ary = toArray(ele.children) : ary = toArray(ele.childNodes);
        for (var k = 0; k < ary.length; k++) {
            var obj = ary[k];
            if (obj.nodeType === 1) {
                if (attr && attr.toLowerCase() !== obj.tagName.toLowerCase()) {
                    ary.splice(k, 1);
                    k--;
                }
            } else {
                ary.splice(k, 1);
                k--;
            }
        }
        return ary;
    }

    //=>getElementsByClassName
    function getElementsByClassName(classStr, context) {
        if (arguments.length === 0) return [];
        context = context || document;
        // if(document.getElementsByClassName){
        if (isCompatible) {
            return toArray(context.getElementsByClassName(classStr));
        }
        var eleList = toArray(context.getElementsByTagName("*"));
        var classList = classStr.replace(/^ +| +$/g, "").split(/ +/);
        for (var i = 0; i < classList.length; i++) {
            var cur = classList[i];
            var reg = new RegExp("(^| +)" + cur + "( +|$)");
            for (var j = 0; j < eleList.length; j++) {
                if (!reg.test(eleList[j].className)) {
                    eleList.splice(j, 1);
                    j--;
                }
            }
        }
        return eleList;
    }

    //=>css
    function getCss(curEle, attr) {
        var value = null, reg = null;
        // if('getComputedStyle' in window){
        if (isCompatible) {
            value = window.getComputedStyle(curEle, null)[attr];
        } else {
            if (attr === 'opacity') {
                value = curEle.currentStyle['filter'];
                reg = /^alpha\(opacity=(.+)\)$/i;
                return reg.test(value) ? reg.exec(value)[1] / 100 : 1;
            }
            value = curEle.currentStyle[attr];
        }
        reg = /^-?\d+(.\d+)?(pt|px|rem|em)?$/i;
        return reg.test(value) ? parseFloat(value) : value;
    }

    function setCss(curEle, attr, value) {
        if (attr === 'opacity') {
            curEle.style.opacity = value;

            curEle.style.filter = 'alpha(opacity=' + value * 100 + ')';
            return;
        }
        !isNaN(value) && !/(fontWeight|lineHeight|zoom|zIndex)/i.test(attr) ? value += 'px' : null;
        curEle.style[attr] = value;
    }

    function setGroupCss(curEle, options) {
        if (Object.prototype.toString.call(options) !== '[object Object]') return;
        for (var attr in options) {
            if (options.hasOwnProperty(attr)) {
                setCss(curEle, attr, options[attr])
            }
        }

    }

    function css() {
        var len = arguments.length,
            type = Object.prototype.toString.call(arguments[1]),
            fn = getCss;

        len >= 3 ? fn = setCss : (len === 2 && type === '[object Object]' ? fn = setGroupCss : null)
        return fn.apply(this, arguments);

    }

    return {
        toArray: toArray,
        toJSON: toJSON,
        offset: offset,
        winBox: winBox,
        children: children,
        getElementsByClassName: getElementsByClassName,
        css: css
    }
})();

复制代码

5、DOM封装-惰性思想之函数重写

项目中惰性思想还有一种形式: 函数重写的惰性思想


var _flag = window.getComputedStyle;
function getCss(curEle, attr) {
    if (_flag) {//每次进来之后也得从新验证_flag,只是不需要再获取window.getComputedStyle,也需要去验证(这是通过变量先获取到判断值的惰性思想),所以还有种惰性思想:函数覆盖
        getCss = function (curEle, attr) {
            return window.getComputedStyle(curEle, null)[attr];
        }
    } else {
        getCss = function (curEle, attr) {
            return curEle.currentStyle[attr];
        }
    }
    return getCss(curEle, attr);//=>此处执行的GET-CSS是其中的某一个小方法了,把得到的结果返回
}
//----------------------------------------------------------------------------------
// 函数覆盖的惰性思想,比以上写法性能上更优化
function getCss(curEle, attr) {
    if (window.getComputedStyle) {
        getCss = function (curEle, attr) {
            return window.getComputedStyle(curEle, null)[attr];
        }
    } else {
        getCss = function (curEle, attr) {
            return curEle.currentStyle[attr];
        }
    }
    return getCss(curEle, attr);//=>此处执行的GET-CSS是其中的某一个小方法了,把得到的结果返回
}

//=>getCss:最外层大方法
//=>第一次执行GET-CSS 
//[标准]
// getCss = function (curEle, attr) {
//     return window.getComputedStyle(curEle, null)[attr];
// }
//[IE6~8]
// getCss = function (curEle, attr) {
//     return curEle.currentStyle[attr];
// }

// 第一次执行GET-CSS 不仅把最外层大方法getCss改成其中的某一个小方法,也得到了结果,所以第二次执行根据当前浏览器直接
// 执行其中的某一个小方法,不需要再去做兼容处理验证,这就是函数重写的惰性思想。


console.log(getCss(box, 'padding'));
复制代码

哪种需要用函数重写的惰性思想:凡是加载一个方法,进来首先要验证兼容不兼容,然后再怎么做的,都可以重写这个函数,第一次 执行大方法的时候根据兼容性问题把大方法从写成某一个小方法,以后只要浏览器不变,再执行就会直接执行对应的小方法,不需要再做兼容判断处理,更能提高性能

凡是这个方法后期要执行很多次,第一次执行性能可能会消耗一点,但是后期想要性能好,都可以用这种重写的惰性思想来实现,把需要验证的值(window.getComputedStyle)也可以提取出来用一个公共变量现保存起来,来达到js的性能优化

6、重写选项卡-原有选项卡实现的弊端

html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>-选项卡</title>
    <link rel="stylesheet" href="css/reset.min.css">
    <link rel="stylesheet" href="css/index.css">
</head>
<body>
<div class="tabBox" id="tabBox">
    <ul class="tab clearfix">
        <li class="select">新闻</li>
        <li>电影</li>
        <li>动漫</li>
    </ul>
    <div class="con select">1</div>
    <div class="con">2</div>
    <div class="con">3</div>
</div>
<script src="js/utils.js"></script>
<script src="js/index.js"></script>
</body>
</html>
复制代码

css

.tabBox {
    margin: 20px auto;
    width: 500px;
    font-size: 14px;
}

.tabBox .tab {
    position: relative;
    top: 1px;
}

.tabBox .tab li {
    float: left;
    margin-right: 15px;
    padding: 0 15px;
    height: 35px;
    line-height: 35px;
    border: 1px solid #999;
    background: #EEE;
    cursor: pointer;
}

.tabBox .tab li.select {
    background: #FFF;
    border-bottom-color: #FFF;
}

.tabBox .con {
    display: none;
    padding: 10px;
    height: 150px;
    border: 1px solid #999;
}

.tabBox .con li {
    line-height: 29px;
    border-bottom: 1px dashed #CCC;
}

.tabBox .con.select {
    display: block;
}

.tabBox2 {
    background: lightcyan;
}
复制代码

reset.min.css

body,h1,h2,h3,h4,h5,h6,hr,p,blockquote,dl,dt,dd,ul,ol,li,button,input,textarea,th,td{margin:0;padding:0}body{font-size:12px;font-style:normal;font-family:"\5FAE\8F6F\96C5\9ED1",Helvetica,sans-serif}small{font-size:12px}h1{font-size:18px}h2{font-size:16px}h3{font-size:14px}h4,h5,h6{font-size:100%}ul,ol{list-style:none}a{text-decoration:none;background-color:transparent}a:hover,a:active{outline-width:0;text-decoration:none}table{border-collapse:collapse;border-spacing:0}hr{border:0;height:1px}img{border-style:none}img:not([src]){display:none}svg:not(:root){overflow:hidden}html{-webkit-touch-callout:none;-webkit-text-size-adjust:100%}input,textarea,button,a{-webkit-tap-highlight-color:rgba(0,0,0,0)}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block}audio:not([controls]),video:not([controls]){display:none;height:0}progress{vertical-align:baseline}mark{background-color:#ff0;color:#000}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}button,input,select,textarea{font-size:100%;outline:0}button,input{overflow:visible}button,select{text-transform:none}textarea{overflow:auto}button,html [type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-input-placeholder{color:inherit;opacity:.54}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}.clearfix:after{display:block;height:0;content:"";clear:both}
复制代码

js

//法一:-------------------------------------------------------

var tabBox = document.getElementById('tabBox'),
    tabList = tabBox.getElementsByTagName('li'),
    conList = tabBox.getElementsByTagName('div');

for(var i = 0; i < tabList.length; i++){
    var curTab = tabList[i];
    curTab.myIndex = i;
    curTab.onclick = function(){
        //=>清楚所有li和div的选中样式(但是这样每次清楚所有的浪费性能,只需要清除上一个li和div)
        for(var k = 0; k < tabList.length; k++){
            tabList[k].className = null;
            conList[k].className = 'con';
        }
        //=>让当前操作的li和div有选中样式
        this.className='select';
        conList[this.myIndex].className = 'con select';
    }
}

// 法二:-------------------------------------------------------

// 清除上一个li和div(性能好点,但是通过getElementsByTagName获取元素时,会获取到所有后代元素,
// 项目中每个con里面假设还有ul、li,这样通过getElementsByTagName去获取,会获取后代中所有标签名为li或者div的
// 元素,而不是单纯的就这几个页卡而已)

var tabBox = document.getElementById('tabBox'),
    tabList = tabBox.getElementsByTagName('li'),
    conList = tabBox.getElementsByTagName('div');
var _prevIndex = 0;//记录上一次选中li的索引(初始选中第一个,所以默认值是零)
for(var i = 0; i < tabList.length; i++){
    var curTab = tabList[i];
    curTab.myIndex = i;
    curTab.onclick = function(){
        //=> 先把上一个清掉即可
       tabList[_prevIndex].className = null;
       conList[_prevIndex].className = 'con';

       //=>当前点击有选中
       this.className = 'select';
       conList[this.myIndex].className = 'con select';

       //=>当前点击这一项是下一次点击的上一项
       _prevIndex = this.myIndex;

    }
}
复制代码

7、重写选项卡-使用DOM库完成选项卡的封装

html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>-选项卡</title>
    <link rel="stylesheet" href="css/reset.min.css">
    <link rel="stylesheet" href="css/index.css">
</head>
<body>
<div class="tabBox" id="tabBox">
    <ul class="tab clearfix">
        <li>新闻</li>
        <li>电影</li>
        <li>动漫</li>
    </ul>
    <div class="con">
        <ul>
            <li>1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
            <li>5</li>
        </ul>
    </div>
    <div class="con">
        <ul>
            <li>6</li>
            <li>7</li>
            <li>8</li>
            <li>9</li>
            <li>10</li>
        </ul>
    </div>
    <div class="con">
        <ul>
            <li>11</li>
            <li>12</li>
            <li>13</li>
            <li>14</li>
            <li>15</li>
        </ul>
    </div>
</div>



<script src="js/utils.js"></script>
<script src="js/index201711141717.js"></script>
</body>
</html>
复制代码

js

//------------------------------------------------------------

var tabBox = document.getElementById('tabBox'),
    //由于通过getElementsByTagName获取元素时,会获取到所有后代元素,
    //所以通过样式类名去获取子元素,这里用utils里面封装好的方法
    tab = utils.getElementsByClassName('tab', tabBox)[0],
    tabList = utils.children(tab, 'li'),
    conList = utils.children(tabBox, 'div'),
    _prevIndex = 0;
for (var i = 0; i < tabList.length; i++) {
    tabList[i].myIndex = i;
    tabList[i].onclick = function () {
        tabList[_prevIndex].className = null;
        conList[_prevIndex].className = 'con';
        this.className = 'select';
        conList[this.myIndex].className = 'con select';
        _prevIndex = this.myIndex;
    }
}

//------------------------------------------------------------
// 初步批量实现某一个相同操作,不算是封装插件
~function () {
    var tabBoxList = utils.getElementsByClassName('tabBox');
    for (var i = 0; i < tabBoxList.length; i++) {
        change(tabBoxList[i]);
    }

    function change(tabBox) {
        var tab = utils.getElementsByClassName('tab', tabBox)[0],
            tabList = utils.children(tab, 'li'),
            conList = utils.children(tabBox, 'div'),
            _prevIndex = 0;
        for (var i = 0; i < tabList.length; i++) {
            tabList[i].myIndex = i;
            tabList[i].onclick = function () {
                tabList[_prevIndex].className = null;
                conList[_prevIndex].className = 'con';
                this.className = 'select';
                conList[this.myIndex].className = 'con select';
                _prevIndex = this.myIndex;
            }
        }
    }
}();

/*html结构要一样
 <div class='tabBox'>
 <ul class='tab'>
 <li class='select'></li>
 <li>...</li>
 ...
 </ul>
 <div class='con select'>...</div>
 <div class='con'>...</div>
 </div>
 */
复制代码

8、重写选项卡-使用构造函数初步封装选项卡插件

html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>-选项卡</title>
    <link rel="stylesheet" href="css/reset.min.css">
    <link rel="stylesheet" href="css/index.css">
</head>
<body>
<div class="tabBox" id="tabBox">
    <ul class="tab clearfix">
        <li>新闻</li>
        <li>电影</li>
        <li>动漫</li>
    </ul>
    <div class="con">
        <ul>
            <li>1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
            <li>5</li>
        </ul>
    </div>
    <div class="con">
        <ul>
            <li>6</li>
            <li>7</li>
            <li>8</li>
            <li>9</li>
            <li>10</li>
        </ul>
    </div>
    <div class="con">
        <ul>
            <li>11</li>
            <li>12</li>
            <li>13</li>
            <li>14</li>
            <li>15</li>
        </ul>
    </div>
</div>

<div class="tabBox tabBox2">
    <ul class="tab clearfix">
        <li>新闻</li>
        <li>电影</li>
        <li>动漫</li>
    </ul>
    <div class="con">
       1
    </div>
    <div class="con">
        2
    </div>
    <div class="con">
        3
    </div>
</div>

<div class="tabBox">
    <ul class="tab clearfix">
        <li>新闻</li>
        <li>电影</li>
        <li>动漫</li>
        <li>科技</li>
    </ul>
    <div class="con">
        <ul>
            <li>1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
            <li>5</li>
        </ul>
    </div>
    <div class="con">
        <ul>
            <li>6</li>
            <li>7</li>
            <li>8</li>
            <li>9</li>
            <li>10</li>
        </ul>
    </div>
    <div class="con">
        <ul>
            <li>11</li>
            <li>12</li>
            <li>13</li>
            <li>14</li>
            <li>15</li>
        </ul>
    </div>
    <div class="con">
        <ul>
            <li>16</li>
            <li>17</li>
            <li>18</li>
            <li>19</li>
            <li>20</li>
        </ul>
    </div>
</div>

<script src="js/utils.js"></script>
<script src="js/index.js"></script>
</body>
</html>
复制代码

js

封装选项卡插件,封装插件、组件、类库必用构造函数模式

封装插件的思想:先把一个功能实现,以后调用它就能实现别的

    function ChangeTab(tabBox) {//1、构造函数创建一个ChangeTab类
   
        //=>3、技巧:为了保证这些值在类的任何方法中都可以调取使用,我们一般都把信息值存放在当前实例上(THIS):只要保证每个方法中的THIS都是当前类的实例,我们就可以获取这些值了
        // 构造函数封装的时候,我们一般会把常用的公共用的东西写在this上(也就是当前实例上)
        this.tabBox = tabBox;

        //=>5、开始实现选项卡的功能
        // ChangeTab.prototype.init().call(this);
        this.init();
    }

    ChangeTab.prototype = {//2、往类的原型上写方法,(重构自定义类的原型,需要保证constructor: ChangeTab)
        constructor: ChangeTab,

        //7、实现页卡切换
        change:function(){

            // var tabBox = this.tabBox,          
            //     tab = this.tab,
            //     tabList = this.tabList,
            //     conList = this.conList,
            //     prevIndex = this.prevIndex,
            //     _this = this;
            // for (var i = 0; i < tabList.length; i++) {
            //     tabList[i].myIndex = i;
            //     tabList[i].onclick = function () {
            //         //=>this:当前点击的这个li,不是实例(_this是实例)
            //         tabList[prevIndex].className = '';
            //         conList[prevIndex].className = 'con';
            //         this.className = 'select';
            //         conList[this.myIndex] = 'con select';
            //         _this.prevIndex = prevIndex = this.myIndex;
            //     }
            // }

            // 实际项目中开发写法如下:不定义变量,都通过this直接操作
            var _this = this;
            for (var i = 0; i < _this.tabList.length; i++) {
                 _this.tabList[i].myIndex = i;
                 _this.tabList[i].onclick = function () {
                  //=>this:当前点击的这个LI,不是实例(_this是实例)
                  _this.tabList[_this.prevIndex].className = '';
                  _this.conList[_this.prevIndex].className = 'con';
                  this.className = 'select';
                  _this.conList[this.myIndex].className = 'con select';
                  _this.prevIndex = this.myIndex;
                }
            }

        },

        //4 init初始化,完成选项卡的操作
        init: function () {

            //=>6、获取当前页卡区域中的元素(LI & DIV) 把获取到的值都通过this挂载到这个实例上去,以后再其他方法中也能用
            // var tabBox = this.tabBox,
            //     tab = utils.children(tabBox, 'ul')[0],
            //     tabList = utils.children(tab, 'li'),
            //     conList = utils.children(tabBox, 'div'),
            // this.tab = tab;
            // this.tabList = tabList;
            // this.conList = conList;
            // this.prevIndex = 0;

            //=>直接通过this写到实例上
            var tabBox = this.tabBox,
            this.tab = utils.children(this.tabBox, 'ul')[0],
            this.tabList = utils.children(this.tab, 'li'),
            this.conList = utils.children(this.tabBox, 'div'),
            this.prevIndex = 0;

            // =>7、实现页卡切换
            this.change();

        }
    };
复制代码

使用构造函数封装插件,核心点都在于实例this,所有值和方法的共享都是通过this来实现的,构造函数中的this就是当前实例

只要new ChangeTab()就会执行this.init(),

new ChangeTab,this就是构造函数ChangeTab的实例,

不new ChangeTab,直接执行ChangeTab(),this是window,第一次执行和第二次执行ChangeTab传的值都是window,会冲突,所已要new ChangeTab()执行,

为了防止冲突,我们把它封装成闭包,但是外面要用这个类,我们通过window暴露出去

~function(){
    function ChangeTab(tabBox) {//1、构造函数创建一个ChangeTab类
   
        //=>3、技巧:为了保证这些值在类的任何方法中都可以调取使用,我们一般都把信息值存放在当前实例上(THIS):只要保证每个方法中的THIS都是当前类的实例,我们就可以获取这些值了
        // 构造函数封装的时候,我们一般会把常用的公共用的东西写在this上(也就是当前实例上)
        this.tabBox = tabBox;

        //=>5、开始实现选项卡的功能
        // ChangeTab.prototype.init().call(this);
        this.init();
    }

    ChangeTab.prototype = {//2、往类的原型上写方法,(重构自定义类的原型,需要保证constructor: ChangeTab)
        constructor: ChangeTab,

        //7、实现页卡切换
        change:function(){

            // 实际项目中开发写法如下:不定义变量,都通过this直接操作
            var _this = this;
            for (var i = 0; i < _this.tabList.length; i++) {
                _this.tabList[i].myIndex = i;
                _this.tabList[i].onclick = function () {
                  //=>this:当前点击的这个LI,不是实例(_this是实例)
                  _this.tabList[_this.prevIndex].className = '';
                  _this.conList[_this.prevIndex].className = 'con';
                  this.className = 'select';
                  _this.conList[this.myIndex].className = 'con select';
                  _this.prevIndex = this.myIndex;
                }
            }

        },

        //4 init初始化,完成选项卡的操作
        init:function(){

            //=>6、获取当前页卡区域中的元素(LI & DIV) 把获取到的值都通过this挂载到这个实例上去,以后再其他方法中也能用


            //=>直接通过this写到实例上
            this.tab = utils.children(this.tabBox, 'ul')[0],
            this.tabList = utils.children(this.tab, 'li'),
            this.conList = utils.children(this.tabBox, 'div'),
            this.prevIndex = 0;

            // =>7、实现页卡切换
            this.change();

        }
    };

    window.CT = ChangeTab;

}();
var tabBoxList = utils.getElementsByClassName('tabBox');
for (var i = 0; i < tabBoxList.length; i++) {
    new CT(tabBoxList[i]);
}
复制代码

完善插件,options额外配置项,通过options完成很多其他功能

~function(){
    function ChangeTab(tabBox,options) {//1、构造函数创建一个ChangeTab类

        //=>8、参数初始化
        var _default = {
            initIndex:0,
            eventType:'click'
        };
        //=>9、循环遍历替换默认_default参数值
        for(var key in options){
            if(options.hasOwnProperty(key)){
                _default[key] = options[key];
            }
        }
        //10、把默认值放到实例上,这些值在类的任何方法中都可以调取使用
        this.initIndex = _default.initIndex;
        this.eventType = _default.eventType;
   
        //=>3、技巧:为了保证这些值在类的任何方法中都可以调取使用,我们一般都把信息值存放在当前实例上(THIS):只要保证每个方法中的THIS都是当前类的实例,我们就可以获取这些值了
        // 构造函数封装的时候,我们一般会把常用的公共用的东西写在this上(也就是当前实例上)
        this.tabBox = tabBox;

        //=>5、开始实现选项卡的功能
        // ChangeTab.prototype.init().call(this);
        this.init();
    }

    ChangeTab.prototype = {//2、往类的原型上写方法,(重构自定义类的原型,需要保证constructor: ChangeTab)
        constructor: ChangeTab,

        //7、实现页卡切换
        change:function(){

            // 实际项目中开发写法如下:不定义变量,都通过this直接操作
            var _this = this;
            for (var i = 0; i < _this.tabList.length; i++) {
                _this.tabList[i].myIndex = i;
                //13、事件
                // _this.tabList[i].onclick = function () {
                _this.tabList[i]['on' + _this.eventType] = function () {
                  //=>this:当前点击的这个LI,不是实例(_this是实例)
                  _this.tabList[_this.prevIndex].className = '';
                  _this.conList[_this.prevIndex].className = 'con';
                  this.className = 'select';
                  _this.conList[this.myIndex].className = 'con select';
                  _this.prevIndex = this.myIndex;
                }
            }

        },

        //=>11、清空所有的样式类
        clear:function(){
            for(var i = 0; i < this.tabList.length; i++){
                this.tabList[i].className = '';
                this.conList[i].className = 'con';
            }
            //=>初始化默认的选中页卡
            this.tabList[this.initIndex].className = 'select';
            this.conList[this.initIndex].className='con select';
        },

        //4 init初始化,完成选项卡的操作
        init:function(){

            //=>6、获取当前页卡区域中的元素(LI & DIV) 把获取到的值都通过this挂载到这个实例上去,以后再其他方法中也能用


            //=>直接通过this写到实例上
            this.tab = utils.children(this.tabBox, 'ul')[0],
            this.tabList = utils.children(this.tab, 'li'),
            this.conList = utils.children(this.tabBox, 'div'),
            this.prevIndex = 0;

            //12、清空所有的样式类
            this.clear();
            // =>7、实现页卡切换
            this.change();

        }
    };

    window.CT = ChangeTab;

}();
var tabBoxList = utils.getElementsByClassName('tabBox');
// for (var i = 0; i < tabBoxList.length; i++) {
//     new CT(tabBoxList[i],{
//         initIndex:1,
//         eventType:'mouseover'
//     });
// }

new CT(tabBoxList[0]);
new CT(tabBoxList[1], {
    eventType: 'mouseover'
});
var aa = new CT(tabBoxList[2], {
    eventType: 'mouseover',
    initIndex: 2
});
console.log(aa);
// 插件必须构造函数来写
// 所有信息之间的传输都是基于实例this来实现的,所有的方法都写在原型上,所有要使用的值都存在实例的私有属性上
// 插件封装不是把功能实现就可以了,而是尽可能提供更多的可变性,更多的可变性通过传参options来一步步实现,封装插件可以保证独立性
// 如果有额外的需求可以去扩展,然后通过实例调用这个方法

//=>插件:iscroll swiper
//=>组件:UI组件 bootstrap
//=>类库:jQuery zepto
//=>框架:vue react
复制代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值