第十三章:事件类型(简述、UI事件、焦点事件)

事件

事件类型

  • 由于事件类型这一小节篇幅很长,所以我决定再细化一下。
  • 以下是DOM3级事件规定的几类事件:
    1. UI(User Interface,用户界面)事件,当用户与页面上的元素交互时触发。
    2. 焦点事件,当元素获得或失去焦点时触发。
    3. 鼠标事件,当用户通过鼠标在页面上执行操作时触发。
    4. 滚轮事件,当使用鼠标滚轮(或类似设备)时触发。
    5. 文本事件,当在文档中输入文本时触发。
    6. 键盘事件,当用户通过键盘在页面上执行操作时触发。
    7. 合成事件,当为IME(Input Method Editor,输入法编辑器)输入字符时触发。
    8. 变动(mutation)事件,当底层DOM结构发生变化时触发。
    9. 变动名称事件,当元素或属性名变动时触发(已废弃)。
  • 除了这几类事件之外,HTML5也定义了一组事件。而有些浏览器还会在DOM和BOM中实现其他专有事件。这些专有事件一般没有什么规范,不同的浏览器实现方式也有所不同。

UI事件

  • UI 事件指的是那些不一定与用户操作有关的事件。来看看UI事件:
    1. DOMActivate:表示元素已经被用户操作(通过鼠标或键盘)激活。这个事件在DOM3 级事件中被废弃,但Firefox 2+和Chrome 支持它。考虑到不同浏览器实现的差异,不建议使用这个事件。
    2. load:当页面完全加载后在window上面触发,当所有框架都加载完毕时在框架集上面触发,当图像加载完毕时在<img>元素上面触发,或者当嵌入的内容加载完毕时在<object>元素上面触发。
    3. unload:当页面完全卸载后在window上面触发,当所有框架都卸载后在框架集上面触发,或者当嵌入的内容卸载完毕后在<object>元素上面触发。
    4. abort:在用户停止下载过程时,如果嵌入的内容没有加载完,则在<object>元素上面触发。
    5. error:当发生JavaScript 错误时在window上面触发,当无法加载图像时在<img>元素上面触发,当无法加载嵌入内容时在<object>元素上面触发,或者当有一或多个框架无法加载时在框架集上面触发。第17章将继续讨论这个事件(现在我也不知道具体功能)。
    6. select:当用户选择文本框(<input><texterea>)中的一或多个字符时触发。第14章将继续讨论这个事件。
    7. resize:当窗口或框架的大小变化时在window框架上面触发。
    8. scroll:当用户滚动带滚动条的元素中的内容时,在该元素上面触发。<body>元素中包含所加载页面的滚动条。
  • 除了DOMActivate之外,其他事件在DOM2 级事件中都归为HTML 事件(DOMActivate 在DOM2级中仍然属于UI 事件)。要确定浏览器是否支持DOM2 级事件规定的HTML 事件,可以使用如下代码:
    var isSupported = document.implementation.hasFeature("HTMLEvents", "2.0");
  • 要确定浏览器是否支持“DOM3 级事件”定义的事件,可以使用如下
    代码:
    var isSupported = document.implementation.hasFeature("UIEvent", "3.0");
  • 为了方便,我下列使用的代码都是基于支持DOM2级事件的浏览器,除非特别说明,否则IE8-(IE9+两种方式都可以)等浏览器则请用其特有的添加事件的方式。
load事件
  • 这个事件非常常见。当页面完全加载后(包括所有图形、JavaScript文件、CSS文件等外部资源),就会触发window上面的load事件。所以我们可以用下面的代码为window添加load事件处理程序:
    function loaded(event) {
        alert("Loaded!");
        alert(event.target === document);//true
        alert(event.target === document.body);//false
        alert(event.target === window);//false
    }
    window.addEventListener("load", loaded, false);//方式1
    window.onload = loaded;//方式2
  • 这里要注意的是,虽然是为window绑定事件,但event对象中的target会被设置为document(IE9+中target不会改变)。而在IE浏览器中,当使用attachEvent()方法设置load事件event.srcElement不会被设置(包括IE9+)。
    function loaded(event) {
        alert("Loaded!");
        alert(event.srcElement);//null
    }
    window.attachEvent("onload", loaded);//run in IE
  • 至于为什么会被设置为document是有原因的:根据DOM 2级事件规范,应该在document上而非window上触发load事件,但是所有浏览器都在window上面实现了该事件,以确保向后兼容。所以该事件从原理上应该是document上触发的,于是target就被设置为了document
  • 第二种设置load事件的方式就是为<body>元素添加onload特性。这个和设置onclick是一样的,不过需要记住是设置在body上而不是html上。
<!DOCTYPE html>
<html>
<head>
    <title>Load Event Example</title>
</head>
<body onload="alert('Loaded!');alert(event.target);">
    <p>Load event example.</p>
</body>
</html>
----------------------
即使是在onload特性中指定事件处理程序,target依旧指向document。而在IE中依旧是null。
  • 因为在HTML中无法访问window元素,所以在window上面发生的任何事件都可以在<body>元素中通过相应的特性来指定,这不过是一种保证向后兼容的权宜之计,但所有浏览器都很好地支持这个方式。建议还是使用JS指定事件处理程序的方式。
  • img元素同样也有load事件
    <!--img元素也可以指定load事件,当图像成功下载执行load事件-->
    <!--如果图像路径有误,或者其他原因导致没有下载完成,则不会执行load事件-->
    <img src="smile.gif" onload="alert('Image loaded.')" />
  • 当然也可以用JS的方式去指定img元素的事件处理程序,这里就不多说了。
  • 有个有意思的地方需要注意,当我们要在文档中新建一个img节点,并且设置load事件时,我们需要先设置它的onload特性(或者用addEventListener()),再去设置src特性的值。这是因为img元素在设置src特性后会立即开始下载。所以为了避免下载完毕后还未执行到设置load事件的代码,我们一般先设置load事件
    //之所以放在window的load事件内部,是因为文档未加载完毕时,
    // 调用document.body.appendChild(image);会报错,因为此时body还未加载完毕
    window.addEventListener("load", function(){
        var image = document.createElement("img");
        image.src = "smile.gif";
        //setTimeout(function () {
            image.addEventListener("load", function(event){
                alert(event.target.src);
            });
            document.body.appendChild(image);
        //} ,1000);//运行后,页面一秒后出现图片,但是并没有弹窗
    });
  • 还有一些元素也以非标准的方式支持load事件。在IE9+、Firefox、Safari3+、Opera、Chrome中,<script>也会触发load事件,以便开发人员确定动态加载的JavaScript文件是否加载完毕。与图像不同,只有在设置了src属性并将元素添加到文档后才会开始下载JavaScript文件。某些浏览器<link>也会触发load事件,同样也是在元素加入文档后才会开始下载CSS文件。
    <script type="text/javascript" src="EventUtil.js" onload="alert('Loaded');"></script>
    <link href="example.css" type="text/css" rel="stylesheet" onload="alert('css loaded')">
unload事件
  • 顾名思义,这个事件的除非条件与load刚好相反。如果是针对当前页面卸载的事件,可以直接为window指定onunload事件处理程序,也可以为body添加onunload特性(与load事件设置方式相同)。可以用刷新页面的方式去触发这个事件。
    var addHandler = function(element, type, handler){
        if (element.addEventListener){
            element.addEventListener(type, handler, false);
        } else if (element.attachEvent){
            element.attachEvent("on" + type, handler);
        } else {
            element["on" + type] = handler;
        }
    }
    addHandler(window, "unload", function(event){
        alert("Unloaded");//IE刷新会执行,Chrome不会
        //console.log("unload");//IE8-刷新会执行,IE9不会执行,Chrome会
    });
  • 上面的代码在Chrome中是没有办法弹窗的。我找了一下原因,貌似是Chrome浏览器的设定问题,一种说法是弹窗被认为是广告就被屏蔽了,还有一种说法是在页面关闭期间,弹窗这种行为的方法已经被销毁了不能再调用。而IE9中刷新页面居然不调用console.log()(也许我说的不对,但是辣鸡IE浏览器我实在不会调试,也懒得去学,打印连对象结构都看不到,真是辣鸡)。
  • 要注意的是unload事件是一切被卸载之后才触发,也就是说此时document已经被销毁了,如果你的事件处理程序还要去访问document,那必然是会出错的。无独有偶,DOM2级事件规定应该在body元素而非window上触发unload事件。不过所有浏览器为了确保向后兼容,都在window上触发unload事件(我做了个测试:在Chrome上target指向document,在IE9+上srcElement指向window,IE8-上没有变化)。
  • unload事件类似的还有一个beforeunload事件。这个事件触发后会弹出一个询问框(问你是否要离开这个页面)。这个事件在我看来和unload事件的区别应该是这个事件是在一切被卸载之前触发。所以该事件的事件处理程序应该可以访问document
    <p id="xx">onbeforeunload</p>
    window.onbeforeunload = function(){
        console.log(document.getElementById("xx").innerHTML);
        return "真的离开?";//该方法必须返回一个值作为提示信息,在IE中会显示在提问框中。Chrome中则不知道去哪了。
        //不返回值,或返回空类型的值(undefined null)在Chrome中不会触发弹窗提示。(null 在IE中会显示)
        //注意在做这个实验时,IE必须调出调试界面,不然可能不会弹窗。
    }
resize事件
  • 当浏览器窗口被调整到一个新的高度或宽度,就会触发resize事件。与load和unload类似。该事件在window上触发,可以通过JS或者body元素中的onresize特性来指定事件处理程序。传入的event对象的target为document。IE8-则未提供任何属性。不同浏览器有不一样的触发机制。书上说主流浏览器中除了Firefox(用户停止调整窗口时才触发)以外,其他浏览器都是在改变了1像素的情况下就会触发。所以千万不要在这个事件处理程序中加入计算量大的代码。因为我这里没有Firefox,我只对IE和Chrome做了简单测试:
    window.onresize = function(){
        console.log(window.innerWidth + " " + window.innerHeight);
    }
    //document.documentElement.clientWidth; IE8-
    //document.documentElement.clientHeight IE8-
scroll事件
  • 与resize事件类似,scroll事件也会在文档被滚动期间重复触发。直接上代码:
<!DOCTYPE html>
<html>
<head>
    <title>Scroll Event Example</title>
</head>
<body>
    <p style="height: 10000px">Scroll event example - scroll the browser window.</p>
    <script type="text/javascript">
        var addHandler = function(element, type, handler){
            if (element.addEventListener){
                element.addEventListener(type, handler, false);
            } else if (element.attachEvent){
                element.attachEvent("on" + type, handler);
            } else {
                element["on" + type] = handler;
            }
        }
        addHandler(window, "scroll", function(event){
            if (document.compatMode == "CSS1Compat"){
                //标准模式 在Chrome下都是0,Chrome也得通过body.scrollTop
                console.log("1:" + document.documentElement.scrollTop);
                //console.log("1:" + ((document.documentElement.scrollTop || 0) + (document.body.scrollTop || 0)));
            } else {
                //混杂模式
                console.log("2:" + document.body.scrollTop)
            }
        });
    </script>
</body>
</html>

