JS盒子模型、图片懒加载、DOM库封装 — 1、JS盒子模型及常用操作样式方法的封装

217 阅读20分钟

1、大纲

JS中的盒子模型

  • clientWidth/clientHeight
  • clientTop/clientLeft
  • offsetWidth/offsetHeight
  • scrollWidth/scrollHeight
  • getCss方法的封装
  • offsetTop/offsetLeft
  • offsetParent
  • offset方法的封装:获取盒子偏移量
  • scrollLeft/scrollTop
  • 定时器基础应用
  • 案例:回调顶部
  • 案例:跑马灯
  • 案例:瀑布流
  • 案例:京东楼层导航
  • ...

图片延迟加载

  • 响应式布局:流式布局发
  • 图片懒加载的意义
  • 单张图片延迟加载
  • 多张图片延迟加载
  • JS同步异步编程
  • 由图片延迟加载引发的一些思考

2、传统和新的CSS盒子模型

css盒子模型

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>css盒子模型</title>
    <style>
    *{
        margin:0;
        padding:0;
    }
    .box{
        margin: 50px auto;
        padding: 20px;
        width: 200px;
        height: 200px;
        border: 10px solid green;
        background: lightcoral;
        line-height:23px;
    }
    </style>
</head>
<body>
    <div class="box" id="box">
        夫君子之行,静以修身,俭以养德。非淡泊无以明志,非宁静无以致远。
        夫学须静也,才须学也,非学无以广才,非志无以成学。淫慢则不能励精,
        险躁则不能治性。年与时驰,意与日去,遂成枯落,多不接世,悲守穷庐,
        将复何及!
    </div>
</body>
</html>

3、JS盒子模型之clientWidth和clientHight

JS盒子模型

提供一些属性和方法用来描述当前盒子的样式的

clientWidth和clientHight

当前盒子可视区域的宽度和高度

可视区域:内容宽高+padding

clientWidth = width + padding(Left/Right)

clientHight = height + padding(Top/Bottom)

和内容是否溢出以及我们是否设置了overflow:hidden没有关系

项目中:

// 获取当前页面一屏幕的宽度(加或者是为了兼容所有的浏览器)
document.documentElement.clientWidth || document.body.clientWidth

// 获取当前页面一屏幕的高度
document.documentElement.clientHeight || document.body.clientHeight

面试题: 让一个盒子在整个页面水平和垂直都居中的位置?如果不知道盒子的宽度和高度如何处理?

css

//使用定位,需要知道盒子的具体宽高才可以
 .box{
        position: absolute;
        top:50%;
        left: 50%;
        margin: -100px 0 0 -100px;
        width: 200px;
        height: 200px;
        background: lightcoral;
        
    }
//使用定位:不需要知道盒子的具体宽高(不兼容ie低版本浏览器)
  .box{
        position: absolute;
        top:0;
        left: 0;
        bottom: 0;
        right: 0;
        margin: auto;
        width: 200px;
        height: 200px;
        background: lightcoral;
    }
//使用css3的flex伸缩盒模型(不兼容ie,低版本安卓系统不兼容)
    html{
        height: 100%;
    }
    body{
        display: flex;
        height: 100%;
        flex-flow: row wrap;
        align-items: center;
        justify-content: center;
    }
    .box{
        width: 200px;
        height: 200px;
        background: lightcoral;
    }

js

    // css
    .box{
        position: absolute;
        width: 200px;
        height: 200px;
        background: lightcoral;
    }
    // JS中只要获取到当前盒子具体的left/top值即可
    // 一屏幕的宽高-盒子的宽高,最后除以2,获取的值就是它应该具备的left/top(这个值可以让他处于页面中间)
    var winW = document.documentElement.clientWidth || document.body.clientWidth;
    var winH = document.documentElement.clientHeight || document.body.clientHeight;
    var boxW = box.offsetWidth;
    var boxH = box.offsetHeight;
    box.style.left = (winW - boxW) / 2 + "px";
    box.style.top = (winH - boxH) / 2 + "px";

4、JS盒子模型之clientTop和clientLeft

clientTop和clientLeft

只有top和left,没有right和bottom这两个属性

clientTop:盒子上边框的高度

clientLeft:左边框的宽度

获取的结果其实就是border-width值

通过js盒子模型属性获取的结果是不带单位的,而且只能是整数(它会自动四舍五入)

  .box{
        border: 10.8px solid red;
    }
    box.clientLeft => 11

