Go to comments

JavaScript 事件(下)

事件处理模型(事件冒泡、捕获) 


事件的重点问题事件处理模型,事件处理模型基本上有两个模型,一个是冒泡一个是捕获


事件处理模型是什么意思?

一个元素针对事件的处理方式,正常看到的方式只是展现出了第一种方式(冒泡),还有第二种方式(捕获),

即使是之前看到的方式也不是非常透彻的,下面演示一下真正标准的方式是什么,然后还有一种特殊的方式是什么


一、事件冒泡

把下面代码中三个div选出来,然后给它们三个分别绑定事件

<!DOCTYPE html>
<html>
<head>
<title>事件冒泡</title>
<style>
  .wrapper{
    width:300px;height:300px;background-color: red;
  }
  .content{
    width:200px;height:200px;background-color: orange;
  }
  .box{
    width:100px;height:100px;background-color:yellow;
  }
</style>
</head>
<body>

  <div class="wrapper">
    <div class="content">
      <div class="box"></div>
    </div>
  </div>

  <script>

    var wrapper = document.getElementsByClassName('wrapper')[0];
    var content = document.getElementsByClassName('content')[0];
    var box = document.getElementsByClassName('box')[0];

    wrapper.addEventListener('click', function(){
      console.log('wrapper');
    }, false);

    content.addEventListener('click', function(){
      console.log('content');
    }, false);

    box.addEventListener('click', function(){
      console.log('box');
    }, false);

  </script>

</body>
</html>

三个div元素绑定了三个事件,点击哪一个的时候就会出现它自己的名字


点击红色的部分时,出现它自己名字wrapper

image.png


点击橙色时,

1. 首先橙色在红色上面,

2. 点击橙色的部分显示的是content加上wrapper都显示了

image.png


当点击黄色的部分时,三个div的名字都显示了box、content、wraooer

image.png


也就是说事件执行的顺序是漏下去的,点击黄色时黄色也算橙色区域的,橙色也触发了,然后红色的区域也触发了,紧接着一系列往下漏的现象,

我们管这个现象叫事件冒泡,事件会像水泡一样一层一层的往上冒,但是这个例子太不形象了,事件冒泡怎么感觉像往下渗漏呢?


翻译一下事件冒泡的概念:

结构上(非视觉上)存在父子关系的元素,事件触发的顺序是,如果事件点击到子元素上,它会一级一级的向它父元素传递(冒泡)这个事件,

所以从代码的视觉上是自底向上的,这样一层一层向上冒泡的,不是视觉上的,

结构上嵌套不是视觉上嵌套,结构上嵌套的元素就会存在一个冒泡功能,自子元素冒泡向父元素。


继续看下面,在css上做一个障眼法

<!DOCTYPE html>
<html>
<head>
<title>事件冒泡</title>
<style>
  .wrapper{
    width:300px;height:300px;background-color: red;
  }
  .content{
    width:200px;height:200px;background-color: orange;
    margin-left:300px; /* CSS上的改变,加一个margin-left:300px */
  }
  .box{
    width:100px;height:100px;background-color:yellow;
    margin-left:200px; /* CSS上的改变,加一个margin-left:200px */
  }
</style>
</head>
<body>

  <div class="wrapper">
    <div class="content">
      <div class="box"></div>
    </div>
  </div>

  <script>

    var wrapper = document.getElementsByClassName('wrapper')[0];
    var content = document.getElementsByClassName('content')[0];
    var box = document.getElementsByClassName('box')[0];

    wrapper.addEventListener('click', function(){
      console.log('wrapper');
    }, false);

    content.addEventListener('click', function(){
      console.log('content');
    }, false);

    box.addEventListener('click', function(){
      console.log('box');
    }, false);

  </script>

</body>
</html>


三个div元素在视觉上不嵌套了,但结构上还依然嵌套,结构上嵌套的元素就能从子元素冒泡向父元素,点击黄色区域依然是这个顺序

image.png

