JavaScript 進階知識 - 特效篇(二)

9. 三大系列

本篇一開始咱們已經學了三大系列中的 offset系列,三大系列分別是 offset系列、 scroll系列、 client系列。學習這些有什麼用呢?在後面的特效案例中,會大量的使用到獲取元素的寬度、獲取元素內部的寬度、獲取元素距離頂部的距離等。這時候就須要用到三大系列,下面爲你們一一講解三大系列的用法。

9.1 offset 系列

第一章已經講過了,詳見第一章。

9.2 scroll 系列

scroll 是用來獲取盒子內容的大小和位置。 scroll家族有: scrollWidthscrollHeightscrollLeftscrollTop

一、onscroll 事件html

前面DOM的時候,咱們知道了觸發事件,這裏講下onscroll事件。

對於有滾動條的盒子,可使用onscroll註冊滾動事件,每滾動一像素,就會觸發該事件。ajax

示例代碼: [31-scroll系列-onscroll事件.html]正則表達式

<!-- 樣式部分 -->
<style>
    #box {
        width: 300px;
        height: 300px;
        border: 2px solid salmon;
        margin: 100px auto;
        /* 當內容超出盒子大小的時候 自動生成滾動條 */
        overflow: auto;
    }
</style>

<!-- html 部分 -->
<div id="box">
    我是內容我是內容我是內容我是內容我是內容我是內容我是內容
                            ...
                            ...
                            ...
    我是內容我是內容我是內容我是內容我是內容我是內容我是內容
</div>

<!-- js 部分 -->
<script>
    var box = document.getElementById('box');
    box.onscroll = function() {
        console.log("滾了!滾了");
    }
</script>

效果圖:編程

image

二、scrollWidth 和 scrollHeightsegmentfault

scrollWidthscrollHeight是盒子內容的真實的寬度和高度。與和盒子大小無關,僅僅與盒子的內容有關係,不包括 bordermargin,包括 padding
scrollWidth = padding + width;

// 若是盒子裏面的內容超出盒子高度的時候,這裏的scrollHeight獲取的就是內容的高度了
scrollHeight = padding + height;

示例代碼: [32-scroll系列-scrollWidth&scrollHeight.html]數組

<!-- 樣式部分 -->
<style>
    #box {
        width: 100px;
        height: 100px;
        border: 10px solid salmon;
        margin: 50px;
        padding: 10px;
    }
</style>

<!-- html 部分 -->
<div id="box">
    楊柳青青江水平,聞郎江上踏歌聲。東邊日出西邊雨,道是無晴卻有晴。
    楊柳青青江水平,聞郎江上踏歌聲。東邊日出西邊雨,道是無晴卻有晴。
</div>

<!-- js 部分 -->
<script>
    var box = document.getElementById('box');
    console.log(box.scrollWidth);    // 120
    console.log(box.scrollHeight);   // 241 獲取的是內容的高度
</script>

效果圖: 瀏覽器

image

若是盒子裏面的內容超出盒子高度的時候,這裏的scrollHeight獲取的就是內容的高度了app

注意:框架

  • 在現代高版本瀏覽器中,scrollHeight,在內容沒有超度盒子的狀況下,獲取到的高度是height+padding
  • 可是在IE8如下的時候,即便內容沒有超出盒子,獲取到的高度也是內容的高度。這裏就不演示了,scrollHeight不多用到。

三、scrollTop 和 scrollLeftless

scrollTop是盒子內容被滾動條捲去的頭部的高度。 scrollLeft是盒子內容被滾動條捲去的左側的寬度。一般來講, scroll系列用的最多的地方就是用來獲取頁面被捲去的寬度和高度,很是的經常使用。

scrollTopscrollLeft存在兼容性

示例代碼: [33-scroll系列-scrollTop&scrollLeft.html]

<!-- 樣式部分 -->
<style>
    body {
        height: 5000px;
    }
</style>

<!-- js 部分 -->
<script>
    // 給頁面註冊滾動事件
    window.onscroll = function() {
        // 滾動條滾動一次,瀏覽器就會獲取一次被捲去的頭部的高度
        // 將高度賦值給title  
        // 獲取scrollTop的時候是有兼容性的:現代瀏覽器用的是 window.pageYOffset
        // IE678 用的是 document.documentElement.scrollTop
        document.title = window.pageYOffset || document.documentElement.scrollTop;
    }
</script>

效果圖:

image

完整版封裝函數: [34-scroll系列-scrollTop&scrollLeft兼容性封裝.html]

function getScroll() {
    // 返回的是一個對象,調用的時候 getScroll().top 獲取頁面被捲去的頭部的距離
    return {
        left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0,
        top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
    }
}

返回值是一個對象,須要得到捲去頭部的距離,只須要調用getScroll.top便可。

scroll 系列圖解:

image

示例代碼:固定導航欄 [ 35-scroll系列-固定導航欄.html ]

  • 經過offsetHeight獲取導航欄上部元素自身的高度,判斷scrollTop的高度大於等於上部元素高度的時候,說明scrollTop到導航欄的位置了;
  • 到達導航欄位置,給導航欄絕對定位在頁面頂部,同時由於固定定位,導航欄脫標,因此要得到導航欄下部的元素,將其margin-top設置爲導航欄的高度,將位置空出來;
  • scrollTop小於上部元素高度的時候,導航欄去掉固定定位,同時將下部元素的margin-top設置爲0

爲何一開始不直接拿導航欄到頂部的距離跟 scrollTop 比較呢?,由於導航欄固定定位以後位置就變了,恢復原來位置時的判斷就不生效了

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
        list-style: none;
    }
    html,
    body {
        width: 100%;
    }
    .header {
        height: 130px;
        background: #FBFBFB;
        font: 700 28px/130px serif;
        color: #666;
        text-align: center;
    }
    .nav {
        height: 60px;
        width: 100%;
        background: #B9E1DC;
        font: 700 24px/60px serif;
        color: #52524E;
        text-align: center;
    }
    ul {
        display: inline-block;
    }
    li {
        float: left;
        margin-left: 60px;
    }
    .content1,
    .content2,
    .content3 {
        height: 800px;
        background: #DFFCB5;
        font: 700 60px/800px serif;
        color: #52524E;
        text-align: center;
    }
    .content2 {
        background: #FFE1B6;
    }
    .content3 {
        background: #CDE3EB;
    }
    .fixed {
        position: fixed;
        top: 0;
        left: 0;
    }
</style>

<!-- html 部分 -->
<div class="header" id="header">
    頂部廣告欄
</div>
<div class="nav" id="nav">
    <ul>
        <li>HOME</li>
        <li>ABOUT</li>
        <li>SERVICES</li>
        <li>TEAM</li>
        <li>CONTACT</li>
    </ul>
</div>
<div class="content1" id="con">
    內容1
</div>
<div class="content2">
    內容2
</div>
<div class="content3">
    內容3
</div>

<!-- js 部分 -->
<script>
    var header = document.getElementById('header');
    var nav = document.getElementById('nav');
    var content = document.getElementById('con');

    // 封裝一個scrollTop兼容性函數
    function getScrollTop() {
        return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
    }

    // 給頁面註冊滾動事件
    window.onscroll = function() {
        // 判斷廣告欄header 與 滾動的scrollTop的值
        // 當scrollTop > header高度的時候 讓導航欄 nav 固定定位
        var scrollTop = getScrollTop();
        if (scrollTop >= header.offsetHeight) {
            // 樣式中有的類名這裏必定不要忘了加上去,不然就會被替換掉
            nav.className = "nav fixed";
            // 一旦標題欄設置了固定定位以後,就脫離標準流了,下面的內容就會頂上來,
            // 因此要手動給下面的內容添加一個margin-top,將導航欄的位置留下來
            content.style.marginTop = nav.offsetHeight + "px";
        } else {
            // 當scrollTop < header高度的時候 讓導航欄 nav 恢復到原來的位置
            // nav 取消固定定位,恢復到原來的位置,因此下面內容的margin-top也要去掉
            nav.className = "nav"; // 去掉固定定位的樣式,保留以前的樣式
            content.style.marginTop = 0;
        }
    };
</script>

效果圖:

image

示例代碼:兩側跟隨小廣告 [ 36-offset系列-兩側跟隨小廣告.html ]

  • 需求:屏幕滾動多少,兩側廣告緩動等距離
  • 將兩張圖片絕對定位在屏幕兩側的中間
  • 經過滾動事件,實時獲取scrollTop的值
  • 將獲取到的scrollTop的值,經過緩動動畫設置給兩側圖片(須要將以前top的高度加上去)
<!-- html 部分 -->
<img src="../image/兩側固定小廣告/advert.jpg" alt="" id="img1">
<img src="../image/兩側固定小廣告/advert.jpg" alt="" id="img2">
<div>
    內 容
    . . .
    . . .
    
</div>