5、JS盒子模型之offsetWidth和offsetHeight

在clientWidth和clientHeight的基础上加上盒子的边框即可

和内容是否溢出没关系

真实项目中如果想获取一个盒子的宽度和高度,我们一般用offsetWidth(而不是clientWidth),border也算是当前盒子的一部分

6、JS盒子模型之scrollHeight和scrollWidth

scrollHeight和scrollWidth

没有内容溢出

  • 获取的结果和clientWidth/clientHight是一样的

有内容溢出

  • 真实内容的宽度或者高度(包含溢出的内容),再加上左padding或者上padding

有内容溢出的情况下,我们通过scrollHeight和scrollWidth获取的结果是一个约等于的值

  • 1)有内容溢出,每个浏览器由于对行高或者文字的渲染不一样,获取的结果也是不一样的
  • 2)是否设置overflow:hidden对最后获取的结果也有影响
    // 获取当前页面真实高度(包含溢出内容)
    document.documentElement.scrollHeight || document.body.scrollHeight

7、获取当前元素所有经过浏览器计算的样式

在js中获取元素具体的样式值

通过js盒子模型属性获取的结果都是盒子的组合样式值,不能直接获取某一个具体样式值,例如:我就想获取左padding...

 curEle.style.xxx
 
 获取当前元素所有写在行内样式上的样式值
 
 特殊:只有把样式写在行内样式上,才可以通过这种办法获取到(写在其他地方的样式是获取不到的)
 
 这种办法在真实项目中使用的特别少:因为我们不可能把所有元素的样式都写在行内上(这样要内嵌和外联式就没用了)
 
 window.getComputedStyle / currentStyle
 
 只要当前元素在页面中显示出来了,我们就可以获取其样式值(不管写在行内还是样式表中);
 也就是获取所有经过浏览器计算过的样式(当前元素只要能再页面中展示,那么它的所有样式都是经过浏览器计算的,
 包含一些你没有设置,浏览器按照默认样式渲染的样式)
 
 window.getComputedStyle:适用于标准浏览器,但是在ie6~8中就不兼容了,ie6~8下我们使用curEle.currentStyle来获取需要的样式值
 

获取当前元素所有经过浏览器计算的样式及兼容性处理

        通过getComputedStyle获取的结果是一个对象,对象中包含了所有被浏览器计算过的样式属性及属性值
        window.getComputedStyle([当前需要操作的元素],[当前元素的伪类,一般写null])

        window.getComputedStyle(box,null)['paddingLeft']这种方式可以在获取的对象中找到某一个具体样式属性
        的值(通过.paddingLeft也可以)

        在ie6~8浏览器中window全局对象中,并没有提供getComputedStyle这个属性方法,我们只能使用currentStyle
        属性获取元素的样式

        [当前元素].currentStyle 获取的结果也是一个包含当前元素所有的样式属性和值的对象
        box.currentStyle.paddingLeft/ box.currentStyle['paddingLeft']

        以后再想获取元素的样式,我们需要写两套,这样的话,我们可以把获取样式的操作封装成为一个公共的方法:getCss
        getCss:获取当前元素的某一个样式属性值
        @parameter:
            curEle:当前需要操作的元素
            attr:当前需要获取的样式属性名
        @return
            获取的样式属性值
        function getCss(curEle,attr){
            var value=null;
            if('getComputedStyle' in window){
                value = window.getComputedStyle(curEle,null)[attr];
            }else{
                value = curEle.currentStyle[attr];
            }
            return value;
        }

8、getCss获取元素样式方法的优化

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>css盒子模型</title>
    <style>
    *{
        margin:0;
        padding:0;
    }
    .box{
        width: 200px;
        height: 200px;
        background: lightcoral;
        opacity: 0.2;
        filter: alpha(opacity=20);
        /* ie低版本浏览器不支持opacity,需要使用滤镜出来*/
    }
    </style>
