事件笔记整理

事件

一、事件绑定方式
  • HTML标签属性绑定

    • 优点:结构简单,代码简洁

    • 缺点:

      • 01:JS混在HTML中写,不方便维护
      • 02:绑定的事件处理函数,必须是全局变量,有变量名污染的风险。
      • 03:内部获取this需要通过传参才能拿到,直接获取只能拿到window,操作不便。
      • 04:JS以事件为驱动的设计场景下,想实现事件处理函数的组合,代码结构复杂。
    • 举例

    <body>
        <button onclick='btn()'>点击</button>
    </body>
    <script>
    	function fn(){
            alert('被点击了')
        }
        //删除方式
        el.setAttribute('onclick','');
    </script>
    
  • 通过DOM元素节点属性绑定

    • 优点:

      1. JS和HTML分离,方便维护

      2. 分割作用域,放置变量名污染

      3. 内部的this可以直接获取到节点对象,不需要传参

    • 缺点:不能实现多个事件处理函数的叠加绑定,后面绑定的会覆盖前面的。JS的事件处理函数设计内部代码会比较冗长。结构相对复杂。

    • 应用场景:一般主要使用的绑定方式,可以用在任何需要绑定事件的场景。

    • 删除事件:node.onType = null;

    • 举例

    <body>
        <button id="btn"></button>
    </body>
    <script>
        //方式一
    	btn.onclick = function(){}
        //方式二
        btn.onclick = fn;
        function fn()
    </script>
    
  • 事件侦听器绑定事件

    • 优点:

      1. JS和HTML分离

      2. 不受作用域限制

      3. 内部this指向调用对象

      4. 可以动态添加、动态删除事件处理函数,方便设计灵活的事件处理程序

    • 缺点:Ie8以下不支持,需要编写额外的兼容代码

    • 其他特点:

      1. 通过addEventListener绑定多个事件时,执行顺序与绑定顺序保持一致

      2. 通过addEventListener多次绑定相同的事件(相同事件:必须三个参数都一致),后面书写的会被丢弃,只有一个生效。

      3. 通过addEventListener绑定多个事件时,只要三个参数中有一个参数不一样,就是新创建了一个侦听器。

      4. 在删除侦听事件处理函数时,通过removeEventListener(‘type’,函数名,true);三个参数必须给定,如果通过匿名函数绑定的侦听器,是不能被删除的,如果需要能够删除,那就要用命名函数进行函数绑定。

    • 举例应用

    <body>
        <button onclick='btn'>点击</button>
        <button onclick='btn1'>点赞</button>
        <button onclick='btn2'>删除事件</button>
        <button onclick='btn3'></button>
    </body>
    <script>
        /*
        参数一:"click"
        参数二:匿名函数 || 命名函数 || 对象
        */
    	//两个侦听绑定函数之间传参
        btn.addEventListener('click',function(){
            alert('被点击了');
            var name = '张三';
            this.name = name;//给btn按钮添加name属性
        })
        btn.addEventListener('click',function(){
            this.innerHTML = this.name;//将btn name属性的值拿出来。放到按钮中
        })
        /*****************************************************/
        
        //实现点赞效果
        btn1.addEventListener('click',zan);//这里如果写true或者false,下面删除的地方也必须写上对应的true或false
        function zan(){
            this.innerHTML = '已赞';
            this.removeEventListener('click',zan);//函数最后,将该事件删除
        }
        /*****************************************************/
        //当删除事件时,三个参数必须相同
        btn2.addEventListener('click',fn1,true);//1标记
        btn2.addEventListener('click',fn1,false);//2标记
        function fn1(){
            alert('111');
        }
        //删除事件
        btn2.removeEventListener('click',fn1,true);//删除的是1标记
        /*****************************************************/
        
        //拓展:addEventLisenter的第二个参数除了可以是匿名函数、命名函数外,还可以是对象
        var obj = {
            handleEvent : function(){
                console.log(this);  //指向window
                alert('dddd')
            }
        }
        btn3.addEventListener('click',obj);
        
    </script>
    
