第九周第四天笔记

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有内容时,当鼠标移动到文字上会默认将其选中,默认对文字进行拖拽,此时会出错;
  • 思路:
    1. 添加onmousedown和onmousemove和onmouseup事件;
    2. 当鼠标按下事件触发后,计算出初始位置时,光标在div中的距离,然后放在div的私有属性上,然后在鼠标按下事件中触发鼠标移动事件和鼠标抬起事件;
    3. 解决鼠标移动太快,会移出div的问题:在IE浏览器中使用setCapture将焦点捕获到div上,在标准浏览器中,将鼠标移动事件和鼠标抬起事件添加到document上;
    4. 解决移动过程中默认选中文字的问题:在IE中已经焦点捕获到div上,所以不会出现问题;在标准浏览器中,要取消默认事件;
    5. 在鼠标移动事件中,计算移动后位置的定位数值,进而实现移动效果
    6. 在鼠标抬起事件中要释放事件,在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));
    • 注意点:
      • 在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>
      
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • "use strict";function _classCallCheck(e,t){if(!(e instanc...
    久些阅读 2,027评论 0 2
  • 事件 1 事件流 冒泡:从该元素开始由里向外触发一类事件,事件对象中的事件源为该元素,触发同一类事件,在子级父级祖...
    果木山阅读 192评论 0 0
  • 1. tab列表折叠效果 html: 能源系统事业部 岗位名称: 工作地点 岗位名...
    lilyping阅读 1,829评论 0 1
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,421评论 1 45
  • 遇到困难和挑战,我想打退堂鼓的时候,就会对自己说“没关系,我不想取得多大成就,只想做个普通人”。那时候以为做一个普...
    冬素阅读 808评论 2 1