</head>
<body>
    <div class="box" id="box"></div>
    <script>
        // 优化2
            // 已知:给元素设置透明度,需要写两套(兼容ie6~8)
            //     opacity:n
            //     filter:alpha(opacity=n*100)
            
            // 在js中如果我们想要获取元素的透明度,我们一般都会这样执行方法
            //     var result = getCss(box,'opacity')

            // 这样写会存在一些问题:
            //     1、标准浏览器下可以获取到值,但是ie6~8下获取不到结果(ie6~8识别filter不识别opacity)
            //     => 需要处理:在ie6~8下,如果我们传递的是opacity(用户想获取的是透明度),我们应该用
            //     filter获取
            //     2、通过filter获取的结果是'alpha(opacity=xxx)',通过opacity获取的直接是xxx值 =>解决:
            //     我们应该把通过filter获取的结果中的具体值捕获到,然后除以100,让其返回的值和opacit一样

            //         正则捕获
            //             /\d+/ :不行,因为获取的值可能是小数
            //             /\d+(\.\d+)?/ : ok
            //             /^alpha\(opacity=(.+)\)$/:捕获第一个小分组内容
            //             ...

                
        // 优化1:去除获取结果的单位:项目中我们一般都会把获取的样式值进行后续的计算,不带单位方法计算
         function getCss(curEle,attr){
            var value=null;
            // 获取样式值-----------------------------------
            if('getComputedStyle' in window){
                value = window.getComputedStyle(curEle,null)[attr];
            }else{
                // ie6~8下处理透明度
                if (attr === 'opacity'){
                    value = curEle.currentStyle['filter'];
                    // 优化2:把获取的结果转换为和opacity一样的结果filter:alpha(opacity=20)
                    var reg = /^alpha\(opacity=(.+)\)$/;
                    reg.test(value) ? value = reg.exec(value)[1] / 100 : value = 1;
                } else{
                    value = curEle.currentStyle[attr];
                }
                value = curEle.currentStyle[attr];
            }
            // 去除获取值的单位-----------------------------------
            // 首先把我们获取的结果转换为数字,然后看是否为NaN,不是NaN说明可以去除单位,我们把转换的结果返回,如果是NaN
            // ,说明当前获取的结果并不是数字+单位的,此时我们把之前获取的值返回即可
            var temp = parseFloat(value);
            !isNaN(temp) ? value = temp : null;
            return value;
        }
        console.log(getCss(box,"color"));
        console.log(getCss(box,"width"));
    </script>
</body>
</html>

9、解决isNaN去除单位对于复合值不准确的问题

10、setCss给元素设置样式方法的封装

方法一:符合加单位的css样式(传递的是纯数字,大部分css样式都需要加单位)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>css盒子模型</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .box {
            width: 200px;
            height: 200px;
            background: lightcoral;
            opacity: 0.2;
            filter: alpha(opacity=20);
            /* ie低版本浏览器不支持opacity,需要使用滤镜出来*/
        }
    </style>
</head>

<body>
    <div class="box" id="box" style="color: #000;">123456</div>
    <script>
        // 设置元素的样式
        // curEle.style.xxx=xxx 设置当前元素的行内样式值(JS中设置的样式一般都要设置在行内样式上:行内上的样式优先级最大)
        // curEle.className = xxx 设置元素的样式类名 (addClass)
        // box.style.backgroundColor = 'lightblue';
        function setCss(curEle, attr, value) {

            // 如果传递的value值没有单位,我们根据需求自动补充单位(px)
            // 1、并不是所有的样式属性传递进来的值都要补充单位
            // 需要补充单位的常用样式属性:width、height、margin、padding、marginLeft...、paddingLeft...、top、left、right、
            // bottom、borderWidth...
            // 2、传递的值已经存在单位,我们不需要再补充,没有单位我们再补充
            // var reg = /^(width|height|margin|padding|marginLeft|marginRight|marginTop|marginBottom|paddingLeft|paddingRight|paddingTop|paddingBottom|top|left|bottom|right|borderWidth)$/i;
            var reg = /^(width|height|((margin|padding)?(top|left|right|bottom)?)|borderWidth)$/i;
            if (reg.test(attr)) {
                if (!isNaN(value)) {
                    // 传递的是纯数字,我们需要加单位
                    value += 'px';
                }
            }
            curEle['style'][attr] = value;
        }

        setCss(box, "backgroundColor", "red");
        setCss(box, "padding", "20");
        setCss(box, "padding", "20px");
        setCss(box, "padding", "20px 30px");
        setCss(box, "padding", "20 30");//这个这里没做处理不会执行
        setCss(box, "paddingleft", "20");//浏览器不能识别小写 最终不执行curEle['style'][attr] = value;
        setCss(box, "paddingLeft", "150");

    </script>
</body>

</html>

方法一:不符合加单位的css样式(传递的是纯数字,大部分css样式都需要加单位,但是某些特殊的样式是不需要加单位的)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>css盒子模型</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .box {
            width: 200px;
            height: 200px;
            background: lightcoral;
            opacity: 0.2;
            filter: alpha(opacity=20);
            /* ie低版本浏览器不支持opacity,需要使用滤镜出来*/
        }
    </style>
