// myEvent 0.2
// 2011.04.06 - TangBin - planeart.cn - MIT Licensed
/**
* 이벤트 프레임
* @namespace
* @see http://www.planeart.cn/?p=1285
*/
var myEvent = (function () {
var _fid = 1,
_guid = 1,
_time = (새 날짜).getTime(),
_nEid = '{$eid}' _time,
_nFid = '{$fid }' _time,
_DOM = document.addEventListener,
_noop = 함수() {},
_create = 함수(guid) {
반환 함수(이벤트) {
event = api. fix(event || window.event);
var i = 0,
type = (event || (event = document.event)).type,
elem = _cache[guid].elem,
데이터 = 인수,
events = _cache[guid].events[type]
for (; i < events.length; i ) {
if (events[i].apply( elem, 데이터) === false) event.preventDefault();
},
_cache = {/*
1: {
elem: ( HTMLElement),
이벤트: {
클릭: [(함수), (..)],
(..)
},
리스너: (함수)
},
(..)
*/};
var api = {
/**
* 이벤트 바인딩
* @param {HTMLElement} 요소
* @param {String} 이벤트 이름
* 바인딩할 @param {Function} 함수
*/
bind: 함수(요소, 유형, 콜백) {
var guid = elem[_nEid] || (elem[_nEid] = _guid );
if (!_cache[guid]) _cache[guid] = {
elem,
listener: _create(guid) ,
이벤트: {}
};
if (type && !_cache[guid].events[type]) {
_cache[guid].events[type] = [];
api.add(elem, type, _cache[guid].listener);
};
if (콜백) {
if (!callback[_nFid]) 콜백[_nFid] = _fid ;
_cache[guid].events[유형].push(콜백);
} else
return _cache[guid];
},
/**
* 이벤트 언로드
* @param {HTMLElement} 요소
* @param {String} 이벤트 이름
* 언로드할 @param {Function} 함수
*/
바인딩 해제: 함수(elem, 유형, 콜백) {
var events, i, list,
guid = elem[_nEid],
핸들러 = _cache[guid];
(!handler)가 반환되는 경우;
이벤트 = handler.events;
if(콜백) {
list = 이벤트[유형];
(!list)가 반환되는 경우;
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 (유형) {
이벤트 삭제[유형];
api.remove(elem, type, handler.listener);
} else {
for (i in events) {
api.remove(elem, i, handler.listener);
};
_cache[guid] 삭제;
};
},
/**
* 이벤트 트리거(참고: 브라우저 기본 동작 및 버블링은 트리거되지 않음)
* @param {HTMLElement} 요소
* @param {String} 이벤트 이름
* @param {Array} ( 선택사항) 추가 데이터
*/
triggerHandler: 함수(elem, 유형, 데이터) {
var guid = elem[_nEid],
event = {
유형: 유형,
대상: elem,
현재 대상: elem,
preventDefault: _noop,
stopPropagation: _noop
};
데이터 = 데이터 || [];
data.unshift(이벤트);
guid && _cache[guid].listener.apply(elem, data);
try {
elem['on' 유형] && elem['on' 유형].apply(elem, data);
//elem[유형] && elem[유형]();
} catch (e) {};
},
// 原生事件绑정接구
추가: _DOM ? 함수(elem, 유형, 리스너) {
elem.addEventListener(유형, 리스너, false);
} : 함수(elem, 유형, 리스너) {
elem.attachEvent('on' 유형, 리스너);
},
// 原生事件卸载接口
제거: _DOM ? 함수(elem, 유형, 리스너) {
elem.removeEventListener(유형, 리스너, false);
} : 함수(elem, 유형, 리스너) {
elem.detachEvent('on' 유형, 리스너);
},
// 수정
fix: function (event) {
if (_DOM) return event;
var 이름,
newEvent = {},
doc = document.documentElement,
body = document.body;
newEvent.target = event.srcElement || 문서;
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.관련Target = event.fromElement === newEvent.target ? event.toElement : 이벤트.fromElement;
// !!IE写event会极其容易导致内存泄漏,Firefox写event会报错
// 拷贝event
for (이벤트 이름) newEvent[name] = event[name];
newEvent를 반환합니다.
}
};
API 반환;
})();
我给一万个元素绑定事件进行了测试,测试工具为sIEve,结果:
事件框架 |
耗时 |
内存 |
IE8 |
jQuery.bind |
1064 MS |
79.80 MB |
myEvent.bind |
623 MS |
35.82 MB |
IE6 |
jQuery.bind |
2503 MS |
74.23 MB |
myEvent.bind |
1810 MS |
28.48 MB |
myEvent는 실행 효율성과 메모리 사용량 측면에서 특정 이점을 갖고 있음을 알 수 있습니다. 이는 jQuery 이벤트 메커니즘이 너무 강력하여 성능이 저하되기 때문일 수 있습니다.
테스트 샘플: http://www.planeart.cn/demo/myEvent/
2. 사용자 정의 이벤트 메커니즘 확장
jQuery는 사용자 정의 이벤트를 저장하기 위해 특별한 네임스페이스를 사용합니다. 위의 코드를 기반으로 jQuery 사용자 정의 이벤트를 모방했습니다. 유명한 Ready 이벤트와 또 다른 jQuery hashchange 이벤트 플러그인이 있습니다.
이 두 가지 맞춤 이벤트는 매우 중요합니다. Ready 이벤트는 DOM이 준비되면 이벤트를 요소에 바인딩할 수 있으며, 이는 window.onload의 기존 사용보다 훨씬 빠릅니다. hashchange 이벤트는 앵커 포인트 변경을 모니터링할 수 있으며 자주 사용됩니다. 예를 들어, 새 버전의 Twitter에서는 AJAX를 처리하기 위해 이 방법을 사용합니다. AJAX 애플리케이션의 사용자 경험을 향상시키는 것 외에도 특정 규칙을 따르는 경우 앵커 메커니즘을 사용하여 Google에서 색인을 생성할 수도 있습니다.
물론, 이전 글에서 구현한 imgReady 이벤트도 이 확장을 통해 포함될 수 있으며 추후 업데이트될 예정입니다.
// myEvent 0.2.2
// 2011.04.07 - TangBin - planeart.cn - MIT 라이센스
/**
* 이벤트 프레임
* @namespace
* @see http://www.planeart.cn/?p=1285
*/
var myEvent = (함수 () {
var _ret, _name,
_fid = 1,
_guid = 1,
_time = (새 날짜).getTime(),
_nEid = '{$eid}' _time,
_nFid = '{$fid}' _time,
_DOM = document.addEventListener,
_noop = 함수() {},
_create = 함수(guid) {
반환 함수(이벤트) ) {
event = myEvent.fix(event || window.event);
var type = (event || (event = document.event)).type,
elem = _cache[guid]. elem,
데이터 = 인수,
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
};
},
_cache = {/*
1: {
elem: (HTMLElement),
events: {
click: [(함수), (..)],
(..)
},
리스너: (함수)
},
(..)
*/}
var API = 함수 () {} ;
API.prototype = {
/**
* 이벤트 바인딩
* @param {HTMLElement} 요소
* @param {String} 이벤트 이름
* 바인딩할 @param {Function} 함수
*/
bind: 함수(elem, 유형, 콜백) {
var 이벤트, 리스너,
guid = elem[_nEid ] || (elem[_nEid] = _guid ),
special = this.special[type] || {},
cacheData = _cache[guid];
if (!cacheData) 캐시데이터 = _cache[guid] = {
elem: elem,
listener: _create(guid),
events: {}
};
이벤트 = 캐시데이터.이벤트;
리스너 = 캐시데이터.리스너;
if (!events[type]) events[type] = [];
if (!callback[_nFid]) 콜백[_nFid] = _fid ;
if (!special.setup || 특수.setup.call(elem, 리스너) === false) {
events[type].length === 0 && this.add(elem, 유형, 리스너 );
};
이벤트[유형].push(콜백);
},
/**
* 이벤트 언로드
* @param {HTMLElement} 요소
* @param {String} 이벤트 이름
* 언로드할 @param {Function} 함수
*/
바인딩 해제: 함수(elem, 유형, 콜백) {
var events, Special, i, list, fid,
guid = elem [_nEid],
cacheData = _cache[guid];
(!cacheData)가 반환되는 경우;
이벤트 = 캐시데이터.이벤트;
if(콜백) {
list = 이벤트[유형];
fid = 콜백[_nFid];
(!list)가 반환되는 경우;
for (i = 0; i < list.length; i ) {
list[i][_nFid] === fid && list.splice(i--, 1);
};
if (!list.length) this.unbind(elem, type);
} else if (유형) {
special = this.special[type] || {};
if (!special.teardown || Special.teardown.call(elem) === false) {
this.remove(elem, type,cacheData.listener);
};
이벤트 삭제[유형];
} else {
for (i in events) {
this.remove(elem, i,cacheData.listener);
};
_cache[guid] 삭제;
};
},
/**
* 이벤트 트리거(참고: 브라우저 기본 동작 및 버블링은 트리거되지 않음)
* @param {HTMLElement} 요소
* @param {String} 이벤트 이름
* @param {Array} ( 선택사항) 추가 데이터
*/
triggerHandler: 함수(elem, 유형, 데이터) {
var guid = elem[_nEid],
cacheData = _cache[guid] ,
event = {
유형: 유형,
대상: elem,
currentTarget: elem,
preventDefault: _noop,
stopPropagation: _noop
};
데이터 = 데이터 || [];
data.unshift(이벤트);
cacheData && 캐시데이터.이벤트[유형] && _cache[guid].listener.apply(elem, data);
try {
elem['on' 유형] && elem['on' 유형].apply(elem, data);
//elem[유형] && elem[유형]();
} catch (e) {};
},
// 自定义事件接口
특수: {},
// 原生事件绑定接口
추가: _DOM ? 함수(elem, 유형, 리스너) {
elem.addEventListener(유형, 리스너, false);
} : 함수(elem, 유형, 리스너) {
elem.attachEvent('on' 유형, 리스너);
},
// 原生事件卸载接口
제거: _DOM ? 함수(elem, 유형, 리스너) {
elem.removeEventListener(유형, 리스너, false);
} : 함수(elem, 유형, 리스너) {
elem.detachEvent('on' 유형, 리스너);
},
// 수정
fix: function (event) {
if (_DOM) return event;
var 이름,
newEvent = {},
doc = document.documentElement,
body = document.body;
newEvent.target = event.srcElement || 문서;
newEvent.target.nodeType === 3 && (newEvent.target = newEvent.target.parentNode);
newEvent.preventDefault = function () {event.returnValue = false};
newEvent.stopPropagation = 함수() {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.관련Target = event.fromElement === newEvent.target ? event.toElement : 이벤트.fromElement;
// !!直接写event IE导致内存泄漏,Firefox会报错
// 伪装event
for (이벤트 이름) newEvent[name] = event[name];
newEvent를 반환합니다.
}
};
새 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 =
; };
};
function binReady() {
if (readyBound) return;
readyBound = true
if (document.readyState === 'complete') return Ready();
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', DOMContentLoaded, false)
window.addEventListener('load', 준비, 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: binReady,
teardown: function () {}
};
if (document.addEventListener) {
DOMContentLoaded = function () {
document.removeEventListener('DOMContentLoaded', DOMContentLoaded, false);
준비();
};
} else if (document.attachEvent) {
DOMContentLoaded = function () {
if (document.readyState === 'complete') {
document.detachEvent('onreadystatechange', DOMContentLoaded) ;
준비();
};
};
};
function doScrollCheck () {
if (isReady) return;
시도해 보세요 {
document.documentElement.doScroll('left');
} catch (e) {
setTimeout(doScrollCheck, 1);
반환;
};
준비();
};
반환 함수(콜백) {
bindReady();
if (isReady) {
callback.call(document, {});
} else if (readyList) {
readyList.push(콜백);
};
이것을 돌려주세요;
};
})();
// Hashchange Event v1.3
(function (window, undefine) {
var config = {
delay: 50,
src: null,
domain: null
},
str_hashchange = 'hashchange',
doc = 문서,
isIE = !-[1,],
fake_onhashchange, 특수 = myEvent.special,
doc_mode = doc.documentMode ,
supports_onhashchange = 'on' 창의 str_hashchange && (doc_mode === 정의되지 않음 || doc_mode > 7)
function get_fragment(url) {
url = url || >return '#' url.replace(/^[^#]*#?(.*)$/, '$1');
}
special[str_hashchange] = {
설정: 함수 () {
if (supports_onhashchange) return false
myEvent.ready(fake_onhashchange.start);
},
teardown: function () {
if (supports_onhashchange) return false; 🎜>myEvent.ready(fake_onhashchange.stop)
}
}
/**@안의*/
fake_onhashchange = (function () {
var self = {},
timeout_id, last_hash = get_fragment(),
/**@안의*/
fn_retval = function(val) {
return val;
},
history_set = fn_retval,
history_get = fn_retval;
self.start = function () {
timeout_id || 투표();
};
self.stop = function () {
timeout_id &&clearTimeout(timeout_id);
timeout_id = 정의되지 않음;
};
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 === 'title') {
iframe_window.document.title = doc.title;
};
} catch (e) {};
};
};
};
self.stop = fn_retval;
/**@안의*/
history_get = function () {
return get_fragment(iframe_window.location.href);
};
/**@안의*/
history_set = 함수(해시, 히스토리_해시) {
var iframe_doc = iframe_window.document,
domain = config.domain;
if (hash !== History_hash) {
iframe_doc.title = doc.title;
iframe_doc.open();
도메인 && iframe_doc.write('<SCRIPT>document.domain="' 도메인 '"</SCRIPT>');
iframe_doc.close();
iframe_window.location.hash = 해시;
};
};
})();
본인 반환;
})();
})(이것);
ready事件是伪事件,调用方式:
myEvent.ready(function () {
//[code..]
});
hashchange사건:
myEvent.bind(window, 'hashchange', function () {
//[code..]
});
更优雅的兼容
(BELLEVE INVIS)