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>