二、事件侦听器兼容写法
  • ie9以上写法

    el.addEventListener('click',fn(,true))
    
  • ie8及以下写法

    el.detachEvent('onclick',fn)
    
  • 兼容写法

    function bindEvent(el, type, callback) {
        if (el.addEventListener) {
            //IE9以上, 其他非IE浏览器
            el.addEventListener(type, callback)
        } else {
            // IE8 及以下
            el.attachEvent('on' + type, callback)
        }
    }
    
    function removeEvent(el, type, callback) {
        if (el.removeEventListener) {
            //非IE8以下
            el.removeEventListener(type, callback)
        } else {
            //IE8以下
            el.detachEvent('on' + type, callback)
        }
    }
    
    //调用
    function dd() {
        alert('按钮被点击了');
    }
    bindEvent(btn1, 'click', dd);
    removeEvent(btn1, 'click', dd);
    

    IE8 使用 attachEvent绑定事件与 addEventListener的区别:

    ​ 1、attachEvent只支持两个参数,等效于addEventKListener第三参数为false.

    ​ 2、attachEvent注册事件与事件处理函数的执行顺序相反,先注册的后执行

    ​ 3、attachEvent注册的事件处理函数内部,this总是指向window

三、事件的传播流
  • 事件的传播流:

    ​ 1、浏览器时刻都在发生着事件流,和是否绑定了事件处理函数无关。

    ​ 2、事件流分为三个阶段

    ​ 阶段一:事件捕获阶段:在事件发生后,浏览器从根标签开始,一层一层向下查找,直到找到触发事件的节点对象。这个阶段就是查找事件触发节点对象的过程

    ​ 阶段二:事件的目标阶段:找到触发事件的目标节点后,记录事件类型,判断目标是否有该类型事件的处理函数,如果有执行。

    ​ 阶段三:事件的传播阶段(事件冒泡阶段):目标阶段结束后,将该类型的事件向上一层一层往回传播,每层节点对象都会判断是否绑定了该种类型事件的处理函数,如果绑定了就执行,执行结束后,接着向上传播,直到根节点结束。

  • 事件流引发的两个问题:

    ​ 1、阻止事件冒泡:在父子级嵌套情况下,都绑定了相同类型的事件处理函数,为了保证各自执行各自的,需要阻止事件冒泡。

    ​ 2、事件代理:利用事件冒泡,可以将子级的事件处理函数,绑定在父级的同类型事件处理函数内

  • 事件代理的优缺点:

    ​ 1、优点:
    (1)不用给新增的标签节点绑定事件
    (2)减少事件注册,节省内存

    ​ 2、缺点:
    (1)事件绑定不直观,如果代码被二次接手或者太长时间没有接触项目,无法再浏览器的事件侦听那里直接观察到事件绑定。
    (2)事件代理基于冒泡,如果内层结构阻止冒泡,事件代理就会失效。

事件代理代码

<body>
   <div class="box" id="box">
       <div class="box1">box1</div>
       <div class="box2">box2</div>
   </div>
</body>
<script>
   box.addEventListener('click',function(e){
       switch(e.target.className){
           case 'box1' : 
               box1Event(e.target);
               break;
           case 'box2' :
               box2Event(e.target);
               break;
       }
   })
   function box1Event(el){
       console.log(el);
   }
   function box2Event(el){
       console.log(el);
   }
</script>

事件代理进一步封装

<body>
    <div class="box" id="box">
        <div class="box1">box1</div>
        <div class="box2">box2</div>
    </div>
</body>
<script>
    // bindDelegate(父级,事件类型,'.box3',callback)
    box.addEventListener('click',function(e){
        // e.target : 触发事件的目标对象
        if(typeof this[e.target.className] == 'function')
        this[e.target.className](e.target)
    })
    box.box1 = function(el){
        console.log(el);
    }
    box.box2 = function(el){
        console.log(el);
    }
</script>
四、事件对象

