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:
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.
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.
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.
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:
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: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; },
Código Javascript:
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:Ver original
P('div').click(function(){ // stuff }); P('#clickme').click(function(){ // more stuff });
Código Javascript:
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.Ver original
P('body').eventos.click; // Se supone que undefined, pero en realidad /* [function(){ // stuff }, function(){ // more stuff }] */
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