首页 > 代码库 > JavaScript事件在WebKit中的处理流程研究

JavaScript事件在WebKit中的处理流程研究

    本文主要探讨了JavaScript事件在WebKit中的注册和触发机制。

    JS事件有两种注册方式: 通过DOM节点的属性添加或者通过node.addEventListener()函数注册; 

    通过DOM节点的属性添加如下所示,节点的属性采用on后面紧接event name的形式,比如onclick, onl oad; 

<html>
<head>
<script type="text/javascript">
  function listener(e){
    alert("hello world!");
  }
</script>
</head>
<body>
<button onclick="listener(event)">click</button>
</body>
</html>

       通过addEventListener()函数注册的形式如下, 其完整的形式是:target.addEventListener(typelistener[, useCapture]);其中type为事件类型,listener为响应函数, useCapture表示是否在capture阶段触发,如果不指定,则为false; 

<div>
<button id="button">button</button>
<script type="text/javascript">
  document.getElementById('button').addEventListener("click", listener);
</script>
</div>

         

    WebKit中事件相关的类关系如上图所示:  

1. EventTargetDatatMap: 全局映射表,建立了Node与EventTargetData之间的映射关系 ; 

2. EventTargetData:   成员变量firingEventIterators是Vector, 用于记录正在触发的事件类型,当该Vector非空时,也表示当前正处于firing阶段; 成员变量eventListenerMap是EventlListenerMap类型; 

3. EventlListenerMap:按事件类型分类保存了EventListeners;  成员变量m_entires是Vector,其中每一项可以简化为std::pair<EventType, EventListenerVector>类型; 

4. JSLazyEventListener: 最终响应事件触发的对象; 保存了JS执行的基本信息(源码或者JSObject类型的函数对象);  


    第一种情况下,开始事件注册的时机是发生在页面解析阶段,当创建对了button元素以后,解析到onclick属性,会根据属性值创建对应的EventListener; 这种情况下的EventListener仅保存了JS源码(还没有转换成JSC虚拟机内部的函数对象), 并将EventListener添加到全局Hash表中; 

    第二种情况下,JS在虚拟机中执行到”addEventListener()"时,会根据JSBindings建立的映射关系,最终调用到WebCore中的native实现Node::addEventListener(), 该函数会根据虚拟机中传递过来的函数对象创建EventListener,并在全局Hash表中建立起target node与EventListener(即这里的button)的映射关系; 

    下图是两种情况下,事件注册的流程对比:

  


    事件触发流程有以下几个步骤:

    1. 找到响应事件的target node: 如果是用户交互事件,通过Hit Test算法确定;  如果是浏览器内部生成的事件,一般有固定的响应节点,比如load事件的target node是body节点; 

    2. 事件分发:事件在document与target之间按照(capture, at_target, bubble)的顺序进行分发,capture按照从根节点document到子节点target的路径,而bubble则相反; 

    3. 事件响应:分发流程中,如果事件分发到的当前节点注册了该类型的事件,并且useCapure与事件的分发的顺序一致(即capture阶段时,当前节点注册了useCapture == true的事件), 则进行事件响应; 事件响应分成两步: (1) 从全局映射表中找到当前node对应的EventListeners;(2)将EventListeners封装的JS(源码或者JSC的函数对象)抛到JS虚拟机中执行(下图是mouseup事件的触发时序):



      如前所述,属性中注册的事件在EventListener中仅保存了源码,所以开始执行之前会对源码进行必要的转换,格式化成如下形式:

      "(function(event) {listener(event)\n})"

      简单来讲,事件注册是建立node与响应函数的映射关系的过程 ,这种映射关系基于事件类型进行分类; 而事件触发则是基于这种映射关系,在不同阶段(capture, bubble)响应注册函数的过程; 


(转载请注明出处:http://write.blog.csdn.net/postedit/40620721)


    

     

   


JavaScript事件在WebKit中的处理流程研究