事件对象:在用户触发事件时。传入事件对象,会向事件处理函数传递一个对象数据,该对象数据就称为事件对象,不同类型的事件,传入的事件对象各有差异,通过该事件对象,我们可以对事件进行精准控制,可以扩展事件处理函数功能,可以阻止事件冒泡,可以进行事件代理。

  • 基本获取方法

    • btn.onclick = function(e){
          console.log('我拿到e(事件对象)了')
      }
      
  • ie8写法

    • btn.onclick = function(e){
          //IE8不支持在事件调用时,传入事件对象,但是有一个全局变量event会被赋值成对应的事件对象。
          console.log(window.event)
          console.log('我拿到e(事件对象)了')
      }
      
  • 兼容写法

    • <body>
          <button id='btn1'>点击</button>
          <button id='btn2'>点击二</button>
          <button onclick='fn(event)'>点击三</button>
      </body>
      <script>
          btn1.onclick = function(e){
              //e  event  evt 可能的名称
              e = e || window.event; //兼容IE8
              //IE8不支持事件调用时,传入事件对象, 但是有一个全局变量 event会被赋值成对应的事件对象,因此需要写兼容
               console.log(e);//能拿到
          }
          btn2.addEventListener('click',function(e){
              console.log(e);//能拿到
          })
          function fn(e){
              console.log(e);//能拿到
          }
      </script>
      
五、阻止冒泡事件
1.阻止向上传递冒泡事件
  • 非ie8写法

    e.stopPropagation();
    
  • ie8写法(目前谷歌等其他浏览器也能使用)

    e.cancelBubble = true;
    
  • 兼容写法

// 阻止事件冒泡
if(e.stopPropagation){
    e.stopPropagation();  //非ie8
 }else{
    e.cancelBubble = true;//ie8
 }
2.阻止当前节点其他事件
  • DOM3定义事件冒泡阻止方法,可以处理阻止事件传播以外,还可以阻止当前节点其他事件

    e.stopImmediatePropagation();
    
  • 示例代码

  • <body>
        <div id="box1">
            <div id="box2"></div>
        </div>
        <div id="box3">
            <div id="box4"></div>
        </div>
    </body>
    <script>
        box1.onclick = function(){
            alert('box1');
        }
        box2.onclick = function(e){
            //兼容ie写法
            e = e || window.event;
            // 阻止事件冒泡
            if(e.stopPropagation){
                e.stopPropagation();  //非ie8
            }else{
                e.cancelBubble = true;//ie8
            }
            // e.stopPropagation();
            alert('box2');
        }
        
        box3.addEventListener('click',function(){
            alert("box3")
        })
        //给box4添加两个事件,因为 e.stopImmediatePropagation(),只会出现当前事件
        box4.addEventListener('click',function(e){
            e = e || window.event;
            // 阻止事件传播,还可以阻止当前节点其他事件
            e.stopImmediatePropagation(); 
            alert("box4")
        })
        box4.addEventListener('click',function(){
            alert("box44")
        })
    </script>
    
六、阻止系统默认事件
  • 非ie8写法

    e.preventDefault()
    
  • ie8写法

    e.returnValue = false
    
  • 兼容写法

    if(e.preventDefault){
       e.preventDefault();//非ie8写法
    }else{
       e.returnValue = false; //ie8写法
    }
    
  • 只能给标签绑定点击事件的时候使用(node.ontype )

    return false;
    
    • 侦听器绑定事件示例
    bindEvent(b,'click',function(e){
        //事件传播流兼容  
        e = e || window.event
        if(e.preventDefault){
            e.preventDefault(); //非ie8
        }else{
            e.returnValue = false;  //ie8 其他高级非ie8浏览器
        }
    })
    //处理侦听器绑定事件兼容
    function bindEvent(el, type, callback) {
        if (el.addEventListener) {
            //IE9以上, 其他非IE浏览器
            el.addEventListener(type, callback)
        } else {
            // IE8 及以下
            el.attachEvent('on' + type, callback)
        }
    }
    
    • 行间阻止系统默认事件
    function fn(e){
        if(e.preventDefault){
            e.preventDefault();
        }else{
            e.returnValue = false;
        }
        console.log(11111);
    }
    
    • 标签对象绑定事件阻止系统默认事件
    a.onclick = function(e){
        e = e || window.event
        console.log("1111");
        return false; //只能标签对象绑定事件能够使用
    }
    
七、事件代理

*将事件绑定在父级标签上,通过e.target(触发事件的目标对象)*获得当前点击的按钮,采用给父级标签添加属性,属性中绑定函数的方法,进行事件绑定。

box.addEventListener('click',function(e){
    // e.target : 触发事件的目标对象
    this[e.target.className](e.target)
})
box.box1 = function(el){
    console.log(el);
}
box.box2 = function(el){
    console.log(el);
}
八、文档加载事件
  • 监测文档及文档中资源加载完毕

    window.addEventListener('load',function(){
        console.log('文档及资源加载完毕');
    })
    
  • 监测文档加载完毕

    window.addEventListener('DOMContentLoaded',function(){
        console.log('文档加载完毕');
    })
    
