2008-10-27 11 views
23

Estoy trabajando en una interfaz web complicada que en su mayoría está construida con JavaScript. Es básicamente una forma (muy) grande con muchas secciones. Cada sección se basa en las opciones de otras partes del formulario. Cada vez que cambian esas opciones, los nuevos valores se anotan en un objeto de tipo "registro" y las otras secciones se vuelven a llenar según corresponda.¿Es posible escuchar los cambios a los atributos de un objeto en JavaScript?

Tener detectores de eventos en los muchos campos de formulario está empezando a ralentizarse, y refrescar todo el formulario para cada cambio sería demasiado pesado/lento para el usuario.

Me pregunto si es posible agregar oyentes a los atributos del objeto de registro en lugar de los elementos del formulario para acelerar un poco las cosas? Y, si es así, ¿podría proporcionar/señalarme un código de muestra?

Más información:

  • Este es un plug-in para jQuery, por lo que cualquier funcionalidad que pueda construir-en la biblioteca de esa sería útil, pero no esencial.
  • Nuestros usuarios utilizan IE6/7, Safari y FF2/3, por lo que si es posible, pero solo para los navegadores "modernos", tendré que encontrar una solución diferente.

Respuesta

6

Gracias por los comentarios chicos. He ido con lo siguiente:

var EntriesRegistry = (function(){ 

    var instance = null; 

    function __constructor() { 

     var 
      self = this, 
      observations = {}; 

     this.set = function(n,v) 
     { 
      self[n] = v; 

      if(observations[n]) 
       for(var i=0; i < observations[n].length; i++) 
        observations[n][i].apply(null, [v, n]); 

     } 

     this.get = function(n) 
     { 
      return self[n]; 
     } 

     this.observe = function(n,f) 
     { 

      if(observations[n] == undefined) 
       observations[n] = []; 

      observations[n].push(f); 
     } 

    } 

    return new function(){ 
     this.getInstance = function(){ 
      if (instance == null) 
      { 
       instance = new __constructor(); 
       instance.constructor = null; 
      } 
      return instance; 
     } 
    } 
})(); 

var entries = EntriesRegistry.getInstance(); 

var test = function(v){ alert(v); }; 

entries.set('bob', 'meh'); 

entries.get('bob'); 

entries.observe('seth', test); 

entries.set('seth', 'dave'); 

Tomando a bordo de sus comentarios, voy a estar utilizando delegación de eventos en los objetos de formulario para actualizar el registro y activar los métodos de observación registrados.

Esto está funcionando bien para mí hasta ahora ... ¿pueden ver algún problema con esto?

2

Puede adjuntar un oyente a un contenedor (el cuerpo o el formulario) y luego usar el parámetro de evento para reaccionar al cambio. Obtienes toda la bondad del oyente, pero solo tienes que adjuntar una para el contenedor en lugar de una para cada elemento.

$('body').change(function(event){ 
    /* do whatever you want with event.target here */ 
    console.debug(event.target); /* assuming firebug */ 
}); 

El event.target contiene el elemento al que se hizo clic.

SitePoint tiene una buena explicación aquí de delegación de eventos:

JavaScript delegación de eventos es una técnica simple por el cual se agrega un controlador de eventos a un elemento padre con el fin de evitar tener que añadir controladores de eventos a múltiples elementos infantiles.

1

Los navegadores con motor Mozilla admiten Object.watch, pero no conozco un equivalente compatible con varios navegadores.

¿Ha perfilado la página con Firebug para tener una idea de qué es exactamente lo que está causando la lentitud, o es una "conjetura de muchos manejadores de eventos"?

6

Por lo que sé, no hay eventos activados en los cambios de atributo de objeto (edit: excepto, aparentemente, para Object.watch).

¿Por qué no utilizar la delegación de eventos siempre que sea posible? Es decir, los eventos en la forma más que en los elementos de la forma individual, capturando eventos a medida que surgen?

Por ejemplo (mi jQuery es oxidado, perdóname por el uso de prototipos lugar, pero estoy seguro de que serás capaz de adaptarse fácilmente):

$(form).observe('change', function(e) { 
    // To identify changed field, in Proto use e.element() 
    // but I think in jQuery it's e.target (as it should be) 
}); 