所以事件冒泡是存在代码结构上的而不是视觉上的,视觉上就是摞一起也不一定冒泡,冒泡是结构上的自子元素向父亲元素,

这是第一种事件处理方式,我们管第一种事件处理方式叫做第一种事件处理模型,也叫做事件冒泡模型是常规模型,还有一种不常规的叫事件捕获。


二、事件捕获

事件捕获只有一款浏览器上好使,这款浏览器叫Chrome,

W3C标准里面提供了一个功能,事件处理模型必须有两个,一个事件冒泡一个事件捕获,

事件捕获就chrome浏览器实现了,因为实现事件捕获功能不是很好实现。


在触发事件捕获之前先清楚一点,一个对象的一个事件类型只能遵循(存在)一种事件模型,要么冒泡要么捕获,不可能同时存在捕获和冒泡。


怎么触发事件捕获功能呢?

 ele.addEventListener(type, fn, true  把第三个参数false变成true,事件处理模型立刻就变成捕获功能

<!DOCTYPE html>
<html>
<head>
<title>事件捕获</title>
<style>
  .wrapper{
    width:300px;height:300px;background-color:red;
  }
  .content{
    width:200px;height:200px;background-color:orange;
  }
  .box{
    width:100px;height:100px;background-color:yellow;
  }
</style>
</head>
<body>

  <div class="wrapper">
    <div class="content">
      <div class="box"></div>
    </div>
  </div>

  <script>

    var wrapper = document.getElementsByClassName('wrapper')[0];
    var content = document.getElementsByClassName('content')[0];
    var box = document.getElementsByClassName('box')[0];

    wrapper.addEventListener('click', function(){
      console.log('wrapper');
    }, true);

    content.addEventListener('click', function(){
      console.log('content');
    }, true);

    box.addEventListener('click', function(){
      console.log('box');
    }, true);

  </script>
</body>
</html>


捕获功能也没那么难理解,捕获功能和事件冒泡正好是反着的,比如点击红色的区域还是wrapper

image.png


点击橙色的区域也会触发两个事件,橙色的和红色的,只不过触发顺序是先红色的后橙色的(wrapper -> content)

image.png


点击黄色也是先红色、橙色、黄色(wrapper -> content -> box)

image.png


这叫事件捕获,捕获的顺序是把结构的最外面的先抓住,比如虽然点击黄色区域了,但黄色算不算红色区域?黄色算红色区域,红色区域看事件来了它先抓住,然后再传递到子元素上,捕获与冒泡的传递顺序正好相反。


最外面的先捕获事件,然后里层的再捕获,然后才是最里层的执行,所以点击黄色区域是先红色、再橙色、最后黄色(wrapper -> content -> box)。


而且注意一点,点击黄色的时候,叫红色的地方捕获事件并且执行,然后橙色地方捕获事件并且执行,黄色这块呢?黄色这块不是事件捕获了,点击的就是黄色,所以黄色这块叫按照常规执行。


结构上(非视觉上)嵌套关系的元素会存在"事件捕获"的功能,即同一事件自父元素捕获至子元素,也就是说自顶向下或者说自父元素捕获向事件源元素,就是事件在谁那触发的就捕获到谁那。


IE没有捕获事件

有一点,IE上没有捕获事件,chrome上有、最新版本的火狐、opera也有(老版本opera上没有),最新版本的浏览器的内核都多多少少少渗透了webkit内核。


三、触发顺序(先捕获,后冒泡)

上面说过,同一个对象的一个事件类型上面,绑定的一个处理函数只能遵循一个处理模型,

现在给一个事件类型上绑定两个函数,让两个函数分别遵循事件冒泡和事件捕获,点击元素后是先执行捕获,还是先执行冒泡?

<!DOCTYPE html>
<html>
<head>
<title>触发顺序,先捕获,后冒泡</title>
<style>
.wrapper{
  width: 300px;
  height:300px;
  background-color: red;
}
.content{
  width:200px;
  height:200px;
  background-color: orange;
}
.box{
  width:100px;
  height:100px;
  background-color: yellow;
}
</style>
</head>
<body>

  <div class="wrapper">
      <div class="content">
          <div class="box"></div>
      </div>
  </div>

  <script>

    var wrapper = document.getElementsByClassName('wrapper')[0];
    var content = document.getElementsByClassName('content')[0];
    var box = document.getElementsByClassName('box')[0];
    
    wrapper.addEventListener('click', function(){
      console.log('wrapper');
    }, true);
    
    content.addEventListener('click', function(){
      console.log('content');
    }, true);
    
    box.addEventListener('click', function(){
      console.log('box');
    }, true);
    
    
    wrapper.addEventListener('click', function(){
      console.log('wrapperBubble');
    }, false);
    
    content.addEventListener('click', function(){
      console.log('contentBubble');
    }, false);
    
    box.addEventListener('click', function(){
      console.log('boxBubble');
    }, false);

  </script>
</body>
</html>


点击box黄色方块后,顺序是先捕获后冒泡

image.png

但是发现绑定的顺序就是先捕获后冒泡


换一个顺序,验证一下,看看还是不是先捕获后冒泡

<!DOCTYPE html>
<html>
<head>
<title>触发顺序,先捕获,后冒泡</title>
<style>
.wrapper{
  width: 300px;
  height:300px;
  background-color: red;
}
.content{
  width:200px;
  height:200px;
  background-color: orange;
}
.box{
  width:100px;
  height:100px;
  background-color: yellow;
}
</style>
</head>
<body>

  <div class="wrapper">
    <div class="content">
      <div class="box"></div>
    </div>
  </div>

<script>

  var wrapper = document.getElementsByClassName('wrapper')[0];
  var content = document.getElementsByClassName('content')[0];
  var box = document.getElementsByClassName('box')[0];

  wrapper.addEventListener('click', function(){
    console.log('wrapperBubble');
  }, false);

  content.addEventListener('click', function(){
    console.log('contentBubble');
  }, false);

  box.addEventListener('click', function(){
    console.log('boxBubble');
  }, false);


  wrapper.addEventListener('click', function(){
    console.log('wrapper');
  }, true);

  content.addEventListener('click', function(){
    console.log('content');
  }, true);

  box.addEventListener('click', function(){
    console.log('box');
  }, true);
    
</script>
</body>
</html>

点击黄色方块,中间两个有点变化,顺序一定是先捕获后冒泡

image.png


但是点击黄色方块是,

先捕获红色区域 -> 然后捕获橙色区域 -> 然后叫黄色区域的事件执行,再由黄色区域的事件执行 -> 然后冒泡到橙色区域 -> 冒泡到红色区域。


黄色区域是两个事件执行,事件执行的顺序要符合谁先绑定谁先执行,boxBubble先绑定当然它先执行,所以中间这块可以理解了,正常的顺序还是先捕获后冒泡。


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

有些特殊的事件focus,blur,change,submit,reset,select,这些事件没有冒泡功能,

并不是所有事件都有冒泡功能,比如focus是聚焦事件,鼠标在input上面聚焦了才触发focus,这个怎么冒泡冒给谁啊!


如果有一道选择题,所有事件都能冒泡,必然是错的,有些事件不能冒泡。


事件冒泡有的时候是好事,有的时候也并不一定是好事。

我们可以利用冒泡做很多事,比如事件委托的功能就是利用事件冒泡来做的,但是有的时候我们并不希望它冒泡。


知道为什么不希望它冒泡吗?

比如我们在全局的文档,document上面绑定一个click事件,如果用户闲的没事点击一下文档,我们就提示点文字"你闲的啊"

document.onclick = function () {
  console.log('你闲的啊');
}

点击文档空白区显示提示文字

image.png


我们想实现的效果是,当点击div的时候出现它的功能

<div class="wrapper" style="width:300px;height:300px;background-color:red;"></div>

<script>
  document.onclick = function () {
    console.log('你闲的啊');
  }
</script>

image.png

但是现在点击红色div的时候冒泡了,这是一个好玩的现象,红色div上面没绑定事件处理函数,这块验证了一个什么问题?

我们不绑定事件处理函数,红色div的事件依然有,依然会往上冒泡,帮不绑定是你的事,事件是天生自然有的功能照样能冒泡。


现在给红色div也绑定一个click事件,点击之后实现一个小功能,让它背景变成黄色

<div class="wrapper" style="width:300px;height:300px;background-color:red;"></div>

<script>

  var div = document.getElementsByClassName('wrapper')[0];

  document.onclick = function () {
    console.log('你闲的啊');
  }

  div.onclick = function () {
    this.style.backgroundColor = "yellow";
  }

</script>

点击红色div之后它变成黄色,但是出现了附加效果了,也显示了提示文字"你闲的啊"

image.png


我们想点击div的时候,不能算是点全局(document),全局是一个附加功能,而且这种嵌套关系任何东西都是doumentd的子元素,因为document代表文档代表全局,点击哪都会冒泡到document上,

这个时候冒泡对我们来说是一个伤害,那怎么来取消冒泡功能呢?


四、取消冒泡


1、W3C标准 event.stopPropagation() 但不支持ie9以下版本

W3C标准取消冒泡的方法是 event.stopPropagation(),W3C标准意味IE9以下版本不能用


在讲取消冒泡之前先必须要铺垫一个小概念叫事件对象

在每一个事件处理函数上面,我们可以写一个形参e  div.onclick = function ( e ){}  写形参e干嘛呢?

我们也不能往里面传东西,我们是不能传系统会帮我们传,系统会帮我们传一个事件对象


这个事件对象确实是对象,上面有很多个属性,

每一个属性都记载了这个事件发生时的一些关键性数据,比如事件类型、事件时刻、鼠标的坐标点……,

事件对象记载了这个事件发生时候的一系列数据信息以供我们去使用,系统把他打包成一个对象,传到了我们的第一个形参里面去了。


下面先试一下打印这个 参数e

<div class="wrapper" style="width:300px;height:300px;background-color:red;"></div>

<script>

  var div = document.getElementsByClassName('wrapper')[0];

  div.onclick = function (e) {
    console.log(e); //  MouseEvent {isTrusted: true, screenX: 962, screenY: 453, clientX: 115, clientY: 159, …}
    this.style.backgroundColor = "yellow";
  }

  document.onclick = function () {
    console.log('你闲的啊');
  }

</script>

点击div出现一个事件对象,这个事件对象上面有好些个属性,记录当前事件发生时候的一系列状态和信息

image.png


然后这个事件对象上面有一个 e.stopPropagation() 方法,能够阻止冒泡事件

<div class="wrapper" style="width:300px;height:300px;background-color:red;"></div>

<script>

  var div = document.getElementsByClassName('wrapper')[0];

  div.onclick = function (e) {
    e.stopPropagation(); // 在这里执行e.stopPropagation()方法阻止冒泡事件
    this.style.backgroundColor = "yellow";
  }

  document.onclick = function () {
    console.log('你闲的啊');
  }

</script>

一旦e.stopPropagation()方法执行了,点击div之后它不冒泡了

image.png


点击document文档空白,该执行的还执行,显示提示文字"你闲的啊",但是点击div身上他不冒泡了

image.png


e.stopPropagation()这个叫取消冒泡事件,W3C给出的标准方法


2、IE独有 cancelBubble: true;

第二个不标准的取消冒泡事件是IE独有的,说这个是IE独有真不太标准,谷歌浏览器也给实现了,event.cancelBubble:false是个属性,值变成true就可以了

<div class="wrapper" style="width:300px;height:300px;background-color:red;"></div>

<script>

  var div = document.getElementsByTagName('div')[0];

  div.onclick = function (e) {
    e.cancelBubble = true; // 第二个取消冒泡事件方法
    this.style.backgroundColor = "yellow";
  }

  document.onclick = function () {
    console.log('你闲的啊');
  }

</script>


cancel是取消的意思,

cancelBubble合起来翻译"取消冒泡"的意思


3、封装取消冒泡的函数 stopBubble(event)

取消冒泡就这两种,封装一个 stopBubble(event) 函数,里面传一个事件对象

function stopBubble(event){

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

}


使用封装的stopBubble(e)方法阻止冒泡,把参数e传进去就可以了

<div class="wrapper" style="width:300px;height:300px;background-color:red;"></div>

<script>

  var div = document.getElementsByTagName('div')[0];

  div.onclick = function (e) {
    stopBubble(e); // 使用封装的函数,组织事件冒泡,把事件对象e传进来
    this.style.backgroundColor = "yellow";
  }

  document.onclick = function () {
    console.log('你闲的啊');
  }

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

</script>


4、右键出菜单事件oncontextmenu 

其实有好些默认事件,比如屏幕里面右键弹会出现菜单,

右键弹出菜单还是一个事件叫右键菜单事件,单独给左键列了一个事件,叫右键出菜单事件oncontextmenu

document.oncontextmenu = function() {

  console.log('a'); // 右键出菜单时候提示一下打印a

}

右键点击弹出菜单,控制台也打印出"a"

image.png

这个叫右键出菜单的默认事件,有时候我们为了更好的网页体现效果想把默认事件清除,让浏览器自带事件别出来执行怎么办呢?


五、阻止默认事件


1、return false;以对象属性的方式注册的事件才生效

第一种方式 return false ,兼容性非常好,是最原始句柄式事件绑定方法御用的版本

document.oncontextmenu = function() {
  console.log('a');
  return false; // 阻止默认事件
}

点击右键只在控制台打印"a",不在弹出右键菜单了


默认事件被我们取消了,叫阻止了默认事件,

但是 return false 只有用句柄的方式绑定事件才能阻止,addEventListener用 return false 阻止不了


2、event.preventDefault(); W3C标注,IE9以下不兼容

如果用addEventListener绑定事件,用W3C标准规定的 e.preventDefault() 阻止默认事件(IE9以下不兼容)prevent防止的意思,Default默认的意思

document.addEventListener('contextmenu', function(e){

  console.log('a');
  e.preventDefault(); // W3C标准规定阻止默认事件

}, false)


W3C标准规定阻止默认事件,在句柄方式绑定的事件中也可以用

document.oncontextmenu = function(e){

  console.log('a');
  e.preventDefault(); // 在句柄方式绑定的事件中,也可以用W3C标准规定阻止默认事件

}


3、event.returnValue = false; 兼容IE

然后有一个兼容IE9以下用的阻止默认事件  e.returnValue = false  也可以阻止默认事件,谷歌浏览器也能用

document.oncontextmenu = function(e) {
  console.log('a');
  e.returnValue = false; // 阻止默认事件
}


4、封装阻止默认事件的函数 cancelHandler(event);

封装一方法cancelHandler(event)阻止默认事件的函数


思路:

如果preventDefault不好使,用returnValue = false,再不好使呢!能用return false吗?

return false不行了,因为它封装不进去,如何让一个函数执行完后变成return false的形式(fn() --函数执行不能达到 --> return false)函数执行顶多返回一个false

function cancelHandler(event){
  if(event.preventDefault){
    event.preventDefault();
  }else{
    event.returnValue = false;
  }
}

document.oncontextmenu = function(e) {
  console.log('a');
  cancelHandler(e); // 把e传进去,阻止默认事件
}

这是阻止默认事件的效果,下面看一个阻止A标签默认事件


5、阻止A标签默认事件

我们经常拿A标签当做按钮来用,但是由于A标签自身带跳转,

所以点击后不是刷新页面就是跳到页面最顶上,这个跳转功能是A标签的默认事件,我们可以把A标签的默认事件给取消

<a href="https://www.baidu.com">百度</a>

<script>

  var a = document.getElementsByTagName('a')[0];

  a.onclick = function () {
    return false; // 取消a标签默认事件
  }
    
</script>

再点击A标签,不跳转了地址栏的也不出"#号"了,A标签已经失去了跳转的功能以及刷新页面的功能,证明那是A标签的默认事件


其实还有一种更好的写法在一些大型网站上经常看到,A标签里面可以写协议限定符处置javascript

<a href="javascript:;">demo</a>


协议界定符里面可以写js代码,比如里面写 alert('a') 点击能弹出

<a href="javascript:alert('a');">demo</a>


如果想取消A标签的默认事件,可以在行间协议限定符后面直接加一个ovid(),相当于写返回值return什么什么东西,ovid(false/0) 写0或false就相当于 return 0 或 return false

<a href="javascript:ovid(false);">demo</a>

<a href="javascript:ovid(0);">demo</a>


点击也失去A标签的功能,这是一个默认事件,

其实默认事件还有很多,我们慢慢去接触,只要知道怎么去取消就可以了


六、事件源对象


下面一个知识点叫事件源对象,事件源对象之前先详细的学一下事件对象


1、事件对象

当点击div的时候出现事件对象,浏览器会把事件对象打包成一个对象传到参数e里面去,但是是在非IE的浏览器的情况下,如果在IE8以及IE8以下的浏览器参数e会失效

<div style="width:100px;height:100px;background-color:red"></div>

<script>

  var div = document.getElementsByTagName('div')[0]; 

  div.onclick = function(e) {
    console.log(e); // MouseEvent {isTrusted: true, screenX: 921, screenY: 369, clientX: 74, clientY: 75, …}
  }
    
</script>


在IE浏览器下触发一个事件的时候,会不会记录一个事件的信息呢?

IE也会上记录,它在window.event上记录

如果要想拿到一个事件触发之后的事件对象,我们要有一个兼容性的写法

<div style="width:100px;height:100px;background-color:red"></div>

<script>

  var div = document.getElementsByTagName('div')[0]; 

  div.onclick = function(e) {
    var event = e || window.event; // 标准浏览器会传到e里面,IE不会传到e里面会传到window.event上
    console.log(event);
  }
   
</script>


事件对象上面有很多属性:

cancelBubble: false  阻止冒泡,一开始是默认是false

clientY/clientX          鼠标点击触发事件的时候鼠标的坐标点

pageX/pageY           同样也是鼠标点的位置和clientY/clientX通用

returnValue:true       默认值是true,阻止默认事件在IE里returnValue属于window.returnValue


还有这两个是什么呢?下面事件源对象里会讲到的

srcElement:div

target:div

   

2、事件源对象

红色div里面写一个黄色的小div,给红色div绑定一个事件函数

<div class="wrapper" style="width:100px;height:100px;background-color:red">
  <div class="box" style="width:50px;height:50px;background-color:yellow;" ></div>   
</div>

<script>

  var wrapper = document.getElementsByClassName('wrapper')[0]; 

  wrapper.onclick = function(e) {

    var event = e || window.event;
    console.log(event);

  }

</script>

点击wrapper(红色)上它会执行绑定的函数

image.png


点击黄色小方块,会冒泡执行wrapper

image.png


反正wrapper都会执行,但是有个问题,

点击红色区域上是点击到wrapper元素自己身上执行,点击黄色小方块上其实触发的"点"是在黄色身上,是黄色小方块传递给红色执行的,

我们管触发的这个"点"叫事件源,点击红色方块事件源是它自身,点击黄色小方块事件源是黄色方块,传递到红色方块的。


事件对象上有一个信息是专门记录事件源的,这个信息是什么呢?

现在点击红色的方块,

在事件对象里有一个 srcElement: div.wrapper 是红色方块它自己, target: div.wrapper 也是红色方块自己

image.png


当点黄色小方块的时候,事件对象上面记录的是  srcElement: div.box  target: div.box ,这两个就是事件源

image.png


事件对象上有一个属性叫事件源对象,如果想找wrapper对象(红色方块)上的事件源对象到底是在那里触发的事件,怎么来找事件源呢?

有两个方式:

第一个火狐上有,谷歌上也有 event.target

第二个是IE上的,但是谷歌也有 event.srcElement


要想兼容所有浏览器还要写或运算符

<div class="wrapper" style="width:100px;height:100px;background-color:red">
  <div class="box" style="width:50px;height:50px;background-color:yellow;" ></div>   
</div>

<script>

  var wrapper = document.getElementsByClassName('wrapper')[0]; 

  wrapper.onclick = function(e) {

    var event = e || window.event; // 先兼容事件对象

    var target = event.target || event.srcElement; // 兼容事件源对象

    console.log(target); // 打印事件源对象

  }

</script>

点击红色方块,事件源对象是红色div

image.png

点击黄色方块,事件源对象是黄色div

image.png


求事件源对象有什么用?有很大用处,有个功能叫事件委托


七、事件委托


事件委托的机制是什么呢?

看下面,一个ul上面有十个li,十个li上面都有序号,

要求是给每个li绑定事件,实现的功能是,点击那个li就输出那个li的内容(不涉及闭包)怎么写?

12345678910


常规情况下的写法

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>事件委托</title>
<style>
*{margin:0;padding:0;}
ul{height:60px;border: 1px solid papayawhip;width:600px;margin:50px;}
ul > li{width:50px;height:50px;background-color:orange;list-style:none;float:left;color:papayawhip;line-height:50px;text-align:center;margin:5px;}
</style>
</head>
<body>

  <!-- ul>li{$}*10 -->
  <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>

<script>

  var lis = document.getElementsByTagName('li'),
      len = lis.length;

  for(var i = 0; i < len; i++){
    lis[i].onclick = function(){
      console.log(this.innerText);
    }
  }

</script>
</body>
</html>


如果面试官,

题目类似但是换了一个数,一个ul里面有三千亿个li,给每个li绑定事件同样的功能,打印出li里面的内容,三千亿for循环能转到明天,

而且再有一个不好的地方,现在是十个li,后来又动态的往里面再加十个li,要求这个十个li也存在同样的功能,是不是要重新for循环,各种不好各种low!


所以这时候事件源对象登场了,

ul是父级,如果冒泡是li冒泡给ul,不选li选它父级ul

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>事件委托</title>
<style>
*{margin:0;padding:0;}
ul{height:60px;border: 1px solid papayawhip;width:600px;margin:50px;}
ul > li{width:50px;height:50px;background-color:orange;list-style:none;float:left;color:papayawhip;line-height:50px;text-align:center;margin:5px;}
</style>
</head>
<body>

  <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>

  <script>

    var oUi = document.getElementsByTagName('ul')[0]; // 选择ul

    oUi.onclick = function (e) {

      var event = e || window.event;
      var target = event.target || event.srcElement;
      console.log(target.innerText); // 打印出事件源对象的innerText

    }

  </script>
</body>
</html>


事件源对象干什么的?

ul多大是由li决定的,li基本上覆盖了ul所有的区域,

点击第一个li,"ul"一定会执行事件,执行事件之后找的是它的"事件源对象",事件源对象还是一个li,

无论里面有多少个li都是ul的子元素,点击li之后都能冒泡给ul。


事件绑定到li上又麻烦、效率又低、又不能进行后期扩充,而"事件源对象"效率高不用循环,后期又可以扩充,就是往里面在加20个li也是ul的子元素,是子元素就能冒泡到ul,动态加的问题也解决了,

这种形式利用了"事件冒泡"机制和"事件源对象"进行处理,这种方式我们管它叫"事件委托",本来在li身上的完成的工作交给它爹ul干了。


两个优点,第一个有点性能好不需要循环,第二个灵活可扩展性好随便往容器里填东西,

我们写扫雷游戏的时候,雷盘上面有很多个雷,每一个雷功能都差不多,既然功能都差不多可不可以把它都放到ul上,没必要放到每一个雷身上。


作业

预习几个事件onmouseenter(over是老版本)、onmouseleav(out是老版本)、onmousemove,写一个拖拽功能



Leave a comment 0 Comments.

Leave a Reply

换一张