JS盒子模型、圖片懶載入、DOM庫封裝 — 1、JS盒子模型及常用操作樣式方法的封裝...

weixin_34249678發表於2018-09-28

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>
複製程式碼

相關文章