14

ACTUALIZACIÓN:(recapitular, el violín y la abundancia)JavaScript: Cómo simular evento de cambio en Internet Explorer (delegación)

Esta pregunta no ha estado recibiendo mucha atención, así que voy a gasta un representante en eso. Sé que tiendo a ser demasiado detallado en mis respuestas y preguntas. Es por eso que continué y configuré this fiddle, que es, en mi opinión, una representación decente del tipo de código que estoy teniendo que usar para acercarme a un evento burbujeante change. Un par de cuestiones que estoy tratando de resolver:

  1. El evento pseudo-change no se activa en un elemento seleccionado, a menos que pierda el foco. En algunos casos, el cliente debe ser redirigido al seleccionar un nuevo valor. ¿Cómo logro esto?
  2. El controlador se llama tanto cuando se hace clic en las etiquetas como en las casillas de verificación. En sí mismo es lo que esperarías, pero debido al burbujeo del evento es imposible determinar en qué elemento se hizo clic (AFAIK). El objeto de evento de IE no tiene una propiedad realTarget.
  3. Al cambiar el checked -estado de una casilla en IE haciendo clic en la etiqueta, todo está bien (aunque requiere algunas soluciones desagradables), pero al hacer clic en la casilla de verificación directamente, se llama al controlador, pero el estado comprobado permanece sin cambios, hasta que haga clic por segunda vez. Entonces el valor cambia, pero no se llama al controlador.
  4. Cuando cambio a una pestaña diferente y viceversa, se llama al controlador varias veces. Tres veces si el estado comprobado realmente cambió, dos veces si hice clic directamente en el checbox solo una vez.

Cualquier información que pueda ayudarme a resolver uno o más de los problemas anteriores sería muy apreciada. Por favor, no me olvidé de agregar una etiqueta jQuery, me gusta JS puro, así que estoy buscando una respuesta JS pura.


Tengo una página web con más de 250 elementos seleccionados y 20 ~ 30 casillas de verificación. También tengo que rastrear las acciones de los usuarios y tomar las medidas adecuadas. Por lo tanto, es bastante natural para mí delegar el evento de cambio, en lugar de agregar cientos de oyentes, en mi humilde opinión.

Por supuesto, política de la empresa IE: IE8 tiene que ser compatible, no activa el evento onchange cuando lo necesito. Así que estoy tratando de falsificar un evento onchange. Lo que tengo hasta ahora es trabajar razonablemente bien aparte de 1 cosa que realmente me molesta.
Estoy usando onfocusin y onfocusout para registrar los eventos. En algunos casos, cuando el usuario selecciona un nuevo valor del elemento a select, el script debe responder de inmediato. Sin embargo, mientras el selector no haya perdido el foco, esto no sucederá.

Esto es lo que se me ocurrió hasta el momento:

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); 
} 

He intentado fijar otro detector de eventos dentro del manejador onfocusin, con destino al evento onclick. Disparó el evento onfocusout de cualquier selección que tenga foco ATM. El único problema es que el 99.9% de los usuarios harán clic en una selección, por lo que el evento focusin también activa un clic.

Para evitarlo, creé el cierre y pasé el actual en foco y su valor original como argumentos.Pero algunos usuarios hacen utilizan su teclado, y estos usuarios a menudo tabulan en el siguiente cuadro de selección sin cambiar el valor. Cada vez que vincula un nuevo oyente onclick ... creo que hay HAS que sea una manera más fácil que verificar todos los e.type y tratarlos por separado.
Sólo como ejemplo: el código con un onclick oyente adicional: todo el código es el mismo que el primer fragmento, así que sólo estoy pegando el bloque

   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; 

Respuesta

2

Bueno, tuve otra grieta en ella, y yo he llegado con un enfoque bastante decente (en el trabajo que hizo el truco - He tratado de replicar el código que he escrito, pero después de unas cervezas que podría contener algunos errores, pero el espíritu sigue siendo el mismo)

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! 
}); 

he descubierto que el fuego eventos focusIn dos veces en algunos casos para la radio y de casillas de verificación. Usar un objeto que contenga los estados comprobados reales de todas las casillas de verificación es menos costoso que los manejadores individuales, y solo me permite delegar el evento después de que el valor del elemento haya cambiado.

La función changeDelegator sólo se invoca cuando sea necesario, pero la función anon que he publicado aquí todavía se llama a Waaaay más de lo que quería, pero este enfoque todavía supera a los manipuladores individuales-toman.

Olvidé las selecciones, pero las conseguí trabajando también (toma similar, en la versión completa de mi código el cierre tiene 2 objetos, y lo hice, así que puedo marcar una identificación, encender el desenfoque de evento cuando sea necesario, y el cliente sea redirigido).
Al final de la carrera, aunque aprendí algunos trucos nuevos, lo que más me aleja de este ejercicio es un odio aún más profundo contra ese espantoso y gélido golem de algo llamado IE ... Pero si alguien else podría querer delegar eventos de cambio en IE, saber que es (casi) posible