También puede capturar input y keyup y paste eventos si desea que se active en los campos de texto antes de que pierdan el foco.Mi solución para esto es por lo general:

  1. Gecko/navegadores basados ​​en WebKit:input observar en la form.
  2. También en los navegadores basados ​​en WebKit: observar keyup y paste eventos en textarea s (que no se activan en inputtextarea s por alguna razón).
  3. IE: observar keyup y paste en la form
  4. Observe change en los form (esta incendios en select s).
  5. Para keyup y paste eventos, comparar el valor actual de un campo contra su valor predeterminado (lo que su valor fue cuando se carga la página) mediante la comparación de un campo de texto value a su edición defaultValue

: Aquí es código de ejemplo que he desarrollado para evitar el envío de una forma no modificada y similares:

What is the best way to track changes in a form via javascript?

+0

Object.observe() está desfasada. Intenta evitar usar esto tanto como sea posible por tu propio bien. – andersfylling

0

jQuery es simplemente increíble. Aunque podría echarle un vistazo a ASP.NET AJAX Preview.

Algunas características son solo archivos .js, sin dependencia con .NET. Puede ser que puedas encontrar útil la implementación del patrón de observador.

var o = { foo: "Change this string" }; 

Sys.Observer.observe(o); 

o.add_propertyChanged(function(sender, args) { 
    var name = args.get_propertyName(); 
    alert("Property '" + name + "' was changed to '" + sender[name] + "'."); 
}); 

o.setValue("foo", "New string value."); 

Además, las plantillas de cliente están listas para usar en algunos escenarios interesantes.

Una nota final, esto es totalmente compatible con jQuery (no un problema con $)

Enlaces: Home page, Version I currently use

0

que estaba buscando lo mismo y hitted su pregunta ... ninguna de las respuestas satisfizo mis necesidades por lo que ocurrió con esta solución que me gustaría compartir:

var ObservedObject = function(){ 
    this.customAttribute = 0 
    this.events = {} 
    // your code... 
} 
ObservedObject.prototype.changeAttribute = function(v){ 
    this.customAttribute = v 
    // your code... 
    this.dispatchEvent('myEvent') 
} 
ObservedObject.prototype.addEventListener = function(type, f){ 
    if(!this.events[type]) this.events[type] = [] 
    this.events[type].push({ 
     action: f, 
     type: type, 
     target: this 
    }) 
} 
ObservedObject.prototype.dispatchEvent = function(type){ 
    for(var e = 0; e < this.events[type].length; ++e){ 
     this.events[type][e].action(this.events[type][e]) 
    } 
} 
ObservedObject.prototype.removeEventListener = function(type, f){ 
    if(this.events[type]) { 
     for(var e = 0; e < this.events[type].length; ++e){ 
      if(this.events[type][e].action == f) 
       this.events[type].splice(e, 1) 
     } 
    } 
} 

var myObj = new ObservedObject() 

myObj.addEventListener('myEvent', function(e){// your code...}) 

Es una simplificación de ¡la API de eventos DOM y funciona bien! Aquí está una más completa example

1

pequeña modificación en la respuesta anterior: moviendo el código observable a un objeto, se puede hacer una abstracción de ella y utilizarla para extender otros objetos con el método extend de jQuery.

ObservableProperties = { 
    events : {}, 
    on : function(type, f) 
    { 
     if(!this.events[type]) this.events[type] = []; 
     this.events[type].push({ 
      action: f, 
      type: type, 
      target: this 
     }); 
    }, 
    trigger : function(type) 
    { 
     if (this.events[type]!==undefined) 
     { 
      for(var e = 0, imax = this.events[type].length ; e < imax ; ++e) 
      { 
       this.events[type][e].action(this.events[type][e]); 
      } 
     } 
    }, 
    removeEventListener : function(type, f) 
    { 
     if(this.events[type]) 
     { 
      for(var e = 0, imax = this.events[type].length ; e < imax ; ++e) 
      { 
       if(this.events[type][e].action == f) 
        this.events[type].splice(e, 1); 
      } 
     } 
    } 
}; 
Object.freeze(ObservableProperties); 

var SomeBusinessObject = function(){ 
    self = $.extend(true,{},ObservableProperties); 
    self.someAttr = 1000 
    self.someMethod = function(){ 
     // some code 
    } 
    return self; 
} 

Ver el violín: https://jsfiddle.net/v2mcwpw7/3/

+0

Se podría argumentar que podemos vincular el objeto 'ObservableProperties' directamente al objeto comercial haciendo' $ .extend (true, this, ObservableProperties); ' Se ve mejor. Sin embargo, tenga cuidado con el alcance. Uso una 'self'variable cuando hago cosas un poco complicadas con el alcance (o un Singleton para el caso), de lo contrario, si todo es público, voy con' this'. Ver [este violín] (https://jsfiddle.net/empx6z58/) –

Cuestiones relacionadas