Ver Mensaje Individual
  #1 (permalink)  
Antiguo 22/09/2011, 16:21
Avatar de _cronos2
_cronos2
Colaborador
 
Fecha de Ingreso: junio-2010
Mensajes: 2.062
Antigüedad: 13 años, 9 meses
Puntos: 310
live de jQuery + registro de eventos JSPlus

Puede que los moderadores ya tomen esto como una provocación por poner este tema en JS y no en frameworks La verdad es que por el título podría parecer que debería en el otro sitio, pero a mi juicio la cosa tiene más que ver con JS puro y duro que con un framework.
Y voy a empezar cuál es el problema que he encontrado, o los problemas
El primero es la implementación del método live de jQuery. Tras rebuscar en el código fuente, me di cuenta de que en la documentación explica cómo funciona (), así que intenté comprenderlo.
Cita:
Iniciado por jQuery
Event Delegation
The .live() method is able to affect elements that have not yet been added to the DOM through the use of event delegation: a handler bound to an ancestor element is responsible for events that are triggered on its descendants. The handler passed to .live() is never bound to an element; instead, .live() binds a special handler to the root of the DOM tree. In the example above, when the new element is clicked, the following steps occur:

A click event is generated and passed to the <div> for handling.
No handler is directly bound to the <div>, so the event bubbles up the DOM tree.
The event bubbles up until it reaches the root of the tree, which is where .live() binds its special handlers by default.
* As of jQuery 1.4, event bubbling can optionally stop at a DOM element "context".
The special click handler bound by .live() executes.
This handler tests the target of the event object to see whether it should continue. This test is performed by checking if $(event.target).closest(".clickme") is able to locate a matching element.
If a matching element is found, the original handler is called on it.
Because the test in step 5 is not performed until the event occurs, elements can be added at any time and still respond to events.
Tal y como yo lo entiendo, que seguramente estaré equivocado, se le agrega un evento click al elemento html, y después se comprueba desde html que donde realmente se originó el evento fue en un elemento que es .clickme . Según esto, para click por ejemplo no hay problemas, pero si un input recibe el foco yo diría que el evento no bubblea hacia arriba.
Entonces la pregunta es: ¿cómo funciona live?

