Javascript的事件

137 阅读8分钟

1.什么是事件?

javascript与html之间的交互式通过事件实现的。事件就是文档或者浏览器窗口中发生分一些特定的交互瞬间。

2.事件流

事件流描述的是从页面中接受事件的顺序。

IE的事件流是事件冒泡流,而Netscape Communicator的事件流是事件捕获流.

2.1事件冒泡:

IE的事件流称之为事件冒泡(event bubbling),事件开始时由最具体的元素开始接受,然后主治向上传播奥较为不具体的节点(文档)。

<!DOCYPE html>
<html>
<head>
    <title>Event Bubbling Example</title>
</head>
<body>
    <div id = "myBox">Click Me </div>
</body>
</html>

在上述所述的dom树上,点击

元素是,这个click是事件会按照如下顺序传播

1). div

2).body

3).html

4).document

即click回在div元素上被触发,然后click事件沿着DOM树向上传播,在每一级节点上回发生,知道传播到document对象。也就是,事件 首先在目标元素上被触发,然后沿着dom树的结构一级一级向上向父级传播。

浏览器在实现 事件冒泡上稍微有些区别:

IE5.5及更早版本的事件冒泡回跳过元素直接从 直接跳到document

IE9,Fierfox,Chrome和Safari则将事件一直冒泡到window对象。

2.2事件捕获

事件捕获的思想正好和事件冒泡的思想相反,事件有不太具体的节点更早接收到,最具体的节点最后接收到事件。事件捕获的用意在于事件到达目标之前捕获它。和上面同一个例子。是阿事件捕获触发的顺序就是

1).document

2).html

3).body

4).div

在事件捕获的过程中,document对象首先接收到click事件,然后沿着DOM树一次向下,一直传播到事件的实际目标,即div元素上

2.3DOM事件流:

‘DOM2级事件’规定事件流包含三个阶段:

事件捕获阶段:为截获事件提供了机会

处于目标阶段:实际的目标接收到事件

事件冒泡阶段:可以在这个阶段对事件作出响应。

我的理解就是(先捕获到目标元素之后,然后让实际的目标接收到事件,最后将事件向上冒泡做一些响应)

这里的实际的目标的

元素,

在DOM事件流中,实际的目标在捕获阶段不会接收到事件。即

元素在事件捕获阶段不接收事件,也就是在捕获阶段,事件从document到标签再到,然后事件捕获阶段停止。

接下来一个阶段是‘处于目标’阶段,在‘处于目标’阶段中事件在目标元素

上发生,在事件处理中被看成冒泡阶段的一部分。

然后冒泡阶段发生,事件又再次传播回文档。(借用蛋老师的一张图更加形象的描述事件从捕获到目标阶段再到冒泡阶段的过程)

‘DOM2级事件’规范明确要求捕获阶段不会涉及事件目标,但是IE9、Safari、Chrome、Firefox 和 Opera 9.5 及更高版本都会在捕获阶段触 发事件对象上的事件。结果,就是有两个机会在目标对象上面。

3.事件处理程序

事件就是用户或者浏览器自身执行的某种动作。为了响应某个事件 的函数就叫做事件处理程序(或着事件侦听器)。

3.1HTML事件处理程序

<input type = "button" value = "Click Me" onclick = "alert(&quot;Clicked&quot)">

在HTML中定义的事件处理程序可以包含要执行的具体动作,也可以调用在页面其他地方定义的脚本。

<script type="text/javascript"> function showMessage(){alert("Hello world!"); }</script>
<input type="button" value="Click Me" onclick="showMessage()" />

缺点就是html代码和js代码紧密耦合不易管理

3.2  DOM0级事件处理程序

是通过js指定事件处理程序的传统方式,将一个函数赋值给一个事件处理程序。

每个元素都有自己的事件处理程序属性,这些属性同城 全部小写,例如onclick,,,,

使用DOM0级方法制定的事件处理程序,this指定的是当前元素

var btn = document.getElementById('myBtn');
btn.onclick = function(){
    console.log(this.id)//myBtn
}

以这种凡事添加的事件处理程序会在事件流的冒泡阶段被处理。

删除DOM0级方法指定的事件处理程序

btn.onclick = null;

3.3  DOM2级事件处理程序

指定事件处理程序:addEventListener()

删除事件处理程序:removeEventListener()

这两个函数都接收三个参数:

1)要处理的事件名,例如‘click’

2)作为事件处理程序的函数function(){}

3)一个布尔值,true表示在捕获阶段调用事件处理程序,false表示在冒泡阶段调用时加盟处理程序。

使用DOM2级方法添加事件处理程序的主要好处是可以为一个元素调价多个事件处理程序。

使用DOM2级方法给同一个元素添加多个处理函数时,触发的顺序是按照添加的顺序 执行的

通过addEventLisener添加的事件处理程序只能使用removeEventListenr()来一出,删除时传入的参数一定和添加时传入的参数相同。这就意味着addEventListener()添加的匿名函数无法移除。

var btn = document.getElementById('myBtn');
btn.addEventListener('click',function(){
    alert(this.id)
},false);

