事件

下述内容主要讲述了《JavaScript高级程序设计(第3版)》第13章关于“事件”。

JavaScript与HTML之间的交互式通过事件实现的。 事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用侦听器(或处理程序)来预订事件,以便事件发生时执行相应的代码。

一、 事件流

事件流,描述的是从页面中接收事件的顺序。

1. 事件冒泡

事件冒泡(event bubbling),即事件开始时有最具体的元素(文档中嵌套层次最深的那个节点)接受,然后逐级向上传播到较为不具体的节点(文档)。直到传播到document对象。

2. 事件捕获

事件捕获(event capturing),不太具体的节点早接收到事件,而最具体的节点最后接收事件。事件捕获的用意在于在事件到达预定目标之前捕获它。尽管“DOM2级事件”规范要求事件应该从document对象开始传播,但是大部分浏览器都是从window对象开始捕获事件的。 建议:由于老版本浏览器不能很好的支持事件捕获,所以我们可以放心使用事件冒泡,特殊情况下再使用事件捕获。

3. DOM事件流

“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段处于目标阶段事件冒泡阶段

<div id="div1">
    <button id="btn1">点击我</button>
</div>

<script type="text/javascript">
    // 输出顺序:在btn1的捕获阶段 --> 我才是目标阶段 --> 在btn1的冒泡阶段
    var divDom = document.getElementById("div1"),
        btnDom = document.getElementById("btn1");
    document.addEventListener("click", function() {
        console.log("在btn1的冒泡阶段");
    }, false);
    btnDom.addEventListener("click", function() {
        console.log("我才是目标阶段");
    }, false);
    divDom.addEventListener("click", function() {
        console.log("在btn1的捕获阶段");
    }, true);
</script>

“DOM2级事件”明确要求捕获阶段不会涉及事件目标,但IE9、Safari、Chrome、Firefox和Opera及更高版本浏览器都会在捕获阶段触发事件对象上的事件。这就意味着有两次机会在目标对象上面操作事件。

二、事件处理程序

事件就是用户或浏览器自身执行的某种动作。click、load、mouseover,都是事件的名字。而响应某个事件的函数就叫事件处理程序。事件处理程序的名字以“on”开头,onclick、onload。为事件指定处理程序的方式有好几种。

1. HTML事件处理程序

<input type="button" value="Click Me" οnclick="alert(&quot;click&quot;)" />

由于值是JavaScript,因此不能在其中使用未经转义的HTML语法字符(当然,上述可以使用单引号)。

<input type="button" value="Click Me" onclick="showMessage()" />
<script>
    function showMessage(){
        // 执行过程中,有权访问全局中任何代码
        alert("click");
    }
</script>

包含事件变量event,及函数内部this指向事件目标元素

<!-- 输出“Echo value” -->
<input type="button" value="Echo value" onclick="alert(this.value)" />
<!-- 等价于 -->
<input type="button" value="Echo value" onclick="alert(value)" />

在事件处理函数内部,可以像访问局部变量一样访问document及元素本身的成员。

<!-- 表单元素 -->
<form>
    <input type="text" name="username" value="" />
    <input type="button" value="Echo username" onclick="alert(username.value)" />
</form>

缺点: (1)时差问题。如果上述showMessage方法在页面最底部定义,而用户在页面解析前点击了按钮,会引发错误。 (2)其作用域链在不同浏览器中会导致不同结果。 (3)HTML和JavaScript代码耦合度高。

2. DMO0级事件处理程序

将一个函数赋值给一个事件处理程序属性。每个事件只支持一个事件处理程序。

<input type="button" id="myBtn" value="Click Me"/>
<script>
    var btn = document.getElementById("myBtn");
    btn.onclick = function(){
        btn.onclick = null;     // 删除事件处理程序,这里只处理一次!!
        alert(this.id);
    }
</script>

这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。 注意:在这些代码运行以前不会指定事件处理程序,因此如果这些代码在页面中位于按钮后面,就有可能在一段时间内怎么点击都没有反应。

