zoukankan      html  css  js  c++  java
  • 事件绑定与深入详解

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

  • 相关阅读:
    Resharper的使用
    SQL Server 占用CPU较高的解决方法
    014 FPGA千兆网UDP通信
    012 PCIe总线的基础知识
    008 PCI设备BAR空间的初始化
    016 基于FPGA的网口通信实例设计【转载】
    015 FPGA千兆网TCP通信【转载】
    006 PCI总线的桥与配置(一)
    004 PCI Express体系结构(四)
    007 PCI总线的桥与配置(二)
  • 原文地址:https://www.cnblogs.com/LO-ME/p/3650183.html
Copyright © 2011-2022 走看看