btn.removeEventListener('click',function(){
    alert(this.id)
},false)//没有用,没有把监听函数删掉。因为addEventListener添加的时候添加的是一个匿名函数
两个匿名函数是完全不同的

大多情况下都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度的兼容各种浏览器。

3.4 IE事件处理程序:

attachEvent('on事件处理名','事件处理函数')

detachEvent('on事件处理名','事件处理函数')

这里的事件名 要在前面加一个on

使用attachEvent()方法中,this指向的是window

使用attachEvent()给同一个元素添加多个处理程序是,执行顺序是以添加的相反顺序被触发的。

var btn = document.getElementById("myBtn");btn.attachEvent("onclick", alert("Clicked");});btn.attachEvent("onclick", alert("Hello world!");});
//先Hello world后Clicked

封装一个浏览器事件处理程序

var EventUtil = {
    addHandler: function(elem, type, handler){
        if(elem.addEventListener){
            elem.addEventListenr(type,handler,false)
        }else if(elem.attachEvent){
            elem.attachEvent('on' +  type, handler)
        }else{
            elem['on' + type] = handler;
        }
    },

    removeHandler:  function(elem,type,handler){
        if(elem.removeEventListener){
            elem.removeEventListener(type,handler,false);
        }else if(elem.detachEvent){
            elem.detachEvent('on' + type,handler);
        }else{
            elem['on' + type ] = null;
        }
   }
}

4.事件对象:

在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。包含导致事件的元素,事件的类型以及其他与特定事件相关的信息。

4.1 DOM中的事件对象

event 对象包含 与创建它的特定事件有关的属性和方法,触发的类型不一样,可用的属性和方法也不一样。

只有在事件处理程序执行期间,event对象才会存在,一旦事件处理程序执行完毕,event对象就会被销毁。

4.2 IE中的事件对象

在使用DOM0级方法添加事件处理程序时,event对象作为window对象的一个属性存在。

封装一个事件对象

var EventUtil = {
    addHandler: function(elem,type,handler){
        if(elm.addEventListener){
        elm.addEventListener(type,handler,false)
        }else if(elem.attachEvent){
            elem.attachEvent('on'+type,handler);
        }else{
            elem['on' + type] = handler;
        }

    },
    getEvent: function(event){//返回对event对象的引用
        return event? event : window.event;
    },
    getTarget: function(event){//返回事件的目标
        return event.target || event.srcElement
    },
preventDefault: function(event){//取消默认的行为
    if(event.preventDefault){
        event.preventDefault();
    }else{
        event.returnVale = false;
    }
    },
    removeHandler:function(elem,type,handler){
        if(elem.removeEventListener){
            elem.removeEventListener(type,handler,false);
        }else if(elem.detachEvent){
            elem.detachEvent('on'+ type,handler);
        }else{
            elem['on' + type] = null;
        }    
    },

    stopPropagation: function(event){
        if(event.stopPropagation){
            event.stopPropagation();
        }else{
            event.cancelBubble = ture;
        }
    }
}

DOMContentLoaded事件和load事件

window的load事件会在页面中的一切都加载完毕时触发,但这个过程需要加载完毕所有的外部资源。

DOMContentLoaded事件则在形成完整的DOM树之后就会触发,不理会图像,js文件,css文件或其他资源是否已经下载完毕。与load事件不同DOMContentLoaded支持在页面下在的早起添加事件处理程序。DOMContentLoaded不会而外提供任何信息,他的target属性时document。对于不支持DOMContentLoaded的浏览器,建议使用一个定时器

setTimeout(function(){
    //在此处添加事件处理程序
},0)

readystatechange事件:

IE为DOM文档的某些部分提供了readystatechange事件。这个事件的目的就是提供与文档或元素的加载状态有关的信息。

load事件与readystatechange事件并不能保证以相同的顺序触发

5. 内存和性能

5.1 事件委托:

用来解决事件处理程序过多的问题。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

<ul id = "myLinks">
    <li id = "go"> GO GO GO</li>
    <li id = "do"> DO DO DO</li>
    <li id = "say"> say hi </li>
</ul>

使用事件委托的方式只需要在DOM树中尽量最高的层次上添加一个事件处理程序。

var list = document.getElementById('myLinks');
EventUtil.addHandler(list,"click",function(event){
    event = EventUtil.getEvent(event);//获取事件对象
    var target = EventUtil.getTarget(event);
    switch(target.id){
        case "do":
            document.title = "I changed the document's title";
            beak;
        case 'go':
            location.href = "http://www.wrox.com";
            break;
        case "say":
            alert('hi');
            break;
    }
});

上面的例子直接使用的是上面封装好的函数来使用的,可能不是太好理解

var list = document.getElementById('myLinks');
list.onclick = function(e){
    var e = event || window.event;
    var target = e.target || e.srcElement;
    switch(target.id){
        case 'do':
            document.title = "I changed something";
            break;
            case 'go':
            location.href = "http;//www.baidu.com";
            break;
            case 'say':
            alert('hi');
            break

    }
}