3. DMO2级事件处理程序

指定和删除事件处理程序的操作:addEventListener和removeEventListener addEventListener("事件", "处理程序", boolean),可以添加多个事件处理程序,绑定多个处理程序会按照其添加顺序触发! boolean=true,捕获阶段调用事件处理程序;boolean=false,冒泡阶段调用事件处理程序。 removeEventListener("事件", "处理程序"),参数应与添加处理程序时使用的参数相同,意味着通过addEventListener添加的匿名函数将无法移除!!

var divDom = document.getElementById("myBtn");
divDom.addEventListener("click", function() {
    console.log("冒泡");
}, false);

使用DOM2级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。多个事件处理程序会按照添加他们的顺序触发。

大多数情况下,都将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。最好只在需要在事件到达目标之前捕获它的时候将事件处理程序添加到捕获阶段。如果不是特别需要,不建议在事件捕获阶段注册事件处理程序。

4. IE事件处理程序

attachEvent()和detachEvent(),会在冒泡阶段添加事件处理程序。其通过attachEvent添加的匿名函数也将无法移除!! 可以添加多个事件处理程序,绑定多个处理程序会按照其相反的添加顺序触发!

var divDom = document.getElementById("myBtn");
divDom.attachEvent("onclick", function() {  // 注意,是onclick
    console.log("冒泡");
}, false);

其与DOM0级方法主要区别在于:DOM0级事件处理程序会在其所属元素的作用域内运行;使用attachEvent事件处理程序会在全局作用域中运行,因此this指向window。

5. 跨浏览器事件处理程序

var EventUtil = {
    addHandler: function(element, type, handler) {
        if(element.addEventListener) {  // DOM2 冒泡
            element.addEventListener(type, handler, false);
        } else if(element.attachEvent) {    // IE
            element.attachEvent("on" + type, handler);
        } else {    // DOM0
            element["on" + type] = handler;
        }
    },
    removeHandler: function(element, type, handler) {
        if(element.removeEventListener) {
            element.removeEventListener(type, handler, false);
        } else if(element.detachEvent) {
            element.detachEvent("on" + type, handler);
        } else {
            element["on" + type] = null;
        }
    }
};

三、事件对象

在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件相关的信息。 触发的事件类型不一样,可用的属性和方法也不一样。

属性/方法

类型

说明

bubbles

Boolean

表明事件是否冒泡

cancelable

Boolean

表明是否可以取消事件的默认行为

currentTarget

Element

其事件处理程序当前正在处理事件的那个元素

detail

Integer

与事件相关的细节信息

eventPhase

Integer

调用事件处理程序的阶段:1.捕获 2.处于目标 3.冒泡

preventDefault()

Function

取消事件默认行为

stopPropagation()

Function

取消事件的进一步捕获或冒泡

target

Element

事件的目标

type

String

被触发的事件类型

view

AbstractView

与事件关联的抽象视图

在事件处理程序内部,对象this始终等于currentTarget的值,而target则只包含事件的实际目标。 示例一:如果直接将事件处理程序指定给目标元素,则this、currentTarget和target包含相同的值。

<button id="myBtn">点我</button>
<script>
    var btn = document.getElementById("myBtn");
    btn.onclick = function(event) {
        console.log(event.currentTarget === this);  // true
        console.log(event.target === this);         // true
        console.log(btn === this);                  // true
    }
</script>

示例二:如果事件处理程序存在于按钮的父节点中,那么这些值会不同。

<button id="myBtn">点我</button>
<script>
    document.body.onclick = function(event) {
        console.log(event.currentTarget);   // 元素body
        console.log(event.target);          // 元素button
        console.log(this);                  // 元素body
    };
</script>

1. 通过type属性,用一个函数处理多个事件

var handler = function(event) {
    switch(event.type){
        case 'click':
            console.log("click");
            break;
        case 'mouseover':
            console.log("mouseover");
            break;
        case 'mouseout':
            console.log("mouseout");
            break;
    }
};
btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;

