1 放大鏡實例
- 頁面結構:
- 創建左右兩個容器,左邊容器中添加一張圖片背景圖,右邊容器中添加一張與背景圖成比例放大的圖片,圖片相當于右邊容器進行定位;
- 左右兩個容器設置浮動,使其并排顯示,在左邊容器中創建一個透明度的模糊層mark,相對于左邊容器進行定位,通過改變定位值來實現不斷移動,正常情況下右邊容器及mark均為隱藏;
- 思路:
- 給左邊容器left添加鼠標移入移出事件,當光標移入后,右邊容器及mark顯示,當光標移出后,右邊容器及mark隱藏;
- 給左邊容器添加鼠標移動事件,然后通過事件對象屬性clientX,clientY計算出鼠標相對于左容器的位置,設置給mark定位值;進而實現mark的移動,計算出右面容器中圖片的定位數值,相對的讓其移動,進而達到放大鏡的效果;
- 知識點:
- 鼠標移入移出事件,onmouseover、onmouseout與onmouseenter、onmouseleave兩對事件都可以設置;
- 鼠標移動事件,添加給左邊容器left,但是在實際移動中,光標一直在子級mark上移動,所以會觸發子級的移動事件,通過冒泡觸發left的移動事件,進而實現mark的定位數值設置,完成不斷移動效果;
- 針對于mark元素
- 定位值的計算及設置代碼:
var l=e.clientX-this.offsetLeft-this.clientLeft-oMark.offsetWidth/2; var t=e.clientY-this.offsetTop-this.clientTop-oMark.offsetHeight/2;
- 邊界值的計算及設置
- 針對right中的圖片定位值計算:
- 計算公式:等比例運動,mark運動的實時值/mark運動的最大值==img運動的實時值/img運動的最大值;
//mark運動的實時值為:l和t //img運動的實時值設為:x和y //mark運動的最大值 var maxL=oLeft.offsetWidth-oMark.offsetWidth; var maxT=oLeft.offsetHeight-oMark.offsetHeight; //img運動的最大值 var maxImgL=oRight.offsetWidth-oImg.offsetWidth; var maxImgT=oRight.offsetHeight-oImg.offsetHeight; //計算公式:l/maxL==x/maxImgL t/maxT==y/maxImgT x=-l/maxL*maxImgL=-l/(oLeft.offsetWidth-oMark.offsetWidth)*(oRight.offsetWidth-oImg.offsetWidth); y=-l/maxT*maxImgT=-t/(oLeft.offsetHeight-oMark.offsetHeight)*(oRight.offsetHeight-oImg.offsetHeight);
- 注:計算出來的值為數字,設置時必須加單位;
- 代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>放大鏡實例</title> <style> *{ margin: 0; padding: 0; } body{ margin: 50px; height: 1000px; } .left{ background: url("img/1.jpg") no-repeat center; background-size: 100%; } .left{ width: 215px; height: 215px; float: left; position: relative; margin: 20px; } .left .mark{ width: 50%; height: 50%; background-color: yellow; opacity: 0.6; filter: alpha(opacity:60); position: absolute; display: none; } .right{ width: 300px; height: 300px; float: left; margin: 20px; position: relative; border: 1px solid red; overflow: hidden; display: none; } .right img{ width: 200%; height: 200%; position: absolute; } </style> </head> <body> <div class="left" id="left"> <div class="mark" id="mark"></div> </div> <div class="right" id="right"><img src="img/1.jpg" alt=""></div> <script> var oLeft=document.getElementById("left"); var oRight=document.getElementById("right"); var oMark=document.getElementById("mark"); var oImg=document.getElementsByTagName("img")[0]; oLeft.onmouseover=function () { oRight.style.display=oMark.style.display="block"; }; //光標在運動中觸發的是子級的onmousemove事件,通過冒泡將父級onmousemove事件觸發;進而mark移動位置; oLeft.onmousemove=function (e) { e=e||window.event; var l=e.clientX-this.offsetLeft-this.clientLeft-oMark.offsetWidth/2; var t=e.clientY-this.offsetTop-this.clientTop-oMark.offsetHeight/2; //邊界值判斷 var maxL=this.offsetWidth-oMark.offsetWidth; var maxT=this.offsetHeight-oMark.offsetHeight; if(l<=0){ l=0; }else if(l>=maxL){ l=maxL; } if(t<=0){ t=0 }else if(t>=maxT) { t = maxT; } //分別設置oMark和oImg的位置 //公式:等比例運動 oMark.style.left=l+"px"; oMark.style.top=t+"px"; oImg.style.left=-(oImg.offsetWidth-oRight.offsetWidth)/(this.offsetWidth-oMark.offsetWidth)*l+"px"; oImg.style.top=-(oImg.offsetHeight-oRight.offsetHeight)/(this.offsetHeight-oMark.offsetHeight)*t+"px"; }; oLeft.onmouseout=function () { oRight.style.display=oMark.style.display="none"; } </script> </body> </html>
1.2 放大鏡實例代碼優化
- 代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>放大鏡實例</title> <style> *{ margin: 0; padding: 0; } .wrap{ width: 400px; height: 400px; margin-top: 1000px; margin-bottom: 100px; margin-left: 20px; position: relative; } .wrap .smallimg{ width: 400px; height: 400px; background: url("./img/2.png") no-repeat; background-size: 100%; } .wrap .bigimg{ width: 500px; height: 500px; border: 2px solid lightpink; position: absolute; left: 450px; top: -50px; overflow: hidden; display: none; } .wrap .bigimg img{ position: absolute; left: 0; top: 0; } </style> </head> <body> <div class="wrap"> <div class="smallimg"></div> <div class="bigimg"><img src="./img/3.png" alt="bigimg"></div> </div> <script src="../toolLibrary/myutils.js"></script> <script src="../toolLibrary/myEvent.js"></script> <script> var oWrap=document.getElementsByTagName("div")[0]; var oSmall=oWrap.getElementsByTagName("div")[0]; var oBig=oWrap.getElementsByTagName("div")[1]; var obImg=oBig.getElementsByTagName("img")[0]; var obimgmaxLeft=null; var obimgmaxTop=null; var osObj=utils.offset(oSmall); var oP=null; var maxoLeft=null; var maxoTop=null; on(oSmall,"mouseenter",toEnter); on(oSmall,"mousemove",toMove); on(oSmall,"mouseleave",toLeave); function toEnter() { console.log("in"); oP=document.createElement("p"); utils.css(oP,{ width: 100, height: 100, backgroundColor:"yellow", opacity: 0.5, position: "absolute", cursor: "move" }); this.appendChild(oP); oBig.style.display="block"; maxoLeft=this.clientWidth-oP.offsetWidth; maxoTop=this.clientHeight-oP.offsetHeight; obimgmaxLeft=obImg.offsetWidth-oBig.clientWidth; obimgmaxTop=obImg.offsetHeight-oBig.clientHeight; } function toMove(e) { console.log("moving"); var curLeft=e.pageX; var curTop=e.pageY; var osLeft=osObj.left; var osTop=osObj.top; var oLeft=curLeft-osLeft-this.clientLeft-oP.offsetWidth/2; var oTop=curTop-osTop-this.clientTop-oP.offsetHeight/2; //設置邊界值 if(oLeft<0){ oLeft=0; }else if(oLeft>maxoLeft){ oLeft=maxoLeft; } if(oTop<0){ oTop=0; }else if(oTop>maxoTop){ oTop=maxoTop; } //大圖成比例移動 var obigcurLeft=oLeft/maxoLeft*obimgmaxLeft; var obigcurTop=oTop/maxoTop*obimgmaxTop; //oP設置實時位置 utils.css(oP,{ left:oLeft, top:oTop }); //bigImg設置實時位置 utils.css(obImg,{ left: -obigcurLeft, top: -obigcurTop }) } function toLeave() { console.log("out"); oBig.style.display="none"; this.removeChild(oP); oP=null; } </script> </body> </html>
2 產品展示實例
- 需求:當鼠標移出img元素上時,顯示該元素的大圖,然后大圖隨著光標的移動而移動,當移出后,大圖消失
- 思路:
- 利用jQuery的鏈式操作;
- 添加鼠標移入事件,移出事件,移動事件;
- 不能利用事件委托,事件委托會出現問題,當光標從img上移出到img間距中,這個過程中,會觸發移出事件,但是還會觸發div的移入事件,所以大圖不會消失;
- 代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>產品展示實例</title> <style> *{ margin: 0; padding: 0; } #div1{ width: 668px; height: 86px; margin: 20px auto; position: relative; } #div1 img{ width: 150px; height: auto; border: 1px solid black; float: left; } #div1 img+img{ margin-left: 20px; } #div1 p{ position: absolute; left: 0; top: 0; display: none; } #div1 p img{ display: block; width: 300px; } </style> </head> <body> <div id="div1"> <img src="img1/001.jpg" bigImg="img1/001-01.jpg" alt=""> <img src="img1/002.jpg" bigImg="img1/002-02.jpg" alt=""> <img src="img1/003.jpg" bigImg="img1/003-03.jpg" alt=""> <img src="img1/004.jpg" bigImg="img1/004-04.jpg" alt=""> <p> <img src="img1/004-04.jpg" alt=""> </p> </div> <script src="jquery.js"></script> <script> //需求:當鼠標移出img元素上時,顯示該元素的大圖,然后大圖隨著光標的移動而移動,當移出后,大圖消失 //思路:jQuery的鏈式操作 $("#div1 img").mouseover(function (e) { e=e||window; var target=e.target ||e.srcElement; $("#div1 p").show().find("img").attr("src",$(target).attr("bigImg")) }).mousemove(function (e) { e=e||window; var target=e.target ||e.srcElement; $("#div1 p").css({ left:e.clientX-$("#div1").offset().left+10, top:e.clientY-$("#div1").offset().top+10 }) }).mouseout(function () { $("#div1 p").hide() }) </script> </body> </html>
2.2 原生JS版產品展示實例
- 原生JS制作代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>產品展示實例</title> <style> *{ margin: 0; padding: 0; } h1{ width: 860px; height: 50px; line-height: 50px; text-align: center; margin: 30px auto; } .wrap{ display: flex; width: 860px; margin: 0 auto; position: relative; } .wrap img{ width: 200px; height: 112px; } .wrap img+img{ margin-left: 20px; } .wrap .move{ width: 400px; height: 224px; background: url("img1/001-01.jpg") no-repeat; background-size: cover; position: absolute; left: 0; top: 0; display: none; border: 2px solid red; } </style> </head> <body> <h1>產品展示欄</h1> <div class="wrap"><img src="img1/001.jpg" realImg="./img1/001-01.jpg" alt=""/><img src="img1/002.jpg" realImg="./img1/002-02.jpg" alt=""/><img src="img1/003.jpg" realImg="./img1/003-03.jpg" alt=""/><img src="img1/004.jpg" realImg="./img1/004-04.jpg" alt=""/> <div class="move"></div> </div> <script src="../toolLibrary/myutils.js"></script> <script src="../toolLibrary/myEvent.js"></script> <script> var oWrap=utils.getByClass("wrap")[0]; var oMove=utils.getByClass("move")[0]; var owtbLeft=utils.offset(oWrap).left; var owtbTop=utils.offset(oWrap).top; on(oWrap,"mouseover",toOver); on(oWrap,"mousemove",toMove); on(oWrap,"mouseout",toOut); function toOver(e) { var ele=e.target; if(ele.className==="wrap") return; var realImg=ele.getAttribute("realImg"); utils.css(oMove,{ display:"block", backgroundImage:"url("+realImg+")" }); } function toMove(e) { var eLeft=e.pageX; var eTop=e.pageY; var omLeft=eLeft-owtbLeft-oWrap.clientLeft; var omTop=eTop-owtbTop-oWrap.clientTop; utils.css(oMove,{ left:omLeft+10, top:omTop+20 }); } function toOut() { oMove.style.display="none"; } </script> </body> </html>
3 拖拽實例
3.1 拖拽實例初級版
- 需求:實現div元素在容器中拖拽移動
- 問題:1)當鼠標移動太快時,光標會脫離div,此時div會停止;2)當body有內容時,當鼠標移動到文字上會默認將其選中,默認對文字進行拖拽,此時會出錯;
- 思路:
- 添加onmousedown和onmousemove和onmouseup事件;
- 當鼠標按下事件觸發后,計算出初始位置時,光標在div中的距離,然后放在div的私有屬性上,然后在鼠標按下事件中觸發鼠標移動事件和鼠標抬起事件;
- 解決鼠標移動太快,會移出div的問題:在IE瀏覽器中使用setCapture將焦點捕獲到div上,在標準瀏覽器中,將鼠標移動事件和鼠標抬起事件添加到document上;
- 解決移動過程中默認選中文字的問題:在IE中已經焦點捕獲到div上,所以不會出現問題;在標準瀏覽器中,要取消默認事件;
- 在鼠標移動事件中,計算移動后位置的定位數值,進而實現移動效果
- 在鼠標抬起事件中要釋放事件,在IE中要釋放焦點捕獲,用releaseCapture;在標準瀏覽器下給document添加的事件釋放;
- 知識點:
- 焦點捕獲:setCapture,給一個元素設置焦點捕獲后,讓焦點永遠在該元素上;
- 釋放焦點捕獲:releaseCapture
- 兼容性問題:IE瀏覽器支持;fireFox火狐瀏覽器支持屬性,但是沒有效果;其他標準瀏覽器不支持;
- 注意點:
- up函數中的this指向問題,this指向不同的元素;
- 事件的釋放問題,賦值為null;
- 代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>拖拽</title> <style> body{ width: 500px; height: 1000px; font-size: 30px; } div{ width: 200px; height: 200px; background-color: red; position: absolute; left: 0; top: 0; } </style> </head> <body> fiefehisssjfjsfsdigheifefiefflsjfilfjskljfiefehisssjfjsfsdigheifefieffl sjfiengeifjiwedsnfiefnefkelfjskljfiefehifiefehisssjfjsfsdigheifefiefflsjfilfjskljfiefehi sssjfjsfsdigheifefiefflsjfiengeifjiwedsnfiefnefkelfjskljfiefehifiefehisssjfjsfsdigheifefiefflsjfilfjskljfiefehisssjfjs fsdigheifefiefflsjfiengeifjiwedsnfiefnefkelfjskljfiefehifiefehisssjfjsfsdigheifefiefflsjfilfjskljfiefehisssjfj sfsdigheifefiefflsjfiengeifjiwedsnfiefnefkelfjskljfiefehi <div id="div1"></div> <script> var oDiv=document.getElementById("div1"); oDiv.onmousedown=down; function down(e) { e=e||window.event; this.disX=e.clientX-this.offsetLeft; this.disY=e.clientY-this.offsetTop; if(this.setCapture){//IE瀏覽器 this.setCapture(); this.onmousemove=move; this.onmouseup=up; }else{//標準瀏覽器 var _this=this; document.onmousemove=function (e) { move.call(_this,e); }; document.onmouseup=up;//傳入的this為document; e.preventDefault();//阻止默認事件;防止選中文字; } } function move(e) { //在調用的時候,要始終保證里面的this為div元素; e=e||window.event; this.style.left=e.clientX-this.disX+"px"; this.style.top=e.clientY-this.disY+"px"; } function up() { //此時進入調用up后傳入的this可以不同; if(this.releaseCapture){ this.releaseCapture();//釋放焦點捕獲; } this.onmousemove=null; this.onmouseup=null; } </script> </body> </html>
3.2 拖拽實例終極版
- 知識點:
- 通過bind預處理的函數與原來的函數,二者不再是同一個函數體,是不同的地址;在函數解綁時,會出現問題;
- 解決方法:可以將bind預處理的函數綁定在原函數的靜態屬性上,這樣在解綁時,就可以通過靜態屬性拿到函數體;
- 代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>拖拽實例終極版</title> <style> body{ height: 1000px; font-size: 30px; } div{ width: 200px; height: 200px; background-color: red; position: absolute; left: 0; top: 0; } </style> </head> <body> fiefehisssjfjsfsdigheifefiefflsjfilfjskljfiefehisssjfjsfsdigheifefieffl sjfiengeifjiwedsnfiefnefkelfjskljfiefehifiefehisssjfjsfsdigheifefiefflsjfilfjskljfiefehi sssjfjsfsdigheifefiefflsjfiengeifjiwedsnfiefnefkelfjskljfiefehifiefehisssjfjsfsdigheifefiefflsjfilfjskljfiefehisssjfjs fsdigheifefiefflsjfiengeifjiwedsnfiefnefkelfjskljfiefehifiefehisssjfjsfsdigheifefiefflsjfilfjskljfiefehisssjfj sfsdigheifefiefflsjfiengeifjiwedsnfiefnefkelfjskljfiefehi <div></div> <script src="../toolLibrary/myutils.js"></script> <script src="../toolLibrary/myEvent.js"></script> <script> var oDiv=document.getElementsByTagName("div")[0]; on(oDiv,"mousedown",mouseDown); function mouseDown() { console.log("mousedown"); //鼠標按下事件觸發后,綁定兩個事件,移動事件和鼠標抬起事件 if(this.setCapture){ //IE瀏覽器支持 console.log("ie"); this.setCapture();//焦點捕獲 on(this,"mousemove",mouseMove);//將事件綁定在this上; on(this,"mouseup",mouseUp); }else{ //其他瀏覽器中綁定在document中, console.log("qita"); //mouseMove.bind(this)后的地址與mouseMove地址不同;為兩個不同的函數體 //把用bind預處理后的函數賦值在mouseMove函數的靜態屬性fn上;用于解綁函數; mouseMove.fn=mouseMove.bind(this); on(document,"mousemove",mouseMove.fn); on(document,"mouseup",mouseUp); } } function mouseMove(e) { console.log("move"); var eLeft=e.clientX; var eTop=e.clientY; var oLeft=eLeft-this.offsetWidth/2; var oTop=eTop-this.offsetHeight/2; var maxoLeft=document.body.offsetWidth-this.offsetWidth; var maxoTop=document.body.offsetHeight-this.offsetHeight; e.preventDefault();//阻止默認選中文字事件; //邊界值判斷 if(oLeft<0){ oLeft=0; }else if(oLeft>maxoLeft){ oLeft=maxoLeft; } if(oTop<0){ oTop=0; }else if(oTop>maxoTop){ oTop=maxoTop; } utils.css(this,{ left: oLeft, top: oTop }); } function mouseUp() { console.log("mouseup"); //鼠標抬起事件觸發后,mousemove解綁; if(this.releaseCapture){ this.releaseCapture();//釋放焦點捕獲; off(this,"mousemove",mouseMove); }else{ off(this,"mousemove",mouseMove.fn);//解綁靜態屬性,即bind預處理的函數; } off(this,"mouseup",mouseUp); } </script> </body> </html>
4 bind兼容版myBind()封裝
- 原因:類函數原型上存在三個屬性方法,call(),apply(),bind(),其中call(),apply()兩個方法時改變this指向并傳入參數后,函數就立即執行了,在一些場景中,不希望函數立即執行,比如在點擊事件中,給事件賦值,賦值為函數的定義階段,當點擊行為發生時,才讓函數執行,所以需要進行函數的預處理,讓其改變了this指向和傳入參數后,返回的還是函數定義階段,所以只能用bind()方法,但是問題就是,bind()只在標準瀏覽器(包括IE9,10)下才支持,在IE瀏覽器(IE6,7,8)下不支持,會報錯;所以需要封裝一個都兼容的方法;
- 目的:封裝一個跟bind()方法功能一樣,和使用方法也一樣的方法,就是為了兼容IE瀏覽器;
- 分析bind()方法:
- bind是類函數Function原型上的公有屬性,所有的函數實例均可以使用此屬性方法;
- bind()方法使用時的代碼:
函數名.bind(thisArg,...)
- 參數:第一個參數必傳,為改變后的this,后面的參數可傳可不傳,為函數中的實參值;
- bind()方法的實質為:"函數名.bind"這個函數的執行,將傳入的參數進行一些處理,返回一個匿名函數;
- "函數名.bind()"中的this指向函數名實例;
- 封裝兼容版的myBind()函數
- 封裝思路:
- 封裝位置:封裝在大Function類的原型上
- 參數:至少要傳入一個參數,改變this指向,剩余的參數用arguments獲取
- 函數體封裝:判斷類函數原型上是否存在bind屬性,來判斷瀏覽器種類,確定在標準瀏覽器下執行系統bind,在IE瀏覽器下封裝新的函數
- 返回值:匿名函數;
- 知識點:
- 類數組(arguments)轉數組:利用數組原型上的slice屬性,通過call代表其函數中的this指向,來實現克隆,進而獲取一個真數組
- 代碼:
outArg=[].slice.call(arguments,1);
,其中1代表從索引為1的元素開始截取,形成一個新數組;
- 代碼:
- 在標準瀏覽器下,直接使用bind方法,但是在給bind()中傳入實參時,outArg為一個數組,不能直接往里面傳,所以需要apply來往里面傳數組,將this.bind作為一個函數,用apply傳參,第一個參數為改變this指向的,傳入當前的this,也就是函數實例,但不能傳null;為什么不能傳null,有待解答;
- 代碼:
this.bind.apply(this,[thisArg].concat(outArg));
- 代碼:
- 類數組(arguments)轉數組:利用數組原型上的slice屬性,通過call代表其函數中的this指向,來實現克隆,進而獲取一個真數組
- 注意點:
- 在IE瀏覽器下,需要返回一個匿名函數,但是當匿名函數執行的時候,函數中的this不再是函數實例,而是執行時點前面的元素,或是事件觸發的元素;所以需要創建變量_this來保存住外面的this,然后在匿名函數中使用_this,使用apply方法來實現this指向的改變和實參的傳入;
- 當myBind使用在事件賦值時,會出現問題,因為在事件觸發時,會向賦值中的匿名函數中傳入一個實參,為事件對象,所以必須myBind在封裝時,必須考慮事件對象實參的傳入,由于在IE瀏覽器中,事件觸發不會向匿名函數中傳入事件對象實參,但是匿名函數中需要使用事件對象時,就需要通過window.event獲取,所以必須用apply將事件對象innerArg傳入函數實例對象中;
- 問題:
- 在IE瀏覽器下,myBind不使用在事件賦值中,此時在函數中arguments獲取的所有實參值組成的類數組中的最后一項為null,因為不存在事件對象,但是還是會存在此項,值為null;
- 代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>bind函數封裝</title> <style> div{ width: 200px; height: 200px; background-color: red; } </style> </head> <body> <div id="div"></div> <script> //封裝在類函數的原型上 Function.prototype.myBind=function (thisArg) { var outArg=[].slice.call(arguments,1);//指的是將類數組arguments轉化為數組,然后從索引為1的元素開始截?。猾@取除了thisArg以外的其他實參組成的數組; if("bind" in Function.prototype){ //如果瀏覽器支持bind屬性,則用系統自帶的bind屬性 //bind()方法就是將傳入的參數經過一系列操作,然后到達目的,怎么做不用管,只要保證傳入參數就行,此時就是為了給bind傳參,但是outArg為一個數組,不能直接給bind傳,所以需要用apply來傳入數組,但是apply不需要改變this.bind這個函數中的this,所以傳參為this,后面以一個數組的形式傳入實參,所以需要把thisArg與outArg拼接; return this.bind.apply(this,[thisArg].concat(outArg));//此時apply中必須串this,不能穿null; } //在IE瀏覽器下,要返回一個匿名函數 var _this=this; return function (e) { //事件在觸發時,在標準瀏覽器下,會默認向匿名函數中傳入一個實參,為事件對象,在IE瀏覽器下,不會默認傳入實參,所以獲取事件對象需要用window.event; //此時實際上e肯定沒有值,但是為了以后的代碼更新,所以判斷一下; //匿名函數在執行時里面的this一定不是實例; var innerArg=arguments.length==0?window.event:e; return _this.apply(thisArg,outArg.concat(innerArg)); } }; var oDiv=document.getElementById("div"); var obj={}; function fn(n,m) { console.log(arguments); console.log(this); console.log(n+m); //console.log(e.clientX,e.clientY); } //1 函數一般執行 var res=fn.myBind(obj,2,3); res(); /*在IE瀏覽器下的打印結果: * {0: 2, 1: 3, 2: null} * [Object] * 5 * */ //2 點擊事件觸發函數執行 oDiv.onclick=fn.myBind(obj,2,3); /*在IE瀏覽器下的打印結果: * {0: 2, 1: 3, 2: MouseEvent} * [Object] * 5 * */ </script> </body> </html>
- 封裝思路:
5 事件對象基礎解讀
- 事件對象的獲取
- 若事件賦值為:匿名函數,若匿名函數中需要使用事件對象,就必須在匿名函數中設置形參e,然后在匿名函數體才能使用e來獲取事件對象,如果不需要使用事件對象,就不用在匿名函數中設置形參e;
//此時事件賦值了一個匿名函數,在匿名函數要設置形參e,目的是在匿名函數體中用e來代表傳入的實參 oDiv.onmousemove=function (e) { move.call(this,e);//call用來改變this指向和傳入實參e;此時的e作為實參,傳入到move中,必須要穿; }; function move(e) {//此時形參e就接受到傳入的實參e; e=e||window.event; this.style.left=e.clientX-this.disX+"px"; this.style.top=e.clientY-this.disY+"px"; } //總結:call里面必須要傳入實參e,如果不傳入實參e,在move函數體中,拿到e為undefined,原因是:函數中如果設置形參,形參相當于聲明加定義,會進行預解釋,如果沒有傳入實參,那形參時為undefined,相當于var e=undefined,現在的e為函數的私有變量,跟外界沒有任何關系;如果不設置形參e,在move函數執行后的私有作用域中找不到e,它就會通過作用域鏈向其上一級作用域查找,永遠不會去onmousemove事件賦值中的匿名函數查找,得不到里面的e,它里面的e也是自己的私有變量
- 若事件賦值為:
fn.myBind()
,那么不管在fn中是否需要事件對象,在括號中,都不需要傳入e。如果在fn函數中需要事件對象,就設置形參e,如果不需要事件對象,就不用設置形參e;
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>事件與myBind函數</title> <style> div{ width: 200px; height: 200px; background-color: red; } </style> </head> <body> <div id="div1">11111</div> <script> Function.prototype.myBind=function (thisArg) { var outArg=[].slice.call(arguments,1); if("bind" in Function.prototype){ return this.bind.apply(this,[thisArg].concat(outArg)); } var _this=this; return function (e) { var innerArg=arguments.length==0?window.event:e; return _this.apply(thisArg,outArg.concat(innerArg)); } }; var oDiv=document.getElementById("div1"); var age=3; var obj={age:1}; //1 move中不使用事件對象,不用設置e; oDiv.onclick=move.myBind(null,2,3); function move(n,m) {//此時形參e1就接受到傳入的實參e; console.log(this.age);//打印結果為:3 console.log(n+m); } //2 move中使用事件對象,則設置形參e oDiv.onclick=move.myBind(obj,2,3); function move(n,m,e) {//此時形參e1就接受到傳入的實參e; console.log(this.age);//打印結果為:1 console.log(n+m); console.log(e.clientX,e.clientY); } //無論在move函數中是否使用事件對象,在myBind()括號中都不傳e; </script> </body> </html>
6 點擊事件知識點
- 點擊事件賦值
- 賦值為匿名函數:點擊事件觸發時,匿名函數中的this為被點擊元素,而且在標準瀏覽器下,會默認向匿名函數傳事件對象實參;
- 賦值為函數名:點擊事件觸發時,函數體內的this為被點擊元素,而且在標準瀏覽器下,會默認向函數體內傳入事件對象參數;
- 賦值為
fn.bind()
:點擊事件觸發時,在標準瀏覽器下,會默認向函數體fn內傳入事件對象實參;但函數體內的this不再是被點擊元素,而是自己的this,一般為window;- bind(null)中改變this指向的參數為:null,此時的fn中的this為window;意思是沒有改變fn中的this;
- bind(this)中改變this指向的參數為:this,此時狀態下的this指的是window,所以fn中的this為window;意思是將fn中的this改變為window;將fn中的this改變了;
- bind(oDiv)中改變this指向的參數為:oDiv,此時fn中的this被改變為oDiv元素;
<script> var oDiv=document.getElementById("div1"); function move(e) { console.log(this); console.log(e) } oDiv.onclick=move.bind(null);//打印結果為:window; oDiv.onclick=move.bind(this);//打印結果為:window; oDiv.onclick=move.bind(oDiv);//打印結果為:oDiv; </script>
- 點擊事件實例
- 需求:oDiv添加點擊事件,當點擊事件觸發時,執行move1()函數,保證move1函數中的this為該點擊元素,并在move1函數中獲取事件對象;
- 方法一:給點擊事件賦值為匿名函數,在匿名函數中執行move1函數,通過call來改變this指向和傳入實參;
- 注意兼容處理,在IE瀏覽器下,點擊事件不會向匿名函數傳事件對象實參,所以需要通過window.event獲??;
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>點擊事件驗證</title> <style> div{ width: 200px; height: 200px; background-color: red; } </style> </head> <body> <div id="div1">1111</div> <script> //需求:給oDiv添加點擊事件,當點擊事件觸發時,執行move1()函數,保證move1函數中的this為該點擊元素,并在move1函數中獲取事件對象; var oDiv=document.getElementById("div1"); oDiv.onclick=function (e) { move1.call(this,2,3,e); //move1.call(null,2,3,e);//若此時call中賦值為null,則move1執行時,函數中的this為window; }; function move1(n,m,e) { e=e||window.event;//在IE瀏覽器中,點擊事件觸發不會向匿名函數傳事件對象實參,所以獲取到的e為undefined;此代碼就會執行window.event獲取到事件對象; console.log(this); console.log(n+m); console.log(e.type); } //打印結果為:oDiv,5,click; //總結:點擊事件后面賦值為匿名函數,當事件觸發時,在匿名函數中的this為該元素,在標準瀏覽器下,會默認向匿名函數中傳入事件對象實參;在IE瀏覽器下,不會傳入事件對象實參; </script> </body> </html>
- 方法二:給點擊事件賦值bind方法
- 注意1:bind方法只能在標準瀏覽器中使用,IE瀏覽器不支持
- 注意2:點擊事件觸發后,move1中的this不再是被點擊元素,需要通過bind改變為元素,不要在傳參的時候添加e;
<script> //需求:給oDiv添加點擊事件,當點擊事件觸發時,執行move1()函數,保證move1函數中的this為該點擊元素,并在move1函數中獲取事件對象;用bind方法實現 var oDiv=document.getElementById("div1"); //oDiv.onclick=move1.bind(this,2,3);//此時this為window,函數執行時,獲取到的this也為window; //oDiv.onclick=move1.bind(null,2,3);//此時賦值null,在函數執行時,函數體里獲取的this為window; //oDiv.onclick=move1;//此時賦值函數名,在函數執行時,函數體里獲取的this為oDiv元素; function move1(n,m,e) { console.log(this); console.log(n+m); console.log(e.type); } oDiv.onclick=move1.bind(oDiv,2,3); //打印結果為:oDiv,5,click; //總結:bind方法,是對函數進行預處理,需注意的是:1)添加bind后,如果this執行賦值為null;那么在點擊事件發生時,在函數體內獲取的this為window; </script>
- 方法三:給點擊事件賦值自己封裝的myBind方法
- myBind方法本質上就是模仿bind方法,旨在達到跟bind一樣的效果,保證IE瀏覽器下能夠使用,使所有瀏覽器都兼容;
- 使用:跟bind方法使用一樣
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>點擊事件與myBind函數</title> <style> div{ width: 200px; height: 200px; background-color: red; } </style> </head> <body> <div id="div1">11111</div> <script> //myBind封裝兼容版 //本質上就是模仿bind方法,旨在達到跟bind一樣的效果,保證IE瀏覽器下能夠使用,使所有瀏覽器都兼容; //使用:跟bind方法使用一樣 Function.prototype.myBind=function (thisArg) { var outArg=[].slice.call(arguments,1); if("bind" in Function.prototype){ return this.bind.apply(this,[thisArg].concat(outArg)); } var _this=this; return function (e) { var innerArg=arguments.length==0?window.event:e; return _this.apply(thisArg,outArg.concat(innerArg)); } }; //實現需求 var oDiv=document.getElementById("div1"); function move1(n,m,e) { e=e||window.event; console.log(this); console.log(n+m); console.log(e.type); } oDiv.onclick=move1.myBind(oDiv,2,3); //打印結果為:oDiv,5,click; </script> </body> </html>