1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > myEvent.js javascript跨浏览器事件框架【javascript】

myEvent.js javascript跨浏览器事件框架【javascript】

时间:2021-06-22 15:00:21

相关推荐

myEvent.js javascript跨浏览器事件框架【javascript】

web前端|js教程

myEvent.js,事件框架

web前端-js教程

event究竟有多么复杂?可见前辈的6年前的努力:最佳的addEvent是怎样诞生的,后起之秀jQuery也付出了一千六百多行血汗代码(v 1.5.1)搞定了6年后出现的各种核的浏览器。

源码 微课神器,ubuntu代码编写工具,怎么样安装两个tomcat,爬虫系列视频大全,怎么选择php培训机构学习交流,谷歌seo介绍lzw

我参考前辈的代码以及自己的理解尝试写了一个事件框架,我的框架完成了一个事件机制的核心,它能提供统一接口实现多事件绑定以及避免内存泄漏等其他一些问题,更重要的是性能还不错。

dede音乐网站源码,vscode 反汇编,ubuntu 分区,tomcat 性能 tps,sqlite 导出建表语句,ecshop秒杀插件免费下载,学前端网页框架需要学哪些,大数据爬虫技术有哪些,csrf php,SEO指数下单平台,网站设计模板源码,php网页游戏源代码,人力资源公司 网站模板lzw

我的手法:

帝国软件源码,ubuntu上安装dlib,tomcat8 默认线程池,爬虫集群策略,学习php开发的网站技术,光明seo技巧lzw

所有回调函数根据元素、事件类型、回调函数唯一ID缓存在一个_create对象中(其内部具体结构可见下面源码的关于_cache的注释)。

事件绑定使用一个_create代理函数处理,并且一个元素的各类型事件全部通过此进行分发,同时运用apply方法让IE的指针指向元素。

通过数组队列解决IE回调函数执行顺序的问题。

fix函数将处理回调函数传入的event参数以及其他兼容问题。此处参考了jQuery.event.fix。

断开事件与元素的循环引用避免内存泄漏。

A、核心实现:

// myEvent 0.2

// .04.06 - TangBin - - MIT Licensed

/**

* 事件框架

* @namespace

* @see /?p=1285

*/