</head>

<body>
    <div class="box" id="box" style="color: #000;">123456</div>
    <script>

        function setCss(curEle,attr,value) {
            // 如果传递的是opacity,我们需要兼容处理透明度
            if (attr === "opacity") {
                curEle.style.opacity = value;
                curEle.style.filter = "alpha(opacity=" + value * 100 + ")";
            }

            // 我们也可以反思路验证:传递的是纯数字,大部分都需要加单位,但是某些特殊的样式是不需要加单位的
            // 不需要加单位:zIndex、zoom、lineHeight...
            !isNaN(value) && !/^(zIndex|zoom|lineHeight|fontWeight)$/i.test(value) ? value += "px" : null; 
            curEle['style'][attr] = value;
        }
        setCss(box,"padding","20");
        setCss(box,"opacity","0.1");

    </script>
</body>

</html>

11、setGroupCss实现批量设置元素的样式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>css盒子模型</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .box {
            width: 200px;
            height: 200px;
            background: lightcoral;
            opacity: 0.2;
            filter: alpha(opacity=20);
            /* ie低版本浏览器不支持opacity,需要使用滤镜出来*/
        }
    </style>
</head>

<body>
    <div class="box" id="box" style="color: #000;">123456</div>
    <script>
        function setCss(curEle,attr,value) {
            // 如果传递的是opacity,我们需要兼容处理透明度
            if (attr === "opacity") {
                curEle.style.opacity = value;
                curEle.style.filter = "alpha(opacity=" + value * 100 + ")";
            }
            // 我们也可以反思路验证:传递的是纯数字,大部分都需要加单位,但是某些特殊的样式是不需要加单位的
            // 不需要加单位:zIndex、zoom、lineHeight...
            !isNaN(value) && !/^(zIndex|zoom|lineHeight|fontWeight)$/i.test(value) ? value += "px" : null; 
            curEle['style'][attr] = value;
        }

        // --------批量给当前元素设置css样式(在setCss上的扩充)----------------------------------------------------
        function setGroupCss(curEle,options) {
            //检测options是不是对象,typeof不行,typeof检测正则,对象,数组和null都是对象
            if(Object.prototype.toString.call(options) !== '[object Object]') return;
            for (var attr in options) {
                if (options.hasOwnProperty(attr)) {
                        setCss(curEle,attr,options[attr]);
                }
            }
        }

        setGroupCss(box, {
            width:300,
            height:300,
            padding:50,
            opacity:0.5,
            backgroundColor:"orange"
        });

    </script>
</body>
</html>

12、封装css方法、实现设置、批量设置、获取元素的样式(封装css函数的编程思想很重要)

