Eventos en jQuery, cómo usar on.

En la última entrega vimos que el siguiente código no funcionaba: El anuncio desencadenó un charla con Sergi manteniendo que debería escribir directamente sobre delegación de eventos, que es precisamente lo que vamos a tratar ahora.

Delegando eventos.

jQuery cuenta con muchos métodos para delegar a eventos (bind, delegate o live). Sin embargo, estos métodos están obsoletos desde jQuery 1.7 en favor de on. A pesar de eso, empezaré explicando live, pasaré a delegate y finalmente llegaré a on.

live

El problema del código original se hubiese solucionado fácilmente llamando a live en vez de a on, como se comprueba en el fiddler correspondiente. El código es idéntico, simplemente cambia on por live.

Lo que hace este método es disparar el evento en cualquier elemento que satisfaga el selector, valiéndose de la propagación.

Dado que live está obsoleto y que actualmente live usa on internamente, lo dejaremos aquí.

delegate

delegate es bastante similar a live, sólo cambia cómo llamarlo. La equivalencia de llamadas sería:

El fiddler para delegate simplemente busca el elemento #container y le añade los handlers a él con los selectores correspondientes.

Por cierto, a la hora de escribir este artículo, delegate, ya obsoleto, también delega en on.

on

Finalmente, on es el camino a seguir con jQuery, al fin y al cabo, como habéis podido comprobar, tanto live como delegate usan on internamente.

Usar on para delegar eventos es muy similar a usar delegate, salvando que los eventos y los selectores están en orden inverso. El fiddle lo muestra en funcionamiento y el código completo es el siguiente:

¿Qué es lo que está pasando realmente?

Delegar un evento no hace que jQuery lo añada a los elementos correspondientes en cuanto se creen, sino que se añaden al elemento seleccionado ya existente y, por lo tanto, la propagación se puede parar en cualquier elemento que esté por debajo de él. De ahí que tengamos que pensar dónde delegar el evento ya que podemos cancelarlo sin darnos cuenta.

Por otro lado, tal y como podemos leer en la documentación de on en la sección Event performance, no debemos complicar mucho los selectores dentro de la delegación. Si nos vemos usando selectores jerárquicos, seguramente podamos delegar el evento en un elemento más cercano.

Delegación a mano

Para simular la delegación anterior cuyo selector era una simple clase, podemos hacerla usando el siguiente código:

En él simplemente comprobamos que el elemento que ha lanzado el evento (evt.target en el código) contiene la clase box. Puede parecer poco código extra, pero sin duda son dos líneas que nos ahorramos a cambio de un selector (el cual puede ser más complicado).

Un alto en el camino, eventos.

Hacemos un alto en el camino para aprender un poco más sobre los eventos. Serán tres entregas:

  1. Eventos, cómo funcionan. Características básicas.
  2. Eventos en jQuery. Veremos cómo usar on.
  3. Eventos en jQuery. ¿Qué es eso de trigger?

¿Qué es un evento?

Un evento puede representar varias cosas, pero normalmente es una respuesta a una acción del usuario en nuestra página. Por ejemplo, al pinchar en un elemento lanzamos el evento click. Si seleccionamos un campo, se lanza el evento focus. La lista completa la podéis encontrar la página de w3schools.

 El proceso

El proceso que sigue un evento es algo más elaborado de lo que nos podríamos imaginar en un primer momento. Cuando damos una función para que maneje (handle) un evento, hacemos que maneje el evento en un elemento determinado. Por ejemplo, en este fiddle tenemos tres cajas y, cada vez que pinchamos en una de ellas, nos muestra una alerta con el número de caja, distintas cajas, responden de distinta forma. El código no es importante, pues usaremos jQuery para manejar los eventos y no las apis del DOM, como en este caso. Además, para ahorrarnos código podríamos haber escrito una función que devolviese funciones, como en este fiddle.

Manejando un evento

Para manejar un evento contamos con dos importantes conceptos: el handler y el objeto event.

El handler no es más que la función que será invocada cuando se reciba el evento. Dicha función se ha de relacionar con un nodo del documento, y sólo será invocada si ese nodo en particular recibe el evento correspondiente. Esto es importante cuando veamos qué pasa cuando se dispara un evento.

El handler recibe un argumento con un objeto Event. Aunque dicho objeto es bastante importante, esperaremos a verlo al momento en el que veamos cómo manejar eventos con jQuery.

Sin embargo, el proceso no es tan simple como disparar y recibir un evento.

Every event is bubbling

