DOM事件

DOM事件

DOM 0

  在前端领域一片混沌的时候,浏览器DOM事件的实现并没有一个统一的标准,各浏览器互相借鉴实现了一些事件监听。W3C出来之后,把这些之前分别实现的事件监听做了一个汇总,称为DOM level 1,在此之前这些浏览器实现的叫做DOM level 0。所以DOM 0是那时候的事实标准。
  注意点:
  当时浏览器实现了包括onclick在内的一些事件,其中有一点需要特别注意的是onclick可以写在js里边,同样也可以写在html里边,但是用法上是有区别的。

<button id="aaa" onclick="console.log()">click me</button>
<script>
  aaa.onclick = function(){ console.log('aaa被点击了')  }
</script>

  在html代码块里,onclick后边接的是要执行的代码,一旦用户点击,浏览器就eval('要执行的代码'),所以如果是一个函数的话,我们应该在后边写上函数的调用方法而不是函数名,如果写的是函数名的话那么这就只是一个函数声明。
  在js代码块里,onclick对应的是一个函数对象。

<script>
  function print(){  console.log('hello')  }
  aaa.onclick = print               // hello
  bbb.onclick = print()             // 不好使
  ccc.onclick = print.call()        // 不好使
<script>
<button id="aaa" onclick="print">click me</button>         // 不好使
<button id="bbb" onclick="print()">click me</button>       // hello
<button id="ccc" onclick="print.call()">click me</button>  // hello

DOM 1

  DOM1有两个章节,DOM Core(核心)和DOM HTML。
  DOM Core有DOM结构,DOM内存管理,DOM名称约定…..
  DOM HTML有HTML Collection,HTMLDocument…对象有哪些属性,哪些定义等等。
  DOM1的事件并没有单独讲,只是显示支持哪些事件。

DOM 2

  这次把事件单独拿了出来做了一个整理,并且添加了很多的新功能。DOM Events,把事件写的很详细。
  在DOM0里边的onclick事件显然不是那么的好用,因为onclick实际上是一个属性,它只能等于一个函数,我们不能让它执行多个函数。并且很容易不小心覆盖掉之前绑定的函数。另一个绑定click事件的方法是使用addEventListener来添加监听事件。比如像下边这样:

xxx.addEventListener('click', function(){  console.log('hello')  })
xxx.addEventListener('click', function(){  console.log('world')  })

  这里我们为元素xxx依次添加了两个匿名函数,在触发点击事件的时候,两个函数按照声明顺序依次执行。
  如果要移除这个函数的话必须要为这个函数加一个名字。

function f1(){  console.log('hello world')  }
xxx.addEventListener('click', f1)
xxx.removeEventListener('click', f1)  //这样就可以把f1从click的事件队列中移出去了

  如果只想执行一次这个函数,也就是jQuery中的.one,我们只需要在函数f1的最后一句把这个事件移出去就好了。

function f1(){
  console.log('hello world')
  xxx.removeEventListener('click', f1)
}
xxx.addEventListener('click', f1)

  另一个有趣的知识点是事件的捕获冒泡

<div id="grandpa">爷爷
  <div id="parent">父亲
    <div id="son">儿子</div>
  </div>
</div>

  如果有这样一段代码,那么在我点击儿子的时候,是先触发爷爷的点击事件,还是先触发儿子的点击事件?
  事实是,你想先触发哪个就先触发哪个。如果你在js里边这样写,那当你点击了son的时候触发顺序就依次是papa son grandpa

son.addEventListener('click', function(){
  console.log('son')
})
papa.addEventListener('click', function(){
  console.log('papa')
}, true)
grandpa.addEventListener('click', function(){
  console.log('grandpa')
}, false)

  所以这是为什么呢?这就涉及到addEventListener的第三个参数了。
  在提到这第三个参数之前,我们需要明确的是事件的捕获和冒泡的概念。在你点击son的时候,实际上你同时也点击了papa和grandpa,就像先生打你手心的话,同时也是在打你。有人认为点击了son,应该先触发son的点击事件,然后才是papa和grandpa。也有人认为点击了son,应该先触发grandpa的点击事件,然后才是papa和grandpa。互相争执不下最终的解决办法是把事件分为捕获阶段和冒泡阶段。
  其中捕获阶段是从大到小,也就是从grandpa到son,冒泡是从小到大,也就是从son到grandpa。在你执行一个点击事件的时候,先捕获再冒泡。如此一来看似完美的解决了这个问题。
  所以addEventListener有了第三个参数,如果这第三个参数为true的话,那么这个事件会在捕获阶段执行,如果为false或者不写的话,那么在冒泡阶段执行。其实想想还挺有意思的,默认是冒泡(其实我觉得冒泡阶段执行比较合理),但是冒泡的话第三个参数是false,这样有一种两种人的情绪都顾及到了的感觉呢!
  所以上边的代码里,捕获阶段只有papa会执行,所以先打印出papa,到了冒泡阶段,先从son开始,然后才是grandpa。所以执行结果是papa son grandpa
  值得一提的是如果只是在son,也就是最内层元素上,是不存在捕获和冒泡的。在所有同一个外层元素上,第三个参数是true的永远优先于第三个函数是false的执行。但是这个规则在son本身却行不通。