<!-- js 部分-->
<script>
    window.onload = function() {
        var imgs = document.getElementsByTagName('img');
        window.onscroll = function() {
            // 獲取滾動條滾動距頂部的距離距離
            var scrollTop = getScrollTop();
            // 緩動跟隨
            animate(imgs[0], scrollTop + 300);
            animate(imgs[1], scrollTop + 300);

        };
        // scrollTop兼容性處理
        function getScrollTop() {
            return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
        }
        // 緩動動畫
        function animate(element, target) {
            clearInterval(element.timer);
            element.timer = setInterval(function() {
                var leader = element.offsetTop;
                var step = (target - leader) / 20;
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                leader += step;
                element.style.top = leader + 'px';
                if (Math.abs(target - leader) < Math.abs(step)) {
                    element.style.top = target + "px";
                    clearInterval(element.timer);
                }
            }, 15);
        }
    }
</script>

效果圖:

image

示例代碼:返回頂部 [ 37-offset系列-返回頂部.html ]

window.scrollTo(0, 0);讓滾動條回到 (0,0)位置。這是回到頂部的主要原理
  • 註冊滾動條滾動事件,實時獲取scrollTop的位置,判斷當它距離大於等於800的時候,讓回到頂部的按鈕,緩動的顯示出來。當scrollTop位置小於800的時候,讓回到頂部的按鈕,緩動的隱藏起來。
  • 給回到頂部按鈕註冊點擊事件,當點擊的時候,頁面緩動的回到頂部。這裏就要用到剛剛提到的知識點:window.scrollTo()
  • 從新建立一個緩動動畫框架,目標位置target就是scrollTo(0,target),因此,target的值爲0
  • 實現原理與最基本的緩動框架基本同樣,只是將設置的值改成:window.scrollTo(0,leader);此時的leader仍是一個未知數,leader其實就是當前滾動條的位置,因此,在滾動事件裏,只要將leader實時獲取滾動條位置便可。
<!-- 樣式部分 -->
<style>
    body {
        background: #FDFCE0
    }
    div {
        margin: 200px auto;
        text-align: center;
    }
    img {
        width: 50px;
        height: 50px;
        background: url(../image/返回頂部/top.png);
        cursor: pointer;
        position: fixed;
        right: 50px;
        opacity: 0;
        bottom: 50px;
    }
</style>

<!-- html 部分 -->
<div>
    ...
    內容
    ...
</div>

<img src="../image/返回頂部/top.png" alt="" id="top">

<!-- js 部分 -->
<script src="../js/slow-animate-styles.js"></script>
<script>
    var img = document.getElementsByTagName('img')[0];
    var scrollTop;
    window.onscroll = function() {
        scrollTop = getScrollTop();
        // 當滾動條位置大於等於800 的時候,讓回到頂部圖標緩動的顯示出來
        if (scrollTop >= 800) {
            slowAnimateStyles(img, {
                opacity: 100,

            });
            img.style.display = "block";
        } else {
            // 當位置小於800 的時候,回到頂部圖標緩動的隱藏起來
            slowAnimateStyles(img, {
                opacity: 0,
            }, function() {
                img.style.display = "none";
            });

            // 在這裏獲取leader的位置
            leader = getScrollTop();
        }
    };
    // 點擊img的時候,讓滾動條回到頂部,這裏有個知識點:window.scrollTo(0,0); 滾動條回到頂部
    // 須要單首創建一個緩動動畫
    var timer = null;
    var target = 0; // 目標位置爲0 即:window.scrollTo(0,target)
    var leader = 0; // 初始化leader
    img.onclick = function() {
        clearInterval(timer);
        timer = setInterval(function() {
            var step = (target - leader) / 10;
            step = step > 0 ? Math.ceil(step) : Math.floor(step);
            leader += step;
            // 此時的leader仍是一個未知數,咱們須要獲取到當前滾動條的位置,而後賦值給leader,
            // 而且這個位置應該是實時變化的,咱們只須要在上面的滾動事件裏設置下leader便可
            window.scrollTo(0, leader);
            if (leader === 0) {
                clearInterval(timer);
            }
        }, 15);
    }

    // scrollTop兼容性處理
    function getScrollTop() {
        return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
    }
</script>

效果圖:

image

示例代碼:樓層跳躍 [ 38-offset系列-樓層跳躍.html ]

  • 其實這裏的案例跟上面的返回頂部很相似,一樣的運用到的是window.scrollTo()
  • 頁面佈局,背景繼承body,和html100%,將背景的索引與左邊導航欄綁定
  • 建立一個緩動動畫框架,目標距離target,就是當前索引背景距離頂部的距離,leader就是滾動條此時的位置
<!-- html 部分 -->
<ul>
    <li>鞋子區域</li>
    <li>襪子區域</li>
    <li>褲子區域</li>
    <li>裙子區域</li>
    <li>帽子區域</li>
</ul>
<ol>
    <li>鞋子</li>
    <li>襪子</li>
    <li>褲子</li>
    <li>裙子</li>
    <li>帽子</li>
</ol>

<!-- js 部分 -->
<script>
    var colorArr = ["#B7F5DE", "#FFE9E3", "#CBF078", "#7CDFFF", "#F59292"];
    var ul = document.getElementsByTagName('ul')[0];
    var ol = document.getElementsByTagName('ol')[0];
    var ulLis = ul.getElementsByTagName('li');
    var olLis = ol.getElementsByTagName('li');
    var target = 0,
        leader = 0,
        timer = null;

    for (var i = 0; i < colorArr.length; i++) {
        // 動態設置背景顏色
        ulLis[i].style.background = colorArr[i];
        olLis[i].style.background = colorArr[i];
        // 將olLis屬性綁定索引值
        olLis[i].index = i;
        olLis[i].onclick = function() {
            // 點擊索引,獲取ul下的當前li距離頂部的距離
            target = ulLis[this.index].offsetTop;
            clearInterval(timer);
            timer = setInterval(function() {
                var step = (target - leader) / 10;
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                leader += step;
                // 滾動條y方向到達leader位置
                window.scrollTo(0, leader);
                // 判斷,清除定時器
                if (Math.abs(target - leader) <= Math.abs(step)) {
                    window.scrollTo(0, target);
                    clearInterval(timer);
                }
            }, 15);
        }
    }
    // 滾動事件,實時獲取滾動條的位置
    window.onscroll = function() {
        // 實時獲取leader的值
        leader = getScrollTop();
    }


    // scrollTop兼容性處理
    function getScrollTop() {
        return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
    }
</script>

效果圖:

image

9.3 client 系列

client家族用於獲取盒子可視區的大小。 client家族有 clientWidthclientHeightclientLeftclientTop

一、clientWidth 和 clientHeight

  • clientWidth :獲取網頁可視區域寬度 ;clientHeight :獲取網頁可視區域高度;
  • 調用者不一樣,意義不一樣:

    • 盒子調用,指盒子自己;
    • html/body調用:可視區域大小
  • 不包括bordermargin

圖解clientWidth和clientHeight:

image

clientWidth 和 clientHeight 兼容性封裝:

function getClient() {
    return {
        width: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth || 0,
        height: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight || 0
    };
}

onresize事件:

onresize事件會在窗口被調整大小的時候發生。
window.onresize = function(){
    //事件處理程序
}

示例代碼:模仿響應式佈局 [ 39-client系列-模擬響應式.html ]

// 頁面一進來的時候就執行一次,肯定瀏覽器可視區域的寬度
responsive();
// 瀏覽器窗口調整觸發事件
window.onresize = function() {
    responsive();
};

// 獲取瀏覽器寬度
function responsive() {
    var pageWidth = getClientWidth();
    if (pageWidth >= 960) {
        //說明是pc
        document.body.style.backgroundColor = "#B7F5DE";
    } else if (pageWidth >= 640) {
        //說明是平板
        document.body.style.backgroundColor = "#CBF078";
    } else {
        // 說明是手機
        document.body.style.backgroundColor = "#F59292";
    }

}

// clientWidth 兼容性處理
function getClientWidth() {
    return window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth || 0;
}

效果圖:

image

二、clientX 和 clientY

  • clientX:鼠標距離可視區域左側距離(event調用) event:事件對象,下面會講
  • clientY:鼠標距離可視區域上側距離(event調用)

事件對象的時候,單獨講解

三、clientTop 和 clientLeft

  • clientTop:盒子的上部border的寬度
  • clientleft:盒子的左部border的寬度

用的不多不多,基本不會用到

9.4 screen 系列

clientWidth 獲取的實際上是瀏覽器窗口的寬度,想要獲取用戶顯示的分辨率怎麼辦呢?

獲取用戶顯示器分辨率有專門的方法: window.screen.widthwindow.screen.Height

示例代碼:獲取顯示器分辨率 [ 40-screen系列-獲取顯示器分辨率.html ]

document.write("屏幕分辨率爲:" + window.screen.width + "*" + window.screen.height);

效果圖:

1280*720 分辨率的狀況下:

image

1920*1080 分辨率的狀況下:

image

9.5 三大系列的區別

圖解三大系列區別:

image

