多种形态下的 click 事件

做一个小需求的时候,遇到一个 click事件,眼睛一扫而过也没怎么在意,因为根本没把这东西当一回事,天天用的东西闭着眼睛都不会错,然而这次因为诸多理由居然试了好几次才终于获得想要的效果,这激发了我想要更深一步探究此事件的兴趣。


为一个元素绑定 click事件,常用的有以下三种方法。

// 为元素绑定事件
element.addEventListener
// 为元素设置事件
element.onclick
// 在元素的标签上显性声明事件
<element onclick="fn()"></element>

三种方法的最终目的都是为元素添加一个 click事件,但是其中的门道却不太一样。

  • addEventListener

先上代码:

// HTML
<div class="parent">parent
  <div class="child">child</div>
</div>
// CSS
.parent {
  width: 200px;
  height: 200px;
  background-color: olive;
}
.child {
  width: 50px;
  height: 50px;
  background-color: skyblue;
}
// js
var parent = document.querySelector('.parent')
var child = document.querySelector('.child')

parent.addEventListener('click', function(e){

  console.log('parent click', e.currentTarget.className, e.target.className);
})
child.addEventListener('click', function(e){
  console.log('child click', e.currentTarget.className, e.target.className);
})

tips: 可能有人对于 e.currentTargete.target之间的区别不太清楚,只要记住一点即可:e.currentTarget返回绑定事件的元素,e.target返回触发事件的元素,只有触发元素和绑定元素为同一元素,则二者才指向同一元素

效果如下:
这里写图片描述

点击 parent元素,控制台打印 parent点击事件的输出,这很合理,但是点击 child 元素的时候,则是先打印出 child点击事件输出,又打印出 parent点击事件输出。

了解过 js事件机制的同学应该都清楚后一种情况发生的原因:js事件分为 事件冒泡 和 事件捕获,addEventListener这个函数实际上有三个参数,前两个参数为必选,第三个参数为可选,指定使用冒泡机制还是捕获机制,默认为 false,表示使用事件冒泡机制。

这里 child使用默认的事件冒泡机制,所以 click点击事件先触发 child的事件函数,接着向上冒泡到达 parent,触发 parent的事件函数,因为事件的触发元素依旧是 child,所以 parent事件的 e.targetchild

想要阻止事件冒泡,可在 child 的事件函数中加入下面这句:

e.stopPropagation()
  • 设置事件 onclick

把上面的 js代码换成下面这种:

parent.onclick=function(e) {
  console.log('parent click', e.currentTarget.className, e.target.className);
}
child.onclick=function(e) {
  console.log('child click', e.currentTarget.className, e.target.className);
}

效果:

这里写图片描述

可以看到,效果和 addEventListener是一样的,为 child事件增加阻止冒泡的代码,同样可以阻止事件从 childparent传播。

唯一的区别在于,使用 addEventlistener,可以重复多次监听同一个事件,但是 onclick就只能监听一次,多次设置则后面的覆盖掉前面的,当然,这与事件本身没什么关系,主要是由 addventListener的特性决定的。

  • 在元素的标签上显性声明事件

修改 DOM如下:

<div class="parent" onclick="parentClick()">parent
  <div class="child" onclick="childClick()">child</div>
</div>

JS如下:

function parentClick() {
  console.log('parent click');
}

function childClick() {
  console.log('child click');
}

效果如下:
这里写图片描述

效果似乎和上面两个都是一样的,但是有一点不同,那就是这种直接为标签添加事件的方法,没办法传递 event事件参数,所以也就无法阻止 child的事件向上冒泡。

  • vue 中的 click

vue的方式为一个元素添加 click事件,可以像下面这样:

// HTML
<div id='app' class="parent" @click="parentClick">parent
   <div class="child" @click="childClick">child</div>
</div>
// JS
new Vue({
    el: '#app',
    methods: {
      parentClick: function (e) {
        console.log('parentClick', e.currentTarget.className, e.target.className);
      },
      childClick: function (e) {
        // e.stopPropagation()
        console.log('childClick', e.currentTarget.className, e.target.className);
      }
    }
  })

效果如下:
这里写图片描述

这种方式是直接在标签上增加声明事件的,乍一看好像和第三种方式有点像,但是却能够使用 e.stopPropagation()来阻止事件冒泡,实际上, 这种方法的本质是利用了 addEventListenervue中绑定事件的源码如下:

这里写代码片

  • react 中的 click
// HTML
<div id="app"></div>
// JS
class Box extends React.Component {
  constructor(props) {
    super(props)
  }

  handleClick(type, e) {
    e.stopPropagation()
    console.log(type);
  }

  render() {
    return (
      <div className="parent" onClick={this.handleClick.bind(this, 'parentClick')}>parent
       <div className="child" onClick={this.handleClick.bind(this, 'childClick')}>child</div>
      </div>
    );
  }
}

ReactDOM.render(
  <Box />,
  document.getElementById('app')
);

效果和 vue的一样,都是基于 addEventListener实现的:

这里写图片描述


本文虽然仅仅是拿 click事件来进行举例,但本质上还是想通过 click这一个具体的事件来扩展到其他诸如 mouseoverdoubleclick等事件,事件的本质基本上都一样,通过一个事件自然也就能概括得到其他的事件特性。

猜你喜欢

转载自blog.csdn.net/deeplies/article/details/77932847