var utils = (function () {

    // =>toArray:把类数组转换为数组(兼容所有浏览器的)
    var toArray = function toArray(classAry) {
        var ary = [];
        try {
            ary = Array.prototype.slice.call(classAry);
        } catch (e) {
            for (var i = 0; i < classAry.length; i++) {
                ary[ary.length] = classAry[i];
            }
        }
        return ary;
    };

    // =>toJSON:把JSON格式的字符串转换为JSON格式的对象
    var toJSON = function toJSON(str) {
        //JSON.parse()不兼容的原因是因为window下没有JSON这个属性
        return "JSON" in window ? JSON.parse(str) : eval('('+ str +')');
    };

    // =>getCss:获取当前元素的某一个样式属性值
    var getCss = function (curEle, attr) {
        var value = null,
            reg = null;
        if (window.getComputedStyle) {
            value = window.getComputedStyle(curEle,null)[attr];
        }else{
            if (attr === 'opacity') {
                value = curEle.currentStyle['filter'];
                reg = /^alpha\(opacity=(.+)\)$/i;
                value = reg.test(value)?reg.exec(value)[1] / 100 : 1;
            } else {
                value = curEle.currentStyle[attr];
            }
            value = curEle.currentStyle[attr];//兼容ie6~8
        }
        reg = /^-?\d+(\.\d+)?(px|pt|rem|em)?$/i;
        reg.test(value) ? value = parseFloat(value) : null;
        return value;
    };

    // =>setCss:给当前元素的某一个样式属性设置值
    var setCss = function (curEle, attr, value) {
        if (attr === 'opacity') {
            curEle['style']['opacity'] = value;
            curEle['style']['filter'] = 'alpha(opacity='+ value * 100 +')';
            return;
        }
        !isNaN(value) && !/^(zIndex|zoom|lineHeight|fontWeight)/i.test(attr) ? value += "px" : null;
        curEle['style'][attr] = value;
    };

    // =>setGroupCss:给当前元素批量设置样式属性值
    var setGroupCss = function (curEle, options) {
        // 调用object基类原型上的toString方法实现数据类型检测,检测数据对象
        // ({}).toString.call(options) <=> Object.prototype.toString.call(options)
        if(({}).toString.call(options) !== '[object Object]') return;
            for (var attr in options) {
                if (options.hasOwnProperty(attr)) {
                    setCss(curEle, sttr, options[attr]);
                }
            }
    };

    // =>css:集成设置样式、获取样式、批量设置样式于一身的综合方法 类似jq实现css原理
    var css = function () {
        var len = arguments.length,
            type = Object.prototype.toString.call(arguments[1]),
            fn = getCss;
        
        // if (len >= 3) {
        //     //=>设置样式
        //     // setCss(arguments[0],arguments[1],arguments[2]);把类数组的每一项值都传递给setCss()
        //     setCss.apply(this,arguments);
        //     return;
        // }
        // if (len === 2 && Object.prototype.toString.call(arguments[1])==='[object Object]') {
        //     // =>批量设置样式
        //     setGroupCss.apply(this, arguments);
        //     return;
        // }
        // return getCss.apply(this, arguments);

        // 优化以上方法
        len >=3 ? fn = setCss : (len === 2 && type ==='[object Object]' ? fn = setGroupCss : null);
        return fn.apply(this, arguments)
    };

    return {
        toArray: toArray,
        toJSON: toJSON,
        css: css 
    }

})();

// utils.css(box,'padding')//=>获取样式
// utils.css(box,'padding','20')//=>设置样式
// utils.css(box,{
//     padding:30,
//     margin:20
// })//=>批量设置样式

13、父级参照物及盒子的偏移量

offsetParent:父级参照物(父级参照物不等价于父级元素:父级参照物和它的父级元素没有直接的关系)

  • 父级参照物:同一个平面中最外层的容器是所有里层盒子的父级参照物

  • 默认情况下:一个页面中所有元素的父级参照物都是body(而body的父级参照物是null)

  • 当我们给元素设置定位之后,会改变元素的父级参照物(因为设置定位会让元素脱离平面(脱离文档流))

offsetLeft * offsetTop

  • 当前元素的外边框 距离 父级参照物的内边框 的偏移量(左偏移/上偏移)

  • 标准的ie8浏览器有特殊性:它的偏移量是从 当前元素外边框~父级参照物的外边框

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>父级参照物及盒子的偏移量</title>
    <style>
       *{margin: 0; padding: 0;}
       html,body{
           height: 100%;
           overflow: hidden;
       }
       .outer{
           margin: 50px auto;
           width: 260px;
           height: 260px;
           border: 20px solid green;
       }
       .inner{
           margin: 20px auto;
           width: 80px;
           height: 80px;
           border: 20px solid red;
       }
       .center{
           margin: 10px auto;
           width: 20px;
           height: 20px;
           border: 20px solid blue;
       }
    </style>
</head>
<body>
    <div class="outer" id="outer">
        <div class="inner" id="inner">
            <div class="center" id="center"></div>
        </div>
    </div>
    <script src="utils.js""></script>
    <script>

        utils.css(inner,"position","relative")
        console.log(center.offsetParent);
        console.log(inner.offsetParent);
        console.log(outer.offsetParent);

    </script>
</body>
</html>

14、获取当前元素距离BODY的偏移量(offset方法的封装)

获取页面中任何一个元素距离body的左偏移和上偏移(它的父级参照物是谁不一定)

思路:

1、首先获取自己的偏移量 以及自己的父级参照物

2、如果自己的父级参照物不是body,我们加上父级参照物的边框和偏移量 ... 一直加到某一次找到的父级参照物是body为止

3、标准ie8浏览器不需要加边框(偏移量已经包含父级参照物的边框了)

console.log(navigator.userAgent);//获取浏览器的版本信息

/MSIE 8/i.test(navigator.userAgent);//判断是不是ie8

没有getComputerStyle这个属性说明是ie6~8

utils.js