width 和 height:

  • clientWidth/clientHeight

    • clientWidth = width + padding;
    • clientHeight = height + padding;
  • offsetWidth/offsetHeight

    • offsetWidth = width + padding + border;
    • offsetHeight = heigth + padding + border;
  • scrollWidth/scrollHeight

    • scrollWidth = 內容寬度(不包含border);
    • scrollHeight = 內容高度(不包含border);

top 和 left:

  • offsetTop/offsetLeft

    • 調用者:任意元素。(盒子爲主)
    • 做用 :獲取距離父系盒子中帶有定位的距離。
  • scrollTop/scrollLeft:(盒子也能夠調用,必須有滾動條)

    • 調用者:document.body.scrollTop/.....(window)
    • 做用:瀏覽器沒法顯示的部分(被捲去的部分)。
  • clientY/clientX:(clientTop/clientLeft 值是border)

    • 調用者:event.clientX(event)
    • 做用:鼠標距離瀏覽器可視區域的距離(左、上)。

10. 事件對象

10.1 事件對象的概述

在觸發某個事件的時候,都會產生一個事件對象 Event,這個對象中包含全部與事件相關的一些信息,包括觸發事件的元素,事件的類型以及其餘與事件相關的信息。

好比:

  • 鼠標事件觸發時,事件對象中會包含鼠標的位置信息。
  • 鍵盤事件觸發時,事件對象中會包含按下的鍵相關的信息。

10.2 獲取事件對象

既然事件對象中存儲了這麼多的信息,咱們首先須要作的就是獲取到這個事件對象。獲取事件對象的時候,存在瀏覽器的兼容問題。

現代瀏覽器:

獲取事件對象很是的簡單,只須要在註冊事件的時候,指定一個形參便可。這個形參就是咱們想要獲取到的事件對象。

btn.onclick = function(event){
    // event就是事件對象,裏面包含了事件觸發時的一些信息。
    // 觸發事件的時候,事件是由瀏覽器調用,生成一個事件對象,裏面包含了一些信息,當成實參傳遞進來了。
    console.log(event);
}

IE678:

獲取事件對象則是另外一種方式,在事件裏面,經過window.event來獲取事件對象

btn.onclick = function(){
    // IE678經過window.event獲取事件對象
    // IE678瀏覽器在觸發的事件的時候,生成一個事件對象,可是呢,並無當成實參傳過來。會給window.event這個屬性。
    var event = window.event;
    console.log(event);
}

兼容性封裝: [ 41-事件對象Event兼容性.html ]

btn.onclick = function(event){
    //只要用到了事件對象,就要記得處理瀏覽器兼容性
    event = event || window.event;
}

10.3 事件對象的經常使用屬性

事件對象中有不少不少的屬性,可是不少屬性並不經常使用。咱們常常用到的是鼠標位置信息 和鍵盤碼 相關的信息。

打印event對象咱們能夠看到以下信息:

image

咱們能夠看到一個鼠標按下的時候,它的事件對象裏面有這麼多屬性,可是最經常使用的也就是鼠標位置信息和鍵盤碼相關的信息。

記錄了鼠標位置信息的相關屬性:

  • screenXscreenY:光標相對於屏幕左上角的水平位置與垂直位置。
  • clientXclientY:光標相對於可視區左上角的水平位置和垂直位置。
  • pageXpageY:光標相對於網頁(文檔document)左上角的水平位置與垂直位置(推薦使用)

[ 42-事件對象-鼠標三種獲取位置的屬性.html ]

document.onclick = function(e) {
    var e = e || window.event;
    //獲取鼠標的位置,相對的是可視區最左上角的點。(忽略滾動的距離)
    console.log("client(" + e.clientX + "," + e.clientY + ")");

    //獲取鼠標的位置,相對的頁面最左上角的位置 (計算滾動的距離)
    console.log("page(" + e.pageX + "," + e.pageY + ")");

    //獲取鼠標的位置,相對的是屏幕最左上角的那個點
    console.log("screen(" + e.screenX + "," + e.screenY + ")");
}

圖解:

image

記錄了鍵盤碼的屬性:

  • event.keyCode:鍵盤按下的那個鍵的鍵盤碼

10.4 pageX與pageY的兼容性

在鼠標事件中,記錄鼠標位置信息的屬性有不少,使用最多的仍是 pageXpageY這兩個屬性,可是 pageXpageY存在瀏覽器兼容性問題。

在現代瀏覽器中: 直接經過事件對象就能夠得到pageXpageY

document.onclick = function (event) {
    event = event || window.event;
    console.log(event.pageX+","+event.pageY);
}

在IE678中: 並無pageXpageY,可是咱們能夠經過scrollTop + clientY的方式進行計算來得到pageY

document.onclick = function (event) {
    event = event || window.event;
    // 在IE678中使用document.documentElement.scrollTop就能夠獲取到scrollTop的值
    alert(event.clientY + document.documentElement.scrollTop);
}

pageX與pageY的兼容性封裝:

function getPage(event) {
    return {
        //在IE678中使用document.documentElement.scrollLeft就能夠獲取到scrollLeft的值
        x:event.pageX || event.clientX + document.documentElement.scrollLeft,
        y:event.pageY || event.clientY + document.documentElement.scrollTop
    }
}

調用時:

getPage(event).x;
getPage(event).y;

示例代碼:兼容性封裝測試 [ 43-事件對象-pageX&PageY兼容性處理.html ]

<!-- 樣式部分 -->
<style>
    body {
        height: 5000px;
    }
</style>

<!-- js 部分 -->
<script>
    document.onclick = function(event) {
        event = event || window.event;
        alert("當前座標爲(" + getPage(event).x + "," + getPage(event).y + ")");
    }

    function getPage(e) {
        return {
            x: e.pageX || e.clientX + document.documentElement.scrollLeft,
            y: e.pageY || e.clientY + document.documentElement.scrollTop
        }
    }
</script>

10.5 案例:鼠標跟隨

  • 鼠標跟隨,指的就是,鼠標後面有一張圖片,會在頁面中一直跟隨鼠標
  • 經過事件對象的屬性,咱們知道了有三種方法獲取鼠標的位置信息,咱們只要把鼠標的位置,賦值給後面跟隨圖片的位置,就能夠實現圖片一直跟隨鼠標移動了
  • 咱們知道clientX/YscreenX/YpageX/Y,均可以獲取鼠標的位置,可是各有優劣,咱們先使用pageX/Y獲取,上面咱們已經處理pageX/Y的兼容性了,因此這裏直接使用
  • 咱們只需將得到的鼠標位置,賦值給定位後的圖片,將圖片絕對定位,再給頁面註冊鼠標移動事件,圖片就會一直跟着鼠標移動。

[ 44-事件對象-跟隨鼠標移動.html ]

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
    }
    body {
        height: 5000px;
    }
    #follow {
        position: absolute;
        width: 160px;
    }
</style>

<!-- html 部分 -->
<img src="../image/鼠標跟隨/2.gif" alt="" id="follow">

<!-- js 部分 -->
<script>
var follow = document.getElementById("follow");
//給document註冊一個鼠標移動事件
document.onmousemove = function(e) {
    e = e || window.event;
    console.log(e.clientX + "   " + e.clientY);
    follow.style.left = getPage(e).x + "px";
    follow.style.top = getPage(e).y + "px";
}

function getPage(e) {
    return {
        x: e.pageX || e.clientX + document.documentElement.scrollLeft,
        y: e.pageY || e.clientY + document.documentElement.scrollTop,
    }
}

效果圖:

image

經過效果圖咱們能夠發現,絕對定位時,當鼠標移到最右邊的時候,圖片會撐大瀏覽器自動生成滾動條,那怎麼辦呢?

鼠標跟隨優化版 [ 45-事件對象-跟隨鼠標移動優化版.html ]
只要將圖片固定定位,而後經過clientX/Y,獲取可視區的位置,將它的值賦值給圖片就好了

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
    }
    body {
        height: 5000px;
    }
    #follow {
        position: fixed;
        width: 160px;
    }
</style>

<!-- html 部分 -->
<img src="../image/鼠標跟隨/2.gif" alt="" id="follow">

<!-- js 部分 -->
<script>
    var follow = document.getElementById("follow");
    //給document註冊一個鼠標移動事件
    document.onmousemove = function(e) {
        e = e || window.event;
        console.log(e.clientX + "   " + e.clientY);
        follow.style.left = e.clientX + "px";
        follow.style.top = e.clientY + "px";
    }
</script>

效果圖:

image

10.6 案例:拖拽效果

一、獲取鼠標在盒子中的位置

當在盒子裏麪點擊鼠標的時候,怎麼得到這個鼠標在盒子中的位置呢?

沒有直接的方法可以獲取,可是咱們能夠經過:

獲取鼠標的位置 - 盒子距離頂部和左邊的距離 = 鼠標在盒子裏面的距離

[ 46-事件對象-獲取鼠標在盒子中的位置.html ]

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
    }
    #box {
        width: 150px;
        height: 150px;
        background: #b7f5de;
        margin: 200px;
    }
