事件對象
- 在我們注冊事件后红竭,并不會直接調(diào)用這個(gè)匿名函數(shù)尤勋,而是觸發(fā)這個(gè)事件時(shí)才執(zhí)行內(nèi)部代碼。
- 所以真正調(diào)用匿名函數(shù)的是瀏覽器茵宪。瀏覽器在調(diào)用時(shí)會傳入一個(gè)對象參數(shù)(event)最冰,哪怕我們沒寫形參,也是會傳入的稀火。這個(gè)就叫作事件對象暖哨。
- 事件對象內(nèi)部存儲的是一系列與該事件有關(guān)系的屬性值。所以如果想獲取事件對象,在給匿名函數(shù)寫一個(gè)形參就可以喜每。
獲取事件對象兼容寫法
- 之前已經(jīng)說過了满哪,通過形參可以直接獲取到事件對象,但是這種方法主流瀏覽器支持茴恰,ie678不支持,所以一般推薦使用兼容的方法斩熊。
document.onclick = function (event) {
var event = event || window.event;
};
- 上面是隨便注冊了一個(gè)事件作為例子往枣,設(shè)置一個(gè)形參,并在函數(shù)內(nèi)部聲明一個(gè)event變量來存放事件對象粉渠。
- 寫或的原因是使用到了它的短路判斷分冈,如果第一個(gè)值為true,說明瀏覽器支持這種方法霸株,根據(jù)或的短路原理雕沉,第一個(gè)為true直接返回該值。如果第一個(gè)不為true才繼續(xù)往下判斷去件。
三個(gè)重要坐標(biāo)
-
事件對象.screenX & 事件對象.screenY
獲取鼠標(biāo)在屏幕中的坐標(biāo) -
事件對象.clientX & 事件對象.clientY
獲取鼠標(biāo)在窗口中的坐標(biāo) -
事件對象.pageX & 事件對象.pageY
獲取鼠標(biāo)在頁面中的坐標(biāo) - clientXY和pageXY的區(qū)別在于:如果頁面很長坡椒,那么pageXY會以整個(gè)頁面來計(jì)算坐標(biāo)扰路,而clientXY則是只會根據(jù)當(dāng)前的顯示窗口來計(jì)算坐標(biāo)。
鼠標(biāo)在頁面中坐標(biāo)的兼容寫法
- 也是ie678不支持pageX和pageY倔叼,但是ie678卻支持clientXY汗唱,所以建議使用以下兼容寫法:
pageX = event.pageX || event.clientX + document.documentElement.scrollLeft //Y軸同理
- 獲取鼠標(biāo)在窗口中的位置,再加上被卷去的部分丈攒,也可以得到鼠標(biāo)在頁面中的坐標(biāo)
鼠標(biāo)跟隨案例總結(jié)
- 比較簡單的一個(gè)案例哩罪,主要就是獲取事件對象中的pageX和pageY值,并賦給添加了定位元素的left和top巡验,就能實(shí)現(xiàn)鼠標(biāo)跟隨效果
- 有一個(gè)稍微要注意的是际插,如果直接賦值那么元素在跟隨的時(shí)候,是左上角對齊鼠標(biāo)坐標(biāo)深碱。如果想要讓元素中心對齊鼠標(biāo)坐標(biāo)腹鹉,要減去元素自身高和寬分別的一半。
獲取鼠標(biāo)在盒子中的坐標(biāo)
- 首先要知道敷硅,鼠標(biāo)在盒子中的坐標(biāo)是不能直接獲取的功咒,需要通過計(jì)算。
- 計(jì)算的原理绞蹦,用鼠標(biāo)在頁面中的坐標(biāo)值力奋,減去盒子本身到body兩側(cè)的距離,那么剩下的就是鼠標(biāo)在盒子中的坐標(biāo)值
- 下面例子做一個(gè)參考:
boxX = pageX - box.offsetLeft;
boxY = pageY - box.offsetTop;
- 同時(shí)本節(jié)又講到了一個(gè)新的事件幽七,
onmousemove
鼠標(biāo)移動(dòng)事件
放大鏡案例總結(jié)
- 這個(gè)案例實(shí)際上運(yùn)用到了景殷,上面講的幾個(gè)知識點(diǎn),包括獲取鼠標(biāo)在盒子內(nèi)坐標(biāo)澡屡,設(shè)置鼠標(biāo)跟隨盒子猿挚。
- 有兩個(gè)重點(diǎn):
- 一是在盒子內(nèi)部設(shè)置鼠標(biāo)跟隨盒子時(shí),要限制跟隨盒子可以移動(dòng)的范圍驶鹉,不能讓其超出盒子
- 二是讓大圖同步移動(dòng)時(shí)绩蜻,比例的計(jì)算問題。當(dāng)然比例不是一定要計(jì)算室埋,直接給一個(gè)對應(yīng)的比值也行办绝,不過為了精確顯示,建議還是要計(jì)算出來姚淆。公式就是:
大圖應(yīng)該設(shè)置坐標(biāo) = rate(比例) * 跟隨圖坐標(biāo)
孕蝉。rate的公式為:rate = 大圖可以移動(dòng)總距離 / 跟隨圖可以移動(dòng)總距離
- 詳細(xì)代碼參考案例,更容易理解
鼠標(biāo)拖拽案例總結(jié)
- 這個(gè)案例的重點(diǎn)在于:當(dāng)鼠標(biāo)移動(dòng)時(shí)腌逢,如果不計(jì)算那么默認(rèn)盒子左上角跟隨鼠標(biāo)移動(dòng)降淮,這明顯不是我們想要的效果。
- 所以需要計(jì)算盒子應(yīng)該移動(dòng)的距離搏讶,首先要計(jì)算鼠標(biāo)在按下時(shí)骤肛,在盒子的內(nèi)的坐標(biāo)纳本,然后再用鼠標(biāo)在頁面中的坐標(biāo)減去盒子的坐標(biāo),就可以讓鼠標(biāo)按下的那個(gè)點(diǎn)一直對準(zhǔn)鼠標(biāo)進(jìn)行跟隨腋颠。
- 實(shí)際上就是在移動(dòng)時(shí),默認(rèn)先讓盒子移動(dòng)了鼠標(biāo)在盒子中坐標(biāo)的距離吓笙,然后再跟隨鼠標(biāo)移動(dòng)淑玫,這樣看起來就不是左上角跟隨了。
- 并且涉及到了清除選中文字狀態(tài)的代碼:
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
滾動(dòng)條案例總結(jié)
- 滾動(dòng)條案例邏輯其實(shí)是結(jié)合了上面的兩個(gè)案例面睛,有以下幾個(gè)重點(diǎn)
- 滾動(dòng)條的長度不是定值絮蒿,需要通過公式來計(jì)算:
滾動(dòng)條高度/側(cè)邊欄高度 = 內(nèi)容顯示高度/內(nèi)容總高度
- 想要鼠標(biāo)拖動(dòng)時(shí)不是滾動(dòng)條頂部對齊跟隨移動(dòng),就要求到鼠標(biāo)在滾動(dòng)條內(nèi)的坐標(biāo)叁鉴,用
鼠標(biāo)在頁面中的坐標(biāo)-盒子到頂部的距離-滾動(dòng)條到盒子頂部的距離
土涝,就可以得到。然后用鼠標(biāo)在盒子中的坐標(biāo)-鼠標(biāo)在滾動(dòng)條中的坐標(biāo)
幌墓,讓滾動(dòng)條移動(dòng)時(shí)默認(rèn)先往上走這個(gè)距離但壮,就可以實(shí)現(xiàn)滾動(dòng)條根據(jù)鼠標(biāo)點(diǎn)擊的那個(gè)點(diǎn)來跟隨移動(dòng) - 要讓內(nèi)容根據(jù)滾動(dòng)條移動(dòng)的比例來移動(dòng),就要根據(jù)這個(gè)公式:
內(nèi)容應(yīng)該移動(dòng)的位置/滾動(dòng)條當(dāng)前的位置 = 內(nèi)容能夠移動(dòng)的總距離/滾動(dòng)條能夠移動(dòng)的總距離
瀑布流案例總結(jié)
- 瀑布流案例的幾個(gè)重點(diǎn):
- 求出每行剩余空白寬度后常侣,還要除以列數(shù)-1蜡饵,平均分配空白間距
- 第一行布局不需要設(shè)置top值
- left設(shè)置時(shí),
值 = (圖片寬度 + 間距) * (對應(yīng)索引||最小高度列索引值)
胳施,這樣才能讓圖片的位置在其對應(yīng)列或者最小高度列上 - 第二行開始要把圖片放到高度最小的那列上溯祸,所以需要一個(gè)數(shù)組來存放每列的高度,然后比較數(shù)組中的值舞肆,找出高度最小的那列焦辅。
- 每次把圖片放到高度最小那列后,還要給那列的高度值重新賦值椿胯,
值 = 原高度+新圖片高度+間距
- 瀑布流需要瀏覽器滾動(dòng)事件
window.onscroll
筷登,為了避免出現(xiàn)滾動(dòng)時(shí)出現(xiàn)空白影響效果,所以在追加圖片時(shí)压状,判斷條件為:窗口可視區(qū)高度 + 頁面被卷去高度 > 最小高度列的高度值
仆抵,一旦大于肯定會出現(xiàn)空白,所以就要追加圖片种冬,并再次調(diào)用瀑布流布局函數(shù)
事件冒泡
- 在一個(gè)對象上觸發(fā)某類事件(比如單擊onclick事件)镣丑,如果此對象定義了此事件的處理程序,那么此事件就會調(diào)用這個(gè)處理程序娱两,如果沒有定義此事件處理程序或者事件返回true莺匠,那么這個(gè)事件會向這個(gè)對象的父級對象傳播,從里到外十兢,直至它被處理(父級對象所有同類事件都將被激活)趣竣,或者它到達(dá)了對象層次的最頂層摇庙,即document對象(有些瀏覽器是window)。正常情況下這個(gè)不會有什么影響
- 但是也有特例遥缕,比如鼠標(biāo)離開事件卫袒,如果父元素注冊了鼠標(biāo)事件,子元素沒有注冊单匣。當(dāng)鼠標(biāo)離開子元素時(shí)夕凝,雖然沒有注冊事件不會執(zhí)行,但是該事件確實(shí)觸發(fā)了就會冒泡户秤,到上級發(fā)現(xiàn)父元素注冊了码秉。就會執(zhí)行父元素注冊的鼠標(biāo)離開事件,這顯然是不行的
阻止事件冒泡
- 為了避免事件冒泡而產(chǎn)生的一些不可控結(jié)果鸡号,我們需要阻止事件冒泡转砖,要阻止誰的事件冒泡,就在讓該元素調(diào)用阻止冒泡的方法鲸伴。
event.stopPropagation();
- 調(diào)用這個(gè)方法府蔗,需要先獲取事件對象來配合
阻止事件冒泡兼容寫法
- 寫法如下
if( event.stopPropagation ) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
- 如果調(diào)用stopPropagation方法有返回值,說明瀏覽器支持挑围,如果為空說明瀏覽器不支持礁竞,那就設(shè)置cancelBubble屬性。一般只有ie678不支持stopPropagation方法
獲取事件目標(biāo)
-
var target = event.target || event.srcElement
可以獲取事件的源目標(biāo)杉辙,就是說由哪個(gè)元素觸發(fā)了這個(gè)事件模捂,就獲取到哪個(gè)元素,target.id
還能獲取該元素的id值
事件監(jiān)聽器
- 如果使用之前的事件注冊方法蜘矢,那么同一個(gè)元素相同的事件會相互覆蓋狂男,一般只會保留后寫注冊的事件
- 但是如果使用事件監(jiān)聽器的方法來注冊事件,那么即使是同一元素的相同事件也不會覆蓋品腹,而是根據(jù)注冊順序依次執(zhí)行
- 主流瀏覽器的寫法:
元素.addEventListener([事件名字符串岖食,注意這里事件名不加on],[事件處理程序function],[是否捕獲true||false]);
- ie678的寫法:
元素.attachEvent([事件名字符串,注意這里事件名要加on],[事件處理程序function]);
- 捕獲參數(shù)詳解:如果傳入的參數(shù)是false舞吭,就是不使用捕獲泡垃,那么事件的執(zhí)行順序就是默認(rèn)順序冒泡,從內(nèi)到外羡鸥;如果傳入true使用捕獲蔑穴,那么瀏覽器會從外到內(nèi)先捕獲事件源,找到事件源后惧浴,再執(zhí)行冒泡存和。捕獲就是從外內(nèi)到尋找的過程。
監(jiān)聽器移除事件方法
- 普通注冊事件的方式,想要移除事件捐腿,利用事件會相互覆蓋的原理纵朋,重新注冊該事件并賦值為空,就能移除茄袖。但是如果是使用監(jiān)聽器來注冊的事件操软,那么需要用到以下方法:
- 主流瀏覽器:
元素.removeEventListener()[事件名字符串,注意這里事件名不加on],[處理函數(shù)名],[是否捕獲true||false]);
- ie678
元素.detachEvent([事件名字符串绞佩,注意這里事件名要加on],[處理函數(shù)名]);
- 從代碼可以看出寺鸥,如果要移除事件,需要傳入事件處理函數(shù)的名稱品山,所以如果使用監(jiān)聽器注冊函數(shù),并且在之后想要移除烤低,那么建議不要用匿名函數(shù)肘交,直接在外部寫好傳入函數(shù)名稱來注冊,也方便后面移除
事件階段
- 事件階段也叫做事件流模型扑馁。通過
event.phase
可以查看事件的各個(gè)階段 - 一個(gè)完整的事件三階段:從外到內(nèi)捕獲階段-事件目標(biāo)執(zhí)行階段-事件冒泡階段
- 但是傳統(tǒng)注冊事件方法涯呻,和監(jiān)聽器注冊事件方法傳入false參數(shù),它都是跳過了捕獲階段腻要,直接找到事件源執(zhí)行再事件冒泡,目前捕獲階段我們很少接觸到