Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript异步编程之事件 #19

Open
zhuanyongxigua opened this issue Jun 23, 2018 · 0 comments
Open

JavaScript异步编程之事件 #19

zhuanyongxigua opened this issue Jun 23, 2018 · 0 comments

Comments

@zhuanyongxigua
Copy link
Owner

zhuanyongxigua commented Jun 23, 2018

更多相关内容见博客 https://github.com/zhuanyongxigua/blog

DOM0

html标签中的写法

<image id="element" src="hello.png" onclick="alert('Hi')"/>

在JS中的绑定方法:

var element = document.getElementById("element");
element.onclick = function(e) {}

dom0是节点元素的私有属性。同一个事件只能绑定一个,绑定多个,只有最后一个有效。直接写在html节点上的也会被覆盖。如下面的代码,最后点击触发的时候只会打印3。

var element = document.getElementById("element");
element.onclick = function(e) {console.log(1)};
element.onclick = function(e) {console.log(2)};
element.onclick = function(e) {console.log(3)};

所以,在使用如window.onload之类的事件的时候就需要注意了。

DOM1

有标准,不涉及实践。

DOM2

addEventListener方法在节点对象的原型链上。绑定实名函数更好,因为可以移除。第三个参数用于选择在什么阶段触发,默认false表示在冒泡阶段触发。

element.addEventListener('change',function(){},false)

与DOM0的相比,DOM2事件可以绑定多个,如下,1和2都会被打印:

element.addEventListener('change', function() {console.log(1)}, false);
element.addEventListener('change', function() {console.log(2)}, false);

可是,在绑定实名函数的时候,如果事件、函数名和触发阶段完全相同的话,最终也是只有最后一个有效,如下,最终事件触发的时候只会打印出一个1:

function fn() {console.log(1)};
element.addEventListener('change', fn, false);
element.addEventListener('change', fn, false);

此外,addEventListener还可以绑定DOM0中没有的事件,如DOMContentLoaded。

DOM3

与DOM2的区别在于事件的种类,其他的一样。如keyupkeydownkeypress都是DOM3的事件,click也是DOM3级事件,具体到MDN中搜索DOM L3

事件流

简要的说就是事件的三个阶段:捕获阶段--目标阶段--冒泡阶段。详细说就是如下图。

当用户点击浏览器窗口,事件发生,捕获阶段开始:window--document--html--body--父节点--目标节点(目标阶段),然后开始冒泡阶段:目标节点--父节点--body--html--document--window。

几个需要注意的api

preventDefault的作用是阻止浏览器默认行为,什么是默认行为?除了你自己绑定的操作之外的行为都是浏览器的默认行为,如a标签的跳转,表单的提交,鼠标右键呼出菜单等等。

此外event.returnValue = false;return false;也可以达到跟proventDefault同样的效果。如果是a标签,阻止跳转,可以href="javascript:;"href=javascript:void 0;

stopPropagation的作用是阻止事件的传播。阻止的是其他节点上的事件。

stopImmediatePropagation的作用也是阻止事件的传播。它不但能阻止其他节点上的事件,当前节点上的其他未触发的事件也会一并阻止。如下代码,只打印1:

element.addEventListener('click', function(event) { 
  event.stopImmdiatePropagation();
  console.log(1);
}, false);
element.addEventListener('click', function() {console.log(2)}, false);

currentTarget,当前会执行的回调函数绑定的那个节点。

elementFirst.addEventListener('click', function(event) {
  console.log(event.currentTarget);		// elementFirst
}, false);
elementSecond.addEventListener('click', function(event) {
  console.log(event.currentTarget);		// elementSecond
}, false);

target,就是事件流中的目标阶段的那个节点,也就是目标节点。需要注意的是,事件是否发生与是否绑定的操作没有关系,即便没有在一个节点上绑定事件的操作,事件依然会发生。target可以在事件委托中使用。

自定义事件

var myEvent = new Event('theEvent');
theTarget.addEventlistener('theEvent', function() {doSomething()})
theTarget.dispatchEvent(myEvent);	// theTarget是一个节点,或者说是一个可以调用addEventListener方法的对象。

此外customEvent做到跟Event同样的效果,他们的区别在于,customEvent可以加一些自己需要的数据进去,不过兼容性没有Event好。

Event创建了实例之后,也可以用myEvent.data = {}的方式给数据,效果是一样的。

这有什么用?可以用来做全局范围内的广播,使模块间解耦。在vue中有一个用于组件间通讯的EventBus,自定义事件就可以用来实现这一功能。

事件委托和代理

事件委托就是把本来应该由你来做的事情,让别人帮忙做一下。

在实际编程中,可能会出现这样的场景,一个节点下有多个子节点,每个子节点都要有相应的事件绑定,这个时候我们就可以利用事件的冒泡,把事件绑定在父节点上,然后用event.target来区分到底是哪个子节点触发的事件。当这些子节点需要动态增减的时候,使用事件委托就显得尤为方便了,无论是写代码的效率还是程序运行的效率都会更高。

局限性:focus、blur这样的事件没有在冒泡机制,无法委托。另外一些消耗较高的事件如mousemove和mouseout也不适合委托。如下图

如果我们把B区域的mousemove事件委托到A区域,当事件在A区域发生的时候,对应的操作也会执行,如果是消耗较低的事件,就不会有太大的影响,可如果是消耗较高的事件,就会显得有点浪费了。

观察者模式与发布订阅模式

这两种设计模式与事件非常的类似。与事件的区别在于,事件是异步的,而我们自己实现的这两种模式,虽然使用了回调函数,但大都是同步的。而这两种设计模式之间的区别在于,发布订阅模式的事件池是统一的,观察者模式的事件池分散在各个发布者上面。

参考文章

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant