事件处理模型之冒泡与捕获,绑定与解除

事件处理模型

以下就是事件处理模型例子的代码

 <style>
    .red{
        width: 200px;
        height: 200px;
        background-color: red;
    }
    .green{
        width: 150px;
        height: 150px;
        background-color: green;
    }
    .yellow{
        width: 100px;
        height: 100px;
        background-color: yellow;
    }
    </style>
  <div class = "red">
       <div class = "green">
           <div class = "yellow">              
           </div>
       </div>
   </div>

  <script>
    var red = document.getElementsByClassName('red')[0];
    var green = document.getElementsByClassName('green')[0];
    var yellow = document.getElementsByClassName('yellow')[0];
    red.addEventListener('click',function(){
        console.log("red");
    },false);
    green.addEventListener('click',function(){
        console.log("green");
    },false);
    yellow.addEventListener('click',function(){
        console.log("yellow");
    },false);
   </script> 
冒泡

html结构上(非视觉上)嵌套关系的元素,会存在事件冒泡的功能,即同一事件,自子元素冒泡向父元素。(结构上自底向上

下层的DOM节点定义的事件处理函数,到了上层的节点如果还有和下层相同事件类型的事件处理函数,那么上层的事件处理函数也会执行。
在这里插入图片描述在这里插入图片描述

捕获

html结构上(非视觉上)嵌套关系的元素,会存在事件捕获的功能,即同一事件,自父元素捕获至子元素(事件源元素)。(结构上自顶向下
elem.addEventListener(‘type’,fn,true);第三个参数为true就是开启了事件捕获
在这里插入图片描述在这里插入图片描述
IE没有捕获事件
事件捕获有chrome浏览器实现了,最新版本的火狐,opera也有,老版本没有

触发顺序,先捕获,后冒泡
  • 同一个对象的同一个事件类型上面绑定了一个理函数只能遵循一种处理模型,就是说一个元素绑定了一个事件处理函数,那么这个事件处理函数要么就是事件冒泡的处理模型,要么就是事件捕获的处理模型。
  • 同一个对象的同一个事件类型上面绑定了两个事件处理函数,一个是事件冒泡,一个是事件捕获,触发顺序就是先捕获再冒泡。
    在这里插入图片描述

参数false事件为冒泡行为
参数为true事件为捕获行为
在这里插入图片描述在这里插入图片描述在这里插入图片描述

focus,blur,change,submit,reset,select等事件不冒泡

取消冒泡事件

event.stopPropagation() W3C标准(IE9以下不支持)
event.cancelBubble = true IE独有
封装一个函数取消事件冒泡 (兼容版)

function stopBubble(event) {
   if (event.stopPropagation) {
       event.stopPropagation();
   } else {
       event.cancelBubble = true;
   }
}

阻止默认事件

默认事件
表单提交,a标签跳转或者刷新页面,点击右键弹出菜单等

  1. return false
    以句柄(elem.onXXX = fn)的方式绑定的事件才生效
  2. event.prevetDefault()
    W3C标注,IE9以下不兼容
  3. event.returnValue = false
    兼容IE

封装阻止默认事件的函数(兼容版)

function cancelHandler(event) {
    if(event.prevetDefault){
        event.prevetDefault();
    }else{
        event.returnValue = false;//包括了兼容IE的,也包括了以句柄形式的return false;
    }
}

阻止a标签默认跳转或者刷新页面,阻止点击右键默认弹出菜单

  • 阻止a标签默认跳转或者刷新页面
//第一种方式
 <a href="javascript:void()">demo</a>
 //void()里面相当于返回什么

//第一种方式
var a = document.getElementsByTagName('a')[0];
a.onclick = function(){
    return false;
}
  • 阻止点击右键默认弹出菜单
//第一种方式
	 document.oncontextmenu = function(){
        console.log('a');
        return false;
    }
    
//第二种方式
	 document.oncontextmenu = function (e) {
	     console.log('a');
	     cancelHandler(e);
	 }
    function cancelHandler(event) {
        if(event.prevetDefault){
            event.prevetDefault();
        }else{
            event.returnValue = false;
        }
    }

JS-阻断事件传播:
1:stopPropagation(原生js上的):
2:preventDefault
3:stopImmediatePropagation(jquery上的)

stopImmediatePropagation做了两件事情:

  • 第一件事:阻止 绑定在事件触发元素的 其他同类事件的callback的运行。

  • 第二件事,阻止事件传播到父元素,这跟stopPropagation的作用是一样的

  • stopImmediatePropagation();这个函数用于阻断同一element的事件传播。

    例如一个element上定义了多个listener,如果其中一个调用这个方法后面的listener则都不会执行。

事件对象

1,事件对象:在触发DOM上的某个事件的时候,会产生一个事件对象event,而在这个对象当中会包含着所有与事件有关的信息。(事件对象的属性详解:(https://blog.csdn.net/weixin_43623871/article/details/88894094))

2,事件对象的兼容写法:
var event =e || window.event (用于IE);

3,事件对象其中有两个信息,我们最为常用,分别是type和target:

  1. type:事件的类型,如onlick中的click;
  2. srcElement/target:事件源,就是发生事件的元素(dom元素);

事件源对象
event.target(火狐只有这个)
event.srcElement(IE只有这个)
chrome这两个都有。

<div class = "red"></div>
-----------------------------
var red = document.getElementsByClassName('red')[0];
red.onclick = function(e){
	  var event = e || window.event;
	  var target = event.target || event.srcElement;
	  console.log(event);
	  console.log(target);//求事件源对象
}

在这里插入图片描述

事件对象和事件源对象的兼容性写法

function(e){
	 var event = e || window.event;
	 var target = event.target || event.srcElement;
}

事件委托

要求:点击每一个li ,打印出对应li里面的内容

<ul>
	<li>1</li>
	<li>2</li>
	<li>3</li>
	<li>4</li>
	<li>5</li>
	<li>6</li>
	<li>7</li>
	<li>8</li>
	<li>9</li>
	<li>10</li>
</ul>

1,土办法: 给每一个li绑定点击事件:

var li = document.getElementsByTagName('li');
    var len = li.length;
    for(var i = 0; i < len; i++) {
        li[i].onclick = function (){
            console.log(this.innerText);
        }
    }

2,事件委托办法:给父元素ul一个人绑定点击事件,利用事件冒泡和事件源对象处理 ,
因为每点击一个li,该li就会成为事件源对象,因为li始终在ul里面。

 var ul = document.getElementsByTagName('ul')[0];
  ul.onclick = function (e) {
    var event = e || window.event;
	var target = event.target || event.srcElement;
      console.log(target.innerHTML);
  }

利用事件冒泡和事件源对象进行处理

性能:不需要循环所有的元素一个个绑定事件

灵活:当有新的子元素添加将进来时不需要重新绑定事件(当有新的li的时候,土方法就要重新给新的li绑定点击事件,但是利用事件委托就可以不用管,因为你再加多少,它总是在ul里面,点击的时候它就会是target)

事件绑定

处理函数的三种方式

ele.onXXX = function(event) {}

  • 兼容性很好,但是一个元素的同一个事件只能绑定一个处理程序

  • 基本等同于写在HTML行间上

<div style = "width: 200px;height: 100px;background-color: rgb(90, 80, 80); position:absolute;left: 0;top:0;" onclick = "console.log('a');"></div>

var div = document.getElementsByTagName('div')[0];
//1,句柄的方式就是用onXXX的绑定方式
// div.onclick = fucntion () {
//     console.log('a');
// }
//但一个元素的同一个事件上只能绑定一个处理程序

obj.addEventListener (type, fn, false);

IE9以下不兼容性,可以为一个事件绑定多个处理程序(W3C标准绑定形式)

div.addEventListener('事件类型',处理函数(也可以放的是函数引用test),false)
一个元素的同一个事件上可以绑定多个处理函数

div.addEventListener('click',function(){
    console.log('a');
},false);
div.addEventListener('click',function(){
    console.log('b');
},false);
//a
//a        打印两个是因为虽然它们是两个一模一样的函数,但是它们还是不同的函数

div.addEventListener('click',test,false);
div.addEventListener('click',test,false);
function test(){
    console.log('a');
}
//a         因为test是指向同一个地址函数的引用

obj.attachEvent (‘on’ + type, fn);

IE独有,一个事件同样可以绑定多个处理程序

事件处理程序的内部this指向

ele.onXXX = function(event) {}
程序this指向dom元素本身
obj.addEventListener (type, fn, false);
程序this指向dom元素本身

第三个参数可选,为布尔值,指定事件是否在捕获或者冒泡阶段执行
obj.attachEvent (‘on’ + type, fn);
程序this指向window

让attachEvent的this指向dom元素:

div.attachEvent('onclick',function() {
    handler.call(div);
});
function handler() {
//事件处理程序
}

给一个dom对象添加该事件类型的处理函数,封装兼容性的
addEvent(elem, type, handler)

function addEvent(elem, type, handler) {
    if(elem.addEventListener) {
        elem.addEventListener(type, handler, false);
    }else if(elem.attachEvent){
        elem.attachEvent('on' + type, function () {
            handler.call(elem);
        })
    }else{
        elem['on' + type] = handler;
    }
}

解除事件绑定
ele.onclick = false/null

div.onclick = fucntion () {
    console.log('a');
}
div.onclick = null;
// 只能执行一次的事件
div.onclick = fucntion () {
    console.log('a');
    this.onclick = null;
}
ele.removeEventListener(type, fn, false);
div.addEventListener('click',function(){
    console.log('a');
},false);
div.removeEventListener('click', , false);//不好使,绑定了匿名函数,就无法解除
div.addEventListener('click',test,false);
function tset() {};
div.removeEventListener('click', test, false);

ele.detachEvent(‘on’ + type, fn);
若绑定匿名函数,则无法解除

实现一个拖拽的小功能

<div style="width: 100px;height: 100px;background-color:red;position: absolute;left: 0;top: 0"></div>
// JavaScript Document
function drag(elem){}
    var div = document.getElementsByTagName('div')[0];
    var disX,
       disY;
    div.onmousedown  = function (e) {
        disX = e.pageX - parseInt(div.style.left);//确定移动的中心点
        disY = e.pageY - parseInt(div.style.top);

        document.onmousemove = function(e) {
            var event = e || window.event;
            div.style.left = e.pageX - disX + "px";
            div.style.top = e.pageY -  disY +"px";
        }
        document.onmouseup = function (){
            div.onmousemove = null;
        }
    }

elem.setCapture(); elem.releaseCapture();
只有IE能用,它会把页面所有的事件捕获到自己身上,那么就可以解决拖拽的时候,鼠标快于系统帧频的速度

事件分类

鼠标事件

  • click

  • mousemove

  • mousedown mouseup

  • contextmenu(右键菜单栏)

  • mouseover mouseout

  • mouseenter mouseleave(迎合h5的新特性)
    一次click = mousedown + mouseup


document.onclick = function() {
        console.log('onclick');
    }
    document.onmousedown = function() {
        console.log('mousedown');
    }
    document.onmouseup = function() {
        console.log('mouseup');
    }

移动端onmousemove,onmousedown,onmouseup用不了
要用 touchmove ,touchstart ,touchend

使用event.button来区分鼠标事件的触发键

event.x 鼠标横轴
event.y 鼠标纵轴
event.keycode 键盘值
events.button==0 默认。没有按任何按钮。
events.button==1 鼠标左键
events.button==2 鼠标右键
events.button==3 鼠标左右键同时按下
events.button==4 鼠标中键
events.button==5 鼠标左键和中键同时按下
events.button==6 鼠标右键和中键同时按下
events.button==7 所有三个键都按下

click事件只能监听左键,只能通过mousedown和mouseup才能区分鼠标左右键

document.onmousedown = function(e) {
    if(e.button == 2){
        console.log('right');
    }else if(e.button == 0){
        console.log('left');
    }
}

案例:实现一个a标签的小方块拖拽(拖拽不等于点击)
用时间差来确定它是点击还是拖拽,点击的时间差是非常短的。

var firstTime = 0;
var lastTime = 0;
var key = false;
document.onmousedown = function () {
    firstTime = new Date().getTime();
}
document.onmouseup = function () {
    lastTime = new Date().getTime();
    if(lastTime - firstTime < 300){
        key = true;
    }
}
document.onclick = function () {
    if(key){
        console.log('click');
        key = false;
    }
}

键盘事件

  • keydown

  • keyup

  • keypress

  • keydown > keypress > keyup

    keydown 和keypress很相似,但是也有区别
    keydown 可以敲响任意按键
    keypress只可以敲响字符类键盘按键,然后返回ASCII码,可以转换成相应字符

//转换成相应字符

document.onkeypress = function (e) {
    console.log(String.fromCharCode(e.CharCode));
}

//一次keypress 不等于keydown + keyup, 它们是这样的关系:keydown > keypress > keyup

 document.onkeypress = function() {
       console.log('onkeypress');
   }
   document.onkeydown = function() {
       console.log('onkeydown');
   }
   document.onkeyup = function() {
       console.log('keyup');
   }

文本操作事件

  • input

  • focus

  • blur

  • change
    input事件监听input输入栏的值

var input = document.getElementsByTagName('input')[0];  
input.oninput = function(e){
    console.log(this.value);
}

change事件其实是比较聚焦前和失焦时候的两个状态的不同

var input = document.getElementsByTagName('input')[0];  
input.onchange = function(e){
    console.log(this.value);
}

实现输入栏的聚焦输入信息,失焦依旧有提示信息

<input type = "text" value="请输入用户名" onfocus="if(this.value == '请输入用户名'){this.value = ''}" onblur="if(this.value == ''){this.value = '请输入用户名'}">

窗体操作类(window上的事件)

  • scroll

  • load(window.onload()是要等到DomTree和CssTree生成后渲染成RenderTree后,才开始调用js)

ie6没有position : fixed定位
实现兼容

JavaScript 事件参考手册
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述 在这里插入图片描述在这里插入图片描述在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章