var myEvent = (function () {

var _fid = 1,

_guid = 1,

_time = (new Date).getTime(),

_nEid = {$eid} + _time,

_nFid = {$fid} + _time,

_DOM = document.addEventListener,

_noop = function () {},

_create = function (guid) {

return function (event) {

event = api.fix(event || window.event);

var i = 0,

type = (event || (event = document.event)).type,

elem = _cache[guid].elem,

data = arguments,

events = _cache[guid].events[type];

for (; i < events.length; i ++) {

if (events[i].apply(elem, data) === false) event.preventDefault();

};

};

},

_cache = {/*

1: {

elem: (HTMLElement),

events: {

click: [(Function), (..)],

(..)

},

listener: (Function)

},

(..)

*/};

var api = {

/**

* 事件绑定

* @param {HTMLElement} 元素

* @param {String} 事件名

* @param {Function} 要绑定的函数

*/

bind: function (elem, type, callback) {

var guid = elem[_nEid] || (elem[_nEid] = _guid ++);

if (!_cache[guid]) _cache[guid] = {

elem: elem,

listener: _create(guid),

events: {}

};

if (type && !_cache[guid].events[type]) {

_cache[guid].events[type] = [];

api.add(elem, type, _cache[guid].listener);

};

if (callback) {

if (!callback[_nFid]) callback[_nFid] = _fid ++;

_cache[guid].events[type].push(callback);

} else

return _cache[guid];

},

/**

* 事件卸载

* @param {HTMLElement} 元素

* @param {String} 事件名

* @param {Function} 要卸载的函数

*/

unbind: function (elem, type, callback) {

var events, i, list,

guid = elem[_nEid],

handler = _cache[guid];

if (!handler) return;

events = handler.events;

if (callback) {

list = events[type];

if (!list) return;

for (i = 0; i < list.length; i ++) {

list[i][_nFid] === callback[_nFid] && list.splice(i--, 1);

};

if (list.length === 0) return api.unbind(elem, type);

} else if (type) {

delete events[type];

api.remove(elem, type, handler.listener);

} else {

for (i in events) {

api.remove(elem, i, handler.listener);

};

delete _cache[guid];

};

},

/**

* 事件触发 (注意:不会触发浏览器默认行为与冒泡)

* @param {HTMLElement} 元素

* @param {String} 事件名

* @param {Array} (可选)附加数据

*/

triggerHandler: function (elem, type, data) {

var guid = elem[_nEid],

event = {

type: type,

target: elem,

currentTarget: elem,

preventDefault: _noop,

stopPropagation: _noop

};

data = data || [];

data.unshift(event);

guid && _cache[guid].listener.apply(elem, data);

try {

elem[on + type] && elem[on + type].apply(elem, data);

//elem[type] && elem[type]();

} catch (e) {};

},

// 原生事件绑定接口

add: _DOM ? function (elem, type, listener) {

elem.addEventListener(type, listener, false);

} : function (elem, type, listener) {

elem.attachEvent(on + type, listener);

},

// 原生事件卸载接口

remove: _DOM ? function (elem, type, listener) {

elem.removeEventListener(type, listener, false);

} : function (elem, type, listener) {

elem.detachEvent(on + type, listener);

},

// 修正

fix: function (event) {

if (_DOM) return event;

var name,

newEvent = {},

doc = document.documentElement,

body = document.body;

newEvent.target = event.srcElement || document;

newEvent.target.nodeType === 3 && (newEvent.target = newEvent.target.parentNode);

newEvent.preventDefault = function () {event.returnValue = false};

newEvent.stopPropagation = function () {event.cancelBubble = true};

newEvent.pageX = newEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);

newEvent.pageY = newEvent.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);

newEvent.relatedTarget = event.fromElement === newEvent.target ? event.toElement : event.fromElement;

// !!IE写event会极其容易导致内存泄漏,Firefox写event会报错

// 拷贝event

for (name in event) newEvent[name] = event[name];

return newEvent;

}

};

return api;

})();

我给一万个元素绑定事件进行了测试,测试工具为sIEve,结果:

可以看到无论是执行效率还是内存占用myEvent都比有一定优势,这是可能是由于jQuery事件机制过于强大导致其性能的损耗。

测试样本:/demo/myEvent/

B、扩展自定义事件机制

jQuery是可以自定义事件的,它用一个special命名空间存储自定义事件,我在上面代码的基础上模仿jQuery自定义事件机制,并把其著名的ready事件与另外一个jQuery hashchange事件插件移植过来。

这两个自定义事件非常重要,ready事件可以在DOM就绪给元素绑定事件,比传统使用window.onload要快很多;hashchange事件可以监听锚点改变,常用于解决AJAX历史记录问题,如Twitter新版本就就采用此处理AJAX,使用锚点机制除了可以提高AJAX应用程序的用户体验外,如果按照一定规则还能被google索引到。

当然,我前面文章实现的imgReady事件也可以通过此扩展进来,稍后更新。

// myEvent 0.2.2

// .04.07 - TangBin - - MIT Licensed

/**

* 事件框架

* @namespace

* @see /?p=1285

*/

