14

UPDATE:(回顧一下,小提琴和獎金)JavaScript的:如何模擬變化事件在Internet Explorer(代表團)

這個問題還沒有得到太多的關注,所以我要去花一些代表就可以了。我知道我的答案和問題往往過於冗長。這就是爲什麼我繼續設立this fiddle,這一點,在我看來,我目前不必使用到接近沸騰change事件樣的代碼體面的表現。我試圖解決幾個問題:

  1. pseudo-change事件不會觸發select元素,除非它失去焦點。在某些情況下,應在選擇新值時重定向客戶端。我如何實現這一目標?
  2. 的處理程序被調用這兩個標籤被點擊時,以及複選框自己。本身就是你所期望的,但由於事件冒泡,它(AFAIK)無法確定哪個元素被點擊。 IE的事件對象沒有realTarget屬性。
  3. 如果通過單擊標籤改變checked -state在IE複選框的,一切都很好(儘管它需要一些討厭的解決方法),而是直接點擊複選框時,調用處理程序,但選中狀態保持不變,直到我再次點擊。然後值改變,但處理程序不被調用。
  4. 當我切換到不同的選項卡,然後再返回,處理程序被調用多次。如果檢查狀態實際發生了變化,則爲三次,如果直接單擊checbox,則只有一次。

任何可以幫助我解決上述問題的信息都將不勝感激。請,我不會忘記添加一個jQuery標記,我喜歡純JS,所以我正在尋找一個純JS的答案。


我有一個網頁上有超過250個選擇元素和20〜30個複選框。我也必須跟蹤用戶的行爲,並採取適當的行動。因此,我很自然地委派變革事件,而不是增加數百名聽衆,恕我直言。

當然,IE -company政策:IE8必須是當我需要它supported-不會觸發onchange事件。所以我試圖僞造一個onchange事件。到目前爲止,我所做的工作除了一件讓我真正感到困惑的事情外,還有相當好的工作。
我使用onfocusinonfocusout來註冊事件。在某些情況下,當用戶從select元素中選擇一個新值時,腳本應該立即響應。然而,只要選擇沒有失去焦點,這將不會發生。

這就是我想出迄今:

i = document.getElementById('content'); 
if (!i.addEventListener) 
{ 
    i.attachEvent('onfocusin',(function(self) 
    { 
     return function(e) 
     { 
      e = e || window.event; 
      var target = e.target || e.srcElement; 
      switch (target.tagName.toLowerCase()) 
      { 
       case 'input': 
        if (target.getAttribute('type') !== 'checkbox') 
        { 
         return true; 
        } 
        return changeDelegator.apply(self,[e]);//(as is 
       case 'select': 
        self.attachEvent('onfocusout',(function(self,current) 
        { 
         return function(e) 
         { 
          e = e || window.event; 
          var target = e.target || e.srcElement; 
          if (target !== current) 
          { 
           return; 
          } 
          self.detachEvent('onfocusout',arguments.callee);//(remove closure 
          return changeDelegator.apply(self,[e]); 
         } 
        })(self,target)); 
       default: return; 
      } 
     } 
    })(i));//(fake change event, buggy 
} 
else 
{//(to put things into perspective: all major browsers: 
    i.addEventListener('change',changeDelegator,false); 
} 

我試着安裝了onfocusin處理器內的另一個事件監聽器,綁定到onclick事件。它發射了任何選擇重點ATM的onfocusout事件。唯一的問題是,99.9%的用戶會點擊一個選擇,因此focusin事件也會觸發onclick。

爲了避開這一點,我創建了封閉,並接受當前選擇,對焦和它的原始值,將其作爲參數。但有些用戶使用他們的鍵盤,並且這些用戶經常選項卡到下一個選擇框而不更改值。每次綁定一個新的onclick聽衆...我相信有HAS比檢查所有e.type和分開處理它們更容易。
只是作爲一個例子:有一個額外的onclick監聽器代碼:所有代碼都是一樣的第一個片段,所以我只粘貼case 'select':

   case 'select': 
        self.attachEvent('onfocusout',(function(self,current) 
        { 
         return function(e) 
         { 
          e = e || window.event;//(IE... 
          var target = e.target || e.srcElement; 
          if (!(target === current)) 
          { 
           return; 
          } 
          self.detachEvent('onfocusout',arguments.callee);//(remove closure 
          return changeDelegator.apply(self,[e]); 
         }; 
        })(self,target)); 
        self.attachEvent('onclick',(function(self,current,oldVal) 
        { 
         return function(e) 
         { 
          e = e || window.event; 
          var target = e.target || e.srcElement; 
          if (target.tagName.toLowerCase() === 'option') 
          { 
           while(target.tagName.toLowerCase() !== 'select') 
           { 
            target = target.parentNode; 
           } 
          } 
          if (target !== current) 
          {//focus lost, onfocusout triggered anyway: 
           self.detachEvent('onclick',arguments.callee); 
           return; 
          } 
          var val = target.options[target.selectedIndex].innerHTML;//works best in all browsers 
          if (oldVal !== target.options[target.selectedIndex].innerHTML) 
          { 
           self.detachEvent('onclick',arguments.callee); 
           return target.fireEvent('onfocusout'); 
          } 
         }; 
        })(self,target,target.options[target.selectedIndex].innerHTML)); 
       default: return; 

回答

2

