2010-07-06 63 views
2

我有下面的代碼,它允許我在用戶第一次將元素放在元素上然後移除事件時執行某些操作。IE中的事件處理

它在W3C事件模型瀏覽器中玩的很好,但在IE6-8中一直拋出一個錯誤。我從另一個問題得到了代碼,並相信它會處理IE。任何人看到我做錯了什麼?

<script type="text/javascript"> 
    function setMouseEvent() { 
     //Tel: 01 8279 400 
     event = addEvent(document.getElementById('contactButton'), 'mouseover', changeText); 
    } 

    function changeText() { 
     alert("worked!"); 
     removeEvent(document.getElementById('contactButton'), 'mouseover', changeText); 
    } 

    function addEvent(obj, type, fn) { 
     if (typeof obj.addEventListener != undefined) { 
      obj.addEventListener(type, fn, false); 
     } 
     else if (typeof obj.attachEvent != undefined) { 
      obj.attachEvent("on" + type, fn); 
     } 
    } 

    function removeEvent(obj, type, fn) { 
     if (typeof obj.addEventListener != undefined) { 
      obj.removeEventListener(type, fn, false); 
     } 
     else if (typeof obj.attachEvent != undefined) { 
      obj.detachEvent("on" + type, obj[type + fn]); 
      obj[type + fn] = null; 
      obj["e" + type + fn] = null; 
     } 
    } 

    window.onload = setMouseEvent; 
</script> 

更新:我只是在最新的Chrome,Opera和FF沒有問題的測試,但Safari瀏覽器也沒有,當我鼠標懸停和IE瀏覽器的onload拋出錯誤提到。

+1