var myEvent = (function () {

var _ret, _name,

_fid = 1,

_guid = 1,

_time = (new Date).getTime(),

_nEid = {$eid} + _time,

_nFid = {$fid} + _time,

_DOM = document.addEventListener,

_noop = function () {},

_create = function (guid) {

return function (event) {

event = myEvent.fix(event || window.event);

var type = (event || (event = document.event)).type,

elem = _cache[guid].elem,

data = arguments,

events = _cache[guid].events[type],

i = 0,

length = events.length;

for (; i < length; i ++) {

if (events[i].apply(elem, data) === false) event.preventDefault();

};

event = elem = null;

};

},

_cache = {/*

1: {

elem: (HTMLElement),

events: {

click: [(Function), (..)],

(..)

},

listener: (Function)

},

(..)

*/};

var API = function () {};

API.prototype = {

/**

* 事件绑定

* @param {HTMLElement} 元素

* @param {String} 事件名

* @param {Function} 要绑定的函数

*/

bind: function (elem, type, callback) {

var events, listener,

guid = elem[_nEid] || (elem[_nEid] = _guid ++),

special = this.special[type] || {},

cacheData = _cache[guid];

if (!cacheData) cacheData = _cache[guid] = {

elem: elem,

listener: _create(guid),

events: {}

};

events = cacheData.events;

listener = cacheData.listener;

if (!events[type]) events[type] = [];

if (!callback[_nFid]) callback[_nFid] = _fid ++;

if (!special.setup || special.setup.call(elem, listener) === false) {

events[type].length === 0 && this.add(elem, type, listener);

};

events[type].push(callback);

},

/**

* 事件卸载

* @param {HTMLElement} 元素

* @param {String} 事件名

* @param {Function} 要卸载的函数

*/

unbind: function (elem, type, callback) {

var events, special, i, list, fid,

guid = elem[_nEid],

cacheData = _cache[guid];

if (!cacheData) return;

events = cacheData.events;

if (callback) {

list = events[type];

fid = callback[_nFid];

if (!list) return;

for (i = 0; i < list.length; i ++) {

list[i][_nFid] === fid && list.splice(i--, 1);

};

if (!list.length) this.unbind(elem, type);

} else if (type) {

special = this.special[type] || {};

if (!special.teardown || special.teardown.call(elem) === false) {

this.remove(elem, type, cacheData.listener);

};

delete events[type];

} else {

for (i in events) {

this.remove(elem, i, cacheData.listener);

};

delete _cache[guid];

};

},

/**

* 事件触发 (注意:不会触发浏览器默认行为与冒泡)

* @param {HTMLElement} 元素

* @param {String} 事件名

* @param {Array} (可选)附加数据

*/

triggerHandler: function (elem, type, data) {

var guid = elem[_nEid],

cacheData = _cache[guid],

event = {

type: type,

target: elem,

currentTarget: elem,

preventDefault: _noop,

stopPropagation: _noop

};

data = data || [];

data.unshift(event);

cacheData && cacheData.events[type] && _cache[guid].listener.apply(elem, data);

try {

elem[on + type] && elem[on + type].apply(elem, data);

//elem[type] && elem[type]();

} catch (e) {};

},

// 自定义事件接口

special: {},

// 原生事件绑定接口

add: _DOM ? function (elem, type, listener) {

elem.addEventListener(type, listener, false);

} : function (elem, type, listener) {

elem.attachEvent(on + type, listener);

},

// 原生事件卸载接口

remove: _DOM ? function (elem, type, listener) {

elem.removeEventListener(type, listener, false);

} : function (elem, type, listener) {

elem.detachEvent(on + type, listener);

},

// 修正

fix: function (event) {

if (_DOM) return event;

var name,

newEvent = {},

doc = document.documentElement,

body = document.body;

newEvent.target = event.srcElement || document;

newEvent.target.nodeType === 3 && (newEvent.target = newEvent.target.parentNode);

newEvent.preventDefault = function () {event.returnValue = false};

newEvent.stopPropagation = function () {event.cancelBubble = true};

newEvent.pageX = newEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);

newEvent.pageY = newEvent.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);

newEvent.relatedTarget = event.fromElement === newEvent.target ? event.toElement : event.fromElement;

// !!直接写event IE导致内存泄漏,Firefox会报错

// 伪装event

for (name in event) newEvent[name] = event[name];

return newEvent;

}

};

return new API();

})();

// DOM就绪事件

