事件绑定与深入详解

事件绑定分为两种:一种是传统事件绑定(内联模型,脚本模型),一种是现代事件绑定(DOM2 级模型)。现代事件绑定在传统绑定上提供了更强大更方便的功能。

一.传统事件绑定的问题
传统事件绑定有内联模型和脚本模型,内联模型我们不做讨论,基本很少去用。先来看一下脚本模型,脚本模型将一个函数赋值给一个事件处理函数。

<script type="text/javascript">
    window.onload = function(){
        var box = document.getElementById('box'); //获取元素
            box.onclick = function () { //元素点击触发事件
            alert('Lee');
        };
    };
</script>
</head>
<body>
    <div id="box">
           测试DIV
    </div>
</body>

问题一:一个事件处理函数触发两次事件

<script type="text/javascript">
    window.onload = function () { //第一组程序项目或第一个 JS 文件
        alert('Lee');
    };
    window.onload = function () { //第二组程序项目或第二个 JS 文件
        alert('Mr.Lee');
    };
    //如果页面有两个或者多个js,并且第一个js是第一个程序员开发的,第二个js是第二个程序员开发的
</script>
</head>
<body>
    <div id="box">
           测试DIV
    </div>
</body>

当两组程序或两个 JS 文件同时执行的时候,后面一个会把前面一个完全覆盖掉。导致前面的 window.onload 完全失效了。解决覆盖问题,我们可以这样去解决:

<script type="text/javascript">
    window.onload = function () { //第一个要执行的事件,会被覆盖
        alert('Lee');
    };
    
    if (typeof window.onload == 'function') { //判断之前是否有 window.onload
        var saved = null; //创建一个保存器
        saved = window.onload; //把之前的 window.onload 保存起来
    }
    
    window.onload = function () { //最终一个要执行事件
        if (saved) 
        saved();
//执行之前一个事件,saved就是window.onload.saved()相当于window.onload(),但window.onload()是不能执行的,所以saved()相当于window.onload=function(){} alert('Mr.Lee'); //执行本事件的代码 }; </script> </head> <body> <div id="box"> 测试DIV </div> </body>

 问题二:事件切换器

<script type="text/javascript">

    window.onload=function(){
        var box = document.getElementById("box");
        box.onclick=toBlue;
    }
    
    function toRed() {
        this.className = 'red';
        this.onclick = toBlue; //第三次执行 toBlue(),然后来回切换
    }
    function toBlue() {
        this.className = 'blue';
        this.onclick = toRed; //第二次执行 toRed()
    }
    
</script>
<style type="text/css">
    .red{
        width:100px;
        height:100px;
        background:red;
    }
    .blue{
        background:blue;
        width:100px;
        height:100px;
    }
</style>
</head>
<body>
    <div id="box" class="red">
           测试DIV
    </div>
</body>

这个切换器在扩展的时候,会出现一些问题:如果增加一个执行函数,那么会被覆盖

<script type="text/javascript">

    window.onload=function(){
        var box = document.getElementById("box");
        box.onclick=function(){   //被下面的覆盖了,无法执行
            alert("lee");
        }
        box.onclick=toBlue;
    }
    
    function toRed() {
        this.className = 'red';
        this.onclick = toBlue; //第三次执行 toBlue(),然后来回切换
    }
    function toBlue() {
        this.className = 'blue';
        this.onclick = toRed; //第二次执行 toRed()
    }
    
</script>
<style type="text/css">
    .red{
        width:100px;
        height:100px;
        background:red;
    }
    .blue{
        background:blue;
        width:100px;
        height:100px;
    }
</style>
</head>
<body>
    <div id="box" class="red">
           测试DIV
    </div>
</body>

如果解决覆盖问题,就必须包含同时执行,但又出新问题(可读性变差,this传递)(解决这些问题要使用事件处理函数)

<script type="text/javascript">

    window.onload=function(){
        var box = document.getElementById("box");
        box.onclick = function () {       //包含进去,但可读性降低
            toAlert();               //第一次不会被覆盖,但第二次又被覆盖
            toBlue.call(this);         //还必须把 this 传递到切换器里
        };
        
    }
    function toAlert(){
        alert("Lee");
    }
    
    function toRed() {
        this.className = 'red';
        this.onclick = toBlue; //第三次执行 toBlue(),然后来回切换
    }
    
    function toBlue() {
        this.className = 'blue';
        this.onclick = toRed; //第二次执行 toRed()
    }
    
</script>
<style type="text/css">
    .red{
        width:100px;
        height:100px;
        background:red;
    }
    .blue{
        background:blue;
        width:100px;
        height:100px;
    }
</style>
</head>
<body>
    <div id="box" class="red">
           测试DIV
    </div>
</body>

 

 

 

二. W3C事件处理函数

“DOM2 级事件”定义了两个方法,用于添加事件和删除事件处理程序的操作:addEventListener()和 removeEventListener()。

所有 DOM 节点中都包含这两个方法,并且它们都接受 3 个参数;事件名、函数、冒泡或捕获的布尔值(true 表示捕获,false 表示冒泡(IE6,7,8只支持冒泡,所以要兼容只能用冒泡))。

<script type="text/javascript">
    window.addEventListener('load', function () {
        alert('Lee');
    }, false);
    
    window.addEventListener('load', function () {
        alert('Mr.Lee');
    }, false);
    
    //解决的覆盖的问题
</script>
<style type="text/css">
    .red{
        width:100px;
        height:100px;
        background:red;
    }
    .blue{
        background:blue;
        width:100px;
        height:100px;
    }
</style>
</head>
<body>
    <div id="box" class="red">
           测试DIV
    </div>
</body>

W3C 的现代事件绑定比我们自定义的好处就是:1.不需要自定义了;2.可以屏蔽相同的函数;3.可以设置冒泡和捕获。

<script type="text/javascript">
    window.addEventListener('load', init, false); //第一次执行了
    window.addEventListener('load', init, false); //第二次被屏蔽了
    
    function init() {
        alert('Lee');
    }
</script>

 

<script type="text/javascript">
    //事件切换器
    window.addEventListener('load', function () {
        var box = document.getElementById('box');
        box.addEventListener('click', function () { //不会被误删
            alert('Lee');
    }, false);
    
    box.addEventListener('click', toBlue, false); //引入切换也不会太多递归卡死
    }, false);
    
    function toRed() {
        this.className = 'red';
        this.removeEventListener('click', toRed, false);
        this.addEventListener('click', toBlue, false);
    }
    function toBlue() {
        this.className = 'blue';
        this.removeEventListener('click', toBlue, false);
        this.addEventListener('click', toRed, false);
    }
</script>
<style type="text/css">
    .red{
        width:100px;
        height:100px;
        background:red;
    }
    .blue{
        background:blue;
        width:100px;
        height:100px;
    }
</style>
</head>
<body>
    <div id="box" class="red">
           测试DIV
    </div>
</body>

综上所述,W3C完美解决了了这些问题,比较好用,但是IE6,7,8不支持,而是采用了自己的事件,当然IE9及以上已经支持了

 

三. IE事件处理函数

IE 实现了与 DOM 中类似的两个方法:attachEvent()和 detachEvent()。这两个方法接受相同的参数:事件名称和函数。
在使用这两组函数的时候,先把区别说一下:1.IE 不支持捕获,只支持冒泡;2.IE 添加事件不能屏蔽重复的函数;3.IE 中的 this 指向的是 window 而不是 DOM 对象。4.在传统事件上,IE 是无法接受到 event 对象的,但使用了 attchEvent()却可以,但有些区别。

<script type="text/javascript">
    window.attachEvent('onload', function () {
        var box = document.getElementById('box');
        box.attachEvent('onclick', toBlue);
    });
    function toRed() {
        var that = window.event.srcElement;
        that.className = 'red';
        that.detachEvent('onclick', toRed);
        that.attachEvent('onclick', toBlue);
    }
    function toBlue() {
        var that = window.event.srcElement;
        that.className = 'blue';
        that.detachEvent('onclick', toBlue);
        that.attachEvent('onclick', toRed);
    }
</script>
<style type="text/css">
    .red{
        width:100px;
        height:100px;
        background:red;
    }
    .blue{
        background:blue;
        width:100px;
        height:100px;
    }
</style>
</head>
<body>
    <div id="box" class="red">
           测试DIV
    </div>
</body>

 

IE 不支持捕获,无解。IE 不能屏蔽,需要单独扩展或者自定义事件处理。IE 不能传递 this,可以 call 过去。

<script type="text/javascript">
    window.attachEvent('onload', function () {
        var box = document.getElementById('box');
        box.attachEvent('onclick', function () {
            alert(this === window); //this 指向的 window
        });
    });
    window.attachEvent('onload', function () {
        var box = document.getElementById('box');box.attachEvent('onclick', function () {
            toBlue.call(box); //把 this 直接 call 过去
        });
    });
    
    function toThis() {
        alert(this.tagName);
    }
</script>
<style type="text/css">
    .red{
        width:100px;
        height:100px;
        background:red;
    }
    .blue{
        background:blue;
        width:100px;
        height:100px;
    }
</style>
</head>
<body>
    <div id="box" class="red">
           测试DIV
    </div>
</body>

 

在传统绑定上,IE 是无法像 W3C 那样通过传参接受 event 对象,但如果使用了attachEvent()却可以。

<script type="text/javascript">
    box.onclick = function (evt) {
        alert(evt); //undefined
    }
    box.attachEvent('onclick', function (evt) {
        alert(evt); //object
        alert(evt.type); //click
    });
    box.attachEvent('onclick', function (evt) {
        alert(evt.srcElement === box); //true
        alert(window.event.srcElement === box); //true
    });
</script>

 

最后,为了让 IE 和 W3C 可以兼容这个事件切换器,我们可以写成如下方式:

<script type="text/javascript">
    function addEvent(obj, type, fn) { //添加事件兼容
        if (obj.addEventListener) {
            obj.addEventListener(type, fn);
        } else if (obj.attachEvent) {
            obj.attachEvent('on' + type, fn);
        }
    }
    function removeEvent(obj, type, fn) { //移除事件兼容
        if (obj.removeEventListener) {
            obj.removeEventListener(type, fn);
        } else if (obj.detachEvent) {
            obj.detachEvent('on' + type, fn);
        }
    }
    function getTarget(evt) { //得到事件目标
        if (evt.target) {
            return evt.target;
        } else if (window.event.srcElement) {
            return window.event.srcElement;
        }
    }
</script>

PS:调用忽略,IE 兼容的事件,如果要传递 this,改成 call 即可。
PS:IE 中的事件绑定函数 attachEvent()和 detachEvent()可能在实践中不去使用,有几个原因:
  1.IE9 就将全面支持 W3C 中的事件绑定函数;

  2.IE 的事件绑定函数无法传递 this;

  3.IE的事件绑定函数不支持捕获;
  4.同一个函数注册绑定后,没有屏蔽掉;5.有内存泄漏的问题。

 

 

四.事件对象的其他补充
在 W3C 提供了一个属性:relatedTarget;这个属性可以在 mouseover 和 mouseout 事件中获取从哪里移入和从哪里移出的 DOM 对象。

 

 

 

 

 

 

 

 

posted @ 2014-04-11 16:28  胡椒粉hjf  阅读(224)  评论(0编辑  收藏  举报