九、鼠标事件类型
  • 单击事件 onclick

    <body>
        <button id='btn1'>单击</button>
    </body>
    <script>
    	btn1.addEventListener('click',function(){
            alert('单击');
        })
    </script>
    
  • 双击事件 ondblclick

    <body>
        <button id='btn2'>双击</button>
    </body>
    <script>	
    	btn2.addEventListener('dblclick',function(){
            alert('双击');
        })
    </script>
    
  • 鼠标按下 onmousedown

    <body>
        <button id='btn3'>鼠标按下</button>
    </body>
    <script>	
    	btn3.addEventListener('mousedown',function(){
            alert('鼠标按下');
        })
    </script>
    
  • 鼠标抬起来 onmouseup

    <body>
        <button id='btn4'>鼠标抬起</button>
    </body>
    <script>	
    	btn4.addEventListener('mouseup',function(){
            alert('鼠标抬起');
        })
    </script>
    
  • 鼠标移入 onmouseover

    <body>
        <button id='btn5'>鼠标移入</button>
    </body>
    <script>	
    	btn5.addEventListener('mouseover',function(){
            alert('鼠标移入');
        })
    </script>
    
  • 鼠标移出 onmouseout

    <body>
        <button id='btn6'>鼠标移出</button>
    </body>
    <script>	
    	btn6.addEventListener('mouseout',function(){
            alert('鼠标移出');
        })
    </script>
    
  • 不带冒泡鼠标移入和移出 onmouseenter(移入) onmouseleave(移出)

    <body>
        <div id='box'>
            <button id='btn7'>不带冒泡鼠标移入</button>
            <button id='btn8'>不带冒泡鼠标移出</button>
        </div>
    </body>
    <script>	
        box.addEventListener('mouseover',function(){
            alert('鼠标移入');
        })
        box.addEventListener('mouseout',function(){
            alert('鼠标移出');
        })
    	btn7.addEventListener('mouseenter',function(){
            alert('不带冒泡鼠标移入');
        })
        btn8.addEventListener('mouseleave',function(){
            alert('不带冒泡鼠标移出');
        })
    </script>
    
  • 鼠标移动 onmousemove

    <body>
        <button id='btn9'>鼠标移出</button>
    </body>
    <script>	
    	btn9.addEventListener('mousemove',function(){
            alert('鼠标移动');
        })
    </script>
    
  • 滚轮滚动 onmousewheel

    <body>
        <button id='btn10'>鼠标移出</button>
    </body>
    <script>	
    	btn9.addEventListener('mousewheel',function(e){
            //为了不让页面跟着滚动,所以在这里处理一下默认事件
            e.preventDefault();
            console.log('滚轮在动');
        })
    </script>
    
十、获取鼠标事件对象位置
  • 获取鼠标在屏幕中的坐标 screenX(Y)

  • 获取鼠标在浏览器可视区域中的坐标 clientX(Y)

  • 获取鼠标在文档中的坐标 pageX(Y)

  • 获取鼠标在点击块中的坐标 offsetX(Y)

    <!--获取坐标案例 样式自己设置-->
    <body>
        <!--在wrap中获取坐标-->
        <div id='wrap'></div>
        <div id='show'>
            屏幕坐标screen(XY):<span id="show1"></span><br>
            可视区域坐标client(XY):<span id="show2"></span><br>
            文档中坐标page(XY):<span id='show3'></span><br>
            目标对象中坐标offset(XY):<span id='show4'></span><br>
        </div>
    </body>
    <script>
    	wrap.addEventListener('mousemove',function(e){
            e = e || window.event;
            //获取鼠标在屏幕中的坐标
            showXY(e.screenX,e.screenY,show1);
            //获取鼠标在浏览器可视区域中的坐标
            showXY(e.clientX,e.clientY,show2);
            //获取鼠标在文档中的坐标
            showXY(e.pageX,e.pageY,show3);
            //获取鼠标在wrap中的坐标
            showXY(e.offsetX,e.offsetY.show4);
        })
        function showXY(x,y,el){
            el.innerHTML = `(${x}${y})`
        }
    </script>
    