var utils = (function () {

    // =>toArray:把类数组转换为数组(兼容所有浏览器的)
    var toArray = function toArray(classAry) {
        var ary = [];
        try {
            ary = Array.prototype.slice.call(classAry);
        } catch (e) {
            for (var i = 0; i < classAry.length; i++) {
                ary[ary.length] = classAry[i];
            }
        }
        return ary;
    };

    // =>toJSON:把JSON格式的字符串转换为JSON格式的对象
    var toJSON = function toJSON(str) {
        //JSON.parse()不兼容的原因是因为window下没有JSON这个属性
        return "JSON" in window ? JSON.parse(str) : eval('('+ str +')');
    };

    // =>getCss:获取当前元素的某一个样式属性值
    var getCss = function (curEle, attr) {
        var value = null,
            reg = null;
        if (window.getComputedStyle) {
            value = window.getComputedStyle(curEle,null)[attr];
        }else{
            if (attr === 'opacity') {
                value = curEle.currentStyle['filter'];
                reg = /^alpha\(opacity=(.+)\)$/i;
                value = reg.test(value)?reg.exec(value)[1] / 100 : 1;
            } else {
                value = curEle.currentStyle[attr];
            }
            value = curEle.currentStyle[attr];//兼容ie6~8
        }
        reg = /^-?\d+(\.\d+)?(px|pt|rem|em)?$/i;
        reg.test(value) ? value = parseFloat(value) : null;
        return value;
    };

    // =>setCss:给当前元素的某一个样式属性设置值
    var setCss = function (curEle, attr, value) {
        if (attr === 'opacity') {
            curEle['style']['opacity'] = value;
            curEle['style']['filter'] = 'alpha(opacity='+ value * 100 +')';
            return;
        }
        !isNaN(value) && !/^(zIndex|zoom|lineHeight|fontWeight)/i.test(attr) ? value += "px" : null;
        curEle['style'][attr] = value;
    };

    // =>setGroupCss:给当前元素批量设置样式属性值
    var setGroupCss = function (curEle, options) {
        // 调用object基类原型上的toString方法实现数据类型检测,检测数据对象
        // ({}).toString.call(options) <=> Object.prototype.toString.call(options)
        if(({}).toString.call(options) !== '[object Object]') return;
            for (var attr in options) {
                if (options.hasOwnProperty(attr)) {
                    setCss(curEle, sttr, options[attr]);
                }
            }
    };

    // =>css:集成设置样式、获取样式、批量设置样式于一身的综合方法 类似jq实现css原理
    var css = function () {
        var len = arguments.length,
            type = Object.prototype.toString.call(arguments[1]),
            fn = getCss;
        
        // if (len >= 3) {
        //     //=>设置样式
        //     // setCss(arguments[0],arguments[1],arguments[2]);把类数组的每一项值都传递给setCss()
        //     setCss.apply(this,arguments);
        //     return;
        // }
        // if (len === 2 && Object.prototype.toString.call(arguments[1])==='[object Object]') {
        //     // =>批量设置样式
        //     setGroupCss.apply(this, arguments);
        //     return;
        // }
        // return getCss.apply(this, arguments);

        // 优化以上方法
        len >=3 ? fn = setCss : (len === 2 && type ==='[object Object]' ? fn = setGroupCss : null);
        return fn.apply(this, arguments)
    };

    // =>offset:获取当前元素距离body的偏移量,包括左偏移和上偏移
    var offset = function (curEle) {
        var l = curEle.offsetLeft,//获取自己本身左偏移量
            t = curEle.offsetTop,//获取自己本身上偏移量
            p = curEle.offsetParent;//获取自己的父级参照物
            // console.log(document.body.tagName);//=> 'BODY'
        while (p.tagName !== 'BODY') {//父元素不是body继续找当前父级参照物的父级参照物进行累加
            if(!/MSIE 8/i.test(navigator.userAgent)){//标准ie8浏览器不需要加边框
                l += p.clientLeft;//父级参照物的左边框
                t += p.clientTop;//父级参照物的上边框
            }
            l += p.offsetLeft;//父级参照物的左偏移
            t += p.offsetTop;//父级参照物的上偏移
            p = p.offsetParent;//获取当前父级参照物的父级参照物
        }
        return {top: t, left: l};
    };

    return {
        toArray: toArray,
        toJSON: toJSON,
        css: css,
        offset: offset
    }

})();

