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.

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