</style>

<!-- html 部分 -->
<div id="box"></div>

<!-- js 部分 -->
<script>
    var box = document.getElementById('box');

    box.onclick = function(e) {
        var e = e || window.event;
        // 沒有直接的方法可以獲取,可是咱們能夠經過:
        // 獲取鼠標的位置 - 盒子距離頂部和左邊的距離 = 鼠標在盒子裏面的距離
        var x = getPage(e).x - box.offsetLeft;
        var y = getPage(e).y - box.offsetTop;
        console.log("當前位置座標:(" + x + "," + y + ")");
    }

    function getPage(e) {
        return {
            x: e.pageX || e.clientX + document.documentElement.scrollLeft,
            y: e.pageY || e.clientY + document.documentElement.scrollTop
        }
    }
</script>

效果圖:

image

二、拖拽效果

拖拽效果在網頁很常見,好比一個註冊框,彈出來的時候,你能夠拖動它的位置。

新事件:

  • onmousedown :當鼠標按下的時候觸發
  • onmouseup :當鼠標彈起的時候觸發

實現思路:

  • 給盒子註冊按下鼠標(onmousedown)事件,獲取鼠標在盒子裏的位置;
  • 而後在裏面註冊頁面移動鼠標(onmousemove)事件,鼠標移動時,將此時的鼠標距瀏覽器的距離減去鼠標在盒子中的距離後,賦值給盒子的top/left
  • 給頁面註冊鬆開鼠標(onmouseup)事件,盒子應該停在那個位置,因此清除移動事件。

[ 47-事件對象-拖拽效果.html ]

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
    }
    body {
        height: 4000px;
    }
    #box {
        width: 150px;
        height: 150px;
        background: #B7F5DE;
        position: absolute;
    }
</style>

<!-- html 部分 -->
<div id="box"></div>

<!-- js 部分 -->
<script>
    var box = document.getElementById('box');

    // 鼠標按下
    box.onmousedown = function(event) {
        event = event || window.event;
        // 記錄按下的鼠標的位置
        var x = getPage(event).x - box.offsetLeft;
        var y = getPage(event).y - box.offsetTop;
        // 按下的時候才觸發鼠標移動事件
        document.onmousemove = function(e) {
            // 鼠標點擊的時候應該減去鼠標按下時在盒子中的位置
            box.style.left = getPage(e).x - x + "px";
            box.style.top = getPage(e).y - y + "px";
        }
    }

    // 鼠標鬆開
    // 這裏爲何不給盒子註冊鼠標鬆開事件呢? 由於一旦有延遲,鼠標不在盒子上鬆開的時候,move事件就清除不掉了
    // 因此直接給頁面註冊鼠標鬆開事件,只要鼠標鬆開,就清除move事件
    document.onmouseup = function() {
        // 上面註冊的移動事件會一直觸發,因此在鼠標鬆開的時候,咱們應該將移動事件移除掉
        document.onmousemove = null;
    }

    // 獲取事件對象裏的pageX/Y屬性
    function getPage(e) {
        return {
            x: e.pageX || e.clientX + document.documentElement.scrollLeft,
            y: e.pageY || e.clientY + document.documentElement.scrollTop
        }
    }
</script>

效果圖:

image

注意:

  • 拖拽的時候,可能裏面會有文字,當移動的時候,不當心獲取文字焦點的時候,就不能清除鼠標移動事件了。

解決方法:
清除選中的文字

window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();

10.7 案例:放大鏡

放大鏡在開發中是一個很常見的特效,可是全部的放大鏡的實現效果都是同樣。

圖解放大鏡原理:

image

實現思路:

  • 當鼠標通過 smallBox 的時候,顯示 maskbigBox
  • 當鼠標離開 smallBox 的時候,隱藏 maskbigBox
  • 獲取鼠標在 smallBox 裏面的位置;
  • 得到鼠標在 smallBox 裏面的位置後,要減去 mask 一半的寬高,不然鼠標不在 mask 中間顯示;
  • 判斷x的值限定 mask 的位置 :

    • mask 在小盒子裏面可以移動最大的寬度和高度 0
    • mask 在小盒子裏面可以移動最大的寬度 = smallBox的寬度 - mask的寬度
  • 設定 mask 的位置;
  • 讓大圖片等比例的跟着動 :

    • bigImg 可以移動的距離 / mask 能移動的距離 = 大圖片移動的距離 / mask移動的距離

思路圖解:

image

示例代碼: [ 48-事件對象-放大鏡效果.html ]

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
    }
    #box {
        width: 350px;
        height: 350px;
        margin: 100px;
        border: 1px solid #ccc;
        position: relative;
    }
    #bigBox {
        width: 400px;
        height: 400px;
        position: absolute;
        top: 0;
        left: 360px;
        border: 1px solid #ccc;
        overflow: hidden;
        display: none;
    }
    .mask {
        width: 175px;
        height: 175px;
        background-image: url(../image/放大鏡/1.png);
        position: absolute;
        top: 1px;
        left: 1px;
        cursor: move;
        display: none;
    }
    #smallBox {
        position: relative;
    }
    #box img {
        vertical-align: top;
    }
    #bigBox img {
        position: absolute;
    }
</style>

<!-- html 部分 -->
<div id="box">
    <div id="smallBox">
        <img src="../image/放大鏡/img.jpg" width="350" alt="">
        <div id="mask" class="mask"></div>
    </div>
    <div id="bigBox">
        <img src="../image/放大鏡/img.jpg" width="800" id="bigImg" alt="" />
    </div>
</div>

<!-- js 部分 -->
<script>
    var box = document.getElementById('box');
    var smallBox = document.getElementById('smallBox');
    var bigBox = document.getElementById('bigBox');
    var mask = document.getElementById('mask');
    var bigImg = document.getElementById('bigImg');

    // 當鼠標 通過smallBox的時候,顯示 mask 和 bigBox
    smallBox.onmouseover = function() {
        mask.style.display = "block";
        bigBox.style.display = "block";
    };
    // 當鼠標 離開smallBox的時候,隱藏 mask 和 bigBox
    smallBox.onmouseout = function() {
        mask.style.display = "none";
        bigBox.style.display = "none";
    };
    // 鼠標在smallBox裏面移動,移動 mask 和 bigImg
    smallBox.onmousemove = function(e) {
        var e = e || window.event;
        // mask 跟着鼠標移動
        // 1- 獲取鼠標在smallBox裏面的位置
        var spaceX = getPage(e).x - box.offsetLeft;
        var spaceY = getPage(e).y - box.offsetTop;

        // 2- 得到鼠標在smallBox裏面的位置後,要減去mask一半的寬高,不然鼠標不在mask中間顯示
        var x = spaceX - mask.offsetWidth / 2;
        var y = spaceY - mask.offsetHeight / 2;

        // 3- 判斷x的值 限定 mask的位置 
        // mask 在小盒子裏面可以移動最大的寬度和高度 0 
        if (x <= 0) {
            x = 0;
        }
        if (y <= 0) {
            y = 0;
        }
        // mask 在小盒子裏面可以移動最大的寬度 = smallBox的寬度 - mask的寬度
        var maskMaxX = smallBox.offsetWidth - mask.offsetWidth;
        var maskMaxY = smallBox.offsetHeight - mask.offsetHeight;
        if (x >= maskMaxX) {
            x = maskMaxX;
        }
        if (y >= maskMaxY) {
            y = maskMaxY;
        }

        // 4- 設定mask的位置
        mask.style.left = x + "px";
        mask.style.top = y + "px";

        // 5- 讓大圖片等比例的跟着動   
        // bigImg 可以移動的距離 / mask 能移動的距離 = 大圖片移動的距離 / mask移動的距離
        // rate :比例
        var xRate = (bigImg.offsetWidth - bigBox.offsetWidth) / maskMaxX;
        bigImg.style.left = -xRate * x + "px";
        var yRate = (bigImg.offsetHeight - bigBox.offsetHeight) / maskMaxY;
        bigImg.style.top = -yRate * y + "px";
    };

    // pageX/Y 兼容性處理
    function getPage(e) {
        return {
            x: e.pageX || e.clientX + document.documentElement.scrollLeft,
            y: e.pageY || e.clientY + document.documentElement.scrollTop
        }
    }
</script>

效果圖:

image

11. 註冊事件

前面咱們已經知道了許多觸發事件的名稱,可是咱們只知道了一種註冊事件的方式,就是" on + 事件名稱",下面會爲你們再介紹一種註冊事件的方式: addEventListener

11.1 on + 事件名稱 方式

onclickonmouseover這種 on+ 事件名稱的方式註冊事件幾乎全部的瀏覽器都支持。

註冊事件:

box.onclick = function(){
    //事件處理程序    
}

移除事件:

box.onclick = null;

on + 事件名稱註冊事件的缺點:

同一個元素同一類型的事件,只能註冊一個,若是註冊了多個,會出現覆蓋問題。

