事件绑定与深入详解

事件绑定分为两种:一种是传统事件绑定(内联模型,脚本模型),一种是现代事件绑定(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 对象。

 

 

 

 

 

 

 

 

你可能感兴趣的:(事件)