javascript
JavaScript:事件对象Event和冒泡
本文最初發(fā)表于博客園,并在GitHub上持續(xù)更新前端的系列文章。歡迎在GitHub上關(guān)注我,一起入門和進(jìn)階前端。
以下是正文。
綁定事件的兩種方式
我們?cè)谏弦黄恼?DOM操作詳解 中已經(jīng)講過事件的概念。這里講一下注冊(cè)事件的兩種方式,我們以onclick事件為例。
方式一:onclick
舉例:
<body> <button>點(diǎn)我</button> <script>var btn = document.getElementsByTagName("button")[0];//這種事件綁定的方法容易被層疊。btn.onclick = function () {console.log("事件1");}btn.onclick = function () {console.log("事件2");}</script> </body>點(diǎn)擊按鈕后,上方代碼的打印結(jié)果:
事件2我們可以看到,這種綁定事件的方式,會(huì)層疊掉之前的事件。
方式二:addEventListener
addEventListener()里的參數(shù):
參數(shù)1:事件名(注意,沒有on)
參數(shù)2:事件名(執(zhí)行函數(shù))
參數(shù)3:事件名(捕獲或者冒泡)
舉例:
<body> <button>按鈕</button> <script>var btn = document.getElementsByTagName("button")[0];//addEventListener: 事件監(jiān)聽器。 原事件被執(zhí)行的時(shí)候,后面綁定的事件照樣被執(zhí)行//第二種事件綁定的方法不會(huì)出現(xiàn)層疊。(更適合團(tuán)隊(duì)開發(fā))btn.addEventListener("click", fn1);btn.addEventListener("click", fn2);function fn1() {console.log("事件1");}function fn2() {console.log("事件2");}</script> </body>點(diǎn)擊按鈕后,上方代碼的打印結(jié)果:
事件1事件2我們可以看到,這種綁定事件的方式,不會(huì)層疊掉之前的事件。
事件對(duì)象
在觸發(fā)DOM上的某個(gè)事件時(shí),會(huì)產(chǎn)生一個(gè)事件對(duì)象event,這個(gè)對(duì)象中包含著所有與事件有關(guān)的信息。比如鼠標(biāo)操作時(shí)候,會(huì)添加鼠標(biāo)位置的相關(guān)信息到事件對(duì)象中。
所有瀏覽器都支持event對(duì)象,但支持的方式不同。如下。
(1)普通瀏覽器支持 event。比如:
(2)ie 678 支持 window.event。
于是,我們可以采取一種兼容性的寫法。如下:
event = event || window.event; 兼容性寫法代碼舉例:
<!DOCTYPE html> <html> <head lang="en"><meta charset="UTF-8"><title></title> </head> <body> <script>//點(diǎn)擊頁(yè)面的任何部分document.onclick = function (event) {event = event || window.event; 兼容性寫法console.log(event);console.log(event.timeStamp);console.log(event.bubbles);console.log(event.button);console.log(event.pageX);console.log(event.pageY);console.log(event.screenX);console.log(event.screenY);console.log(event.target);console.log(event.type);console.log(event.clientX);console.log(event.clientY);} </script> </body> </html>event 屬性
event 有很多屬性,比如:
由于pageX 和 pageY的兼容性不好,我們可以這樣做:
- 鼠標(biāo)在頁(yè)面的位置 = 被卷去的部分+可視區(qū)域部分。
Event舉例
舉例1:鼠標(biāo)跟隨
代碼實(shí)現(xiàn):
<!DOCTYPE html> <html> <head lang="en"><meta charset="UTF-8"><title></title><style>body {height: 5000px;}img {position: absolute;padding: 10px 0;border: 1px solid #ccc;cursor: pointer;background-color: yellowgreen;}</style> </head> <body> <img src="" width="100" height="100"/><script>//需求:點(diǎn)擊頁(yè)面的任何地方,圖片跟隨鼠標(biāo)移動(dòng)到點(diǎn)擊位置。//思路:獲取鼠標(biāo)在頁(yè)面中的位置,然圖片緩慢運(yùn)動(dòng)到鼠標(biāo)點(diǎn)擊的位置。// 兼容ie67做pageY和pageX;// 原理: 鼠標(biāo)在頁(yè)面的位置 = 被卷去的部分+可視區(qū)域部分。//步驟://1.老三步。//2.獲取鼠標(biāo)在頁(yè)面中的位置。//3.利用緩動(dòng)原理,慢慢的運(yùn)動(dòng)到指定位置。(包括左右和上下)//1.老三步。var img = document.getElementsByTagName("img")[0];var timer = null;var targetx = 0;var targety = 0;var leaderx = 0;var leadery = 0;//給整個(gè)文檔綁定點(diǎn)擊事件獲取鼠標(biāo)的位置。document.onclick = function (event) {//新五步//兼容獲取事件對(duì)象event = event || window.event;//鼠標(biāo)在頁(yè)面的位置 = 被卷去的部分+可視區(qū)域部分。var pagey = event.pageY || scroll().top + event.clientY;var pagex = event.pageX || scroll().left + event.clientX;targety = pagey - 30;targetx = pagex - 50;//要用定時(shí)器,先清定時(shí)器clearInterval(timer);timer = setInterval(function () {//為盒子的位置獲取值leaderx = img.offsetLeft;//獲取步長(zhǎng)var stepx = (targetx - leaderx) / 10;//二次處理步長(zhǎng)stepx = stepx > 0 ? Math.ceil(stepx) : Math.floor(stepx);leaderx = leaderx + stepx;//賦值img.style.left = leaderx + "px";//為盒子的位置獲取值leadery = img.offsetTop;//獲取步長(zhǎng)var stepy = (targety - leadery) / 10;//二次處理步長(zhǎng)stepy = stepy > 0 ? Math.ceil(stepy) : Math.floor(stepy);leadery = leadery + stepy;//賦值img.style.top = leadery + "px";//清定時(shí)器if (Math.abs(targety - img.offsetTop) <= Math.abs(stepy) && Math.abs(targetx - img.offsetLeft) <= Math.abs(stepx)) {img.style.top = targety + "px";img.style.left = targetx + "px";clearInterval(timer);}}, 30);}</script></body> </html>實(shí)現(xiàn)效果:
event應(yīng)用舉例:獲取鼠標(biāo)距離所在盒子的距離
關(guān)鍵點(diǎn):
鼠標(biāo)距離所在盒子的距離 = 鼠標(biāo)在整個(gè)頁(yè)面的位置 - 所在盒子在整個(gè)頁(yè)面的位置代碼演示:
<!DOCTYPE html> <html> <head lang="en"><meta charset="UTF-8"><title></title><style>.box {width: 300px;height: 200px;padding-top: 100px;background-color: pink;margin: 100px;text-align: center;font: 18px/30px "simsun";cursor: pointer;}</style> </head> <body> <div class="box"></div><script src="animate.js"></script> <script>//需求:鼠標(biāo)進(jìn)入盒子之后只要移動(dòng),哪怕1像素,隨時(shí)顯示鼠標(biāo)在盒子中的坐標(biāo)。//技術(shù)點(diǎn):新事件,onmousemove:在事件源上,哪怕鼠標(biāo)移動(dòng)1像素也會(huì)觸動(dòng)這個(gè)事件。//一定程度上,模擬了定時(shí)器//步驟://1.老三步和新五步//2.獲取鼠標(biāo)在整個(gè)頁(yè)面的位置//3.獲取盒子在整個(gè)頁(yè)面的位置//4.用鼠標(biāo)的位置減去盒子的位置賦值給盒子的內(nèi)容。//1.老三步和新五步var div = document.getElementsByTagName("div")[0];div.onmousemove = function (event) {event = event || window.event;//2.獲取鼠標(biāo)在整個(gè)頁(yè)面的位置var pagex = event.pageX || scroll().left + event.clientX;var pagey = event.pageY || scroll().top + event.clientY;//3.獲取盒子在整個(gè)頁(yè)面的位置// var xx =// var yy =//4.用鼠標(biāo)的位置減去盒子的位置賦值給盒子的內(nèi)容。var targetx = pagex - div.offsetLeft;var targety = pagey - div.offsetTop;this.innerHTML = "鼠標(biāo)在盒子中的X坐標(biāo)為:" + targetx + "px;<br>鼠標(biāo)在盒子中的Y坐標(biāo)為:" + targety + "px;"}</script> </body> </html>實(shí)現(xiàn)效果:
舉例:商品放大鏡
代碼實(shí)現(xiàn):
(1)index.html:
<!DOCTYPE html> <html> <head lang="en"><meta charset="UTF-8"><title></title><style>* {margin: 0;padding: 0;}.box {width: 350px;height: 350px;margin: 100px;border: 1px solid #ccc;position: relative;}.big {width: 400px;height: 400px;position: absolute;top: 0;left: 360px;border: 1px solid #ccc;overflow: hidden;display: none;}/*mask的中文是:遮罩*/.mask {width: 175px;height: 175px;background: rgba(255, 255, 0, 0.4);position: absolute;top: 0;left: 0;cursor: move;display: none;}.small {position: relative;}img {vertical-align: top;}</style><script src="tools.js"></script><script>window.onload = function () {//需求:鼠標(biāo)放到小盒子上,讓大盒子里面的圖片和我們同步等比例移動(dòng)。//技術(shù)點(diǎn):onmouseenter==onmouseover 第一個(gè)不冒泡//技術(shù)點(diǎn):onmouseleave==onmouseout 第一個(gè)不冒泡//步驟://1.鼠標(biāo)放上去顯示盒子,移開隱藏盒子。//2.老三步和新五步(黃盒子跟隨移動(dòng))//3.右側(cè)的大圖片,等比例移動(dòng)。//0.獲取相關(guān)元素var box = document.getElementsByClassName("box")[0];var small = box.firstElementChild || box.firstChild;var big = box.children[1];var mask = small.children[1];var bigImg = big.children[0];//1.鼠標(biāo)放上去顯示盒子,移開隱藏盒子。(為小盒子綁定事件)small.onmouseenter = function () {//封裝好方法調(diào)用:顯示元素show(mask);show(big);}small.onmouseleave = function () {//封裝好方法調(diào)用:隱藏元素hide(mask);hide(big);}//2.老三步和新五步(黃盒子跟隨移動(dòng))//綁定的事件是onmousemove,而事件源是small(只要在小盒子上移動(dòng)1像素,黃盒子也要跟隨)small.onmousemove = function (event) {//新五步event = event || window.event;//想要移動(dòng)黃盒子,必須要知道鼠標(biāo)在small小圖中的位置。var pagex = event.pageX || scroll().left + event.clientX;var pagey = event.pageY || scroll().top + event.clientY;//x:mask的left值,y:mask的top值。var x = pagex - box.offsetLeft - mask.offsetWidth / 2; //除以2,可以保證鼠標(biāo)mask的中間var y = pagey - box.offsetTop - mask.offsetHeight / 2;//限制換盒子的范圍//left取值為大于0,小盒子的寬-mask的寬。if (x < 0) {x = 0;}if (x > small.offsetWidth - mask.offsetWidth) {x = small.offsetWidth - mask.offsetWidth;}//top同理。if (y < 0) {y = 0;}if (y > small.offsetHeight - mask.offsetHeight) {y = small.offsetHeight - mask.offsetHeight;}//移動(dòng)黃盒子console.log(small.offsetHeight);mask.style.left = x + "px";mask.style.top = y + "px";//3.右側(cè)的大圖片,等比例移動(dòng)。//如何移動(dòng)大圖片?等比例移動(dòng)。// 大圖片/大盒子 = 小圖片/mask盒子// 大圖片走的距離/mask走的距離 = (大圖片-大盒子)/(小圖片-黃盒子) // var bili = (bigImg.offsetWidth-big.offsetWidth)/(small.offsetWidth-mask.offsetWidth);//大圖片走的距離/mask盒子都的距離 = 大圖片/小圖片var bili = bigImg.offsetWidth / small.offsetWidth;var xx = bili * x; //知道比例,就可以移動(dòng)大圖片了var yy = bili * y;bigImg.style.marginTop = -yy + "px";bigImg.style.marginLeft = -xx + "px";}}</script> </head> <body> <div class="box"><div class="small"><img src="images/001.jpg" alt=""/><div class="mask"></div></div><div class="big"><img src="images/0001.jpg" alt=""/></div> </div> </body> </html>(2)tools.js:
/*** Created by smyhvae on 2018/02/03.*///顯示和隱藏 function show(ele) {ele.style.display = "block"; }function hide(ele) {ele.style.display = "none"; }function scroll() { // 開始封裝自己的scrollTopif (window.pageYOffset != null) { // ie9+ 高版本瀏覽器// 因?yàn)?window.pageYOffset 默認(rèn)的是 0 所以這里需要判斷return {left: window.pageXOffset,top: window.pageYOffset}}else if (document.compatMode === "CSS1Compat") { // 標(biāo)準(zhǔn)瀏覽器 來判斷有沒有聲明DTDreturn {left: document.documentElement.scrollLeft,top: document.documentElement.scrollTop}}return { // 未聲明 DTDleft: document.body.scrollLeft,top: document.body.scrollTop} }效果演示:
事件冒泡
事件傳播的三個(gè)階段是:事件捕獲、事件冒泡和目標(biāo)。
事件捕獲階段:事件從最上一級(jí)標(biāo)簽開始往下查找,直到捕獲到事件目標(biāo) target。(從祖先元素往子元素查找,DOM樹結(jié)構(gòu))。在這個(gè)過程中,事件相應(yīng)的監(jiān)聽函數(shù)是不會(huì)被觸發(fā)的。
事件目標(biāo):當(dāng)?shù)竭_(dá)目標(biāo)元素之后,執(zhí)行目標(biāo)元素該事件相應(yīng)的處理函數(shù)。如果沒有綁定監(jiān)聽函數(shù),那就不執(zhí)行。
事件冒泡階段:事件從事件目標(biāo) target 開始,往上冒泡直到頁(yè)面的最上一級(jí)標(biāo)簽。(從子元素到祖先元素冒泡)
如下圖所示:
PS:這個(gè)概念類似于 Android 里的 touch 事件傳遞。
事件冒泡的概念
事件冒泡: 當(dāng)一個(gè)元素上的事件被觸發(fā)的時(shí)候(比如說鼠標(biāo)點(diǎn)擊了一個(gè)按鈕),同樣的事件將會(huì)在那個(gè)元素的所有祖先元素中被觸發(fā)。這一過程被稱為事件冒泡;這個(gè)事件從原始元素開始一直冒泡到DOM樹的最上層。
通俗來講,冒泡指的是:子元素的事件被觸發(fā)時(shí),父盒子的同樣的事件也會(huì)被觸發(fā)。取消冒泡就是取消這種機(jī)制。
代碼演示:
//事件冒泡box3.onclick = function () {alert("child");}box2.onclick = function () {alert("father");}box1.onclick = function () {alert("grandfather");}document.onclick = function () {alert("body");}上圖顯示,當(dāng)我點(diǎn)擊兒子 box3的時(shí)候,它的父親box2、box1、body都依次被觸發(fā)了。即使我改變代碼的順序,也不會(huì)影響效果的順序。
冒泡順序:
一般的瀏覽器: (除IE6.0之外的瀏覽器)
- div -> body -> html -> document -> window
IE6.0:
- div -> body -> html -> document
事件捕獲
addEventListener可以捕獲事件:
box1.addEventListener("click", function () {alert("捕獲 box3");}, true);上面的方法中,參數(shù)為true,代表捕獲;參數(shù)為false或者不寫參數(shù),代表冒泡。
代碼演示:
//參數(shù)為true,代表捕獲;參數(shù)為false或者不寫參數(shù),代表冒泡box3.addEventListener("click", function () {alert("捕獲 child");}, true);box2.addEventListener("click", function () {alert("捕獲 father");}, true);box1.addEventListener("click", function () {alert("捕獲 grandfather");}, true);document.addEventListener("click", function () {alert("捕獲 body");}, true);效果演示:
不是所有的事件都能冒泡
以下事件不冒泡:blur、focus、load、unload、onmouseenter、onmouseleave。意思是,事件不會(huì)往父元素那里傳遞。
我們檢查一個(gè)元素是否會(huì)冒泡,可以通過事件的以下參數(shù):
event.bubbles如果返回值為true,說明該事件會(huì)冒泡;反之則相反。
舉例:
box1.onclick = function (event) {alert("冒泡 child");event = event || window.event;console.log(event.bubbles); //打印結(jié)果:true}阻止冒泡的方法
w3c的方法:(火狐、谷歌、IE11)
event.stopPropagation();IE10以下則是:
event.cancelBubble = true兼容代碼如下:
box3.onclick = function (event) {alert("child");//阻止冒泡event = event || window.event;if (event && event.stopPropagation) {event.stopPropagation();} else {event.cancelBubble = true;}}上方代碼中,我們對(duì)box3進(jìn)行了阻止冒泡,產(chǎn)生的效果是:事件不會(huì)繼續(xù)傳遞到 father、grandfather、body了。
事件委托
事件委托,通俗地來講,就是把一個(gè)元素響應(yīng)事件(click、keydown......)的函數(shù)委托到另一個(gè)元素。
比如說有一個(gè)列表 ul,列表之中有大量的列表項(xiàng) li,我們需要在點(diǎn)擊列表項(xiàng)li的時(shí)候響應(yīng)一個(gè)事件。如果給每個(gè)列表項(xiàng)一一都綁定一個(gè)函數(shù),那對(duì)于內(nèi)存消耗是非常大的,效率上需要消耗很多性能。
因此,比較好的方法就是把這個(gè)點(diǎn)擊事件綁定到他的父層,也就是 ul 上,然后在執(zhí)行事件的時(shí)候再去匹配判斷目標(biāo)元素。
所以事件委托可以減少大量的內(nèi)存消耗,節(jié)約效率。
事件委托的參考鏈接:JavaScript 事件委托詳解
我的公眾號(hào)
想學(xué)習(xí)代碼之外的軟技能?不妨關(guān)注我的微信公眾號(hào):生命團(tuán)隊(duì)(id:vitateam)。
掃一掃,你將發(fā)現(xiàn)另一個(gè)全新的世界,而這將是一場(chǎng)美麗的意外:
轉(zhuǎn)載于:https://www.cnblogs.com/qianguyihao/p/8413602.html
總結(jié)
以上是生活随笔為你收集整理的JavaScript:事件对象Event和冒泡的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#AES加密
- 下一篇: tensorflow import 没找