document.onclick = function(){
    console.log("呵呵");
}

document.onclick = function(){
    console.log("哈哈");   // 最後打印的是 "哈哈",呵呵會被覆蓋掉
}

11.2 addEventListener 方式

現代瀏覽器支持的註冊事件的新方式,這種方式註冊的事件不會出現覆蓋問題。之後在手機端就用這種註冊事件的方式。

一、addEventListener 註冊事件的語法:

// add:添加   Event:事件   Listener:監聽器
// 三個參數:
//      1. type:事件類型  "click","mouseover"... 不要再加 on了
//      2. 函數:事件觸發的時候要執行的程序
//      3. useCapture(是否使用事件捕獲) :true & false 默認是false
document.addEventListener(type,function(){
    // 事件處理程序
},useCapture);

以前咱們說過window.onload,只能註冊一個就是這個緣由,由於"on +"註冊方式會覆蓋。因此若是真的須要執行兩個window.onload事件的時候,咱們就可使用addEventListener註冊:

window.addEventListener("load",function(){
    // 預加載函數 1
});

window.addEventListener("load",function(){
    // 預加載函數 2
});

示例代碼: [ 49-註冊事件-addEventListener.html ]

// 給頁面註冊點擊事件後,會同時打印 "呵呵呵","哈哈哈"
document.addEventListener("click", function() {
    console.log("呵呵呵");
});
document.addEventListener("click", function() {
    console.log("哈哈哈");
});

二、removeEventListener 移除事件的語法:

// remove:移除   Event:事件   Listener:監聽器
// 三個參數:
//      1. type:事件類型  "click","mouseover"
//      2. 函數名:要移除的那個函數
//      3. useCapture(是否使用事件捕獲) :true & false 默認是false
document.addEventListener(type,fn,useCapture);

注意:

要想一個事件可以被移除,在它註冊事件的時候,執行函數必需要有函數名,不能是匿名函數。由於移除事件的時候,就是移除的這個函數名。

示例代碼: [ 50-移除事件-removeEventListener.html ]

// 第二個點擊事件就被移除了
document.addEventListener("click", fn1);
document.addEventListener("click", fn2);

function fn1() {
    console.log("呵呵呵");
};

function fn2() {
    console.log("哈哈哈");
}

// 移除第二個點擊事件
document.removeEventListener("click", fn2);

三、IE678兼容性問題:

IE678不支持 addEventListenerremoveEventListen兩個方法,可是支持 attachEventdetachEvnet

attachEvent註冊事件的語法:

// attach :附上;繫上;貼上 
// 參數:
//     1. type:事件類型   須要加上on "onclick","onmouseenter"...
//     2. 函數fn:須要執行的那個事件函數
attachEvent(type, function(){
    // 事件處理程序
});

attach註冊時間的時候,事件類型要加上on,沒有爲何,IE就這樣

detachEvent的用法:

// detach :脫離
// 參數:
//     1. type:事件類型   須要加上on "onclick","onmouseenter"...
//     2. 函數名: 須要執行的那個事件函數名
detachEvent(type, fn);

示例代碼: [ 51-註冊事件-IE678方法.html ]

// IE678 下運行
document.attachEvent("onclick", fn1);
document.attachEvent("onclick", fn2);

function fn1() {
    alert("123");
};

function fn2() {
    alert("456");
};

// 移除第一個註冊事件
document.detachEvent("onclick", fn1);

四、兼容性處理:

註冊事件的新方式的解決了事件覆蓋的問題,可是存在瀏覽器兼容性問題,所以能夠進行兼容性封裝。
// 添加事件兼容性封裝
function addEvent(element, type, fn) {
    // 能力檢測
    if (element.addEventListener) {
        element.addEventListener(type, fn);
    } else if (element.attachEvent) {
        element.attachEvent("on" + type, fn);
    } else {
        //若是都不行,那就用on方式
        element["on" + type] = fn;
    }
};

//移除事件兼容性封裝
function removeEvent(element, type, fn) {
    if (element.removeEventListener) {
        element.removeEventListener(type, fn);
    } else if (element.detachEvent) {
        element.detachEvent("on" + type, fn);
    } else {
        element["on" + type] = null;
    }
}

示例代碼: [ 52-註冊事件-封裝兼容性.html ]

// 添加事件兼容性封裝
function addEvent(element, type, fn) {
    // 能力檢測
    if (element.addEventListener) {
        element.addEventListener(type, fn);
    } else if (element.attachEvent) {
        element.attachEvent("on" + type, fn);
    } else {
        //若是都不行,那就用on方式
        element["on" + type] = fn;
    }
};

//移除事件兼容性封裝
function removeEvent(element, type, fn) {
    if (element.removeEventListener) {
        element.removeEventListener(type, fn);
    } else if (element.detachEvent) {
        element.detachEvent("on" + type, fn);
    } else {
        element["on" + type] = null;
    }
}

function fn1() {
    alert("呵呵");
}

function fn2() {
    alert("哈哈");
}

addEvent(document, "click", fn1);
addEvent(document, "click", fn2);
removeEvent(document, "click", fn1);

12. 事件冒泡和事件捕獲

事件冒泡和事件捕獲其實能夠理解成同樣東西,就是當父級元素和子元素都具備點擊事件的時候,點擊觸發子級元素的時候父級元素也會被觸發。事件冒泡是 IE678在處理事件間機制的一種說法,它執行的順序是由內向外的,就是從子元素一直到 window。 事件捕獲是火狐在處理機制時的一種說法,它執行的順序是由外向內的,就是 window一直到子元素。

圖解事件冒泡和事件捕獲:

image

12.1 事件冒泡

當一個元素的事件被觸發時,一樣的事件將會在該元素的全部祖先元素中依次被觸發。這一過程被稱爲事件冒泡。

說白了就是:當父元素和子元素都設置了點擊事件的時候,觸發子盒子點擊事件的時候,父盒子的點擊事件也會被執行。

image

示例代碼: [ 53-事件冒泡.html ]

<!-- 樣式部分 -->
<style>
        #big-box {
            width: 500px;
            height: 500px;
            margin: 100px auto;
            vertical-align: middle;
            text-align: center;
            border: 1px solid transparent;
            background-color: aquamarine;
        }
        
        #box {
            width: 300px;
            height: 300px;
            margin-top: 100px;
            display: inline-block;
            background-color: darkorange;
        }
</style>

<!-- html 部分 -->
<div id="big-box">
    <div id="box"></div>
</div>

<!-- js 部分 -->
<script>
    var bigBox = document.getElementById('big-box');
    var box = document.getElementById('box'); 
    document.onclick = function() {
        document.body.style.background = "#000";
    }
    bigBox.onclick = function() {
        bigBox.style.background = "fuchsia";
    }
    box.onclick = function() {
        // 當咱們點擊box的時候,bigBox、body的點擊事件 也會被觸發
        box.style.background = "lightgreen";
    }
    </script>

效果圖:

image

咱們會發現,當點擊中間小盒子的時候,他的父級元素,只要有點擊事件的,都被觸發了,這就是事件冒泡。

12.2 阻止事件冒泡

正常狀況下,咱們確定不想,點擊子元素觸發事件的時候,父元素事件也跟着觸發,因此咱們就要知道一個知識點:阻止事件冒泡。在阻止事件冒泡中是存在兼容性的:

正常瀏覽器:

前面咱們知道了事件觸發的時候,會有一個事件對象,咱們只要給事件對象加上:stopPropagation方法便可。stopPropagation方法不只能夠阻止事件冒泡,還能夠阻止事件委託

element.onclick = function (e) {
    e = event || window.event;
    //stop :中止  propagation:傳播
    e.stopPropagation();
}

IE678瀏覽器:

ie是給事件對象的屬性cancelBubble賦值

element.onclick = function (e) {
    e = event || window.event;   
    e.cancelBubble = true;
}

兼容性處理:

// 能力檢測
element.onclick = function (e) {
    e = event || window.event;
    if(e.stopPropagation){
          e.stopPropagation();
    }else {
          e.cancelBubble = true;
    }
}

12.3 事件捕獲

事件捕獲( capture)是火狐瀏覽器提出來的, IE678不支持事件捕獲(基本上,咱們都是用事件冒泡)。事件的處理將從 DOM層次的根開始,而不是從觸發事件的目標元素開始,事件被從目標元素的全部祖先元素依次往下傳遞。

image

addEventListener第三個參數爲true時,表示事件捕獲:

element.addEventListener("click", function () {
    console.log("哈哈哈");
},true);

12.4 事件流

事件流就是事件的三個階段,首先發生的是捕獲階段,而後是目標階段,最後纔是冒泡階段,對於捕獲和冒泡,咱們只能干預其中的一個,一般來講,咱們可能會干預事件冒泡階段,而不去幹預事件捕獲階段。
  • 事件的捕獲階段
  • 事件的目標階段
  • 事件的冒泡階段

image

注意:

其實這三個階段在執行是都會發生,可是冒泡和捕獲只能執行一個,因此經過usecaptrue = false可讓捕獲階段執行可是不觸發。