呸。你從哪裏得到'removeEvent'?看起來類似於John Resig的實現(http://ejohn.org/blog/flexible-javascript-events/),但與'addEvent'不對稱。 – 2010-07-06 10:53:11

+0

也許我只是一個傻瓜,但如果這是我的頁面,我會用一個簡單的標誌變量處理一次性事件處理程序,我會在第一次調用後翻轉它。如果我真的想做很多事件爭吵,我會讓一些經過良好測試的框架處理醜陋的細節。 – Pointy 2010-07-06 12:02:17

+0

我很接近只是再次抓住jQuery,但這次想挖一點點。一直想繼續學習。 – 2010-07-06 21:30:49

回答

5
event = addEvent(

addEvent不返回任何東西;您將undefined缺少返回值分配給全局變量event(因爲您在函數中沒有說var event)。這會在IE中引發異常,它使用全局event作爲特殊對象將事件詳細信息傳遞給處理程序,因此不允許您爲其分配其他內容。

if (typeof obj.addEventListener != undefined) 

typeof總是返回一個字符串,它永遠不會測試等於undefined unvalue,所以IE將始終以非IE叉和失敗。你的意思是if (typeof obj.addEventListener !== 'undefined'),帶有一個字符串。

obj.detachEvent("on" + type, obj[type + fn]); 

既然你不寫的屬性與typefnaddEvent功能粘在一起的名字,這將無法獲取任何東西。

正如Crescent所說,它看起來像是使用Resig的removeEvent函數,與不匹配的addEvent不同。如果您使用他的removeEvent,則需要使用他的addEvent才行。

但是,我不會使用這些函數:它們非常狡猾。我知道這是2005年,當這個錯誤的代碼'贏得'quirksmode的addEvent比賽,但即使如此,我們應該知道更多。問題是它會根據事件名稱和功能代碼的文本序列號(type+fn)創建一個字符串,並將其用作存儲回調的鍵。這個鍵看起來像'mouseoverfunction changeText() {...code...}'

但依靠函數序列化是一個可怕的想法。格式不被ECMAScript標準化(「函數的實現相關表示被返回」);有many browser quirks;而並非最不重要的,兩種不同的功能可以很容易地返回相同的字符串 - 瀏覽器目前的序列化方法:

var f1= function() {}; 
var f2= function() {}; 

f1f2將具有相同的字符串序列化,但它們不是同一個對象。如果你在IE上爲f1執行addEvent,然後在f2上執行另一個,則第二個屬性將使用相同的序列化字符串並覆蓋第一個屬性。然後調用removeEventf1將檢索功能f2,嘗試detachEvent它並失敗,因爲它不是相同的功能。這個例子可能看起來很有意思,但是當你使用通用閉包的時候,它實際上是非常容易的,因爲現代JavaScript越來越多。由於這個原因,我建議在所有情況下避免Resig的addEvent

(jQuery的用戶:不用擔心,它的所有問題jQuery的不屬於使用此代碼的陷阱)

這個技巧是有保存價值this當你changeText函數被調用回由IE,其attachEvent不設置this。但你甚至不使用this值,所以你可以用一個更簡單的版本,比如它創建的original addEvent來代替。

在至少缺點是的addEvent是衆所周知的,並立即顯示出來,當你在IE測試使用this,而不是隻在特殊情況下,當它影響到你,很可能是極其腳麻的混亂和難以調試。

話又說回來,你現在還沒有使用,即使是同一事件的多個監聽器,讓您可以輕鬆地擺脫了老式的DOM 0級事件處理程序,是這樣的:

window.onload= function() { 
    var changed= false; 
    document.getElementById('contactButton').onmouseover= function() { 
     if (!changed) { 
      alert('changing text'); 
     } 
     changed= true; 
    }; 
}; 
+0

我認爲這是第一個解決Resig的addEvent條目的問題。在這個答案的評論中有一個簡短的交流:http://stackoverflow.com/questions/1466875/ies-understanding-of-this/1471006#1471006其中kangax啓發我依靠函數反編譯/序列化的失敗標識一個函數對象。好寫。 A ++++++++ – 2010-07-06 23:20:37

+0

+1秒。 – galambalazs 2010-07-11 00:21:52

1

轉到爲Dean's addEvent,如果你想有一個靈活的解決方案:

// written by Dean Edwards, 2005 
// with input from Tino Zijdel, Matthias Miller, Diego Perini 
// http://dean.edwards.name/weblog/2005/10/add-event/ 

function addEvent(element, type, handler) { 
    if (element.addEventListener) { 
     element.addEventListener(type, handler, false); 
    } else { 
     // assign each event handler a unique ID 
     if (!handler.$$guid) handler.$$guid = addEvent.guid++; 
     // create a hash table of event types for the element 
     if (!element.events) element.events = {}; 
     // create a hash table of event handlers for each element/event pair 
     var handlers = element.events[type]; 
     if (!handlers) { 
      handlers = element.events[type] = {}; 
      // store the existing event handler (if there is one) 
      if (element["on" + type]) { 
       handlers[0] = element["on" + type]; 
      } 
     } 
     // store the event handler in the hash table 
     handlers[handler.$$guid] = handler; 
     // assign a global event handler to do all the work 
     element["on" + type] = handleEvent; 
    } 
}; 
// a counter used to create unique IDs 
addEvent.guid = 1; 

function removeEvent(element, type, handler) { 
    if (element.removeEventListener) { 
     element.removeEventListener(type, handler, false); 
    } else { 
     // delete the event handler from the hash table 
     if (element.events && element.events[type]) { 
      delete element.events[type][handler.$$guid]; 
     } 
    } 
}; 

function handleEvent(event) { 
    var returnValue = true; 
    // grab the event object (IE uses a global event object) 
    event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event); 
    // get a reference to the hash table of event handlers 
    var handlers = this.events[event.type]; 
    // execute each event handler 
    for (var i in handlers) { 
     this.$$handleEvent = handlers[i]; 
     if (this.$$handleEvent(event) === false) { 
      returnValue = false; 
     } 
    } 
    return returnValue; 
}; 

function fixEvent(event) { 
    // add W3C standard event methods 
    event.preventDefault = fixEvent.preventDefault; 
    event.stopPropagation = fixEvent.stopPropagation; 
    return event; 
}; 
fixEvent.preventDefault = function() { 
    this.returnValue = false; 
}; 
fixEvent.stopPropagation = function() { 
    this.cancelBubble = true; 
};