十一、鼠标拖拽

拖拽步骤分解

  1. 绑定鼠标按下事件 onmousedown(绑定到盒子上)
  2. 获取鼠标坐标 坐标 = 鼠标针对点击盒子的偏移坐标 + 定位父级到文档
    • this._x = e.offsetX + getBoxToDom(this.offsetParent,‘Left’);
    • this._y = e.offsetY + getBoxToDom(this.offsetParent,‘Top’);
  3. 绑定鼠标移动事件 onmousemove (这里应该选择命名函数,方便后面删除移动事件)(绑定到document上)
  4. 绑定鼠标抬起事件 onmouseup (绑定到document上),删除移动事件
  5. 完善其余函数,移动事件函数(move),得到盒子到文档边缘距离函数(getBoxToDom).其中move函数中核心算法是设置拖拽盒子的left和top.(图解在下方 ,仅top方向)
    • box.style.left = e.pageX - box._x + ‘px’;
    • box.style.top = e.pageY - box._y + ‘px’;

示例

box.addEventListener('mousedown',function(e){
    e = e || window.event;
    //拿到鼠标的x  这里用this._x是为了在两个绑定事件中传参
    this._x = e.offsetX + getBoxToDom(this.offsetParent,'Left');
   	//拿到鼠标的y
    this._y = e.offsetY + getBoxToDom(this.offsetParent,'Top');
    //绑定移动事件  采用命名函数,方便后期删除
    document.addEventListener('mousemove',move);
})
document.addEventListener('mouseup',function(){
    //删除move
    this.removeEventListener('mousemove',move);
})
//工具函数01 移动函数
function move(e){
    e = e || window.event;
    box.style.left = e,pageX - box._x + 'px';
    box.style.top = e.pageY - box._y + 'px';
}

//工具函数02 获取盒子到文档边缘距离
function getBoxToDom(el,fx){
    if(el = null) return 0;
    return el['offset' + fx] + el['client' + fx] + getBoxToDom(el.offsetParent,fx)
}
十二、拖拽函数封装

函数参数介绍

/*
	{
		el : 能够被拖拽的标签对象
		dragging : function(el,evt),  //监测拖拽过程
		dragEnd : function(el,evt), //拖拽结束以后调用
		dire : 'x' //控制拖拽方向,x方向只能水平,y代表只能垂直,不写就是两个方向
	}
*/

drag.js代码 (采用标签对象来绑定鼠标事件)

function drag(options){
    options = options || {};
    //获取要实现拖拽的标签对象
    var el = options.el || null;
    //获取回调函数
    var dragging = options.dragging;
    //获取结束函数
    var dragEnd = options.dragEnd;
    //获取方向
    var dire = options.dire;
    //判断el是否存在
    if(!el)return;
    el.style.position = 'absolute';//避免el不是绝对定位
    //绑定事件
    el.onmousedown = function(e){
        e = e || window.event;
        //详情见 事件.md 笔记第十一条
        this._x = e.offsetX + getBoxToDom(this.offsetParent,'Left');
        this._y = e.offsetY + getBoxToDom(this.offsetParent,'Top');
        //绑定移动事件
        document.onmousemove = move;
    }
    document.onmouseup = function(e){
        document.onmousemove = null;
        //调用外部回调函数
        if(typeof dragEnd == 'function'){
            dragEnd(el,e);
        }
    }
    //move移动过程中
    function move(e){
        e = e || window.event;
        //判断如果
        if(dire != 'y') el.style.left = e.pageX - el._x + 'px';
        if(dire != 'x') el.style.top = e.pageY - el._y + 'px';
        if(typeof dragging == 'function'){
            dragging(el,e);
        }
    }
    function getBoxToDom(el,fx){
        if(el == null) return 0;
        return el['offset' + fx] + el["client" + fx] + getBoxToDom(el.offsetParent,fx);
    }
}

调用

<script src='指定路径'></script>
<script>
	drag({
        el : document.auerySelector('.box'),
        dragging : function(el,evt){
            //拖拽过程 用户自定义
        },
        dragEnd : function(el,evt){
            //拖拽结束  用户自定义
        },
        dire : 'x'('y','zzzz')  //'x'是只能横向,'y'是只能竖向,写其他的或者啥都不写就是两个方向都能拖动
    })
</script>
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值