12.5 鍵盤事件

對於鼠標事件,事件對象中有一系列的 XY記錄了鼠標的位置信息。而鍵盤事件中,事件對象有一個 event.keyCode屬性,記錄了按下去的鍵的鍵盤碼。 [ 54-鍵盤事件-鍵盤碼.html ]
document.onkeydown = function (e) {
    // 鍵盤按下的時候觸發的事件對象 
    console.log(e);
    // keyCode: 鍵盤碼
    console.log(e.keyCode);
}

image

鍵盤碼對應值:

image

常見的鍵盤事件:

  • onkeydown:鍵盤按下時觸發
  • onkeyup:鍵盤彈起時觸發

示例代碼: [ 55-鍵盤事件-ESC鍵關閉遮罩層彈出框.html ]

// 點擊登錄按鈕
var btn = document.getElementById('btn');
// 登錄框
var login = document.getElementById('login');
// 遮罩層
var bg = document.getElementById('bg');
btn.addEventListener("click", function() {
    login.style.display = "block";
    bg.style.display = "block";
});
document.addEventListener("keyup", function(e) {
    e = e || window.event;
    // ESC 鍵的鍵盤碼是27
    if (e.keyCode == 27) {
        login.style.display = "none";
        bg.style.display = "none";
    }
});

效果圖:

image

12.6 案例:彈幕效果

咱們都看過直播,都知道彈幕的效果,下面咱們就模擬直播中的彈幕作個小案例。

實現步驟:

  • 獲取輸入框的的 value 值;並生成 span 標籤
  • span 標籤添加到 頁面中,隨機顏色 隨機高度 span動畫從右向左
  • 到達最左邊的時候刪除 span 標籤(不刪除會隨着輸入的內容愈來愈多影響性能)

示例代碼:

<style>
    html,
    body {
        margin: 0px;
        padding: 0px;
        width: 100%;
        height: 100%;
        font-family: "微軟雅黑";
        font-size: 62.5%;
        background: #ccc;
    }
    #page {
        width: 100%;
        height: 100%;
        position: relative;
        overflow: hidden;
    }
    #import {
        width: 100%;
        height: 60px;
        background: #666;
        position: fixed;
        bottom: 0px;
    }
    #content {
        display: inline-block;
        width: 430px;
        height: 40px;
        position: absolute;
        left: 0px;
        right: 0px;
        top: 0px;
        bottom: 0px;
        margin: auto;
    }
    .title {
        display: inline;
        font-size: 25px;
        vertical-align: bottom;
        color: #fff;
    }
    #text {
        border: none;
        width: 300px;
        height: 30px;
        border-radius: 5px;
        font-size: 15px;
        padding-left: 10px;
    }
    #btn {
        width: 60px;
        height: 30px;
        background: #f90000;
        border: none;
        color: #fff;
        font-size: 15px;
    }
    span {
        width: 300px;
        height: 40px;
        position: absolute;
        overflow: hidden;
        color: #000;
        font-size: 25px;
        line-height: 37.5px;
        cursor: pointer;
        white-space: nowrap;
    }
</style>

<!-- html 部分-->
<div id="page">
    <div id="import">
        <div id="content">
            <p class="title">吐槽</p>
            <input type="text" name="" id="text" placeholder="發送彈幕,與小夥伴一塊兒互動!">
            <button id="btn">發射</button>
        </div>
    </div>
</div>

<!-- js 部分 -->
<script src="../js/animate-callback.js"></script>
<script>
    var page = document.getElementById('page');
    var text = document.getElementById('text');
    var btn = document.getElementById('btn');

    // 定義一個顏色數組
    var colorArr = ['#FF895D', '#78BBE6', '#FF4273', '#00BBF0', '#7C73E6', '#EE2B47', '#F60C86', '#9870FC', '#F96D00', '#303481'];

    btn.onclick = function() {
        // 點擊發射按鈕的時候,要作的事情:
        // 1- 獲取 input 的 value 值;並生成 span 標籤
        // 2- 將 span 標籤添加到 page中,隨機顏色 隨機高度 span動畫從右向左
        // 3- 到達最左邊的時候刪除 span 標籤(不刪除會隨着輸入的內容愈來愈多影響性能)
        // a. 獲取input的值,並清空
        var content = text.value;
        // b. 生成span標籤 添加到 page中
        if (content != "" && content.trim()) {
            text.value = '';
            var span = document.createElement('span');
            page.appendChild(span);
            span.innerText = content;
            // c. 隨機顏色
            var randomColor = parseInt(Math.random() * colorArr.length);
            span.style.color = colorArr[randomColor];
            // d. 隨機高度  隨機位置
            var randomHeight = parseInt(Math.random() * 201);
            span.style.top = randomHeight + "px";
            span.style.left = getClient().width + "px";
            // e. 動畫效果
            animate(span, -300, function() {
                // f. 動畫執行完成以後,回調函數中移除執行完的 span
                page.removeChild(span);
            });
        }
    };

    // text 註冊鍵盤按下事件 當爲回車按鍵的時候,執行發射操做
    text.onkeydown = function(e) {
        e = e || window.event;
        if (e.keyCode == 13) {
            btn.click();
        }
    }

    // 獲取可視區域寬高
    function getClient() {
        return {
            width: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth || 0,
            height: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight || 0
        }
    }
</script>

效果圖:

image

13. 瀑布流

瀑布流,又稱瀑布流式佈局。是比較流行的一種網站頁面佈局,視覺表現爲良莠不齊的多欄佈局,隨着頁面滾動條向下滾動,這種佈局還會不斷加載數據塊並附加至當前尾部。

image

一、首先瀑布流全部的圖片應該保持寬度一致,高度是由內容決定。

image

左浮動的話,咱們能夠看到第6個盒子直接就在第4個盒子旁邊停下了,由於第4個高度最高,擋住了它左浮動的去路。第6個盒子是第2行的最後一個,因此第7個盒子只能在第3行排列了。當排到第12個盒子的時候,盒子會以第11個盒子的位置爲基礎左浮動(這就是第12個盒子爲何沒有‘跳到’第9個盒子下面的緣由),碰到第8個盒子後又被擋住了。

image

經過定位的方式是咱們實現瀑布流的最基本的原理,只要咱們動態的設置它的top值、left值,就能讓它排列。

二、定位後肯定瀏覽器顯示區域內,一行能放多少列圖片盒子。

  • 獲取頁面的寬度
  • 獲取圖片盒子的寬度
  • 顯示的列數 = 頁面寬度/圖片盒子寬度
  • column = pageWidth / itemWidth

image

三、爲了美觀咱們能夠加上一個空隙

  • 顯示的列數 = 頁面寬度/(圖片盒子寬度+間隙);
  • column = pageWidth / (itemWidth + gap);

image

四、 肯定列數以後,排列第一行

  • 下面還有不少圖片盒子,咱們先要排列第1行,因此在for循環裏就要判斷一下,當i(全部圖片盒子的索引) < column(顯示列數)的時候,說明在第1行;
  • 知道在第1行以後,動態設置每一個圖片盒子的left值就能排好第1行。
  • left = i * ( itemWidth + gap );

image

五、第1行排列好以後,獲取第1行全部圖片盒子的高度

  • 須要定義一個數組arr,將獲取到的高度存在數組中,由於第2行排列的時候須要考慮top值,此時只能根據第1行圖片盒子的高度來設置;
  • 獲取圖片高度的時候要注意,程序必須寫在入口函數onload裏面,由於圖片的加載特性是:等頁面都加載完以後纔去請求加載,因此不寫在入口函數裏可能會出現高度獲取不到的狀況。

image

六、排列第2行

  • 獲取到剛剛數組中,高度最小的那一列,將第2行的第1個圖片盒子放置在它的下方;
  • 此時的left值就是高度最小列的offsetLefttop值就是:第1行高度最小列的高度(爲了佈局美觀能夠加上上下間隙gap)。
  • 記錄下高度最小列的索引index,後面計算會用到;
  • 設置完成以後,會發現後面全部的圖片都疊在這個高度最小列的下面,緣由就是此時的最小列高度沒有改變,應該加上下面圖片的高度,得出一個新高度。

image

七、改變最小列當前高度

  • 此時的這一列高度其實已經發生改變了,因此須要將新高度賦值給數組
  • 當前高度最小列的高度 = 當前高度最小列的高度 + 間隙 + 下面圖片盒子的高度

image

八、觸發resize事件

  • 將整個設置樣式的部分封裝成一個函數,在onload裏面註冊一個resize事件,只要頁面一發生改變,就觸發樣式部分的代碼。
  • 實時改變pageWidth的寬度,這樣瀑布流就會是一個響應式的效果了

九、懶加載效果

  • 目前咱們用的是30張圖片,假如一個頁面中有幾百張圖片的時候,咱們不可能等到它都加載完再顯示,全部這裏引入一個懶加載的概念,咱們規定第30張爲顯示的最後一張圖片,當滾動條滾動到30張的時候,應該加載下一批圖片。
  • 即頁面可視區高度+滾動條捲去的高度 = 第30圖片的offsetTop;的時候加載下面的圖片。