2. eventPhase属性,确定事件当前处于事件流的那个阶段

var myBtn = document.getElementById("myBtn2");
myBtn.onclick = function(event){
    alert(event.eventPhase);    // 2,目标阶段
};
document.body.addEventListener("click", function(event){
    alert(event.eventPhase);    // 1,捕获阶段
}, true);
document.body.onclick = function(event){
    alert(event.eventPhase);    // 3,冒泡阶段
};
// 弹窗输出顺序:1 -> 2 -> 3

3. stopPropagation()用于立即停止事件在DOM层中的传播。即取消进一步的事件捕获或冒泡

var myBtn = document.getElementById("myBtn2");
myBtn.onclick = function(event){
    alert(event.eventPhase);    // 2,目标阶段
    event.stopPropagation();
};
document.body.addEventListener("click", function(event){
    alert(event.eventPhase);    // 1,捕获阶段
}, true);
document.body.onclick = function(event){
    alert(event.eventPhase);    // 3,冒泡阶段
};
// 弹窗输出顺序:1 -> 2

注意的是,在执行到目标阶段,捕获阶段已被执行。 可以在body的捕获阶段执行event.stopPropagation();来阻止后续的所有事件处理!

4. IE中的事件对象

在使用DOM0级方法添加事件处理程序时,event对象作为window对象的一个属性存在。 在IE中,使用attachEvent()方法添加事件,我们可以传入event参数,也可以使用全局的event对象。 注意的几点:

  • (1)srcElement等价于target,来获取事件目标
  • (2)returnValue=false等价于preventDefault(),阻止默认行为
  • (3)cancelBubble=true等价于stopPropagation(),阻止冒泡(注意,IE不支持事件捕获!!!

5. 跨浏览器的事件对象

var EventUtil = {
    // 获取event对象
    getEvent: function(event){
        return event ? event : window.event;
    },
    // 获取目标元素
    getTarget: function(event){
        return event.target || event.srcElement;
    },
    // 阻止默认行为
    preventDefault: function(event){
        if (event.preventDefault){
            event.preventDefault();
        } else {
            event.returnValue = false;
        }
    },
    // 阻止冒泡
    stopPropagation: function(event){
        if (event.stopPropagation){
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    }
};

由于IE不支持事件捕获,因此这个方法在跨浏览器的情况下,也只能用来阻止事件冒泡。

四、事件类型

1. UI事件

(1)load事件 当页面完全加载完后(包括所有图像、JavaScript文件、CSS文件等外部资源),就会触发window上面的load事件。 有两种定义onload事件处理程序的方式 方式一:JavaScript代码

// 这里使用上述的兼容方式
EventUtil.addHandler(window, "load", function(event){
    alert("loaded");
});

方式二:在<body onload="alert('loaded')"> 说明:一般在window上发生的任何事件都可以在元素中通过相应的特性来指定,因为在HTML中无法访问window元素。实际上,这只是为了保证向后兼容的一种权宜之计。

图片load事件:

EventUtil.addHandler(window, "load", function(event){
    var image = document.createElement("img");
    EventUtil.addHandler(image, "load", function(e){
        e = EventUtil.getEvent(e);
        alert(EventUtil.getTarget(e).src);
    });
    document.body.appendChild(image);   // 先添加DOM
    image.src = "test.jpg";             // 后指定src
});

注意

  • A. 想向DOM中添加一个新元素,所以必须确定页面已经加载完毕。
  • B. 新图片元素设置了src属性就会开始下载。所以必须在指定src元素之前先指定事件!

了解了上述特性,我们可以在客户端预先加载图片

EventUtil.addHandler(window, "load", function(event){
    var image = new Image();
    EventUtil.addHandler(image, "load", function(e){
        alert("加载完成");
    });
    image.src = "test.jpg"; // 指定src,图片立即下载
});

<script>元素、<link>元素,不同于图片,只有在设置了src|href属性并将元素添加到文档后,才开始下载资源。 所以,其监听事件和指定src的顺序也就不重要了!

EventUtil.addHandler(window, "load", function(){
    var script = document.createElement("script");
    EventUtil.addHandler(script, "load", function(){
        alert("script加载完成");
    });
    script.src = "EventUtil.js";
    document.body.appendChild(script);  // 无所谓顺序
});

(2)unload事件 同load事件一样,也有两种方式,这里不再赘述!

EventUtil.addHandler(window, "unload", function() {
    alert("unload");
});

需要注意的是,unload事件是在一切都被卸载之后才触发。此时再操作DOM节点或者元素的样式就会报错。 其常用于清除引用,以避免内存泄漏!

(3)resize事件

EventUtil.addHandler(window, "resize", function() {
    alert("resize");
});

窗口变化1像素就会触发resize事件,然后随着变化不断的重复触发。所以我们需要特别注意,最好做“节流处理”。 请参考JavaScript高级技巧-节流处理 (4)scorll事件 scroll事件也会在文档被滚动期间重复触发,所以也很有必要做节流控制。

EventUtil.addHandler(window, "scroll", function(event){
    if (document.compatMode == "CSS1Compat"){
        alert(document.documentElement.scrollTop);
    } else {
        alert(document.body.scrollTop); // 兼容Safari
    }
});

需要注意的是,Safari之外的所有浏览器都会通过<html>元素来反映这一变化,Safari是基于<body>跟踪滚动位置。

2. 焦点事件

焦点事件会在页面元素获得或失去焦点时触发。

事件

说明

blur

在元素失去焦点时触发,不会冒泡

focus

在元素获得焦点时触发,不会冒泡

focusin

在元素获得焦点时触发,会冒泡

focusout

在元素失去焦点时触发,会冒泡

当焦点从页面中的一个元素移动到另一个元素,会依次触发下列事件: (1)focusout在失去焦点的元素上触发; (2)focusin在获得焦点的元素上触发; (3)blur在失去焦点的元素上触发; (4)DOMFocusOut在失去焦点的元素上触发; (5)focus在获得焦点的元素上触发; (6)DOMFocusIn在获得焦点的元素上触发。 其中:blur、DOMFoucsOut和focusout的事件目标对象是失去焦点的元素;而focus、DOMFocusIn和focusIn的事件目标是获得焦点的元素。 也可以通过event.relatedTarget来获取对应相关元素(哪个元素失去焦点导致目标元素获得焦点,哪个元素获得焦点导致目标元素失去焦点)。

<input type="text" id="ipt1" />
<input type="text" id="ipt2" />
<script>
// 焦点放到ipt2上,然后让ipt1获取焦点
ipt1.addEventListener("focus", function(event){
    console.log(event.target);          // ipt1
    console.log(event.relatedTarget);   // ipt2
});
// 焦点放到ipt1上,然后点击ipt2让ipt1失去焦点
ipt1.addEventListener("blur", function(event){
    console.log(event.target);          // ipt1
    console.log(event.relatedTarget);   // ipt2
});
</script>

3. 鼠标与滚轮事件

事件

说明

click

用户点击主鼠标按钮(一般是左边的按钮)或者按下回车键时触发;

dblclick

用户双击主鼠标按钮(一般是左边的按钮)时触发。这个事件并不是DOM2级事件规范中规定的,其得到广泛应用,在DOM3中将其纳入了标准;

mousedown

用户按下任意鼠标按钮时触发;

mouseenter

鼠标光标从元素外部首次移动到元素范围内时触发;这个事件不冒泡,而且在鼠标移动到后代元素上不会触发;DOM3被纳入标准;

mouseleave

在位于元素上方的鼠标光标移动到元素范围之外时触发;这个事件不冒泡,而且在鼠标移动到后代元素上不会触发;DOM3被纳入标准;

mousemove

鼠标指针在元素内部移动时重复地触发;

mouseout

鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。移入的另一个元素可能位于元素外部,也可能使其子元素。

mouseover

鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发。

mouseup

用户释放鼠标按钮时触发。

示例:区别mouseleave和mouseout

<div id="div1" style="height: 100px; width: 100px; background-color: #2aabd2">
    <div id="div2" style="height: 50px; width: 50px; background-color: #7a43b6">
    </div>
</div>
var div1Dom = document.getElementById("div1");
div1Dom.addEventListener("mouseleave", function(event){
    console.log("div1 mouseleave");
});
div1Dom.addEventListener("mouseout", function(event){
    console.log("div1 mouseout");
});
  • 当鼠标从上图“区域2”移动到“区域3”中,会同时触发“mouseleave”和“mouseout”事件;
  • 当鼠标从上图“区域2”移动到“区域1”(子元素)中,只会触发“mouseout”;
  • 需要注意的时,从“区域1”(子元素)移动到“区域2”中,也会触发“mouseout”。

示例:区别mouseenter和mouseover

div1Dom.addEventListener("mouseenter", function(event){
    console.log("div1 mouseenter");
});
div1Dom.addEventListener("mouseover", function(event){
    console.log("div1 mouseover");
});

注意:其区别类似于上述mouseoutleave和mouseout,mouseover在移入到子元素上也会触发!!!

(1)获取坐标位置

var bodyDom = document.getElementsByTagName("body")[0];
bodyDom.addEventListener("click", function(){
    console.log(event.clientX + "," + event.clientY);
    console.log(event.pageX + "," + event.pageY);
    console.log(event.screenX + "," + event.screenY);
});
  • clientX和clientY:鼠标指针在可视窗口的水平坐标和垂直坐标;不受滚动条影响。
  • pageX和pageY:鼠标指针在页面中的位置。在没有滚动条的情况下,同clientX和clientY的值。
  • screenX和screenY:鼠标指针相对于整个屏幕的坐标信息。

(2)修改键 鼠标事件主要是使用鼠标来触发的,但在按下鼠标时键盘上的某些键(Shift、Ctrl、Alt、Meta、Windows/Cmd)的状态也可以影响到所采取的操作。

var modifyKeyDom = document.getElementById("modifyKey");
modifyKeyDom.addEventListener("click", function(event){
    var ary = new Array();

    if(event.shiftKey){
        ary.push("shift");
    }
    if(event.ctrlKey){
        ary.push("ctrl");
    }
    if(event.altKey){
        ary.push("alt");
    }
    if(event.metaKey){
        ary.push("meta");
    }
    console.log(ary.join(","));
});

当用户点击鼠标并同时按下相关键,就会使相关属性值变为true。这通常应用于鼠标配合ctrl或shift进行多选操作! (3)相关元素 DOM通过event对象的relateTarget属性提供了相关元素的信息。这个属性只针对mouseover和mouseout事件才包含值;对于其他事件,这个属性的值为null。IE8之前的版本用fromElement和toElement标识相关元素。

function getRelatedTarget(event){
    if (event.relatedTarget){
        return event.relatedTarget;
    } else if (event.toElement){
        return event.toElement;
    } else if (event.fromElement){
        return event.fromElement;
    } else {
        return null;
    }
}

(4)鼠标按钮 event对象存在一个button属性,标识按下或释放的按钮。DOM的button属性有3种值:0表示主鼠标按钮1表示中间的鼠标按钮(滚轮)3表示次鼠标按钮。IE8之前的版本有很大的差异。

function getButton(event){
    if (document.implementation.hasFeature("MouseEvents", "2.0")){
        return event.button;
    } else {
        switch(event.button){
            case 0:
            case 1:
            case 3:
            case 5:
            case 7:
                return 0;
            case 2:
            case 6:
                return 2;
            case 4: return 1;
        }
    }
}

4. 键盘与文本事件

事件

说明

keydown

用户按下键盘上的任意键时触发,而且如果按住不放的话,会重触发此事件。

keypress

用户按下键盘上的字符键时触发,而且如果按住不放的话,会重触发此事件。

keyup

用户释放键盘上的键时触发。

textInput

只有可编辑区域才有该事件,用户按下能够输入实际字符的键时才会被触发。在文本插入文本框之前触发,通常用于过滤敏感词。

var iptDom = document.getElementById("ipt");
iptDom.addEventListener("keydown", function(event){
    console.log("keydown");
    // keydown和keyup特有属性,
    // keyCode属性的值与ASCII码中对应的大写字母或数值的编码相同
    // a:65 A:65
    console.log(event.keyCode);
});
iptDom.addEventListener("keypress", function(event){
    console.log("keypress");
    // keypress特有属性,
    // charCode属性的值为按下那个键的ASCII码值
    // a:97 A:65
    console.log(event.charCode);
});

说明:在chrome51下测试结果

  • 按住某一字符键不放时,“keydown”会被重复触发,而“keypress”并不会触发;
  • esc、删除键、ctrl、shift等只会触发“keydown”事件;
  • 回车会同时触发“keydown”和“keypress”事件;
  • DOM3级事件中,将keyCode和charCode取消掉了,新增key和char。

示例:过滤敏感词汇

iptDom.addEventListener("textInput", function(event){
    // event.data的值是用户输入的字符
    console.log(event.data);
    var sensitiveWordAry = ["李", "刚"];
    if(sensitiveWordAry.indexOf(event.data) >= 0){
        // 输入的字符存在于敏感数组中,则禁止输入
        event.preventDefault();
    }
});

5. 变动事件

DOM2级的变动事件能在DOM中的某一部分发生变化时给出提示。

事件

说明

DOMSubtreeModified

在DOM结构中发生任何变化时触发。

DOMNodeInserted

在一个节点作为子节点被插入到另一个节点中时触发。

DOMNodeRemoved

在节点从其父节点中移除时触发。

DOMNodeInsertIntoDocument

在一个节点被直接插入文档或通过子树间接插入文档之后触发。在DOMNodeInsert之后触发。

DOMNodeRemovedFromDocument

在一个节点被直接从文档中移除或通过子树间接从文档中移除之前触发。这个事件在DOMNodeRemove之后触发。

DOMAttriModified

在特性被修改之后触发。

DOMCharacterDataModified

在文本节点的值发生变化时触发。

检测浏览器是否支持变动事件:

var isSupport = document.implementation.hasFeature("MutationEvents","2.0");

(1)删除节点 使用removeChild()和replaceChild()从DOM中删除节点时,

  • 首先会触发DOMNodeRemoved事件。这个事件的目标(event.target)是被删除的节点;而event.relatedNode是目标节点的父节点。在这个事件触发时,节点尚未从其父节点删除。
  • 如果被删除的节点包含子节点,那么再起所有子节点以及这个被移除的节点上都会相继触发DOMNodeRemovedFromDocument事件。
  • 最后会触发DOMSubtreeModified事件,其目标是被移除节点的父节点。
EventUtil.addHandler(window, "load", function(){
    var ulList = document.getElementById("myList"),
        aDom = ulList.firstElementChild.firstElementChild;

    // 最后执行
    document.addEventListener("DOMSubtreeModified", function(event){
        console.log(event.type);    // DOMSubtreeModified
        console.log(event.target);  // <ul>...</ul>
    });

    // 最先被执行
    document.addEventListener("DOMNodeRemoved", function(event){
        console.log(event.type);    // DOMNodeRemoved
        console.log(event.target);  // <li>...</li>
        console.log(event.relatedNode);   // <ul>...</ul>
    });

    // 其次执行
    aDom.addEventListener("DOMNodeRemovedFromDocument", function(event){
        console.log(event.type);    // DOMNodeRemovedFromDocument
        console.log(event.target);  // <a>赵</a>
    });

    // 移除第一个li
    ulList.removeChild(ulList.firstElementChild);
});

(2)插入节点 在使用appendChild()和replaceChild()或insertBefore()向DOM中插入节点时,

  • 首先会触发DOMNodeInserted事件。事件目标为被插入的节点,而event.relatedNode属性的值是父节点引用。
  • 接着会在新插入的节点上触发DOMNodeInsertIntoDocument事件。
  • 最后触发DOMSubtreeModified事件,目标是新节点的父节点。

6. HTML5事件

(1)contextmenu事件:表示何时展示上下文菜单,以便开发人员取消默认的上下文菜单而提供自定义的菜单。 contextmenu事件是冒泡的,因此可以为document指定一个事件处理程序,用以处理页面中发生的所有此类事件。

<div id="myDiv" style="height:300px; width:500px; border:solid;">
    Right click or Ctrl+click me to get a custom context menu. 
    Click anywhere else to get the default context menu.
</div>
<ul id="myMenu" style="position:absolute;visibility:hidden;background-color:silver">
    <li><a href="http://www.nczonline.net">Nicholas' site</a></li>
    <li><a href="http://www.wrox.com">Wrox site</a></li>
    <li><a href="http://www.yahoo.com">Yahoo!</a></li>
</ul>
<script type="text/javascript">
    EventUtil.addHandler(window, "load", function(event){
        var div = document.getElementById("myDiv");

        EventUtil.addHandler(div, "contextmenu", function(event){
            event = EventUtil.getEvent(event);
            // 取消默认行为
            EventUtil.preventDefault(event);

            var menu = document.getElementById("myMenu");
            // 用鼠标在视口中的坐标定位自定义菜单
            menu.style.left = event.clientX + "px";
            menu.style.top = event.clientY + "px";
            menu.style.visibility = "visible";
        });

        EventUtil.addHandler(document, "click", function(event){
            document.getElementById("myMenu").style.visibility = "hidden";
        });
    });
</script>

(2)beforeunload事件 为了让开发人员有可能在页面卸载前阻止这一操作。

EventUtil.addHandler(window, "beforeunload", function(event){
    event = EventUtil.getEvent(event);
    var message = "I'm really going to miss you if you go.";
    event.returnValue = message; // 兼容处理:IE Firefox
    return message; // 兼容处理:Chrome Safari
});

在关闭标签页时,会提示!需要注意的是,在使用最新chrome51时,不能修改弹窗文字!

(3)DOMContentLoaded事件 window的load事件会在页面中的一切都加载完毕时触发,但这个过程可能会因要加载外部资源过多破费周折。 DOMContentLoaded事件在形成完整的DOM树之后就触发,不理会图像、JavaScript文件、CSS文件或其他资源是否已经下载完毕。

(4)readystatechange事件 提供与文档或元素的加载状态有关的信息 支持readystatechange事件的每个对象都有一个readyState属性

属性

说明

uninitialized(未初始化)

对象存在但尚未初始化;

loading(正在加载)

对象正在加载数据;

loaded(加载完毕)

对象加载数据完成;

interactive(交互)

可以操作对象了,但还没完全加载;

complete(完成)

对象已经加载完毕。

EventUtil.addHandler(document, "readystatechange", function(event){
    if (document.readyState == "interactive" || document.readyState == "complete"){
        EventUtil.removeHandler(document, "readystatechange", arguments.callee); 
        alert("Content loaded");                      
    }
});
EventUtil.addHandler(window, "load", function(event){
    alert("Window loaded");
});

原生JavaScript实现jQuery的ready()方法:

var callback = function(){  
  // Handler when the DOM is fully loaded  
};  
if (document.readyState === "interactive" || document.readyState === "complete"   
    || (document.readyState !== "loading" && !document.documentElement.doScroll)) {  
  callback();  
} else {  
  document.addEventListener("DOMContentLoaded", callback);  
}  

具体请参考: jquery $(document).ready()与window.onload的区别 (5)pageshow事件和pagehide事件 以在用户使用浏览器的“后退”和“前景”按钮时加快页面的转换速度。

事件

说明

pageshow事件

该事件的event对象包含一个名为persisted的布尔值属性,true说明页面保存在bfcache中。在重新加载的页面中,pageshow会在load事件触发后触发,而对于bfcache中的页面,pageshow会在页面状态完全恢复的那一刻触发

pagehide事件

事件会在浏览器卸载页面的时候触发,而且是在unload事件之前触发。

(6)hashchange事件 在URL的参数列表(及URL中“#”号后面的所有字符串)发生变化时通知开发人员。

EventUtil.addHandler(window, "hashchange", function(event){
    // oldURL:变化前的URL;newURL:变化后的URL
    alert("Old URL: " + event.oldURL + "\nNew URL: " + event.newURL);
});

五、内存和性能

1. 事件委托

解决“事件处理程序过多”问题。利用事件冒泡,只指定一个事件处理程序,来管理某一类型的所有事件。

<ul id="myLinks">
    <li id="goSomewhere">Go somewhere</li>
    <li id="doSomething">Do something</li>
    <li id="sayHi">Say hi</li>
</ul>
<script type="text/javascript">
(function(){
    var list = document.getElementById("myLinks");

    EventUtil.addHandler(list, "click", function(event){
        event = EventUtil.getEvent(event);
        var target = EventUtil.getTarget(event);

        switch(target.id){  // 属性区分,可以利用自定义属性区分
            case "doSomething":
                document.title = "I changed the document's title";
                break;

            case "goSomewhere":
                kk = "http://blog.csdn.net/ligang2585116";
                break;

            case "sayHi":
                alert("hi");
                break;
        }
    });
})();
</script>

如果可行的话,可以考虑为document对象添加一个事件处理程序,用以处理页面上发生的某种特定类型的事件。 优点:

  • (1)document对象很快就可以访问,而且可以在页面生命周期的任何时间点上为它添加事件处理程序(无需等待DOMContentLoad或Load事件)。即只要可单击的元素呈现在页面上,就可以立即具备适当的功能。
  • (2)在页面中设置事件处理程序所需的时间更少。只添加一个事件处理程序所需的DOM引用更少,所花的时间也更少。
  • (3)整个页面占用的内存空间更少,能提升整体性能。

使用范围:

  • A. 适合采用事件委托技术的事件包括click、mousedown、mouseup、keydown、keyup和keypress。
  • B. 虽然mouseover和mouseout事件也冒泡,但要适当处理他们并不容易,而且经常需要计算元素的位置(因当鼠标从一个元素移动到其子节点时或者鼠标移出该元素时,就会触发mouseout事件)

2. 移除事件处理程序

每当将事件处理程序指定给元素时,运行中的浏览器代码与支持页面交互的JavaScript代码之间就会建立一个连接。这种链接越多,页面执行起来就越慢。 造成上述问题的原因: 第一种,从文档中移除带有事件处理程序的元素(removeChild和replaceChild)时,或innerHTML替换页面中某一部分时,带有事件的元素被删除掉了,但其事件处理程序无法被当成垃圾回收。

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    //先执行某些操作
    btn.onclick = null; //移除事件处理程序
    document.getElementById("myDiv").innerHTML = "prossing";
};

第二种,卸载页面时。在卸载之前,先通过onunload事件处理程序移除所有事件处理程序。

3. 模拟事件

可以使用JavaScript在任意时刻来触发特定的事件,而此时的事件就如同浏览器创建的事件一样。即该冒泡会冒泡,且照样导致浏览器执行已经制定处理它们的事件处理程序。

var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(event){
    console.log("click");
});

// 创建事件对象
var event = document.createEvent("MouseEvents");
// 初始化事件对象
event.initMouseEvent("click", true, true, document.defaultView, 0, 100, 0, 0, 0, false,
        false, false, false, 0, null);
// 触发事件
btn.dispatchEvent(event);

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券