Un evento es una respuesta a una acción. La acción crea y pone en movimiento un objeto Event. ¿Pero qué camino sigue este evento?  Veamos el siguiente fiddler. El código sigue sin ser importante, sólo que estoy añadiendo un handler (así llamaremos a las funciones que se ejecutan cuando sucede el evento) a todos los elementos li, estando estos anidados y con fondo de distinto color para saber en cuál de ellos estamos haciendo click. El handler lo único que hace es cambiar el texto dentro del li.

Una vez el evento se pone en marcha, sigue subiendo en el árbol del documento, yendo nodo padre a nodo padre. A este proceso lo llamaremos propagación o bubbling. Dicho proceso se puede comprobar haciendo click en los distintos colores de fondo en el anterior fiddler.

Podemos refinar un poco más esta idea con el siguiente fidller. En él, añadimos un nodo hermano al nivel 3. Si pulsamos en el nivel 3 o 4, vemos que el hermano no pulsado no recibe el evento.

Parando un evento

Podemos evitar que un evento siga propagándose o cancelar la acción predefinida de un evento.

Un evento se dice cancelable si su acción predefinida puede ser evitada. Por ejemplo, hacer click en un enlace tiene como acción predefinida el seguir dicho enlace. Al hacer click en un botón para enviar la información de un formulario, la acción predefinida consiste en evitar dicho envío. Estos eventos son cancelables y podemos evitar la acción por defecto llamando al método preventDefault en el objeto Event que se pasa al handler. Hay una forma alternativa de evitar la acción por defecto: devolviendo false desde el handler. Sin embargo, prefiero llamar explícitamente a los diferentes métodos puesto que el comportamiento difiere en jQuery.

Por otra parte, también podemos usar el método stopPropagation para evitar que el evento se propague, aunque esto no evitaría la acción por defecto.

Notas finales

Sé que esta entrega se ha retrasado, y seguramente reduzca el número de publicaciones a dos por semana: una al principio y otra al final.

Lo importante de esta entrega no es saber cómo añadir handlers o cómo parar la propagación de un evento. Todos eso lo veremos cuando hablemos de eventos en jQuery.

Aprendiendo jQuery con la web del Senado, I

Notas previas.

En primer lugar, agradecer a los desarrolladores que han trabajado duramente en funciones.js y en aquellos que no se han molestado en pasarlo por un minimizador de código.

Se recomienda leer antes el artículo de Javier Ramírez Los problemas del desarrollo web en España resumidos en senado.es.

El código está disponible en este gist.

Durante esta serie de artículos nos olvidaremos de que todas las funciones son declaradas en el ambiente global.

El problema del peso.

 

Antes de ponernos con jQuery, veamos un poco cómo está hecho el archivo. El archivo pesa unos 45KB (según me chiva ls -lh) y tiene unas 1313 líneas (esto, chivado por wc -l). Lo primero que intento es ver cómo está el código. Para ello, usamos alguna herramienta como jslint o jshint. En este último caso, si pegamos el código y pulsamos en Lint, nos devolverá una lista con 50 errores, añadiendo un último error indicando que ha escaneado el 23% del código y ha parado porque ha encontrado demasiados errores.

Una vez pasado por jsHint, decidi pasarlo por el compilador de Google closure, para minimizarlo un poco. Al fin y al cabo, un vistazo rápido permite ver la ingente cantidad de comentarios que hay en el archivo. Está bien que haya muchos comentarios, lo único que me preocupa es:

  1. Que los han puesto en producción (gracias, Rocío, por sustituir los días escritos a fuego).
  2. Que haya código comentado, también en producción.

De ahí que me preguntase cuánto se ahorraría minimizando de la forma más básica el código. Para ello, he usado el compilador de Google closure en dos versiones. En la primera, he usado los parámetros por defecto; y en la segunda, he pasado la opción --compilation_level ADVANCED_OPTIMIZATIONS. Echando mano otra vez de ls -lh, vemos que con los parámetros por defecto, el archivo resultante pesa 22KB, mientras que usando las optimizaciones avanzadas, el tamaño baja hasta 15KB. El archivo se queda, respectivamente, en un 50% y un 25% del tamaño original. Todo esto, realizado en cinco minutos, sin mayores ajustes de la herramienta. Cabe destacar que closure genera 1 y 6 warnings, todos ellos referidos al uso de la variable this, excepto uno, que se trata de un código que no se ejecutará nunca (ya veremos qué).

cargar_atr_destacados

La primera función que estudiaremos es, justamente la primera del archivo, la función cargar_atr_destacados. Dicha función se encuentra en este gist. Empecemos a tocar las pelotas aprender un poco.

En primer lugar, suelo preferir declarar las funciones de la forma var cargar_atr_destacados = function() {};, pero, mientras se entiendan las diferencias, me da un poco igual.