son.addEventListener('click', function(){
  console.log('son冒泡')
}, false)
son.addEventListener('click', function(){
  console.log('son捕获')
}, true)
papa.addEventListener('click', function(){
  console.log('papa冒泡')
}, false)
papa.addEventListener('click', function(){
  console.log('papa捕获')
}, true)

  按说上述代码的执行结果应该是papa捕获 son捕获 son冒泡 papa冒泡,但事实上不是这样的。我们前面说过,这个规则在son本身是行不通的。对于son本身而言,不论你的第三个参数是什么,都是按照事件的可执行队列来执行的。也就是说,你先写哪个,点击的时候就先执行哪个。
  以及,你可以使用function(e){ e.stopPropagation()}来阻止事件的冒泡。在jQuery里边,如果你想同时阻止事件的冒泡和默认事件的话,可以使用$(xxx).on('click', false)第二个参数的返回值如果是false的话,就同时阻止了冒泡和默认事件。相当于

$(xxx).on('click', function(e){
  e.stopPropagation()
  e.preventDefault()
})

  一个有一点难懂的例子(事件的可视化):
  其实这个例子可以不用看,因为我觉得跟事件没什么很大的关系。但是我花了很久才捋清楚了,所以想做个笔记。

<!DOCTYPE html>
<html>
<head>
<script src="//code.jquery.com/jquery-2.1.1.min.js"></script>
  <meta charset="utf-8">
  <title>JS Bin</title>
  <style>
    .{margin: 0; padding: 0;}
    div{
      margin: 15px;
      display: inline-block;
      border-radius: 50%;
      transition: all 1s;
      border: 1px solid black;
      background-color: white;
    }
    .purple.active{
      background-color: purple;
    }
    .cyan.active{
      background-color: cyan;
    }
    .blue.active{
      background-color: blue;
    }
    .green.active{
      background-color: green;
    }
    .yellow.active{
      background-color: yellow;
    }
    .orange.active{
      background-color: orange;
    }
    .red.active{
      background-color: red;
    }
    .purple{
      width: 50px;
      height: 50px;
    }
  </style>
</head>
<body>
<div class="red">
  <div class="orange">
    <div class="yellow">
      <div class="green">
        <div class="blue">
          <div class="cyan">
            <div class="purple"></div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
<script>
  let divs = $('div').get() //变成数组
  var time = 0
  for (let i = 0; i < divs.length; i++) {
    divs[i].addEventListener('click', () => {
      if(time>=divs.length*2){time = 0}  
      setTimeout( ()=>{
          divs[i].classList.add('active')
        }, 500*time)
        time++
    }, true)
    divs[i].addEventListener('click', () => {
        setTimeout( ()=>{
          divs[i].classList.remove('active')
        }, 500*time)
        time++
    }, false)
  }
</script>
</body>
</html>

  效果预览(基础测试通过,但是有bug,不想改了。所以使用的时候注意点击中心点,并且要在事件冒泡完成之后再进行下一次的点击…….)
  主要有两点不好理解。
  一是click事件里边的函数在我们点击之前并没有执行,此时time不会执行加一的操作。在点击中心点的时候,这些圆圈的click事件被依次触发。先捕获,从最外层到最内层,然后再冒泡,从最内层到最外层。
  二是为什么用let作为for循环的循环变量的声明click事件里边调用的i就可以是我们真正想要调用的i(我们知道用var是不行的)。这个问题的话我想了很久,后来发现画内存图可以解决。因为let是局部变量,i在内存中并不是只有一个取值,所以事实是这里的每个click事件调用的都是不同的i,彼此之间并没有冲突,因此可以得到我们想要的结果。

DOM 3

  DOM3并没有对事件做修正。

DOM 4还在草案中…..

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值