// utils.css(box,'padding')//=>获取样式
// utils.css(box,'padding','20')//=>设置样式
// utils.css(box,{
//     padding:30,
//     margin:20
// })//=>批量设置样式

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>父级参照物及盒子的偏移量</title>
    <style>
       *{margin: 0; padding: 0;}
       html,body{
           height: 100%;
           overflow: hidden;
       }
       .outer{
           margin: 50px auto;
           width: 260px;
           height: 260px;
           border: 20px solid green;
       }
       .inner{
           margin: 20px auto;
           width: 80px;
           height: 80px;
           border: 20px solid red;
       }
       .center{
           margin: 10px auto;
           width: 20px;
           height: 20px;
           border: 20px solid blue;
       }
    </style>
</head>
<body>
    <div class="outer" id="outer">
        <div class="inner" id="inner">
            <div class="center" id="center"></div>
        </div>
    </div>
    <script src="utils.js""></script>
    <script>

        // utils.css(inner,"position","relative")
        // console.log(center.offsetParent);
        // console.log(inner.offsetParent);
        // console.log(outer.offsetParent);


        var obj = utils.offset(center),
            t = obj.top,
            l = obj.left;
        console.log(t, l);

    </script>
</body>
</html>

15、scrollLeft和scrollTop详细解读

scrollLeft:横向滚动条卷去的宽度

scrollTop:竖向滚动条卷去的高度

console.log(document.documentElement.scrollTop || document.body.scrollTop);

存在最大值和最小值

最小值:0

最大值:scrollHeight - clientHeight 页面真实高度减去一屏幕高度

前面讲的js盒子模型属性都是‘只读属性’只能通过属性获取值,不可以修改属性的值(修改也不生效)

但是这两个属性是可读写属性:不仅仅可以获取值,还可以修改它的值

document.documentElement.scrollTo = 0 || document.body.scrollTop = 0;

utils.js

var utils = (function () {

    // =>toArray:把类数组转换为数组(兼容所有浏览器的)
    var toArray = function toArray(classAry) {
        var ary = [];
        try {
            ary = Array.prototype.slice.call(classAry);
        } catch (e) {
            for (var i = 0; i < classAry.length; i++) {
                ary[ary.length] = classAry[i];
            }
        }
        return ary;
    };

    // =>toJSON:把JSON格式的字符串转换为JSON格式的对象
    var toJSON = function toJSON(str) {
        //JSON.parse()不兼容的原因是因为window下没有JSON这个属性
        return "JSON" in window ? JSON.parse(str) : eval('('+ str +')');
    };

    // =>getCss:获取当前元素的某一个样式属性值
    var getCss = function (curEle, attr) {
        var value = null,
            reg = null;
        if (window.getComputedStyle) {
            value = window.getComputedStyle(curEle,null)[attr];
        }else{
            if (attr === 'opacity') {
                value = curEle.currentStyle['filter'];
                reg = /^alpha\(opacity=(.+)\)$/i;
                value = reg.test(value)?reg.exec(value)[1] / 100 : 1;
            } else {
                value = curEle.currentStyle[attr];
            }
            value = curEle.currentStyle[attr];//兼容ie6~8
        }
        reg = /^-?\d+(\.\d+)?(px|pt|rem|em)?$/i;
        reg.test(value) ? value = parseFloat(value) : null;
        return value;
    };

    // =>setCss:给当前元素的某一个样式属性设置值
    var setCss = function (curEle, attr, value) {
        if (attr === 'opacity') {
            curEle['style']['opacity'] = value;
            curEle['style']['filter'] = 'alpha(opacity='+ value * 100 +')';
            return;
        }
        !isNaN(value) && !/^(zIndex|zoom|lineHeight|fontWeight)/i.test(attr) ? value += "px" : null;
        curEle['style'][attr] = value;
    };

    // =>setGroupCss:给当前元素批量设置样式属性值
    var setGroupCss = function (curEle, options) {
        // 调用object基类原型上的toString方法实现数据类型检测,检测数据对象
        // ({}).toString.call(options) <=> Object.prototype.toString.call(options)
        if(({}).toString.call(options) !== '[object Object]') return;
            for (var attr in options) {
                if (options.hasOwnProperty(attr)) {
                    setCss(curEle, sttr, options[attr]);
                }
            }
    };

    // =>css:集成设置样式、获取样式、批量设置样式于一身的综合方法 类似jq实现css原理
    var css = function () {
        var len = arguments.length,
            type = Object.prototype.toString.call(arguments[1]),
            fn = getCss;
        
        // if (len >= 3) {
        //     //=>设置样式
        //     // setCss(arguments[0],arguments[1],arguments[2]);把类数组的每一项值都传递给setCss()
        //     setCss.apply(this,arguments);
        //     return;
        // }
        // if (len === 2 && Object.prototype.toString.call(arguments[1])==='[object Object]') {
        //     // =>批量设置样式
        //     setGroupCss.apply(this, arguments);
        //     return;
        // }
        // return getCss.apply(this, arguments);

        // 优化以上方法
        len >=3 ? fn = setCss : (len === 2 && type ==='[object Object]' ? fn = setGroupCss : null);
        return fn.apply(this, arguments)
    };

    // =>offset:获取当前元素距离body的偏移量,包括左偏移和上偏移
    var offset = function (curEle) {
        var l = curEle.offsetLeft,//获取自己本身左偏移量
            t = curEle.offsetTop,//获取自己本身上偏移量
            p = curEle.offsetParent;//获取自己的父级参照物
            // console.log(document.body.tagName);//=> 'BODY'
        while (p.tagName !== 'BODY') {//父元素不是body继续找当前父级参照物的父级参照物进行累加
            if(!/MSIE 8/i.test(navigator.userAgent)){//标准ie8浏览器不需要加边框
                l += p.clientLeft;//父级参照物的左边框
                t += p.clientTop;//父级参照物的上边框
            }
            l += p.offsetLeft;//父级参照物的左偏移
            t += p.offsetTop;//父级参照物的上偏移
            p = p.offsetParent;//获取当前父级参照物的父级参照物
        }
        return {top: t, left: l};
    };

    // =>winBox:操作有关于浏览器的js盒子模型属性,处理了兼容性
    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];
    };
    // winBox('clientHeight')//获取属性值
    // winBox('scrollTop',0)//设置属性值

    return {
        toArray: toArray,
        toJSON: toJSON,
        css: css,
        offset: offset,
        winBox: winBox
    }

})();