嗯,我有另一個打擊它,我想出了一個相當不錯的方法(在工作中它的伎倆 - 我試圖複製我寫的代碼但經過幾次啤酒它可能包含了一些錯誤,但精神保持不變

window.attachEvent('onload',function ieLoad() 
{ 
    var mainDiv = document.getElementById('main');//main div, from here events will be delegated 
    var checks = mainDiv.getElementsByTagName('input');//node list of all inputs 
    var checkStates = {}; 
    for (var i=0;i<checks.length;i++) 
    { 
     if (checks[i].type === 'checkbox') 
     {//get their checked states on load, this object serves as a reference 
      checkStates[checks[i].id] = checks[i].checked; 
     } 
    } 
    mainDiv.attachEvent('onfocusin',(function(initState) 
    {//initState holds a reference to the checkStates object 
     return function(e) 
     { 
      e = e || window.event; 
      var target = e.target || e.srcElement; 
      //id of checkboxes used as key, so no checking for tagName or type required 
      if (!initState.hasOwnProperty(target.id) || target.checked === initState[target.id]) 
      {//don't call method if checkstate is unchanged, either. I'll explain in a minute 
       return e; 
      } 
      initState[target.id] = target.checked;//set new checked-state 
      changeDelegator.apply(target,[e]);//delegate 
     }; 
    })(checkStates)); 
    window.detachEvent('onload',ieLoad);//avoid mem-leak with onload handler! 
}); 

我發現該於focusIn事件火在某些情況下對無線電的和複選框兩次。使用保存所有複選框的實際選中狀態的對象比單獨的處理程序更便宜,並且它允許我只在元素的值更改後委派事件。

changeDelegator功能在需要的時候纔會被調用,但我仍然張貼在這裏的匿名函數被調用Waaaay比我更想要它,但這種方法仍然優於單獨處理,採取。 (在我的代碼的完整版本中,封閉有2個對象,我做到了,所以我可以標誌一個ID,發射模糊事件需要時,客戶端重定向)。
在運行結束時,儘管我已經學會了一些新的技巧,但我從這個練習中學到的主要東西是對那種稱爲IE的那種可怕,粗暴的魔鬼的更深刻的仇恨......但是如果有人否則可能想要在IE中委託更改事件,知道它是(幾乎可以)可能

3

雖然我同意,這將是隻有更好爲整個表單上的一個事件監聽器而不是許多監聽器,每個元素一個,您必須評估您的決定的成本和收益。一位聽衆的好處是減少了內存佔用。缺點是您必須執行如此複雜的代碼技巧才能解決一個瀏覽器不正確的事件實現問題,增加執行時間,濫用事件類型,重複註冊和註銷事件監聽器,這可能會導致某些用戶混淆。

回想一下,如果您只是爲所有元素附加與偵聽器相同的函數,則內存佔用不會太大。內存是當前電腦不缺的東西。

即使這沒有回答你的問題,我建議你不要試圖做這個工作,而是在每個表單元素上附加監聽器。

爲了解決您的點了一下:

  1. 你能肯定嗎?我試過the basic example from Microsoft's documentation,並且在點擊下拉菜單中的一個選項後,IE7和IE8激發了onchange聽衆。使用上/下鍵更改選擇時甚至會觸發。

  2. 儘管確實不能很容易將標籤上的事件連接到受影響的複選框,爲什麼要這樣做?無論如何,change事件將在複選框上觸發,您應該只關心該事件。但是,如果您必須從標籤上的事件進入複選框,則可以手動執行此操作。如果事件以標籤爲目標,那麼您知道該標籤與輸入有關。根據您使用標籤的方式,您可以選擇嵌套在label內的input元素,也可以獲取labelfor屬性中找到的ID元素。固定複選框

  3. 一種方式回報變化前值 bug是做this.blur()focus的複選框事件。

  4. 當您說「處理程序」時,您的意思是onfocusin事件處理程序或changeDelegator?重新激活選項卡時,看到focusin事件觸發是正常現象。我不知道爲什麼這個事件被解僱了不止一次,所以我只是猜測:一個可能是主動輸入接收的焦點;第二個可能是文件本身收到的重點;我不知道爲什麼第三個電話會發生。我不明白你的意思是「如果檢查狀態實際發生了變化,[...]如果我只直接點擊複選框一次」。

+0

1.是的,我確定:IE8觸發更改事件,但它不會冒泡。這是[已知問題](http://stackoverflow.com/questions/5146784/make-form-elements-onchange-bubble-in-internet-explorer) 2.在兩種情況下調用更改處理程序,那不是問題。然而,當點擊標籤時,處理程序在檢查狀態發生變化後被調用,而直接點擊複選框後,檢查狀態即將改變。所以如果'target.checked === true',它剛剛被檢查過,還是會被取消選中?沒有辦法說。用盡字符 –

+0

3.添加模糊和焦點會導致IE崩潰:請注意,由於「onchange」不冒泡,因此我使用「onfocusin」和「onfocusout」事件。如果處理程序模糊不清並重點關注,它會繼續不斷地調用它自己。 4.處理程序的意思是:嚴格地說'changeDelegator'只是一個函數,但我在處理程序的上下文中調用了一個函數('changeDelegator。 apply(this,[e]);'),所以我認爲它是實際處理程序的擴展/延續。我知道,當選項卡爲_revived_時,'focusin'會被觸發,但對於由委託人函數處理的每個事件,它都會被觸發。字符.... –

+0

我最後的意思是簡單的:我點擊一個複選框(處理程序被觸發,但是選中狀態不會改變!),如果我再次點擊該複選框,那麼處理程序isn沒有打電話,但檢查的狀態都改變了。如果我沒有再次單擊它,但切換標籤並再次返回,'focusin'事件顯示_not_不會被調用,我點擊一次該複選框。真奇怪。在性能方面:有一個主要的區別,尤其是當在V8上不運行它時更是如此。將它附加到整個頁面上的每個元素將加起來爲333(只計數它們)elems!字符... –