image

完整代碼:

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
        position: relative;
    }
    
    img {
        width: 220px;
        display: block;
    }
    
    .item {
        box-shadow: 2px 2px 2px #999;
        position: absolute;
    }
</style>

<!-- html 部分 -->
<div id="box">
    <div class="item"><img src="../image/瀑布流/001.jpg" alt=""></div>
                                .
                                .
                                .
    <div class="item"><img src="../image/瀑布流/030.jpg" alt=""></div>
</div>

<!-- js 部分 -->
<script>
    var box = document.getElementById('box');
    var items = box.children;
    // 定義每一列之間的間隙 爲10像素
    var gap = 10;

    window.onload = function() {
        // 一進來就調用一次
        waterFall();
        // 封裝成一個函數
        function waterFall() {
            // 1- 肯定列數  = 頁面的寬度 / 圖片的寬度
            var pageWidth = getClient().width;
            var itemWidth = items[0].offsetWidth;
            var columns = parseInt(pageWidth / (itemWidth + gap));
            var arr = [];
            for (var i = 0; i < items.length; i++) {
                if (i < columns) {
                    // 2- 肯定第一行
                    items[i].style.top = 0;
                    items[i].style.left = (itemWidth + gap) * i + 'px';
                    arr.push(items[i].offsetHeight);

                } else {
                    // 其餘行
                    // 3- 找到數組中最小高度  和 它的索引
                    var minHeight = arr[0];
                    var index = 0;
                    for (var j = 0; j < arr.length; j++) {
                        if (minHeight > arr[j]) {
                            minHeight = arr[j];
                            index = j;
                        }
                    }
                    // 4- 設置下一行的第一個盒子位置
                    // top值就是最小列的高度 + gap
                    items[i].style.top = arr[index] + gap + 'px';
                    // left值就是最小列距離左邊的距離
                    items[i].style.left = items[index].offsetLeft + 'px';

                    // 5- 修改最小列的高度 
                    // 最小列的高度 = 當前本身的高度 + 拼接過來的高度 + 間隙的高度
                    arr[index] = arr[index] + items[i].offsetHeight + gap;
                }
            }
        }
        // 頁面尺寸改變時實時觸發
        window.onresize = function() {
            waterFall();
        };
        // 當加載到第30張的時候
        window.onscroll = function() {
            if (getClient().height + getScrollTop() >= items[items.length - 1].offsetTop) {
                // 模擬 ajax 獲取數據    
                var datas = [
                    "../image/瀑布流/001.jpg",
                            ...
                    "../image/瀑布流/030.jpg"
                ];
                for (var i = 0; i < datas.length; i++) {
                    var div = document.createElement("div");
                    div.className = "item";
                    div.innerHTML = '<img src="' + datas[i] + '" alt="">';
                    box.appendChild(div);
                }
                waterFall();
            }

        };
    };

    // clientWidth 處理兼容性
    function getClient() {
        return {
            width: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth,
            height: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
        }
    }
    // scrollTop兼容性處理
    function getScrollTop() {
        return window.pageYOffset || document.documentElement.scrollTop;
    }
</script>

效果圖:

image

14. 正則表達式

正則表達式:用於匹配規律規則的表達式,正則表達式最初是科學家對人類神經系統的工做原理的早起研究,如今在編程語言中有普遍的應用,常常用於表單校驗,高級搜索等。

14.1 建立正則表達式

js中的正則表達式用RegExp對象表示,能夠經過RegExp()構造函數來建立RegExp對象,不過更多的是經過字面量語法來建立。

/.../正則表達式必需要有斜槓,它表示的是正則構成

構造函數的方式:

var regExp = new RegExp(/abc/); // 判斷是否包含字符abc

正則字面量:/.../

var regExp = /abc/;

正則的使用:

正則表達式有一個方法:test(); 有一個返回值,是布爾類型。決定參數是否符合正則表達式

console.log(/abc/.test("abc")); // true

示例代碼: [ 57-正則表達式-建立正則表達式.html ]

var reg = new RegExp(/abc/);
console.log(reg.test("abc"));   // ture
console.log(reg.test("efg"));   // false
console.log(reg.test("abcd"));  // true   只要包含abc就正確 後面會細講
console.log(/123/.test(123));   // true

14.2 元字符

正則表達式由一些 普通字符元字符組成,普通字符包括大小寫字母、數字等,而元字符則具備特殊的意義。元字符: ^...

14.3 正則內部類

一、預約義類

正則表達式中具備特殊意義的字符。
預約義類 正則形式 釋義
. [^\n\r] 除了換行和回車以外的任意字符
\d [0-9] 數字字符
\D [^0-9] 非數字字符
\w [a-zA-Z0-9_] 單詞字符(全部的字母數字和'_')
\W [^a-zA-Z0-9_] 非單詞字符
\s [\f\r\n\t\v] 不可見字符,包含空格
\S [^\f\r\n\t\v] 可見字符

示例代碼: [ 59-正則表達式-預約義類.html ]

console.log("----------------'.'---------------");
console.log(/./.test('\n'));        // false
console.log(/./.test('2s#2'))       // true

console.log("----------------'\\d'---------------");
console.log(/\d/.test(123));        // true
console.log(/\d/.test('123abc'));   // true
console.log(/\d/.test('abc'));      // false

console.log("----------------'\\D'---------------");
console.log(/\D/.test(123));        // false
console.log(/\D/.test('123abc'));   // true
console.log(/\D/.test('abc'));      // true

console.log("----------------'\\w'---------------");
console.log(/\w/.test(123));        // true
console.log(/\w/.test('123abc_'));  // true
console.log(/\w/.test(' '));        // false

console.log("----------------'\\W'---------------");
console.log(/\W/.test(123));        // false
console.log(/\W/.test('123abc_'));  // false
console.log(/\W/.test(' '));        // true

console.log("----------------'\\s'---------------");
console.log(/\s/.test(123));        // false
console.log(/\s/.test('123abc_'));  // false
console.log(/\s/.test(' '));        // true

console.log("----------------'\\S'---------------");
console.log(/\S/.test(123));        // true
console.log(/\S/.test('123abc_'));  // true
console.log(/\S/.test(' '));        // false

二、簡單類 [ 60-正則表達式-簡單類.html ]

/ /中什麼特殊符號也不寫,就是簡單類

直接字符: 必須是完整的包含正則選項,只能多不能少

console.log(/levi/.test('levi'));       // true
console.log(/levi/.test('le'));         // false
console.log(/levi/.test('levi_lxh'));   // true

只要完整的包含了「levi」便可(有他就行)

加上[] 只要包含裏面任何一個便可 好比/[abcd]/ => a,b,c,d只要匹配項的裏面有任意一項符合就返回true

console.log(/[levi]/.test("le"));       // true
console.log(/[levi]/.test("less"));     // true
console.log(/[levi]/.test("kill"));     // true
console.log(/[levi]/.test("ss"));       // false

三、負向類

元字符 ^必須出如今中括號內,表示非、取反的意思 [^]

示例代碼: [ 61-正則表達式-負向類.html ]

console.log(/[^levi]/.test("l"));           // false
console.log(/[^levi]/.test("le"));          // false
console.log(/[^levi]/.test("ec"));          // true
console.log(/[^levi]/.test("levi"));        // false
console.log(/[^levi]/.test("levi-lxh"));    // true
console.log(/[^levi]/.test("lxh"));         // true

條件項[^levi],表示不能有l,e,v, i任意組合,當匹配項小於等於條件項而且包含條件項的時候,返回false,當返回項不徹底包含條件項的時候,返回true

四、範圍類:

有時候匹配的東西過多,並且類型又相同,所有輸入太麻煩,咱們能夠在中間加個橫線 -

示例代碼: [ 62-正則表達式-範圍類.html ]

console.log(/[a-d]/.test("a"));     // true
console.log(/[a-d]/.test("ac123")); // true
console.log(/[a-d]/.test("efg"))    // false
console.log(/[a-d]/.test("123"))    // false

五、組合類

用中括號匹配不一樣類型的單個字符串

示例代碼: [ 63-正則表達式-組合類.html ]

console.log(/[a-f1-6]/.test('abs'));    // true
console.log(/[a-f1-6]/.test('12'));     // true
console.log(/[a-f1-6]/.test('sg8'));    // false

14.4 正則邊界

咱們前面學習的正則只要有知足的條件的就會返回true,並不能作到精確的匹配。正則邊界就是以什麼開始,以什麼結束,進行精確匹配。

一、以什麼開始:

^元字符在 //裏面的時候,表示的是必須 以...開始^在中括號 []內才表示取反、非的意思。
console.log(/^levi/.test('lxhlevi'));       // false
console.log(/^levi/.test('levilxh'));       // true
console.log(/^levi/.test('lxhlevilxh'));    // false
console.log(/^levi/.test('levilevi'));      // true