// utils.css(box,'padding')//=>获取样式
// utils.css(box,'padding','20')//=>设置样式
// utils.css(box,{
//     padding:30,
//     margin:20
// })//=>批量设置样式

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>scrollLeft和scrollTop详细解读</title>
    <style>
       *{margin: 0; padding: 0;}
      html,body{
          height: 1000%;
          /* css3新增加的;背景颜色的线型渐变 */
          background: -webkit-linear-gradient(top left,lightcoral,lightblue,
          lightgreen,lightcyan,lightgoldenrodyellow);
      }
    </style>
</head>
<body>

    <script src="utils.js"></script>
    <script>
        console.log(utils.winBox('scrollTop'));//获取属性值
        utils.winBox('scrollTop',3000);//设置属性值
    </script>
</body>
</html>

16、综合案列之回到顶部

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>回到顶部</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        html,
        body {
            height: 1000%;
            /* css3新增加的;背景颜色的线型渐变 */
            background: -webkit-linear-gradient(top left, lightcoral, lightblue,
                lightgreen, lightcyan, lightgoldenrodyellow);
        }

        .link {
            display: none;
            /*开始隐藏,当浏览器卷去的高度超过一屏幕的时候再让这个按钮展示 */
            position: fixed;
            right: 20px;
            bottom: 20px;
            width: 50px;
            height: 50px;
            line-height: 50px;
            text-align: center;
            background: #999;
            color: #fff;
            border-radius: 5px;
        }
    </style>
</head>

<body>
    <a href="javascript:;" class="link" id="link">顶部</a>
    <script src="utils.js"></script>
    <script>
        // winow.onscroll:浏览器滚动条滚动事件(只要滚动条滚动了就会触发这个事件)
        // 1、数遍滚轮控制  或者  手动拖动滚动条
        // 2、键盘按键控制
        // 3、使用js代码控制
        // ...
        // 不管什么方式,只要滚动条动了就会触发这个事件

        ~function () {
            var link = document.getElementById('link');

            window.onscroll = function () {
                // 获取当前卷去的高度和一屏幕高度
                var curTop = utils.winBox('scrollTop'),
                    curHeight = utils.winBox('clientHeight');
                // 已经卷去的高度>=一屏幕的高度的时候,展示回到顶部按钮,否则隐藏按钮即可
                utils.css(link, 'display', curTop >= curHeight ? 'block' : 'none');
            };

            link.onclick = function () {
                // 让浏览器的scrolltop设置为零
                utils.winBox('scrollTop', 0);
            };
        }()
    </script>
</body>
</html>