焦点事件

  • 焦点事件会在页面获得或失去焦点时触发。利用这些事件和document.hasFocus()方法及document.activeElement属性配合,可以知晓用户在页面上的行踪。以下是6个焦点事件:
    1. blur:在元素失去焦点时触发。这个事件不会冒泡;所有浏览器都支持它。
    2. DOMFocusIn:在元素获得焦点时触发。这个事件与HTML事件focus等价,但它冒泡。只有Opera支持这个事件(但如今Chrome也支持了,其他浏览器可能也支持)。DOM3级事件废弃了这个事件,选择了focusin
    3. DOMFocusOut:在元素失去焦点是触发。这个事件是HTML事件blur的通用版本。只有Opera支持这个事件(但如今Chrome也支持了,其他浏览器可能也支持)。DOM3级事件废弃了这个事件,选择了focusout
    4. focus:在元素获得焦点时触发。这个事件不会冒泡,所有浏览器都支持它。
    5. focusin:在元素获得焦点时触发。这个事件与focus等价,但它冒泡(从名字也可以看出来,多了一个in,也就是说内部的元素获得节点,父元素同样也会执行事件处理程序)。支持该事件的浏览器有:IE5.5+、Safari5.1+、Opera11.5+和Chrome。
    6. focusout:在元素失去焦点时触发。这个事件是HTML事件blur的通用版本。支持该事件的浏览器有:IE5.5+、Safari5.1+、Opera11.5+和Chrome。(那么问题来了,它冒泡吗?
  • 要确定浏览器是否支持这些事件(除了DOMFocusIn和DOMFocusOut),可以使用下面的代码:
    var isSupported = document.implementation.hasFeature("FocusEvent", "3.0");
  • 这一类事件中最主要的两个是focus和blur。它们都是JavaScript早期就得到所有浏览器支持的事件。但这两个事件不冒泡,因此IE和Opera就分别为他们创建了focusin、focusoutDOMFocusIn、DOMFocusOutIE的方式最后被DOM3级事件采纳为标准方式。
  • 由于这本书编写已经过去好几年了,所有书上说的未必对(因为后期浏览器可以修正)。
  • 书上说当焦点从页面的一个元素移动到另外一个元素,会依次触发以下事件:
    1. focusout 在失去焦点的元素上触发;
    2. focusin 在获得焦点的元素上触发;
    3. blur 在失去焦点的元素上触发;
    4. DOMFocusOut 在失去焦点的元素上触发;
    5. focus 在获得焦点的元素上触发;
    6. DOMFocusIn 在获得焦点的元素上触发。
  • 以下是我的测试结果(Chrome):
<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
</head>
<body>
    <input type="text" id="input1">
    <input type="text" id="input2">
    <script type="text/javascript">
        function test(e) {
            console.log(e.target.id +":"+ e.type);
        }
        var input1 = document.getElementById("input1");
        input1.addEventListener("blur", test);
        input1.addEventListener("focus", test);
        input1.addEventListener("focusin", test);
        input1.addEventListener("focusout", test);
        input1.addEventListener("DOMFocusOut", test);
        input1.addEventListener("DOMFocusIn", test);
        var input2 = document.getElementById("input2");
        input2.addEventListener("blur", test);
        input2.addEventListener("focus", test);
        input2.addEventListener("focusin", test);
        input2.addEventListener("focusout", test);
        input2.addEventListener("DOMFocusOut", test);
        input2.addEventListener("DOMFocusIn", test);
    </script>
</body>
</html>
---------------------------
从input1框中去点击input2,打印结果如下:
input1:blur
input1:focusout
input1:DOMFocusOut
input2:focus
input2:focusin
input2:DOMFocusIn
所有没事别混起来用。基本上blur和focus就够用了。
  • 下面是我做的一个关于验证blur和focus不会冒泡,focusin和focusout回冒泡的一个实验。
<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
</head>
<body>
    <div id="father">
        <input type="text" id="input1">
        <input type="text" id="input2">
    </div>
    <script type="text/javascript">
        function test(e) {
            console.log(e.currentTarget.id +":"+ e.type);
        }
        var input1 = document.getElementById("input1");
        var input2 = document.getElementById("input2");
        var father = document.getElementById("father");
        input1.addEventListener("blur", test);
        input1.addEventListener("focus", test);
        input2.addEventListener("focusin", test);
        input2.addEventListener("focusout", test);

        father.addEventListener("blur", test);
        father.addEventListener("focus", test);
        father.addEventListener("focusin", test);
        father.addEventListener("focusout", test);
    </script>
</body>
</html>
------------------------------------
1.当我点击input1时,会出现
input1:focus
father:focusin
可见focusin真的会冒泡,focus不会冒泡
2.当我继续1的步骤再点击input2时,会出现
input1:blur
father:focusout
input2:focusin
father:focusin
可见blur也不会冒泡,focusout会冒泡
  • 注意,虽然blur和focus没有冒泡阶段,但是依旧可以在捕获阶段捕获。所以我们可以让父元素在子元素获得或失去焦点时做一些事情。但是这样有点麻烦,需要指定事件处理程序在捕获阶段执行。而支持这样写法的浏览器基本都支持focusin和focusout。所以总的来说还是不要画蛇添足了。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值