Dentro de la función, vemos que estamos ejecutando realmente el mismo código para $('#img_destacados1 img'), $('#img_destacados2 img'), $('#img_destacados3 img'), $('#img_destacados4 img') y $('#img_destacados5 img'). ¿Pesado de leer, ¿eh? ¡Pues imagínate lo pesado que debe ser de mantener! Por eso, antes de estudiar lo que hace, vamos a convertirlo, sin modificarlo mucho, en un bucle. El resultado es el siguiente

No está mucho mejor, pero al menos hemos reducido el número de líneas a casi un 25%.

Empecemos a ver qué hace el código. Todos esos signos de dólar ($) es como se llama comúnmente a la función de jQuery. Dicha función sirve para muchas (pero muchas de verdad, ¿eh?) cosas, entre ellas seleccionar elementos dado un selector de CSS. En este caso estamos seleccionando las etiquetas img dentro de los elementos cuyo id es img_destacados seguidos de un número del 1 al 5. Puesto que hay varias imágenes que cumplen dicha propiedad, el resultado es un array o vector de elementos.

Por otra parte, vemos muchas llamadas al método eq. Dicho método, como podemos observar en la documentación oficial, sirve para seleccionar un elemento específico dentro del array. Por ejemplo, eq(0) selecciona el primer elemento, eq(1) el segundo, etc. Ahora veamos porqué el uso del método anterior es innecesario.

¿Que lo geque y lo seque?

El método attr de jQuery se puede invocar de distintas formas. Si lo invocamos con un sólo argumento, como en la tercera línea del código anterior, tomará dicho parámetro como el nombre del atributo a devolver. Puesto que lo estamos llamando en una colección, podríamos pensar que nos devolverá un array de valores, pero en realidad nos devolverá sólo el valor del primer elemento. A esta forma de llamarlo lo llamaremos getter, porque nos devuelve un valor.

Por otra parte, podemos llamarlo como un setter. Para ello, le daremos dos valores, el nombre del atributo y el valor a tomar. Podríamos pensar que entonces sólo dará el valor pasado al primer objeto; al fin y al cabo, es el valor que nos devolvía. Sin embargo, dará el valor a todos los elementos de la colección. Hay una segunda y tercera forma de usarlo como setter, pero os podéis mirar la documentación para esos casos.

En general, todas las funciones de jQuery que puedan ser usadas como getter y como setter tienen el mismo comportamiento: si están siendo usadas como getter devuelven el valor del primer elemento de la colección; si están siendo usadas como setter, actualizarán el valor de todos los elementos de la colección. Teniendo en cuenta lo que hemos aprendido en el párrafo anterior, podemos refactorizar nuestro código para que quede así:

Si además sabemos que podemos pasar un objeto con todos los pares claves-valor que queremos modificar en una sola llamada, el código es:

Grabado a fuego.

Pero aún tenemos ese for ahí que no me gusta mucho, pues realmente estamos harcodeando o, como diría nuestra amiga Rocío, grabando a fuego, la cantidad de elementos a modificar. Para ello, nos fijamos en que cada grupo de imágenes está metido dentro de un div con clase mod_destacados diferentes entre sí. Además, un rápido vistazo nos permite ver que no hay más div.mod_destacados que grupos de imágenes, si participásemos en el grupo de desarrollo estaría bien que nos aseguráramos de esto o, mejor, añadir una clase estilo js-mod_destacado. Por lo tanto, lo que haremos será buscar todos los div con clase mod_destacados y dentro de cada uno de ellos modificar los attributos alt y title de las imágenes. El selector para los div es tan fácil como $('.mod_destacados'). Veamos el código completo y después lo explicamos:

Para entender este último código, sólo necesitamos aprender lo que hace la función each y la función find.

Llamaremos a la función each siempre sobre una colección jQuery y le pasaremos como parámetro una función. jQuery llamará dicha función, una vez por cada elemento de la colección, con el índice de dicho elemento dentro de la colección actual, y el elemento en sí. Además, dentro de la función parámetro, this se referirá al elemento correspondiente de la colección.

¿Os acordáis de que os comenté que la función $ valía para todo? Pues bien, en la línea 3 podéis ver que la llamamos con un único parámetro, this. Éste no es un selector por lo que no sabemos aún qué hace. Antes llamábamos a $ con un selector CSS y nos devolvía un objeto jQuery que envolvía a la colección; ahora que le pasamos un elemento del DOM, ¿qué devuelve? La respuesta es muy simple: nos devuelvo un objeto jQuery que envuelve a dicho elemento. De esta forma, podemos usar todos las funciones de jQuery en él. Por ejemplo, en este caso, usamos la función find.