Mi segunda duda viene de mi intento de framework Este verano (o invierno, depende de quien lo lea) implementé un bind, unbind y trigger bastante básicos. Cuando se suponía que funcionaba lo di por terminado, sin embargo hace poco me di cuenta de un fallo que no he podido resolver. Pego el código:
Código Javascript:
Ver original
  1. // Eventos
  2.   eventos : {}, // Contenedor de los eventos [JSPlus]
  3.   enlazar : function(tipo, toDo, capture){ // jQuery -> bind
  4.    this.eventos[tipo] = this.eventos[tipo] || []; // Si no existe usamos un array vacío
  5.    var lisnrs = this.eventos[tipo]; // lisnrs -> la lista de eventos [JSPlus]
  6.    var este = this; // Referencia
  7.    toDo.realHandler = function(e){ // Función que se usará para evento, para ejecutar se usa toDo
  8.     // Invocamos la función, y si devuelve false usamos preventDefault y stopPropagation
  9.     if(toDo.call(este, e) === false){ // return false;
  10.      // IE
  11.      e.returnValue = false; // preventDefault
  12.      e.cancelBubble = true; // stopPropagation
  13.      // Chrome, FF, Opera...
  14.      if(e.preventDefault){ // Si existe preventDefault entonces también stopPropagation
  15.       e.preventDefault();
  16.       e.stopPropagation();
  17.      }
  18.     }
  19.    };
  20.    lisnrs.push(toDo); // Añadimos toDo a lisnrs -> eventos [JSPlus]
  21.    toDo.capture = capture; // Seteamos el capture
  22.    this.evento(tipo, toDo.realHandler, capture);
  23.    return this;
  24.   },
  25.   desenlazar : function(tipo, id){ // jQuery -> unbind
  26.    this.eventos[tipo] = this.eventos[tipo] || []; // Si no existe usamos un array vacío
  27.    var lisnrs = this.eventos[tipo]; // lisnrs -> la lista de eventos [JSPlus]
  28.    if(!id){ // Si no hay un id se borran todos
  29.     var este = this; // Referencia
  30.     lisnrs.forEach(function(fn){ este.borrarEvento(tipo, fn.realHandler, fn.capture); }); // Borramos los listeners
  31.     this.eventos[tipo] = []; // Borramos los handlers del eventos [JSPlus]
  32.     return this;
  33.    }
  34.    var fn; // Referencia al handler para poder usar borrarEvento
  35.    switch(typeof id){ // Aceptamos diferentes tipos de if
  36.     case 'string' : // Si es un string, comprobamos que sean iguales con toString
  37.      lisnsrs.forEach(function(act, i){ act.toString() == id && (fn = lisnrs.splice(i, 1)); });
  38.     break;
  39.     case 'number' : // Si es un número, accedemos a la lista de listeners y borramos ese índice
  40.      lisnrs[i] && (fn = lisnrs.splice(i, 1));
  41.     break;
  42.     case 'function' : // Si es un handler, lo buscamos en la lista
  43.      lisnsrs.forEach(function(act, i){ act == id && (fn = lisnrs.splice(i, 1)); });
  44.     break;
  45.    }
  46.    this.borrarEvento(tipo, fn.realHandler, fn.capture); // Usamos el realHandler para borrar
  47.    return this;
  48.   },
  49.   ejecutar : function(tipo, args){ // jQuery -> trigger
  50.    var lisnrs = this.eventos[tipo] = this.eventos[tipo] || [], este = this; // lisnrs -> la lista de eventos, y si no existe un array vacío
  51.    lisnrs.forEach(function(fn){ fn.apply(este, args); }); // Recorremos la lista de eventos y ejecutamos cada función
  52.    return this;
  53.   },
  54.   evento : function(tipo, toDo, capture){
  55.    if(this.addEventListener){ // Chrome, FF, Opera...
  56.     this.addEventListener(tipo, toDo, capture);
  57.    }else if(this.attachEvent){ // IE
  58.     var este = this, fn = function(){ toDo.call(este, window.event); };
  59.     this.attachEvent('on' + tipo, fn);
  60.     this[toDo.toString() + tipo] = fn;
  61.    }else{
  62.     var fn = this['on' + tipo] || function(){};
  63.     this['on' + tipo] = function(){ fn(); toDo(); };
  64.    }
  65.    return this;
  66.   },
El código es bastante espeso, pero en principio el fallo estaría en la función enlazar. El problema es que al hacer algo como esto:
Código Javascript:
Ver original
  1. P('div').click(function(){
  2.  // stuff
  3. });
  4.  
  5. P('#clickme').click(function(){
  6.  // more stuff
  7. });
Al mirar los eventos registrados en el click de cualquier div, debería aparecer sólo la función stuff. Igual, al mirar los de #clickme, debería aparecer sólo more stuff. Sin embargo, salen las dos funciones en todos los elementos del DOM. Si en vez de 2 funciones hubiera 7 pasaría lo mismo: todas las funciones de un tipo de evento salen al mirar los eventos de ese tipo de todo el DOM. Para que se me entienda bien, en el ejemplo anterior:
Código Javascript:
Ver original
  1. P('body').eventos.click; // Se supone que undefined, pero en realidad
  2. /*
  3. [function(){
  4.  // stuff
  5. }, function(){
  6.  // more stuff
  7. }]
  8. */
Pensé que el problema podía ser this, pero al hacer un console.log de this dentro de la función me da el elemento correcto.
Así que, la segunda pregunta es: ¿WTF está pasando aquí dentro?
Puede que me haya explicado fatal y que no me entienda nadie, así que por favor si el texto es ininteligible decídmelo y trataré de explicarme mejor
Saludos y gracias :D
__________________
" Getting older’s not been on my plans
but it’s never late, it’s never late enough for me to stay. "
Cigarettes - Russian Red