zoukankan      html  css  js  c++  java
  • JavaScript中的事件机制

    事件绑定

    最简单的方式莫过于元素上加onclick方法了

            <input id="test" type="button" value="Test" onclick="alert('模拟事件')" />
            //根据元素id获得元素
            function GetID(elementName) {
                return document.getElementById(elementName);
            }
            window.onload = function () {
                GetID("test").click(); //事件模拟
            }

    此时浏览器均可以正常执行,在元素上加事件称为内联事件,

      <input id="test" type="button" value="Test" />
      window.onload = function () {
                GetID("test").onclick = function Test() {
                    alert("事件触发了");
                }
                GetID("test").click(); //事件模拟
            }

    所有的浏览器都可以正常执行,那么现在把input 换成div呢

    <div id="test" style="background: #CD853F;  200px; height: 200px;">DIV</div>

         IE与新版火狐(版本5中已实现对非input/button元素的click方法)没有问题,Chorme中没有弹出,即在Chorme中事件模拟失效了,不是所有浏览器中的所有元素都支持click方法。事实上只有input/button元素在所有浏览器才具有click方法(在Input标签上没有问题)。在讨论如何解决Chorme中事件模拟问题前先说下监听事件!(Chorme事件模拟问题)

    事件监听

    IE: attachEvent、detachEvent 非IE: addEventListener、removeEventListener

    除了上面四个函数还有一个比较通用的方法是 document.getElmentById(元素ID).onclick = function(){} 上面已经使用了

    function addEventListener(element, eventName, fun) {
        if (window.attachEvent) {
            element.attachEvent("on" + eventName, fun);
        }
        else {
            element.addEventListener(eventName, fun, false);
        }
    }

    观察 element.addEventListener(eventName, fun, false)这句; 这里的false是什么意思呢改成true有什么不同吗,后文说(addEventListener问题)

    调用如下

     <input id="test" type="button" value="Test" />
      window.onload = function () {
                addEventListener(GetID("test"), "click", function () { alert("事件触发了") });
                GetID("test").click(); //事件模拟
            }

    所有的浏览器依然没有问题,可"三目下"addEventListener方法

    function addEventListener(element, eventName, fun) {
        window.attachEvent ? element.attachEvent('on' + eventName, fun) : element.addEventListener(eventName, fun, false);
    }

    如果想使用其他事情 当然没有问题 如   mouseover

    addEventListener(GetID("test"), "mouseover", function () { alert("事件触发了") });

    现再次把input 换成div呢,当然事件模拟在Chorme还是不行 O(∩_∩)O~~

    综上所述,元素绑定事件小结有以下几种方式

    1.html元素中绑定

    2.document.getElmentById(元素ID).onclick = function(){}

    3.attachEventaddEventListener

    事件模拟

        现在再来看Chorme事件模拟问题失效问题,前几天 天猫抢红包,挂了二天的脚本啊,才弄了30大洋,淘宝坑爹啊。脚本当然需要自己写啦,首先当然需要实现事件模拟手动点击的苦恼,如果是Button就没有问题了可惜响应事件的元素是DIV ,这时在IE于火狐下没有问题,这时Chorme就有这个问题啦,


    为了实现 任何元素自动点击功能
    先看这里
    https://developer.mozilla.org/en/DOM/document.createEvent 
    来这里
    http://www.w3school.com.cn/xmldom/met_document_createevent.asp

    语法:

    createEvent(eventType)
    参数描述
    eventType

    想获取的 Event 对象的事件模块名。

    关于有效的事件类型列表,请参阅“说明”部分。

    语法

    event.initEvent(eventType,canBubble,cancelable)
    参数描述
    eventType 字符串值。事件的类型。
    canBubble 事件是否起泡。
    cancelable 是否可以用 preventDefault() 方法取消事件。

    说明

    该方法将初始化 Document.createEvent() 方法创建的合成 Event 对象的 type 属性、bubbles 属性和 cancelable 属性。只有在新创建的 Event 对象被 Document 对象或 Element 对象的 dispatchEvent() 方法分派之前,才能调用 Event.initEvent() 方法

    语法:

    dispatchEvent(evt)
    参数描述
    evt 必需。要分派的 Event 对象。

    返回值

    如果在事件传播过程中调用了 evtpreventDefault() 方法,则返回 false,否则返回 true

    看完上面语法,代码如下

    //响应事件模拟
    function TriggerClick(element) {
        if (element.click) {    //判断是否支持click事件模拟
            element.click(); 
        } else {
            try {
                var event = document.createEvent('Event'); //HTMLEvents
                event.initEvent('click', true, true); //事件冒泡
                element.dispatchEvent(event);
            }
            catch (e) {
                alert(e)
            };
        }
    }

    调用

        <input id="test" type="button" value="Test" />
        <div id="div" style="background: #BDB76B;  200px; height: 200px;">
    
                addEventListener(GetID("test"), "click", function () { alert("我是Button") });
                GetID("div").onclick = function Test() {
                    alert("我是DIV");
                }
                TriggerClick(GetID("test"), 'click');
                TriggerClick(GetID("div"), 'click');

    这样所有浏览器事件模拟就没有问题了,  O(∩_∩)O~~

    那是不是只有click事件可以模拟呢,当然不是主要是模拟其他事件没有实质性的意义,但这里还要多说一点的是在ExtJS中有fireEvent来主动触发事件,IE中也有个fireEvent能去主动触发事件,当然如果是点击事件使用click即可。然而非点击事件则只能通过fireEvent去触发了。

    Syntax

    bFired = object.fireEvent(sEvent [, oEventObject])

    Parameters

    sEvent Required. String that specifies the name of the event to fire.
    oEventObject Optional. Object that specifies the event object from which to obtain event object properties.

    Return Value

    Boolean. Returns one of the following values:

    true Event fired successfully.
    false Event was cancelled.

    Remarks

    If the event being fired cannot be cancelled, fireEvent always returns true.

    Regardless of their values specified in the event object, the values of the four event properties—cancelBubble, returnValue, srcElement, and type—are automatically initialized to the values shown in the following table.

    Event object propertyValue
    cancelBubble false
    returnValue true
    srcElement element on which the event is fired
    type name of the event that is fired

    这时上述的函数就有必须改下了

    function TriggerClick(element) {
        if (window.attachEvent) {
            element.fireEvent("onclick");    // element.click();
        } else {
            try {
                var event = document.createEvent('Event'); //HTMLEvents
                event.initEvent('click', true, true); //事件冒泡
                element.dispatchEvent(event);
            }
            catch (e) {
                alert(e)
            };
        }
    }

    参看火狐官方文档也可以如下

    //事件模拟(MouseEvents)
    function SimulateClick(element, eventName) {
        if (window.attachEvent) {
            element.fireEvent("on" + eventName);    // element.click();
        }
        else {
            var evt = document.createEvent("MouseEvents");
            evt.initMouseEvent(eventName, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
            document.getElementById(element).dispatchEvent(evt);
        }
    } 

    使用也没有问题

     addEventListener(GetID("div"), "click", function () { alert("div") });
     SimulateClick("div", "click");

         可能会有朋友问 if(element.click)不行吗,需要说明的是新版的火狐中已经可以实现非input/button元素的click方法,也就是说if(element.click)返回true ,而火狐中没有fireEvent方法,但createEvent对火狐来说同样有效!

        不过呢,click方法已经写入了HTML5章程,Firefox也是刚刚在版本5中实现对非input/button元素的click方法实现,这点Chrome有些落后了,希望浏览器速度改进下,模拟点击将十分简单,直接调用click方法即可,但对了程序员来说,意义不大,国内IE6还居多的说。Ok,事件模拟就说到这里了 O(∩_∩)O~~

    事件流

        JavaScript的事件是一种流的形式存在的,事件流描述的是页面中接受事件的顺序,一个事件的可能会有多个元素响应,有时候这不是我们想要的,我们只需要特定的元素响应绑定的事件即可。

       事件流又分为冒泡型事件又称事件冒泡(event bubbling)与捕获型事件(event capturing)又称事件捕获,这里需要说明一下先阶段所有浏览器都支持事件冒泡,只是实现上有一些小的差别,而非IE 浏览器还支持事件捕获形式,上述非IE浏览器绑定事件中的 Eelement.addEventListener(eventName, fun, false) 中的bool值即是控制事件的形式 ,false即是事件冒泡,反之为事件捕获。这里我们需要再次构造下此方法

    //监听事件(flag可选参数)
    function addEventListener(element, eventName, fun, flag) {
        if (flag) {
            window.attachEvent ? element.attachEvent('on' + eventName, fun) : element.addEventListener(eventName, fun, true); //事件冒泡
        }
        else {
            window.attachEvent ? element.attachEvent('on' + eventName, fun) : element.addEventListener(eventName, fun, false); //事件捕获
        }
    }

    捕获型事件是自上而下的,而冒泡型事件是自下而上的 ,如下图

    事件冒泡

        IE中的事件流属于事件冒泡(非IE也支持),即事件由当前触发事件的元素根节点逐级向上传播, 示例:

    <body id="Body">
        <div id="Orange" style=" 200px; height: 200px; background-color: Orange">
            Orange
            <div id="Gray" style=" 150px; height: 150px; background-color: Gray">Gray</div>
        </div>
    </body>
                addEventListener(document.body, "click", function () { alert("Body") });
                addEventListener(GetID("Orange"), "click", function () { alert("Orange") });
                addEventListener(GetID("Gray"), "click", function () { alert("Gray") });

    此时点击Gray ,所有浏览器依次会弹出 Gray => Orange => Body ,如上图 依次div =>body=> document, addEventListener中的第三个参数是useCapture , 一个bool类型。当为false时为冒泡获取(由里向外),true为capture方式(由外向里)

    事件捕获

        一般情况下冒泡事件遇到的比较多,那么捕获性事件创建呢 很简单了,只需要改下参数

                addEventListener(GetID("Body"), "click", function () { alert("Body") },true);
                addEventListener(GetID("Orange"), "click", function () { alert("Orange") }, true);
                addEventListener(GetID("Gray"), "click", function () { alert("Gray") }, true);

    此时IE中点击Gray ,依次会弹出 Gray => Orange => Body ,非IE中依次会弹出 Body => Orange => Gray,和遇期的一样!

    此时如果调用

                addEventListener(GetID("Body"), "click", function () { alert("Body") }, true);
                addEventListener(GetID("Orange"), "click", function () { alert("Orange") });
                addEventListener(GetID("Gray"), "click", function () { alert("Gray") }, true);

    此时非IE中点击Gray,必须是Gray => Body => Orange,

    阻止冒泡

        如果现在想要点击Gray只弹出Gray ,即只执行被点击元素的事件,需要怎么做呢。 IE中阻止事件冒泡 window.event.cancelBubble = true;非IE stopPropagation 方法

    // 取消冒泡
    function stopPropagation(e) {
        if (typeof event !== 'undefined') {
            window.event.cancelBubble = true;
        }
        else { // w3c
            e.stopPropagation();
        }
    }

     只需要在执行Gray事件后取消冒泡即可 如下

                addEventListener(document.body, "click", function () { alert("Body") });
                addEventListener(GetID("Orange"), "click", function (){ alert("Orange") });
                addEventListener(GetID("Gray"), "click", function (e) { alert("Gray"); stopPropagation(e); }); //取消冒泡

    此时点击Gray只弹出Gray,点击Orange时依然会弹出Orange =>Body, 由于冒泡在执行了Body中事件, 现在又有一个需求,在Orange中我们怎么知道是谁触发了事件(如果嵌套很多层)

    事件源对象

        上面所说如果元素嵌套很多层,由于冒泡的原因,后面的元素事件的执行并不知道是谁触发来了事件,换句话说,能不能在后面的事件中得到事件源的信息,如上面虽然Gray中取消了冒泡,但Orange中并没有取消,所以还会出现冒泡,。

    怎么获取事件源呢,如下

    // 获得事件源对象
    function GetEventSrcElement(e) {
        if (window.event) {
            return window.event.srcElement;
        }
        else {
            return e.target; //e.currentTarget
        }
    }

    修改代码如下

                addEventListener(GetID("Body"), "click", function (e) { alert(GetEventSrcElement(e).id) });//获取事件源对象
                addEventListener(GetID("Orange"),"click",function () { alert("Orange") });
                addEventListener(GetID("Gray"), "click", function (e) { alert("Gray"); CancelBubble(e); }); //取消冒泡

    此时点击Orange,会弹出 两次 Orange ,点击Body当然会弹出Body ,上述的id就是元素的一个属性,当然还可以获取其他值, 这点需要理解好,后面说闭包也会说到

    再次修改下

    function GetEventSrcElement(e) {
        return (e || event).srcElement || (e || event).target;
    }

    效果一样 O(∩_∩)O~~

    如果不想带参数呢要想获取事件信息怎办?
    其实浏览器提供了一个参数arguments参数,这个参数可以获取函数参数的个数,如果要不带参数获取事件信息,在火狐浏览器下可以使用arguments.callee.caller.arguments[0]。既
    var event = window.event || arguments.callee.caller.arguments[0]
    console.log( event.type )就可以获取火狐浏览器的事件信息了。

    取消事件

        如果事件对象的cancelable属性为true,则该方法可以取消事件的默认动作.但并不取消事件的冒泡行为.event.preventDefault()可以取消这个默认动作的发生,

    例如怎样阻止一个input元素内非法字符的输入呢(只能输入小写字母)

    // 得到输入
    function GetCode(e) {
        var charCode = null;
        if (window.event) {
            charCode = window.event.keyCode;
        }
        else {
            charCode = e.which;
        }
        return charCode;
    }
    // 取消事件
    function preventDefault(e) {
        if (window.event) {
            window.event.returnValue = false;
        }
        else {
            // w3c ff 取消事件
            e.preventDefault(); 
        }
    }

    使用

      <input id="txt" type="text" />
    //取消事件 addEventListener(GetID("txt"), "keypress", function (e) { var charCode = GetCode(e); if (charCode < 97 || charCode > 122) { preventDefault(e); alert("只能输入小写字母 " + charCode); } })

    在事件触发后的任何阶段调用preventDefault方法来取消该事件,意味着该事件的所有默认动作都不会发生.

    事件委托

    修订历史

    2012年6月22日21:19:51  开篇

    2012年6月23日15:14:57 增加 事件流

  • 相关阅读:
    随笔
    洛谷
    洛谷
    洛谷
    (水题)洛谷
    洛谷
    (水题)洛谷
    洛谷
    (水题)洛谷
    (水题)洛谷
  • 原文地址:https://www.cnblogs.com/Irving/p/2558654.html
Copyright © 2011-2022 走看看