3

case 'select': Aunque estoy de acuerdo en que sería mejor tener sólo un oyente de eventos en su totalidad en lugar de muchos oyentes, uno para cada elemento, debe evaluar los costos y los beneficios de su decisión. El beneficio de un oyente es una huella de memoria reducida. La desventaja es que tiene que hacer trucos de código complejos para evitar la implementación incorrecta de eventos de un navegador, aumentar el tiempo de ejecución, usar indebidamente tipos de eventos, registrar y anular el registro de escuchas de eventos repetidamente, lo que puede causar confusión a algunos usuarios.

Volviendo al beneficio, la huella de memoria no es tan grande si solo se adjunta la misma función que un oyente para todos los elementos. Y la memoria es algo que las computadoras actuales no carecen.

Aunque esto no responda a su pregunta, le aconsejo que deje de intentar que esto funcione, y en su lugar adjunte oyentes en cada elemento de formulario.

Para hacer frente a sus puntos un poco:

  1. ¿Estás seguro de eso? He intentado the basic example from Microsoft's documentation, y tanto IE7 como IE8 ejecutan el oyente onchange después de hacer clic en una opción del menú desplegable. Incluso se dispara al cambiar la selección con las teclas arriba/abajo.

  2. Si bien es cierto que no puede fácilmente conectar un evento en una etiqueta a la casilla de verificación afectada, ¿por qué querría hacer eso? El evento change se activará en la casilla de verificación de todos modos, y solo debería preocuparse por eso. Sin embargo, si debe acceder a la casilla de un evento en una etiqueta, puede hacerlo manualmente. Si el evento se dirige a una etiqueta, entonces usted sabe que la etiqueta está de alguna manera relacionada con una entrada. Dependiendo de cómo use las etiquetas, puede seleccionar el elemento input anidado dentro de label, u obtener el elemento con la identificación que se encuentra en el atributo for del label.

  3. Una forma de fijar las casillas de verificación devolver el valor anterior sobre el cambio error es hacer un this.blur()focus sobre eventos en casillas de verificación.

  4. Cuando dices "controlador", te refieres al controlador de eventos onfocusin, o changeDelegator? Es normal que se active un evento focusin al reactivar una pestaña. No estoy seguro de por qué el evento se disparó más de una vez, así que estoy adivinando: uno podría ser el enfoque que recibe la entrada activa; el segundo podría ser el enfoque que recibe el documento en sí; No tengo idea de por qué ocurre una tercera llamada. No entiendo a qué te refieres con "si el estado verificado realmente cambió, [...] si hice clic en la casilla directamente solo una vez".

+0

1. Sí, estoy seguro de eso: el evento de cambio se activa en IE8, pero no se dispara. Es [un problema conocido] (http://stackoverflow.com/questions/5146784/make-form-elements-onchange-bubble-in-internet-explorer) 2. se llama al controlador de cambios en ambos casos, ese no es el problema. Sin embargo, al hacer clic en la etiqueta, se llama al controlador después de que el estado verificado ha cambiado, mientras que el estado verificado está a punto de cambiar cuando se hace clic directamente en la casilla de verificación. Entonces, si 'target.checked === true', ¿se acaba de verificar o se va a desmarcar? No hay forma de decirlo. Se están quedando sin los caracteres –

+0

3. Agregar desenfoque y enfoque hace que el IE se cuelgue: tenga en cuenta que, debido a que 'onchange' no burbujea, estoy usando los eventos' onfocusin' y 'onfocusout'. Si el manejador se desenfoca y se enfoca, seguirá llamándose infinitamente 4. Y manejando quiero decir ambos: estrictamente hablando 'changeDelegator' es solo una función, pero a la que llamo en el contexto de los manejadores (' changeDelegator. aplicar (esto, [e]); '), así que lo considero una extensión/continuación del controlador real. Sé que el 'focusin' se dispara cuando se renueva la pestaña_, pero se activa para cada evento tratado por la función del delegador. chars .... –

+0

Lo que quiero decir con lo último es simplemente: hago clic en una casilla de verificación (¡el controlador se activa, pero el estado verificado no cambia!), si hago clic en esa casilla una segunda vez, el controlador no está 't llamado, pero el estado verificado cambia de todos modos. Si no hago clic por segunda vez, sino que cambio de pestaña y viceversa, el evento 'focusin' aparece _no_ para ser llamado para esa casilla de verificación en la que hice clic una vez. Muy raro. En el frente de rendimiento: hay una gran diferencia, especialmente cuando _no_ se ejecuta en V8. adjuntarlo a cada elemento en toda la página agregaría hasta 333 (solo los conté todos) elems! chars ... –

Cuestiones relacionadas