DOMNodeInsertedIntoDocument イベントのクロスブラウザ対応試作
Firefox や、 Opera で DOMNodeInsertedIntoDocument イベントがあまりイケてなかったので、比較的マシな DOMNodeInserted イベント使って、似たような動きになるようなモノを作ってみた。
Firefox 2.0.0.12 と Opera 9.25 と Safari 3.0.4 で動作確認してます。
IE は、今、手元に環境が無いから試してないけど多分動くんじゃないかな。動けばいいな。
var InsertedIntoDocument = function(){}; (function(){ /*************************/ /** * Original Source : * jQuery (jquery.com) */ var ua = navigator.userAgent.toLowerCase(); var browser = { version: (ua.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1], safari: /webkit/.test(ua), opera: /opera/.test(ua), msie: /msie/.test(ua) && !/opera/.test(ua), mozilla: /mozilla/.test(ua) && !/(compatible|webkit)/.test(ua) }; /*************************/ this.uniqid = function(prefix, table) { var id = prefix + ((new Date()).getTime() + Math.random()); return (typeof table[id] != 'undefined') ? arguments.callee(prefix, table) : id; } if (!browser.msie && !browser.safari) { this.handlers = {}; document.addEventListener('DOMNodeInserted',function(evt) { var handler = InsertedIntoDocument.handlers[evt.target.__handler_id__]; if (typeof handler != 'object') return; /* target 周りが上手く行かないからとりあえず DOMNodeInserted を渡す。 var e = document.createEvent("MutationEvents"); e.initMutationEvent( "DOMNodeInsertedIntoDocument", evt.cancelBubble, evt.cancelable, undefined, evt.prevValue, evt.newValue, evt.attrName, evt.attrChange ); */ for (var i in handler) handler[i].call(evt.target,evt); },true); } this.add = (function() { // for Internet Explorer /* @cc_on return function(element, fn, useCapture) { target.attachEvent('onreadystatechange',fn); } @*/ if(browser.safari) { return function(element, fn, useCapture) { var type = (browser.version < 3) ? 'readystatechange' : 'DOMNodeInsertedIntoDocument'; element.addEventListener(type, fn, useCapture); } } else if(!browser.msie) { return function(element, fn, useCapture) { if (typeof element.__handler_id__ == 'undefined') element.__handler_id__ = this.uniqid('handler_', this.handlers); var id = element.__handler_id__; if (typeof this.handlers[id] != 'object') this.handlers[id] = {}; var listener_id = this.uniqid('listener_', this.handlers[id]) fn.listener_id = listener_id; this.handlers[id][listener_id] = fn; } } })(); this.remove = (function() { // for Internet Explorer /* @cc_on return function(element, fn, useCapture) { target.detachEvent('onreadystatechange',fn); } @*/ if(browser.safari) { return function(element, fn, useCapture) { var type = (browser.version < 3) ? 'readystatechange' : 'DOMNodeInsertedIntoDocument'; element.removeEventListener(type, fn, useCapture); } } else if(!browser.msie) { return function(element, fn, useCapture) { delete this.handlers[element.__handler_id__][fn.listener_id]; if (this.countProperty(this.handlers[element.__handler_id__]) == 0) delete this.handlers[element.__handler_id__]; delete fn.listener_id; delete element.__handler_id__; } } })(); this.countProperty = function(obj) { var count = 0; for(var i in obj) count++; return count; } return this; }).call(InsertedIntoDocument);
使い方はこんな感じ。
var e1 = document.createElement('div'); e1.id = "e1"; InsertedIntoDocument.add(e1,function(evt){ console.log("DOMNodeInsertedIntoDocument !"); console.dir(evt); },true); setTimeout(function() { document.body.appendChild(e1); console.log('added'); },1000);
イベントハンドラやリスナの管理とかするために、DOM オブジェクトや Function オブジェクトにそれぞれの ID 振ったりしてるのがあまり綺麗じゃないので、いい方法思いついたらなんとかしたい。
あと、ちゃんと DOMInsertedIntoDocument イベントをリスナに渡せるようにしたい。