La función find(selector) es muy similar a $ sólo que restringe la búsqueda a los nodos de la colección padre. Así $(this).find('img') busca todas las etiquetas imágenes dentro de $(this), es decir, entre sus nodos hijos.

De esta forma, sabiendo muy poquito de jQuery, hemos reducido un script de 37 líneas a sólo 7. Además, si aumentamos el número de imágenes, o lo reducimos, nuestro script seguirá funcionando.

Notas finales.

Como último comentario, decir que actualmente se favorece el uso de prop frente a attr en jQuery. El problema con la web del senado es que están usando jQuery 1.3.2

La versión de jQuery usada en senado.es es 1.3.2
La versión de jQuery usada en senado.es es 1.3.2

 

Un vistazo rápido a github nos permite comprobar que esa versión es de hace 4 años.

The One where I spent a morning because -0.5 turned to be parsed as 1/2

This summer, I have been doing some amazing stuff for GeoGebra. First of all, I would like to thank two people: Miguel, my mentor, who guided me through all that really advanced mathematical stuff; and Heinz Kredel, JAS creator and developer, who answered all my questions and implemented my suggestions as quickly as I could write them.

This story is about a problem we found in JAS. It was really subtle, so I may need a brief introduction to the project I worked on: LocusEqu. That project retrieves the equation of “any” geometrical locus. If you don’t know what a locus is, Wolfram Math World defines it as “the set of all points (usually forming a curve or surface) satisfying some condition” . LocusEqu generates a few EquationExpression, and then, by using a EquationTranslator, it generates a GenPolynomial<BigRational> object.

For translating doubles, it calls the BigRational constructor whose only argument is a String. This is the code:

Can you spot the bug? Look at the 21st line. If s is “-0.5”, then n is new BigInteger(“-0”), and that’s what makes “-0.5” be parsed as 1/2.

Status update for GeoGebra.

My contributions for the first part of the first part of GSoC 2010 can be divided in two: general improvements and coding.

General improvements

I made a few general improvements on GeoGebra:

  1. Refactored build.dir in ant build file. Previously, build.dir wasn’t in the root directory.
  2. A few ant tasks were added, such compile-grammar, compile-oe (outside Eclipse), run-easyb and run-easyb-outside-eclipse.
  3. SVN properties were set in order to work outside Eclipse. This way, .class files will be kept out of the repo without the intervention of any Eclipse plugin.
  4. Easyb, a BDD groovy-based framework, has been included in order to test GeoGebra. It is not RSpec, but I guess it’ll do.

Coding

First, I started creating a few EquationPoint classes,  currently there are six EquationPoint children classes:

EquationPoint type hierarchy

  • EquationFreePoint represents an independent point.
  • EquationSymbolicPoint represents a dependent point,  EquationSpecialSymbolicPoint standing only for the locus point.
  • EquationNormalPoint and EquationPointVectorPoint are only auxiliar elements.

Then, a few EquationElement classes were added, these stand for the different constructions:

EquationElement type hierarchy

EquationElement is an abstract class containing a few basic methods:

  • forPoint: Given an EquationPoint, returns a String with the equation that means that the point is in the construction.
  • isAlgebraic: returns true if the construction is algebraic, and false otherwise.

Both EquationGenericCircle and EquationGenericLine are abstractions of specific line and circle contructions, all of them algebraic. EquationGenericSegment is to segment what EquationGenericLine is to lines. Obviously, EquationGenericSegment is not algebraic.

All of these classes are used together by EquationScope.

A pause for a screenshot.

Screeenshot

Click for enlarge.

A glimpse into the future.

What to do next?:

  • Maybe Equation should be a proper class, not just a String.
  • More equations.
  • Working out the locus equation.
  • Not using an algorithm twice.

Writing your own JRuby extension. Part II: Creating your first class.

More Mormon Matryoshki
What’s the point of coding a JRuby extension if you don’t create classes? Well, I cannot think of any case, but if you find one, please, let me know.

Do you remember Java’s classes hierarchy? If so, you’ll realize  that Java objects inherit from java.lang.Object by default but that is not actually what we need. But how can we tell JRuby that our object inherits from Object, the king of Ruby world? The answer is so simple: just extend RubyObject! (There is a RubyBasicObject too) Taken from Nokogiri::XML::Node:

Of course, you can extend any other class, as long as it is a “Ruby object”. For example, Nokogiri::XML::Document extends from Nokogiri::XML::Node, and we do not need to do anything special to reflect it, just extend XmlNode like XmlDocument does:

After talking a bit about hierarchy, let’s talk about Java constructors. At least two parameters are needed: a org.jruby.Ruby object and a org.jruby.RubyClass object. Being the importance of the former quite obvious, the reason for the latter may not be so clear. Let me show you some real world code: Nokogiri::XML::Node’s dup method. Take a look at the following test:

Both new and dup methods in subclass rely on Nokogiri::XML::Node’s. In here, you can see the this snippet of code:

The rb_obj_class method returns the class of an object, in this case, self. This way, the new node will be an instance of the same class as the original node. That’s the reason a RubyClass is needed in the constructor, in order to know which is the actual class being instantiated.

By the way, do not forget to call super with the Ruby and RubyClass objects.

Next step: creating methods.

Creative Commons License photo credit: quinn.anya

Writing your own JRuby extension. First problem.

Denial
Maybe, when requiring your just created extension, you get a LoadError. If it is the first time you require it, it is quite likely that you have not followed JRuby requiring conventions. If you want to know how require works, you can find the best documentation ever about it in the comment before org.jruby.runtime.load.LoadService class.

Creative Commons License photo credit: cesarastudillo

Writing your own JRuby extension. Part I: BasicLibraryService.

Note: not code in this post, but you can see the code in Github. Follow the links!

Writing a JRuby extension is very easy, but there are almost not post out there about it. As far as I know, there is only one, Ola’s. It is a really good tutorial indeed, but it lacks some details that might be not-that-easy to solve. Please, take some time to read it and, if some details are different, do follow Ola’s way.

Everything’s ready now, so let’s start talking about BasicLibraryService. If you take a look at Nokogiri4J sourcecode, in ext/java/nokogiri folder, you will see a NokogiriService.java file. NokogiriService implements BasicLibraryService. This interface consists only of the method basicLoad which receives a Ruby object.

We will use this method to define classes and methods in the Ruby world. For defining a module, defineModule method is used with the name of the module. After that, modules and classes under that module can be defined easily by using the methods defineModuleUnder, which takes the name as parameter, and defineClassUnder, which takes the name and few parameters more. Let’s dive into it.

defineClassUnder needs three arguments. The first one is the class’ name. The second, is the parent class. If you have defined it previously, just passed it,  otherwise use RubyObject by calling the method getObject on the Ruby instance. The third parameter is an ObjectAllocator. ObjectAllocators returns intances of the classes in Java world. When instantiating Nokogiri::XML::Comment in Ruby world, JRuby will ask the ObjectAllocator for an instance of the Java class. It passes a Ruby object and the RubyClass being instantiated to the allocate method in the ObjectAllocator (more on RubyClass in following posts).

Finally, we will need to define some methods. Easiest way is by using the defineAnnotatedMethods. It takes a Java class as parameter. For knowing what this method does, you need to know a bit more about @JRubyMethod annotation (more on it in following post, have you realized the “Part I” in the title?). As you define methods, you may need to undefine some in a subclass. So easy! Use the undefineMethod method, which takes the name of the method as parameter (surprisingly, it undefines a method by redefining it!).

Next time, Implementing your first class.

If you are (un)happy with Ruby 1.8.7

There are two interesting topics in the Ruby Forum being discussed right now. Both opened by George Brown (the guy behind Prawn). They are:

I’ve read every single message because backwards compatibility in Ruby 1.8.7 is something that I do not fully understand. It all started with this comment in Jaime’s blog. There, Jaime wondered if Ubuntu did the right thing by updating Ruby to 1.8.7, even if that version breaks rails (in fact, it did). Well, in this case, my humble opinion was yes, Ubuntu did well by updating Ruby. But now, let’s consider other things.

So far, I’ve seen two kind of complains against Ruby 1.8.7:

  • Coding working in 1.8.6 that doesn’t work in 1.8.7. I’ve been talking James Coglan about it, and the one of the errors was that his code relied on the order of the keys in a hash. But, there are other he hasn’t been able to fix, and is something regarding regular expression. I am unhappy with this kind of “new features”.
  • Other people is complaining about working code in 1.8.7 that does not work in 1.8.6. I really understand them, because they program really cool gems and they have to test if they have used not-valid-1.8.6 code. I wouldn’t care too much about but we must keep in mind that 1.8.7 is a minor release.

Finally, I just want to say that, as many people has pointed out before me, it would be better to migrate to 1.9.1. Common! It has been already released! Anyway, that’s not the point of the discussion. It is all about a minor release with too many changes (I’m not talking about bugfixes). Do I like the new things? Yes, I do (except some that I cannot really understand). Am I happy with Ruby 1.8.7? No, I’m not because it is suppose to be a minor version release and it is making too much noise.