javascript跨浏览器事件框架,js实例代码
分类:美高梅游戏官网网站

一、ready函数的实现 经常用jQuery类库或其他类库中的ready方法,有时候想想它们到底是怎么实现的,但是看了一下jQuery中的源码,涉及到的模块比较多,(水平有限)代码比较难看懂;自己结合了一些书籍内容,总结一下。
先说一下ready函数的实现思路:

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

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

变量ready通过表达式赋值,右侧为一个自执行匿名函数,在这个匿名函数中,首先为各个浏览器的事件绑定处理函数,并为isReady赋值(根据事件异步处理程序来确定),然后返回一个传参闭包,在闭包中,主要判断isReady值来执行操作,如果dom结构准备就绪(isReady

true),执行回调,否则将回调加入到要执行的队列(funs)中,待事件处理程序执行时,循环遍历队列(funs),并依次执行队列中的函数,执行完队列中的函数后,还需要清除队列(funs = null)。

复制代码 代码如下:

var ready = (function(){
    var isReady = false,
    funs = [];
    function handle (e) {
        if ( isReady ) {
            return;
        }
        if ( e.type === 'readystatechange' && (document.readyState !== 'interactive' && document.readyState !== 'complete') ) {
            return;
        }
        for ( var i = 0; i < funs.length; i++ ) {
            funs[i].call(document);
        }
        isReady = true;
        funs = null;
    }
    if ( document.addEventListener ) {
        document.addEventListener( 'DOMContentLoaded', handle, false );
        document.addEventListener( 'readystatechange', handle, false );
        document.addEventListener( 'load', handle, false );
    }
    else if ( document.attachEvent ) {
        document.attachEvent( 'onreadystatechange', handle );
        document.attachEvent( 'onload', handle );
    }
    return function ready (callback) {
        if ( isReady ) {
            callback.call(document);
        }
        else {
            funs.push(callback);
        }
    };
}());

PS:
该函数代码参照于权威指南书籍,唯一不同的是,多加了一个判断document.readyState !== 'interactive'

复制代码 代码如下:

if ( e.type === 'readystatechange' && (document.readyState !== 'interactive' && document.readyState !== 'complete') ) {
    return;
}

在各个浏览器中交互和完成状态出现顺序并不能保证一致,这取决于浏览器及页面的内容,多加了这个判断document.readyState !== 'interactive'的话,
意思是不管哪个阶段先出现,代码都能更早的执行。
二、按需加载css,js 参照了jQuery源码,写了一个type函数,返回参数类型。

复制代码 代码如下:

/**
 *
 * 判断参数类型
 * createTime: 2013/9/18
 *
 */
function type (obj) {
    var classTypes, objectTypes;
    if ( obj == null ) {
        return String(obj);
    }
    classTypes = {};
    objectTypes = ('Boolean Number String Function Array Date RegExp Object Error').split(' ');
    for ( var i = 0, len = objectTypes.length; i < len; i++ ) {
        classTypes[ '[object ' + objectTypes[i] + ']' ] = objectTypes[i].toLowerCase();
    }
    if ( typeof obj === 'object' || typeof obj === 'function' ) {
        var key = Object.prototype.toString.call(obj);
        return classTypes[key];
    }
    return typeof obj;
}

复制代码 代码如下:

// css按需加载
function loadCss (cssUrl, callback) {
    var elem, bl,
        isExecuted = false; // 防止在ie9中,callback执行两次
    if ( cssUrl == null ) {
        return String(cssUrl);
    }
    elem = document.createElement('link'),
    elem.rel = 'stylesheet';
    if ( type(callback) === 'function' )  {
        bl = true;
    }
    // for ie
    function handle() {
        if ( elem.readyState === 'loaded' || elem.readyState === 'complete' ) {
            if (bl && !isExecuted) {
                callback();
                isExecuted = true;
            }
            elem.onreadystatechange = null;
        }
    }
    elem.onreadystatechange = handle;
    // for 非ie
    if (bl && !isExecuted) {
        elem.onload = callback;
        isExecuted = true;
    }
    elem.href = cssUrl;
    document.getElementsByTagName('head')[0].appendChild(elem);
}
// js按需加载
function loadScript(scriptUrl, callback) {
    var elem, bl,
        isExecuted = false; // 防止在ie9中,callback执行两次
    if (scriptUrl == null) {
        return String(fn);
    }
    elem = document.createElement('script');
    if ( type(callback) === 'function' )  {
        bl = true;
    }
    // for ie
    function handle(){
        var status = elem.readyState;
        if (status === 'loaded' || status === 'complete') {
            if (bl && !isExecuted) {
                callback();
                isExecuted = true;
            }
            elem.onreadystatechange = null;
        }
    }
    elem.onreadystatechange = handle;
    // for 非ie
    if (bl && !isExecuted) {
        elem.onload = callback;
        isExecuted = true;
    }
    elem.src = scriptUrl;
    document.getElementsByTagName('head')[0].appendChild(elem);
}

PS: 在判断link,script元素是否加载完毕,主要依靠load事件;而在ie9以下浏览器中,并没有load事件,ie为它们都添加了一个readystatechange事件,通过判断
元素的readyState状态确定元素是否已经加载完毕;而奇怪的是,在ie9(还可能存在其他浏览器版本)中,元素既有load事件又有readystatechange事件,因此在代码中添加了一个变量isExecuted,如果执行过回调,那么就不再执行,避免回调执行两次。
三、调用方式

复制代码 代码如下:

loadCss('//www.jb51.net/apps/tbtx/miiee/css/base.css', function(){
    console.log('css加载完毕');
});
loadScript('//www.jb51.net/apps/tbtx/miiee/js/jQuery.js', function(){
    console.log('js加载完毕');
});
ready(function(){
    console.log('dom is ready!');
});

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

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

您可能感兴趣的文章:

  • jquery 实现轮播图详解及实例代码
  • jQuery自适应轮播图插件Swiper用法示例
  • jquery实现左右无缝轮播图
  • jQuery插件slides实现无缝轮播图特效
  • 利用jquery写的左右轮播图特效
  • jQuery按需加载轮播图(web前端性能优化)

我的手法:

我的手法:

所有回调函数根据元素、事件类型、回调函数唯一ID缓存在一个_create对象中(其内部具体结构可见下面源码的关于_cache的注释)。
事件绑定使用一个_create代理函数处理,并且一个元素的各类型事件全部通过此进行分发,同时运用apply方法让IE的指针指向元素。
通过数组队列解决IE回调函数执行顺序的问题。
fix函数将处理回调函数传入的event参数以及其他兼容问题。此处参考了jQuery.event.fix。
断开事件与元素的循环引用避免内存泄漏。
一、核心实现:

所有回调函数根据元素、事件类型、回调函数唯一ID缓存在一个_create对象中(其内部具体结构可见下面源码的关于_cache的注释)。
事件绑定使用一个_create代理函数处理,并且一个元素的各类型事件全部通过此进行分发,同时运用apply方法让IE的指针指向元素。
通过数组队列解决IE回调函数执行顺序的问题。
fix函数将处理回调函数传入的event参数以及其他兼容问题。此处参考了jQuery.event.fix。
断开事件与元素的循环引用避免内存泄漏。
一、核心实现:

复制代码 代码如下:

复制代码 代码如下:

// myEvent 0.2
// 2011.04.06 - TangBin - planeart.cn - MIT Licensed
/**
* 事件框架
* @namespace
* @see
*/
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;
})();

// myEvent 0.2
// 2011.04.06 - TangBin - planeart.cn - MIT Licensed
/**
* 事件框架
* @namespace
* @see
*/
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,结果:

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

事件框架

事件框架

耗时

耗时

内存

内存

IE8

IE8

jQuery.bind

jQuery.bind

1064 MS

1064 MS

79.80 MB

79.80 MB

myEvent.bind

myEvent.bind

623 MS

623 MS

35.82 MB

35.82 MB

IE6

IE6

jQuery.bind

jQuery.bind

2503 MS

2503 MS

74.23 MB

74.23 MB

myEvent.bind

myEvent.bind

1810 MS

1810 MS

28.48 MB

28.48 MB

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

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

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

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

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

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

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

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

复制代码 代码如下:

复制代码 代码如下:

// myEvent 0.2.2
// 2011.04.07 - TangBin - planeart.cn - MIT Licensed
/**
* 事件框架
* @namespace
* @see
*/
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('<IFRAME title=empty style="DISPLAY: none" tabIndex=-1 src="' + (iframe_src || 'javascript:0') + '"></IFRAME>');
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 === 'title') {
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('<SCRIPT>document.domain="' + domain + '"</SCRIPT>');
iframe_doc.close();
iframe_window.location.hash = hash;
};
};
})();
return self;
})();
})(this);

// myEvent 0.2.2
// 2011.04.07 - TangBin - planeart.cn - MIT Licensed
/**
* 事件框架
* @namespace
* @see
*/
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('<IFRAME title=empty style="DISPLAY: none" tabIndex=-1 src="' + (iframe_src || 'javascript:0') + '"></IFRAME>');
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 === 'title') {
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('<SCRIPT>document.domain="' + domain + '"</SCRIPT>');
iframe_doc.close();
iframe_window.location.hash = hash;
};
};
})();
return self;
})();
})(this);

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

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

复制代码 代码如下:

复制代码 代码如下:

myEvent.ready(function () {
//[code..]
});

myEvent.ready(function () {
//[code..]
});

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

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

复制代码 代码如下:

复制代码 代码如下:

myEvent.bind(window, 'hashchange', function () {
//[code..]
});

myEvent.bind(window, 'hashchange', function () {
//[code..]
});

这里有一些文章值得阅读:
javascript 跨浏览器的事件系统(司徒正美。他博客有一系列的讲解)
更优雅的兼容(BELLEVE INVIS)

这里有一些文章值得阅读:
javascript 跨浏览器的事件系统(司徒正美。他博客有一系列的讲解)
更优雅的兼容(BELLEVE INVIS)

您可能感兴趣的文章:

  • JavaScript实现跨浏览器的添加及删除事件绑定函数实例
  • JavaScript跨浏览器获取页面中相同class节点的方法
  • javascript跨浏览器的属性判断方法
  • 跨浏览器通用、可重用的选项卡tab切换js代码
  • JavaScript判断窗口是否最小化的代码(跨浏览器)
  • 基于JS快速实现导航下拉菜单动画效果附源码下载
  • js实现加载更多功能实例
  • js 实现一些跨浏览器的事件方法详解及实例

1.5.1)搞定了...

本文由美高梅网址发布于美高梅游戏官网网站,转载请注明出处:javascript跨浏览器事件框架,js实例代码

上一篇:实现原生ajax的几种方法,javascript实现原生ajax的 下一篇:没有了
猜你喜欢
热门排行
精彩图文