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// Eventos
eventos : {}, // Contenedor de los eventos [JSPlus]
enlazar : function(tipo, toDo, capture){ // jQuery -> bind
this.eventos[tipo] = this.eventos[tipo] || []; // Si no existe usamos un array vacío
var lisnrs = this.eventos[tipo]; // lisnrs -> la lista de eventos [JSPlus]
var este = this; // Referencia
toDo.realHandler = function(e){ // Función que se usará para evento, para ejecutar se usa toDo
// Invocamos la función, y si devuelve false usamos preventDefault y stopPropagation
if(toDo.call(este, e) === false){ // return false;
// IE
e.returnValue = false; // preventDefault
e.cancelBubble = true; // stopPropagation
// Chrome, FF, Opera...
if(e.preventDefault){ // Si existe preventDefault entonces también stopPropagation
e.preventDefault();
e.stopPropagation();
}
}
};
lisnrs.push(toDo); // Añadimos toDo a lisnrs -> eventos [JSPlus]
toDo.capture = capture; // Seteamos el capture
this.evento(tipo, toDo.realHandler, capture);
return this;
},
desenlazar : function(tipo, id){ // jQuery -> unbind
this.eventos[tipo] = this.eventos[tipo] || []; // Si no existe usamos un array vacío
var lisnrs = this.eventos[tipo]; // lisnrs -> la lista de eventos [JSPlus]
if(!id){ // Si no hay un id se borran todos
var este = this; // Referencia
lisnrs.forEach(function(fn){ este.borrarEvento(tipo, fn.realHandler, fn.capture); }); // Borramos los listeners
this.eventos[tipo] = []; // Borramos los handlers del eventos [JSPlus]
return this;
}
var fn; // Referencia al handler para poder usar borrarEvento
switch(typeof id){ // Aceptamos diferentes tipos de if
case 'string' : // Si es un string, comprobamos que sean iguales con toString
lisnsrs.forEach(function(act, i){ act.toString() == id && (fn = lisnrs.splice(i, 1)); });
break;
case 'number' : // Si es un número, accedemos a la lista de listeners y borramos ese índice
lisnrs[i] && (fn = lisnrs.splice(i, 1));
break;
case 'function' : // Si es un handler, lo buscamos en la lista
lisnsrs.forEach(function(act, i){ act == id && (fn = lisnrs.splice(i, 1)); });
break;
}
this.borrarEvento(tipo, fn.realHandler, fn.capture); // Usamos el realHandler para borrar
return this;
},
ejecutar : function(tipo, args){ // jQuery -> trigger
var lisnrs = this.eventos[tipo] = this.eventos[tipo] || [], este = this; // lisnrs -> la lista de eventos, y si no existe un array vacío
lisnrs.forEach(function(fn){ fn.apply(este, args); }); // Recorremos la lista de eventos y ejecutamos cada función
return this;
},
evento : function(tipo, toDo, capture){
if(this.addEventListener){ // Chrome, FF, Opera...
this.addEventListener(tipo, toDo, capture);
}else if(this.attachEvent){ // IE
var este = this, fn = function(){ toDo.call(este, window.event); };
this.attachEvent('on' + tipo, fn);
this[toDo.toString() + tipo] = fn;
}else{
var fn = this['on' + tipo] || function(){};
this['on' + tipo] = function(){ fn(); toDo(); };
}
return this;
},
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 originalP('div').click(function(){
// stuff
});
P('#clickme').click(function(){
// more stuff
});
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 originalP('body').eventos.click; // Se supone que undefined, pero en realidad
/*
[function(){
// stuff
}, function(){
// more stuff
}]
*/
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