二、以什麼結尾:

$ 元字符表示的是必須以...結尾
console.log(/levi$/.test('lxhlevi'));       // true
console.log(/levi$/.test('levilxh'));       // false
console.log(/levi$/.test('lxhlevilxh'));    // false
console.log(/levi$/.test('levilevi'));      // true

三、精確匹配:

^...$表示的是精確匹配,匹配項必須是 ^、$之間的內容
console.log(/^levi$/.test('lxhlevi'));      // false
console.log(/^levi$/.test('levilxh'));      // false
console.log(/^levi$/.test('lxhlevilxh'));   // false
console.log(/^levi$/.test('levilevi'));     // false
console.log(/^levi$/.test('levi'));         // true
console.log(/^\d$/.test('111'));            // false  \d表示的是0-9當中的一位數
console.log(/^\d$/.test('1'));              // true

[ 64-正則表達式-正則邊界.html ]

14.5 量詞

量詞用來控制出現的次數,通常來講量詞和邊界會一塊兒使用

一、量詞 *

表示可以出現 0次,或者跟多的次數, x >= 0
// 能夠出現0次或者屢次  要麼不出現 要麼只能出現 a
console.log(/^a*$/.test('abc'));    // false
console.log(/^a*$/.test('bbb'));    // false
console.log(/^a*$/.test('aab'));    // false
console.log(/^a*$/.test('aaa'));    // true
console.log(/^a*$/.test('a'));      // true
console.log(/^a*$/.test(''));       // true

二、量詞 +

表示可以出現 1次或者屢次, x >= 1
// +表示 能夠出現1次或者1次以上
console.log(/^a+$/.test("a"));      //true
console.log(/^a+$/.test(""));       //false
console.log(/^a+$/.test("b"));      //false
console.log(/^a+$/.test("aa"));     //true
console.log(/^a+$/.test("aab"));    //false

三、量詞 ?

表示可以出現 0次或者 1次, x=0或者 x=1
// ? 表示能夠出現0次或者1次
console.log(/^a?$/.test("a"));      //true
console.log(/^a?$/.test(""));       //true
console.log(/^a?$/.test("b"));      //false
console.log(/^a?$/.test("aa"));     //false
console.log(/^a?$/.test("aab"));    //false

四、量詞 {n}

表示可以出現 n
// * ==> {0,}
console.log(/^a{0,}$/.test('a'));    // true
console.log(/^a{0,}$/.test('aa'));   // true
console.log(/^a{0,}$/.test(''));     // true
console.log(/^a{0,}$/.test('abc'));  // fasle
console.log(/^a{0,}$/.test('aaab')); // fasle

五、量詞 {n,}

表示可以出現 n次或者 n次以上
// + ==> {1,}
console.log(/^a{1,}$/.test('a'));    // true
console.log(/^a{1,}$/.test('aa'));   // true
console.log(/^a{1,}$/.test(''));     // fasle
console.log(/^a{1,}$/.test('abc'));  // fasle
console.log(/^a{1,}$/.test('aaab')); // fasle

六、量詞 {n,m}

表示可以出現n-m次
// ? ==> {0,1}
console.log(/^a{0,1}$/.test("a"));   //true
console.log(/^a{0,1}$/.test(""));    //true
console.log(/^a{0,1}$/.test("b"));   //false
console.log(/^a{0,1}$/.test("aa"));  //false
console.log(/^a{0,1}$/.test("aab")); //false

[ 65-正則表達式-量詞.html ]

14.6 括號總結

一、{} 大括號限定出現的次數

// 表示的是 n 重複兩次
console.log(/chuan{2}/.test("chuanchuan"));     // false 
console.log(/chuan{2}/.test("chuann"));         // true
console.log(/chuan{2}/.test("chuann123123"));   // true

二、[] 表示一個字符出現的位置

console.log(/^[fb]oot$/.test("foot"));  // true
console.log(/^[fb]oot$/.test("boot"));  // true

三、() 用來提高優先級

console.log(/^(chuan){2}$/.test("chuanchuan")); // true

14.7 正則表達式綜合案例

一、驗證座機號碼: [ 67-正則案例-驗證座機號碼.html ]

  • 直轄市座機號碼:021-88888888
  • 普通地區作急哦號碼:0515-12345678
  • 前區能夠是三位也能夠是四位,第一位必須是0,後區必須是8位;
var reg = /^0\d{2,3}-\d{8}$/;
console.log(reg.test('021-12345678'));   // true
console.log(reg.test('0515-88888888'));  // true
console.log(reg.test('0515-888880888')); // false

二、驗證中文姓名 [ 68-正則案例-驗證中文姓名.html ]

  • 只能是漢字
  • 長度2-6位之間
  • 漢字範圍[\u4e00-\u9fa5]
  • unicode編碼:萬國碼,其中\u4e00-\u9fa5表示的就是包含全部漢字的unicode編碼
var nameReg = /^[\u4e00-\u9fa5]{2,6}$/;
console.log(nameReg.test('莫'));         // false
console.log(nameReg.test('小澤瑪利亞')); // true
console.log(nameReg.test('柯南'));       // true

三、驗證郵箱 [ 69-正則案例-驗證郵箱.html ]

  • 前面是字母或者數字
  • 必須有@
  • @後面是字母或者數字
  • 必須有.
  • .後面是字母或者數字
var mailBoxReg = /^\w+@\w+(\.\w+)+$/;
console.log(mailBoxReg.test('18888888@qq.com')); // true

四、驗證手機號碼 [ 70-正則案例-驗證手機號碼.html ]

  • 11位數字組成
  • 號段13[0-9] 147 15[0-9] 177[0178] 18[0-9]
var mobileReg = /^(13[0-9]|147|15[0-9]|17[0178]|18[0-9])\d{8}$/;
console.log(mobileReg.test(15812345678));  // true

五、驗證QQ [ 71-正則案例-驗證qq.html ]

  • 只能是數字
  • 開頭不能是0
  • 長度爲5-11
var qqReg = /^[1-9]\d{4,10}$/;
console.log(qqReg.test(18888888)); // true

六、完整版表單驗證 [ 72-正則案例-表單驗證綜合案例.html ]

<!-- 樣式部分 -->
<style>
    body {
        background: #ccc;
    }
    .container {
        margin: 100px auto;
        width: 400px;
        padding: 50px;
        line-height: 40px;
        border: 1px solid #999;
        background: #efefef;
    }
    label {
        width: 40px;
        display: inline-block;
    }
    span {
        color: red;
    }
    span {
        margin-left: 30px;
        font-size: 12px;
    }
</style>

<!-- html 部分 -->
<div class="container">
    <label>Q Q</label><input type="text" id="inp1"><span></span><br/>
    <label>手機</label><input type="text" id="inp2"><span></span><br/>
    <label>郵箱</label><input type="text" id="inp3"><span></span><br/>
    <label>座機</label><input type="text" id="inp4"><span></span><br/>
    <label>姓名</label><input type="text" id="inp5"><span></span><br/>
</div>

<!-- js 部分 -->
<script>
    function checkReg(element, reg) {
        element.onblur = function() {
            var content = this.value;
            if (reg.test(content)) {
                this.nextElementSibling.innerHTML = "合法";
                this.nextElementSibling.style.color = "green";
            } else {
                this.nextElementSibling.innerHTML = "不合法";
                this.nextElementSibling.style.color = "red";
            }
        }
    }
    checkReg(document.getElementById("inp1"), /^[1-9]\d{4,11}$/);
    checkReg(document.getElementById("inp2"), /^(13[0-9]|14[57]|15[0-25-9]|17[0137]|18[0-9])\d{8}$/);
    checkReg(document.getElementById("inp3"), /^\w+@\w+(\.\w+)+$/);
    checkReg(document.getElementById("inp4"), /^0\d{2,3}-\d{7,8}$/);
    checkReg(document.getElementById("inp5"), /^[\u4e00-\u9fa5]{2,4}$/);
</script>

效果圖:

image

14.8 正則補充知識點

這裏要補充幾個正則的小知識點,好比在正則裏‘ |’,表示的是或。‘ g’,表示的是 global:全局,所有。‘ i’,表示的是 ignore:忽視大小寫。

一、或‘|’ :

var mobileReg = /^(13[0-9]|14[57]|15[0-25-9]|17[0137]|18[0-9])\d{8}$/;

咱們能夠看到在判斷手機號碼前三位的時候,咱們就用到了或:「|」

二、所有‘g’:

var str = '123123123123';
// 找到全部的1  替換成3
var newStr = str.replace(/1/g, 3);
console.log(newStr);  // 323323323

三、忽略大小寫‘i’:

var str2 = 'abcdAAAA';
var newStr2 = str2.replace(/a/gi, 'e');
console.log(newStr2);  // ebcdeeee

上一篇:JavaScript 進階知識 - 特效篇(一)
下一篇:JavaScript 進階知識 - Ajax篇

相關文章
相關標籤/搜索