【javascript】事件-内存性能&模拟事件

内存和性能

  • 在JavaScript 中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能。
  • 导致这一问题的原因是多方面的。
    • 每个函数都是对象,都会占用内存;内存中的对象越多,性能就越差。
    • 必须事先指定所有事件处理程序而导致的DOM访问次数,会延迟整个页面的交互就绪时间。
//事件处理程序,方便后面使用
var EventUtil = {
    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;
        }
    },
    getEvent: function(event){
        return event ? event : window.event;
    },
    getTarget: function(event){
        return event.target || event.srcElement;
    },
    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;
        }
    }
};

1、事件委托

  • 对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
<ul id="myLinks">
    <li id="goSomewhere">Go somewhere</li>
    <li id="doSomething">Do something</li>
    <li id="sayHi">Say hi</li>
</ul>
//传统的做法
var item1 = document.getElementById("goSomewhere");
var item2 = document.getElementById("doSomething");
var item3 = document.getElementById("sayHi");
EventUtil.addHandler(item1, "click", function(event){
    location.href = "http://www.wrox.com";
});
EventUtil.addHandler(item2, "click", function(event){
    document.title = "I changed the document's title";
});
EventUtil.addHandler(item3, "click", function(event){
    alert("hi");
});
//使用事件委托
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":
            location.href = "http://www.wrox.com";
            break;
        case "sayHi":
            alert("hi");
            break;
    }
});
  • 这样使用事件委托与采取传统的做法相比具有如下优点。
    • document 对象很快就可以访问,而且可以在页面生命周期的任何时点上为它添加事件处理程序(无需等待DOMContentLoaded或load事件)。换句话说,只要可单击的元素呈现在页面上,就可以立即具备适当的功能。
    • 在页面中设置事件处理程序所需的时间更少。只添加一个事件处理程序所需的DOM 引用更少,所花的时间也更少。
    • 整个页面占用的内存空间更少,能够提升整体性能。
  • 最适合采用事件委托技术的事件包括click、mousedown、mouseup、keydown、keyup 和keypress。

2、移除事件处理程序

  • 每当将事件处理程序指定给元素时,运行中的浏览器代码与支持页面交互的JavaScript 代码之间就会建立一个连接。这种连接越多,页面执行起来就越慢。
  • 在不需要的时候移除事件处理程序,也是解决这个问题的一种方案。
  • 在两种情况下,可能会造成导致“空事件处理程序”。
    • 从文档中移除带有事件处理程序的元素时。更多地是发生在使用innerHTML替换页面中某一部分的时候。如果带有事件处理程序的元素被innerHTML删除了,那么原来添加到元素中的事件处理程序极有可能无法被当作垃圾回收。
<div id="myDiv">
    <input type="button" value="Click Me" id="myBtn">
</div>
<script type="text/javascript">
    var btn = document.getElementById("myBtn");
    btn.onclick = function(){
    //先执行某些操作
    document.getElementById("myDiv").innerHTML = "Processing..."; //麻烦了!
};
</script>
<!--- 如果你知道某个元素即将被移除,那么最好手工移除事件处理程序。-->
<div id="myDiv">
    <input type="button" value="Click Me" id="myBtn">
</div>
<script type="text/javascript">
    var btn = document.getElementById("myBtn");
    btn.onclick = function(){
        //先执行某些操作
        btn.onclick = null; //移除事件处理程序
        document.getElementById("myDiv").innerHTML = "Processing...";
    };
</script>
  • 另一种情况,就是卸载页面的时候。如果在页面被卸载之前没有清理干净事件处理程序,那它们就会滞留在内存中。可以在页面卸载之前,先通过onunload事件处理程序移除所有事件处理程序,
  • 注意,使用onunload 事件处理程序意味着页面不会被缓存在bfcache 中。

模拟事件

  • 可以使用JavaScript 在任意时刻来触发特定的事件,而此时的事件就如同浏览器创
    建的事件一样。

1、DOM中的事件模拟

  • 可以在document 对象上使用createEvent()方法创建event对象。这个方法接收一个参数,即表示要创建的事件类型的字符串。
事件名称 解释
UIEvents 一般化的UI 事件。鼠标事件和键盘事件都继承自UI 事件
MouseEvents 一般化的鼠标事件
MutationEvents 一般化的DOM 变动事件
HTMLEvents 一般化的HTML 事件
  • 在创建了event 对象之后,还需要使用与事件有关的信息对其进行初始化。
  • 每种类型的event 对象都有一个特殊的方法,为它传入适当的数据就可以初始化该event 对象。不同类型的这个方法的名字也不相同,具体要取决于createEvent()中使用的参数。
  • 模拟事件的最后一步就是触发事件,使用dispatchEvent()方法,需要传入一个参数,即表示要触发事件的event 对象。

(1)模拟鼠标事件

  • 创建新的鼠标事件对象并为其指定必要的信息,就可以模拟鼠标事件。
  • 创建鼠标事件对象的方法是为createEvent()传入字符串"MouseEvents"。返回的对象有一个名为initMouseEvent()方法,用于指定与该鼠标事件有关的信息。
  • 这个方法接收15 个参数,分别与鼠标事件中每个典型的属性一一对应。
参数 含义
type(字符串) 表示要触发的事件类型,例如"click"。
bubbles(布尔值) 表示事件是否应该冒泡。为精确地模拟鼠标事件,应该把这个参数设置为true。
cancelable(布尔值) 表示事件是否可以取消。为精确地模拟鼠标事件,应该把这个参数设置为true。
view(AbstractView) 与事件关联的视图。这个参数几乎总是要设置为document.defaultView。
detail(整数) 与事件有关的详细信息。这个值一般只有事件处理程序使用,但通常都设置为0。
screenX(整数) 事件相对于屏幕的X 坐标。
screenY(整数) 事件相对于屏幕的Y 坐标。
clientX(整数) 事件相对于视口的X 坐标。
clientY(整数) 事件想对于视口的Y 坐标。
ctrlKey(布尔值) 表示是否按下了Ctrl 键。默认值为false。
altKey(布尔值) 表示是否按下了Alt 键。默认值为false。
shiftKey(布尔值) 表示是否按下了Shift 键。默认值为false。
metaKey(布尔值) 表示是否按下了Meta 键。默认值为false。
button(整数) 表示按下了哪一个鼠标键。默认值为0。
relatedTarget(对象) 表示与事件相关的对象。这个参数只在模拟mouseover或mouseout时使用。
var btn = document.getElementById("myBtn");
//创建事件对象
var event = document.createEvent("MouseEvents");
//初始化事件对象
event.initMouseEvent("click", true, true, document.defaultView, 0, 0, 0, 0, 0,
false, false, false, false, 0, null);
//触发事件
btn.dispatchEvent(event);

(2)模拟键盘事件

  • 调用createEvent()并传入"KeyboardEvent"就可以创建一个键盘事件。返回的事件对象会包含一个initKeyEvent()方法,这个方法接收下列参数。
参数 含义
type(字符串) 表示要触发的事件类型,如"keydown"。
bubbles(布尔值) 表示事件是否应该冒泡。为精确模拟鼠标事件,应该设置为true。
cancelable(布尔值) 表示事件是否可以取消。为精确模拟鼠标事件,应该设置为true。
view 与事件关联的视图。这个参数几乎总是要设置为document.defaultView。
key(布尔值) 表示按下的键的键码。
location(整数) 表示按下了哪里的键。0 表示默认的主键盘,1 表示左,2 表示右,3 表示数字键盘,4 表示移动设备(即虚拟键盘),5 表示手柄。
modifiers(字符串) 空格分隔的修改键列表,如"Shift"。
repeat(整数) 在一行中按了这个键多少次。
/**模拟按住Shift 的同时又按下A 键。**/
var textbox = document.getElementById("myTextbox"),
event;
//以DOM3 级方式创建事件对象
if (document.implementation.hasFeature("KeyboardEvents", "3.0")){
    event = document.createEvent("KeyboardEvent");
    //初始化事件对象
    event.initKeyboardEvent("keydown", true, true, 
    document.defaultView, "a",0, "Shift", 0);
}
//触发事件
textbox.dispatchEvent(event);

(3)模拟其它事件

  • 要模拟变动事件, 可以使用createEvent("MutationEvents")创建一个包含initMutationEvent() 方法的变动事件对象。
  • 这个方法接受的参数包括:type、bubbles、cancelable、relatedNode、preValue、newValue、attrName 和attrChange。
var event = document.createEvent("MutationEvents");
event.initMutationEvent("DOMNodeInserted", true, false, someNode, "","","",0);
targ et.dispatchEvent(event);
  • 模拟HTML 事件,同样需要先创建一个event对象——通过createEvent("HTMLEvents"),然后再使用这个对象的initEvent()方法来初始化它。
var event = document.createEvent("HTMLEvents");
event.initEvent("focus", true, false);
targ et.dispatchEvent(event);

(4)自定义DOM事件

  • 创建新的自定义事件,可以调用createEvent("CustomEvent")。返回的对象有
    一个名为initCustomEvent()的方法,接收如下4 个参数。
参数 含义
type(字符串) 触发的事件类型,例如"keydown"。
bubbles(布尔值) 表示事件是否应该冒泡。
cancelable(布尔值) 表示事件是否可以取消。
detail(对象) 任意值,保存在event 对象的detail 属性中。
var div = document.getElementById("myDiv"),
event;
EventUtil.addHandler(div, "myevent", function(event){
    alert("DIV: " + event.detail);
});
EventUtil.addHandler(document, "myevent", function(event){
    alert("DOCUMENT: " + event.detail);
});
if (document.implementation.hasFeature("CustomEvents", "3.0")){
    event = document.createEvent("CustomEvent");
    event.initCustomEvent("myevent", true, false, "Hello world!");
    div.dispatchEvent(event);
}
  • 支持自定义DOM事件的浏览器有IE9+和Firefox 6+。

2、IE中的事件模拟

  • 调用document.createEventObject()方法可以在IE中创建event对象,这个方法不接受参数,结果会返回一个通用的event 对象。
  • 然后,你必须手工为这个对象添加所有必要的信息。
  • 最后一步就是在目标上调用fireEvent()方法,这个方法接受两个参数:事件处理程序的名称和event 对象。
var btn = document.getElementById("myBtn");

//创建事件对象
var event = document.createEventObject();

//初始化事件对象
event.screenX = 100;
event.screenY = 0;
event.clientX = 0;
event.clientY = 0;
event.ctrlKey = false;
event.altKey = false;
event.shiftKey = false;
event.button = 0;

//触发事件
btn.fireEvent("onclick", event);

/**采用相同的模式也可以模拟触发keypress 事件**/
var textbox = document.getElementById("myTextbox");

//创建事件对象
var event = document.createEventObject();

//初始化事件对象
event.altKey = false;
event.ctrlKey = false;
event.shiftKey = false;
event.keyCode = 65;

//触发事件
textbox.fireEvent("onkeypress", event);
好好学习
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258

推荐阅读更多精彩内容

  • JavaScript 程序采用了异步事件驱动编程模型。在这种程序设计风格下,当文档、浏览器、元素或与之相关的对象发...
    劼哥stone阅读 1,230评论 3 11
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,083评论 18 139
  • JavaScript 与 HTML 间通过事件实现交互。事件——文档或浏览器窗口中发生的一些特定的交互瞬间,即用户...
    sylvia_yue阅读 422评论 0 0
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,462评论 6 13
  • 喧嚣已经远去,黑夜中我把世界丢在了身后。 星辰不与我做伴,只有月光追逐我的背影。 而前方不是我的方向,沉睡的是我的...
    少侠易风阅读 198评论 0 0