myEvent.ready = (function () {

var readyList = [], DOMContentLoaded,

readyBound = false, isReady = false;

function ready () {

if (!isReady) {

if (!document.body) return setTimeout(ready, 13);

isReady = true;

if (readyList) {

var fn, i = 0;

while ((fn = readyList[i++])) {

fn.call(document, {});

};

readyList = null;

};

};

};

function bindReady () {

if (readyBound) return;

readyBound = true;

if (document.readyState === complete) {

return ready();

};

if (document.addEventListener) {

document.addEventListener(DOMContentLoaded, DOMContentLoaded, false);

window.addEventListener(load, ready, false);

} else if (document.attachEvent) {

document.attachEvent(onreadystatechange, DOMContentLoaded);

window.attachEvent(onload, ready);

var toplevel = false;

try {

toplevel = window.frameElement == null;

} catch (e) {};

if (document.documentElement.doScroll && toplevel) {

doScrollCheck();

};

};

};

myEvent.special.ready = {

setup: bindReady,

teardown: function () {}

};

if (document.addEventListener) {

DOMContentLoaded = function () {

document.removeEventListener(DOMContentLoaded, DOMContentLoaded, false);

ready();

};

} else if (document.attachEvent) {

DOMContentLoaded = function () {

if (document.readyState === complete) {

document.detachEvent(onreadystatechange, DOMContentLoaded);

ready();

};

};

};

function doScrollCheck () {

if (isReady) return;

try {

document.documentElement.doScroll(left);

} catch (e) {

setTimeout(doScrollCheck, 1);

return;

};

ready();

};

return function (callback) {

bindReady();

if (isReady) {

callback.call(document, {});

} else if (readyList) {

readyList.push(callback);

};

return this;

};

})();

// Hashchange Event v1.3

(function (window, undefined) {

var config = {

delay: 50,

src: null,

domain: null

},

str_hashchange = hashchange,

doc = document,

isIE = !-[1,],

fake_onhashchange, special = myEvent.special,

doc_mode = doc.documentMode,

supports_onhashchange = on + str_hashchange in window && (doc_mode === undefined || doc_mode > 7);

function get_fragment(url) {

url = url || location.href;

return # + url.replace(/^[^#]*#?(.*)$/, $1);

};

special[str_hashchange] = {

setup: function () {

if (supports_onhashchange) return false;

myEvent.ready(fake_onhashchange.start);

},

teardown: function () {

if (supports_onhashchange) return false;

myEvent.ready(fake_onhashchange.stop);

}

};

/** @inner */

fake_onhashchange = (function () {

var self = {},

timeout_id, last_hash = get_fragment(),

/** @inner */

fn_retval = function (val) {

return val;

},

history_set = fn_retval,

history_get = fn_retval;

self.start = function () {

timeout_id || poll();

};

self.stop = function () {

timeout_id && clearTimeout(timeout_id);

timeout_id = undefined;

};

function poll() {

var hash = get_fragment(),

history_hash = history_get(last_hash);

if (hash !== last_hash) {

history_set(last_hash = hash, history_hash);

myEvent.triggerHandler(window, str_hashchange);

} else if (history_hash !== last_hash) {

location.href = location.href.replace(/#.*/, \) + history_hash;

};

timeout_id = setTimeout(poll, config.delay);

};

isIE && !supports_onhashchange && (function () {

var iframe,iframe_src, iframe_window;

self.start = function () {

if (!iframe) {

iframe_src = config.src;

iframe_src = iframe_src && iframe_src + get_fragment();

iframe = doc.createElement(\);

myEvent.bind(iframe, load, function () {

myEvent.unbind(iframe, load);

iframe_src || history_set(get_fragment());

poll();

});

doc.getElementsByTagName(html)[0].appendChild(iframe);

iframe_window = iframe.contentWindow;

doc.onpropertychange = function () {

try {

if (event.propertyName === itle) {

iframe_window.document.title = doc.title;

};

} catch (e) {};

};

};

};

self.stop = fn_retval;

/** @inner */

history_get = function () {

return get_fragment(iframe_window.location.href);

};

/** @inner */

history_set = function (hash, history_hash) {

var iframe_doc = iframe_window.document,

domain = config.domain;

if (hash !== history_hash) {

iframe_doc.title = doc.title;

iframe_doc.open();

domain && iframe_doc.write(document.domain=" + domain + \");

iframe_doc.close();

iframe_window.location.hash = hash;

};

};

})();

return self;

})();

})(this);

ready事件是伪事件,调用方式:

myEvent.ready(function () {

//[code..]

});

hashchange事件可以采用标准方式绑定:

myEvent.bind(window, hashchange, function () {

//[code..]

});

这里有一些文章值得阅读:

javascript 跨浏览器的事件系统(司徒正美。他博客有一系列的讲解)

更优雅的兼容(BELLEVE INVIS)

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。