Mostrando entradas con la etiqueta tutoriales. Mostrar todas las entradas
Mostrando entradas con la etiqueta tutoriales. Mostrar todas las entradas

martes, 15 de abril de 2014

CSS Float: Tutorial Visual (II)

En la primera parte de este tutorial vimos los conceptos básicos de la propiedad float, ejemplos de cómo se comportan los elementos flotantes y cómo afectan a su entorno en el documento. Vamos a continuar viendo, también a través de ejemplos, cómo podemos modificar ese comportamiento.

Clear: Para no estar al lado de un float

Como ya hemos visto,  un elemento flotante modifica el comportamiento de otros elementos de bloque, que suben para ocupar su sitio y su contenido se desplaza para dejarle hueco . Este comportamiento se puede evitar con la propiedad clear, que puede tomas los valores: left, right, both, none o inherit.

Clear se aplica a elementos de bloque no flotantes o a cualquier elemento flotante, forzándolos a desplazarse debajo de un float anterior. Los valores indican a que lado del elemento no permitimos que haya un float: a la derecha, a la izquierda o a ambos.

Clear se aplica a elementos de bloque no flotantes o a cualquier elemento flotante, forzándolos a mostrarse debajo de un float

Vamos a retomar algunos ejemplos y aplicar clear para ver cómo afecta al resultado.
En la siguiente imagen tenemos 3 bloques con dimensiones diferentes y con float:left:

Los tres bloques con float:left

Si no queremos que el bloque azul se quede donde está, podemos ponerle clear:left. Con esto estamos indicando que este bloque no puede tener ningún float a su izquierda. Si hubiera alguno debe bajar hasta colocarse debajo. Y esto es lo que hace:

div#azul {
clear: left;
}

Bloque azul clear:left


Si en vez de eso aplicamos clear:left sólo al naranja:

div#naranja {
clear: left;
}


Bloque naranja clear:left

El naranja baja, porque no puede tener a su izquierda ningún bloque flotante y el azul se coloca a su lado. Si al bloque naranja le ponemos clear:both significa que no puede tener ningún elemento flotante a su izquierda ni a su derecha, pero si lo aplicamos veremos que no ocurre nada. Este ejemplo nos sirve para resaltar que la propiedad clear obliga a un elemento a colocarse debajo de un float anterior. Como el bloque azul está declarado despues en el HTML, no cambia nada.
Para desplazar el bloque azul podemos añadirle también clear:left:

div#naranja {
clear: left;

div#azul {
clear: left;
}
Bloque naranja y bloque azul con clear:left

Cuando tratamos con elementos no flotantes, la propiedad se aplica sólo a bloques, los elementos de línea no se ven afectados. En este ejemplo tenemos tres imágenes, el pez azul es flotante a la izquierda:

Pez azul float:left

Podriamos pensar que si le ponemos a la mariquita clear:left bajará a la siguiente línea, pero no es así, al ponerlo no cambia nada porque es un elemento de línea y no es flotante. Sin embargo, si tenemos imágenes dentro de bloques, sí podemos utilizarlo. En el siguiente ejemplo teníamos el pez azul con float:left y las otras dos imágenes en un div con borde rojo. El div ha subido al ser flotante la imagen del pez azul y las dos imágenes se han desplazado para dejarle sitio.


Pez azul con float:left. Las otras dos imágenes en un bloque

Si ahora le ponemos al div con borde rojo clear:left:

pez float:left, div con imágenes clear:left
El bloque no puede tener un float a su izquierda por lo que se desplaza abajo.

La propiedad clear es muy útil también para controlar como se coloca el texto alrededor de una imagen. En el siguiente ejemplo tenemos una imagen flotante a la izquierda y dos parrafos que hemos marcado con borde rojo:

imagen con float:left

Si no queremos que el segundo párrafo empiece a la derecha de la imagen le ponemos clear:left:

Imagen float:left, segundo párrafo con clear:left

Todo el bloque del párrafo baja para colocarse debajo del elemento flotante.
Si tenemos varias imágenes podemos utilizar clear para controlar cómo se colocan. En el siguiente ejemplo tenemos tres imágenes y a continuación 2 párrafos con el borde marcado en rojo. Ninguno es flotante:



Como queremos que el texto quede alrededor de las imágenes, vamos a hacerlas flotantes a la derecha, las tres ( float:right ). El resultado es:

Ejemplo con las imágenes flotantes a la derecha

Si queremos que el corazón quede debajo del caballo, le ponemos clear: right:

Las tres imágenes float:right, Corazón clear:right

Como el corazón no puede tener ningún elemento flotante a su derecha, bajará para colocarse por debajo. El periódico, que es flotante a la derecha, como los otras imágenes, quedará al lado del corazón, y no del caballo porque los elementos flotantes se desplazan a derecha o izquierda en su línea. El corazón a bajado y, para el periódico, declarado en el HTML después del corazón, esta es ahora su linea.

Es muy importante tener en cuenta que para lograr la disposición que queramos tenemos que jugar con float/clear y con el orden en que declaramos los elementos en el HTML. Si queremos que el periódico quede al lado del caballo y el corazón debajo, tenemos que cambiar el orden de las imágenes en el HTML. Ponemos primero el caballo, después el periódico y después el corazón. Las tres imágenes con float:right y el corazón, ademas con clear:right. Nos quedaría:

Las tres imágenes float:right. Corazón clear:right


Igualmente, si queremos que el primer párrafo quede por encima de las imágenes, tendría que estar declarado antes.

Es muy importante tener en cuenta que para lograr la disposición que queramos tenemos que jugar con float/clear y con el orden en que declaramos los elementos en el HTML

Para que las tres imágenes queden en vertical tendríamos que poner clear:right a la segunda y la tercera:

Las tres imágenes con float:right. Periódico y corazón con clear:right

Y este es un buen punto para retomar la explicación de los elementos contenedores que se encogen sin tener en cuenta los floats…


El contenedor menguante


La razón por la que el bloque contenedor se va encogiendo es sencilla: para los elementos de bloque (como el div contenedor) los elementos con float no ocupan espacio en la página, por lo tanto no los tiene en cuenta. En alguno de los ejemplos que hemos visto, con todos los elementos flotantes, el bloque no contiene nada. La única razón por la que no desaparece completamente es que tiene un padding declarado de 10px.

Este comportamiento a veces no es lo que queremos (ya veremos como se evita) y puede parecer un bug, un error de diseño, pero no lo es. Es una decisión consciente de los desarrolladores del estándar CSS y tiene una razón de ser: el texto que fluye alrededor de las imágenes flotadas.

Para entenderlo nos sirve un ejemplo similar al del apartado anterior. El siguiente documento tiene dos párrafos, y el primero de ellos contiene también una imagen:

Imagen y texto, ninguno flotante

Si hacemos la imagen flotante a la izquierda, todo queda como debe, el texto se coloca alrededor de la imagen como en los medios impresos tradicionales:

Imagen con float:left

Cada párrafo es un elemento de bloque que ha subido para recolocarse como si el float no estuviera ahí. El contenido de los bloques, al ser elementos de línea que quedan al lado de un float, acortan su longitud para dejarle sitio. El pez azul está dentro del párrafo 1, pero éste no le tiene en cuenta y se ha encogido hasta encerrar sólo al texto. Si el contenedor no ignorara al elemento flotante el resultado sería este:


Soluciones al problema del contenedor

Vamos a partir de un ejemplo con una imagen y un bloque de texto, los dos dentro de un div contenedor (#wrapper) que hemos marcado con borde negro.
Ponemos la imagen flotante a la izquierda y el bloque de texto flotante a la derecha. El texto está dentro de un div al que hemos puesto borde rojo y al que hemos fijado una anchura de 365 pixels para que quepa al lado de la imagen. Estos serían los estilos aplicados a cada elemento:

div#wrapper {
   padding: 10px;
   width: 500px;
   border: 1px solid black;
}

img {
   float:left;
}

div#texto {
   float:right;
   border: 1px dotted red;
   width: 365px;
}

El resultado visual es:

imagen float:left, bloque de texto float:right

Como el texto y la imagen son flotantes, el contenedor no los tiene en cuenta y queda vacío. No desaparece por completo porque tiene un padding de 10px. Hay varias formas de solucionar esto, vamos a ir viéndolas una por una.

1.-Hacer el contenedor flotante

Si el contenedor es también flotante sí abarcará a los elementos flotantes dentro de él. Si le ponemos, por ejemplo, float:right :

Contenedor con float:right
Naturalmente esta solución no siempre puede aplicarse, porque muchas veces necesitamos que el contenedor no sea flotante. Si lo tenemos centrado en la página, por ejemplo, al ponerlo flotante se nos desplazará estropeándonos el diseño. Afortunadamente hay más soluciones.

2.- Poner un elemento con clear después de los flotantes

Esta solución es muy utilizada porque no tiene ‘efectos secundarios’. Si tenemos un elemento de bloque después de los flotantes es perfecto, le ponemos clear:both  y el contenedor, al tener que contener a este elemento, incluirá también a los que están por encima (los flotantes). Recuerda que clear se aplica a elementos de bloque.
Cuando no tenemos ningún elemento más, como en nuestro ejemplo, tenemos que incluir uno vacío, normalmente un div,  sólo para ponerle el clear y arreglar el contenedor. Debajo del bloque que contiene el texto, añadiríamos el siguiente:

  <div class="clear"></div>

Definimos clear como una clase porque podemos utilizarla sobre otros elementos si los tenemos disponibles:

.clear {
clear: both;
}

Este div sería invisible, en la imagen de abajo le hemos puesto unos pixels de padding y borde verde para que se vea:

Div vacío con clear:both

El bloque contenedor se ha tenido que extender incluyendo a los float. El único problema de esta solución es que a veces tenemos que incluir elementos vacíos, que no tienen un significado real (semántico) en la página.

Para evitar esto actualmente se utiliza un pseudo elemento:

.clearfix:after {
 content: "";
 visibility: hidden;
 display: block;
 clear: both;
 height: 0;
 }

Esta clase se podría aplicar sobre el último elemento real, en este caso el párrafo, de forma que se aplica el clear sobre el pseudo elemento 'after' posterior.

3.- Utilizar la propiedad CSS  overflow

Si ponemos overflow:hidden o overflow:auto en el elemento padre (el contenedor en este caso) se expandirá siempre para abarcar a los elementos flotantes. Esta solución también es muy utilizada, sólo hay que tener en cuenta que esta propiedad implica también algo más, para saber si podemos aceptarlo:
overflow: hidden – Significa que el contenido que exceda de las dimensiones del div (si las tiene) se oculta, no habrá barra de desplazamiento ni se verán por fuera del div.
overflow: auto – Significa que si el contenido excede las dimensiones del div, aparecerán unas barras de desplazamiento para hacer scroll.
Algunos navegadores (IE6 por ejemplo) necesitan que el contenedor tenga fijada una anchura o altura para que la solución funcione bien.





sábado, 11 de enero de 2014

Tutorial de @font-face

La regla @font-face de CSS nos permite incluir en una página web tipografías diferentes de las instaladas en el PC del usuario. Es decir, nos da la libertad de incluir cualquier fuente en nuestro diseño y enviarsela al usuario como parte de la página web que está viendo.

Uso básico

Si quisieramos utilizar la fuente "shattered.ttf" que tenemos en nuestro servidor, podríamos incluirla de la siguiente forma:

@font-face {
    font-family: myFontName;    //aqui podemos usar cualquier nombre
    src: url('fonts/shattered.ttf') format("truetype");
}

Este es el uso más básico. Incluimos la fuente sólo en un formato y le damos un nombre para utilizarla en nuestra applicación. El nombre que ponemos en font-family puede ser cualquiera, no tiene que coincidir con el nombre real de la fuente. En este caso, cuando queramos usarla, la referenciamos como "myFontName".

La utilizaremos de la siguiente forma:

h1 {
    font-family: myFontName, Georgia, "Times New Roman", serif;
}

@font-face está soportado en todos los navegadores modernos (e incluso en IE6), pero no todos soportan el mismo formato de fuentes. Si queremos que nuestra letra se muestre en todos los navegadores, estamos obligados a tener los ficheros en varios formatos distintos.

Incluir varios formatos de fuente

Las incompatibilidades entre navegadores nos impiden utilizar una regla tan sencilla como la que hemos visto. Por un lado, IE<9 sólo admite el formato propio EOT, que no está soportado por ningún otro navegador. Algunos navegadores de móviles sólo admiten formato SVG, y luego tenemos los formatos TTF, OTF y WOFF. En el siguiente cuadro vemos los formatos diferentes que podemos incluir y el soporte que tienen:

@font-face file types browser support
  WOFF OTF TTF SVG EOT
IE 9 9 9 no 4
Firefox 3.6 3.5 3.5 no no
Opera 11.1 10 10 10 no
Opera Mobile 11.0 9.7 9.7 9.7 no
Safari 5.1 3.1 3.1 3.1 no
Chrome 6 4* 4* 0.3 no
Safari on iOS 5 no 4.2 3.1 no

Fuente: Stunning CSS3

Afortunadamente podemos utilizar el excelente servicio gratuito Font Squirrel para convertir nuestra letra en diferentes formatos e incluso generar el css necesario para utilizarlos.

Podemos incluir todos los formatos necesarios con la siguiente declaración, conocida como Fontspring Syntax o New Bulletproof Syntax:

@font-face {
    font-family: 'MyFontName';
    src: url('fonts/shattered.eot?#iefix') format('embedded-opentype'),
         url('fonts/shattered.woff') format('woff'),
         url('fonts/shattered.ttf')  format('truetype'),
         url('fonts/shattered.svg#svgFontName') format('svg');
}

La clave de esta declaración es un pequeño hack: incluir la interrogación despues del fichero EOT 'shattered.eot?#iefix'. La razón es que IE<9 no entiende múltiples urls separadas por comas, ni tampoco la indicación de formato format(). Considera que todo lo que aparece despues del paréntesis de apertura, hasta el último, es una URL.

Al incluir la interrogación, conseguimos que el IE entienda que la url es fonts/shattered.eot y considere el resto como parámetros de url que serán ignorados en el servidor. Es muy importante que el fichero EOT aparezca el primero.

Si queremos que IE>9 utilice el formato WOFF en vez de EOT, podemos hacer una pequeña variación cambiando el format del EOT a un valor incorrecto (el correcto es 'embedded-opentype'). De esta forma lo descartará y utilizará el siguiente que reconozca:

@font-face {
    font-family: 'MyFontName';
    src: url('fonts/shattered.eot?#iefix') format('eot'),
         url('fonts/shattered.woff') format('woff'),
         url('fonts/shattered.ttf')  format('truetype'),
         url('fonts/shattered.svg#svgFontName') format('svg');
}

Esta es la declaración más compacta, es compatible con todos los navegadores pero puede presentar algún problema con el IE9 cuando se utiliza en modo compatibilidad con IE7-8, como se explica aqui. Si nos interesa cubrir también esas opciones, podemos duplicar la src, incluyendo el fichero .eot de dos formas diferentes:

@font-face {
    font-family: 'MyFontName';
    src: url('fonts/shattered.eot'); /* IE9 Compat Modes */
    src: url('fonts/shattered.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
         url('fonts/shattered.woff') format('woff'), /* Modern Browsers */
         url('fonts/shattered.ttf')  format('truetype'), /* Safari, Android, iOS */
         url('fonts/shattered.svg#svgFontName') format('svg'); /* Legacy iOS */
    }

Cualquier navegador diferente del IE ignorará los ficheros .eot y saltará a la siguiente declaración hasta encontrar un formato que pueda usar.

Esta es una sintaxis universal, compatible con todos los navegadores en todos los casos.

Descartando fuentes locales con: local('☺')

Podemos encontrar también declaraciones incluyendo local('☺') con un smiley o cualquier otro caracter extraño:

@font-face {
    font-family: 'MyFontName';
    src: url('fonts/shattered.eot');
    src: local('☺'),
         url('fonts/shattered.woff') format('woff'),
         url('fonts/shattered.ttf')  format('truetype'),
         url('fonts/shattered.svg#svgFontName') format('svg');
}

El src: local() normalmente sirve para especificar una tipografía local (en el ordenador del usuario) que puede utilizarse sin tener que descargar ningún fichero. En este caso se incluye con un carácter especial (smiley) sólo para evitar que el navegador utilice una fuente local con el mismo nombre que la nuestra, si existe.

El problema con esto es que Android 2.2-2.3, aunque tienen soporte para @font-face, no soporta local(), con lo que nuestra declaración no funcionará para esos sistemas.

Definiendo cursiva, negrita, etc

La fuente que estamos utilizando puede tener versiones cursiva, negrita, etc:

shattered-regular.ttf
shattered-bold.ttf
shattered-italic.ttf

Para incluirlas primero tenemos que convertir todos los ficheros a los formatos necesarios. Despues tenemos dos posibilidades: incluirlas con nombre distinto (MyFontName, MyFontName-italic, etc) o incluirlas con el mismo nombre para utilizarlas con font-weight o font-style (Style Linking):

font-weight: bold;
o
font-style: italic;

La segunda solución es mejor, pero debemos tener en cuenta que no funciona en IE.

En cualquiera de los dos casos necesitaremos una declaración @font-face diferente para cada tipo.

Si las declaramos con diferente nombre:

/* Regular */
@font-face {
    font-family: 'MyFontName';
    src: url('fonts/shattered-regular.eot?#iefix') format('embedded-opentype'),
         url('fonts/shattered-regular.woff') format('woff'),
         url('fonts/shattered-regular.ttf')  format('truetype'),
         url('fonts/shattered-regular.svg#svgFontName') format('svg');
}

/* Bold */
@font-face {
    font-family: 'MyFontName-bold';
    src: url('fonts/shattered-bold.eot?#iefix') format('embedded-opentype'),
         url('fonts/shattered-bold.woff') format('woff'),
         url('fonts/shattered-bold.ttf')  format('truetype'),
         url('fonts/shattered-bold.svg#svgFontName') format('svg');
}

/* Italic */
@font-face {
    font-family: 'MyFontName-italic';
    src: url('fonts/shattered-italic.eot?#iefix') format('embedded-opentype'),
         url('fonts/shattered-italic.woff') format('woff'),
         url('fonts/shattered-italic.ttf')  format('truetype'),
         url('fonts/shattered-italic.svg#svgFontName') format('svg');
}

Tendríamos que utilizarla de la siguiente forma:

body { font-family: MyFontName, Georgia, serif; }
h1 { font-family: 'MyFontName-bold', Georgia, serif; }
em { font-family: 'MyFontName-italic', Georgia, serif; }

Si las declaramos con el mismo nombre, utilizando Style Linking:

/* Regular */
@font-face {
    font-family: 'MyFontName';
    font-style: normal;
    font-weight: normal;
    src: url('fonts/shattered-regular.eot?#iefix') format('embedded-opentype'),
         url('fonts/shattered-regular.woff') format('woff'),
         url('fonts/shattered-regular.ttf')  format('truetype'),
         url('fonts/shattered-regular.svg#svgFontName') format('svg');
}

/* Bold */
@font-face {
    font-family: 'MyFontName';
    font-style: normal;
    font-weight: bold;
    src: url('fonts/shattered-bold.eot?#iefix') format('embedded-opentype'),
         url('fonts/shattered-bold.woff') format('woff'),
         url('fonts/shattered-bold.ttf')  format('truetype'),
         url('fonts/shattered-bold.svg#svgFontName') format('svg');
}

/* Italic */
@font-face {
    font-family: 'MyFontName';
    font-style: italic;
    font-weight: normal;
    src: url('fonts/shattered-italic.eot?#iefix') format('embedded-opentype'),
         url('fonts/shattered-italic.woff') format('woff'),
         url('fonts/shattered-italic.ttf')  format('truetype'),
         url('fonts/shattered-italic.svg#svgFontName') format('svg');
}

NOTA: La fuente regular tiene que estar la primera.

Definiéndolo de esta manera, podemos utilizar la fuente con el mismo nombre y definirla como negrita o italica con font-weight o font-style:

body { font-family: shattered, Georgia, serif; }
h1 { font-weight: bold; }
em { font-style: italic; }

Fuentes:
Stunning CSS3: @font-face File Types Browser Support
Paul Irish: Bulletproof @font-face Syntax
Fontspring: The New Bulletproof @Font-Face Syntax
CSS Tricks: Using @font-face
Ksesocss: @Font-face y sus problemas. Guía de uso y solución de problemas
Especificación del W3C

miércoles, 5 de junio de 2013

Tutorial de expresiones regulares en JavaScript

¿Qué son las expresiones regulares?

Una expresión regular es una forma de definir una 'plantilla', con una sintaxis propia, para localizar un patrón dentro de un texto. El ejemplo más sencillo sería definir una palabra como plantilla:

Expresión regular: /casa/

Encontraría la palabra casa en el texto:

La casa de la montaña.

La utilidad real se muestra cuando utilizamos caracteres especiales para crear patrones de busqueda mucho más complejos, del tipo:

Expresión regular: /\W*\s(\S)*$/

Parece mucho más complicado de lo que es en realidad. Sólo hay que aprender unas reglas básicas y unos cuantos caracteres especiales.

JavaScript tiene dos notaciones

En JavaScript hay dos formas de crear una expresión regular: mediante la notación literal o utilizando el constructor RegExp():


//notación literal
var myRegExp = /casa/g; /* /reg. expression/flags(g|i|m) */

//Usando el constructor RegExp
var myRegExp = new RegExp('casa','g'); /* RegExp('expression','flags(g|i|m)'); */

Las dos son equivalentes. La primera se compila cuando el script se carga y es más rápida. La segunda se compila en tiempo de ejecución por lo que es la única opción si la expresión contiene variables que tienen que ser interpoladas. En este tutorial utilizaremos la notación literal por simplicidad. Todas las explicaciones son válidas también para el constructor.

Para entender mejor el funcionamiento de las expresiones regulares conviene pensar en una busqueda caracter a caracter. Si tenemos el patrón 'casa', lo que estamos buscando no es la palabra casa, sino una 'c' seguida de una 'a' seguida de una 's', etc. Es mucho más facil entender el uso de los caracteres especiales de esta manera.

Por ejemplo, si sabemos que \s representa un espacio en blanco y que ? significa que el caracter anterior es opcional, ¿ que estamos seleccionando en la siguiente expresión regular?:

var myRegex = /\sglobos?/;

Estamos buscando un espacio en blanco, seguido de una 'g', de una 'l', de una 'o', de una 'b', de una 'o' y de una 's' que es opcional, puede aparecer o no. Por tanto seleccionaremos:

Un globo, dos globos, tres Globos.

Podemos ver que sólo ha seleccionado el primer 'globo'. Por defecto la búsqueda termina cuando se encuentra la primera coincidencia. Si queremos que siga buscando tenemos que utilizar el flag g.


Flags (g, i, m)

Los flags modifican el comportamiento por defecto de la búsqueda. Aparecen justo al lado de la barra que marca el final de la expresión regular y pueden ser:

  • g (global). Se busca siempre en el texto completo en vez de detenerse cuando encuentra la primera coincidencia con el patrón.
  • i (ignora Mayusculas). No diferencia mayúsculas y minúsculas.
  • m (multilínea). El texto incluye saltos de línea. ^ y $ se aplican a 'comienzo de línea' y 'final de línea', en vez de 'comienzo de texto' y 'final de texto'.

Si en la cadena de nuestro ejemplo queremos seleccionar también otras ocurrencias de la palabra 'globo', tenemos que utilizar:

var myRegex = /globo/g;
Un globo, dos globos, tres Globos.

La expresión encontraría dos coincidencias. El tercer 'Globo' no coincide con el patrón porque la G es mayúscula. Si añadimos el modificador i sí se seleccionará:

var myRegex = /globo/gi;
Un globo, dos globos, tres Globos.

La segunda y tercera aparición están en plural. Sería bueno que nuestra expresión regular capturara tanto globo como globos. Como ya vimos antes, podemos utilizar la ? para indicar que el carácter anterior es opcional:

var myRegex = /globos?/gi;
Un globo, dos globos, tres Globos.

?, + , *   Cuantificadores sobre el carácter anterior

Con estos tres caracteres extraños podemos indicar que el carácter inmediatamente anterior puede ser opcional, o que tiene que aparecer como mínimo una vez o que puede aparecer muchas veces.

?El carácter anterior es opcional. Aparece 0 ó 1 veces.
+El carácter anterior aparece una vez o más.
*El carácter anterior puede no aparecer, aparecer una vez o aparecer repetido muchas veces (aparece 0 o más veces).

Si tenemos el texto:

"Tengo un gloooooobo grande y tres globos pequeños."

¿Como podemos hacer para capturar los dos globos?. La expresión anterior no nos sirve porque la primera aparición quedaría fuera.

Necesitamos una expresión regular que nos seleccione cualquier patrón con una 'g' seguida de una 'l', seguida de una o más 'o', una 'b', una 'l', una 'o' y una 's' opcional.

var myRegex = /glo+bos?/gi;
Tengo un gloooooobo grande y tres globos pequeños.

En este caso, si ponemos * en vez de + también funcionaría. La diferencia es que el asterisco permite que el carácter no aparezca ( cero o más veces). Por lo tanto también seleccionaría glbo. Con + obligamos a que el carácter aparezca al menos una vez.

Para seleccionar todas las palabras en el siguiente texto:

"bo, boooo, b."

sólo nos sirve el *:

var myRegex = /bo*/g;
bo, boooo, b.

'.' Un comodín para un solo car.cter

El punto '.' es un comodín que puede sustituir a cualquier carácter excepto a un salto de línea.

La siguiente expresión:

var myRegex = /gat./gi;

capturará 'gat' seguido de cualquier carácter:

Gato, gata, gamo, gatuno, gatitos, gatoooooos, gatk, gallego, gat , gat.

En las dos últimas coincidencias podemos ver que también captura el espacio en blanco y el punto final.

{n,m} Cuantificadores más concretos

Con {n,m} podemos indicar exactamente el número de veces que aparece el carácter anterior o delimitarlo en un rango concreto ( por ejemplo que aparezca entre 3 y 5 veces).

{n}El carácter anterior aparece exactamente *n* veces.
{n,}El carácter anterior aparece *n* o más veces.
{n,m}El carácter anterior aparece un mínimo de *n* y un máximo de *m* veces

var myRegex = /r{2}/gi;
b, brr, brrr, brrrr.

En el último caso tenemos brrrr. Se seleccionan las dos primeras y despues las dos segundas.

var myRegex = /r{2,}/gi;
b, brr, brrr, brrrr.
var myRegex = /r{2,3}/gi;
b, brr, brrr, brrrr.

[abc] Conjuntos de caracteres

Para indicar que en un lugar de nuestra expresión puede aparecer cualquier de los caracteres de un conjunto concreto podemos utilizar [ ]. Por ejemplo, si queremos seleccionar 'sal', 'cal' y 'mal', podemos indicar que el primer caracter puede ser [scm]. Es decir, puede ser uno de los caracteres del conjunto:

var myRegex = /[scm]al/gi;
tal, sal, perro, mal, sota, cal, tal, salir, ramal.

También podemos negar el conjunto con el carácter ^, indicando que puede aparecer cualquier carácter menos los que están en el conjunto.

var myRegex = /[^scm]al/gi;
tal, sal, perro, mal, sota, cal, tal, salir, ramal.

Se puede especificar un rango mediante un guión: [0-7] indica números del 0 al 7, [a-z] indica letras de la 'a' a la 'z'

'^' y '$' Principio y final de línea

Muchas veces es necesario seleccionar una cadena sólamente si está al principio o al final de un texto. Por ejemplo, es muy común eliminar los espacios en blanco que puedan aparecer al principio o al final.

Sabemos que el símbolo \s selecciona un espacio en blanco. Con la siguiente expresión seleccionariamos todos los espacios al principio de un texto:

var myRegex = /^\s+/g;

Estamos buscando un 'principio de texto' seguido de uno ó mas espacios en blanco:

   Este es mi texto.

Si queremos seleccionar los del final:

var myRegex = /\s+$/g;

Estamos buscando uno ó mas espacios en blanco justo antes de un final de texto:

Este es mi texto.   

En el apartado de flags vimos que existe uno para modificar el comportamiento de estos dos carácteres. El flag 'm', cuando está presente indica que el texto es multilínea y queremos que ^ y $ seleccionen principio y final de cada línea, en vez de principio y final de texto.

\s, \d, \D, \w, \W .... Símbolos para hacer la vida más facil

Algunos conjuntos de caracteres son de uso tan común que se han creado unos símbolos para incluirlos más fácilmente. Por ejemplo, para indicar que un carácter debe ser un número podemos escribir [0-9] o podemos utilizar \d (digit) que es mucho más sencillo. Cuando el símbolo aparece en mayúscula significa exactamente lo contrario. \D sería cualquier carácter que NO sea un número.

Los más utilizados son:

\sCualquier tipo de espacio en blanco ( espacio, tabulador, salto de línea, etc)
\STodo carácter que NO sea un espacio en blanco
\tTabulador
\wCualquier carácter alfanumérico. Equivalente a [a-zA-Z0-9_]
\WCualquier carácter NO alfanumérico
\dUn dígito. Equivale a [0-9]
\DCualquier carácter que NO sea una dígito
\b'Word Boundary'. Marca el inicio o el fin de una palabra ( similar a '^' y '$' para una línea)

Como ejemplo podemos escribir una expresión para comprobar un nombre de usuario. Suponemos que el nombre de usuario no puede contener espacios y sólo puede contener números y letras (incluimos tambien el guión bajo o subrayado). Tiene que tener un mínimo de 6 carácteres y un máximo de 12:

var myRegex = /^\w{6,12}$/g;

Sólo seleccionará un nombre de usuario cuando cumpla las condiciones. En caso contrario no seleccionará nada. Puede utilizarse en una condición de JavaScript para aceptarlo o rechazarlo.

'|' OR

La barra vertical nos permite definir varias expresiones diferentes para buscar una o otra:

var myRegex = /perro|gato/gi;
perro, loro, gato, hamster, serpiente.

Se busca la coincidencia con la expresión de la de la derecha o de la de la izquierda completas. Si queremos que el OR sólo afecte a una parte de la expresión, tenemos que encerrar esa parte entre paréntesis. Por ejemplo, si queremos buscar 'elefante' y 'elemento'

var myRegex = /ele(mento|fante)/gi;
elegante, elemento, testamento, elefante, chanante, elemental.

Tambien podemos definir más de dos opciones:

var myRegex = /ele(mento|fante|gante)/gi;
elegante, elemento, testamento, elefante, chanante, elemental.

() Creando subexpresiones en nuestra regex

Los paréntesis se utilizan para marcar una parte de la expresión sobre la que actúa algún operador. En el ejemplo anterior vimos que podemos delimitar el rango sobre el que actuaba OR para que no se aplicara a la expresión completa. También podemos extender el rango sobre el que actuan los cuantificadores.

var myRegex = /1(23)+/gi;

En la expresión anterior el '+' se aplica al grupo completo '23'.

123, 1232323, 1232222, 123333333, 12323232323.

Sin los paréntesis los cuantificadores se aplican sólo al carácter inmediatamente anterior:

var myRegex = /123+/gi;
123, 1232323, 1232222, 123333333, 12323232323.

Los paréntesis tienen también otra función: memorizan todas las coincidencias que se encuentren en el texto y pueden utilizarse despues como $1, $2, $3, etc. Sólo se memoriza y guarda (en $n) la parte de la expresión marcada con los paréntesis. Por ejemplo, en una expresión que seleccione fechas, podemos encerrar entre parentesis la parte que coge el dia y el mes y luego cambirlo de orden colocando $2-$1.

Podemos utilizar ?: para evitar que se memorize el contenido:

var myRegex = /1(?:23)+/gi;

Esto es todo por ahora. En otro tutorial veremos las funciones que tenemos disponibles para utilizar las expresiones regulares de varias formas en JavaScript.

Fuentes: MDN Regular Expressions

Regular expression enlightenment
Regexpal (regex tester)

miércoles, 6 de marzo de 2013

Tutorial: como hacer tooltips con CSS

Un tooltip es esa ventanita de información que aparece cuando colocamos el ratón sobre algún elemento, como podemos ver en esta imagen de un botón de Gmail:


Se puede implementar de muchas maneras, aquí vamos a ver las 2 opciones más sencillas: con HTML y con CSS. Dejaremos para otra entrada la implementación con JavaScript.

HTML, tooltips básicos con el atributo title


En HTML el atributo title puede utilizarse para mostrar un tooltip básico. Es válido para casi todos los elementos ( en HTML5 ya se puede incluir en todos ):
<button title="This is a tooltip" type="button">Press</button>
El problema es que no admite ningún tipo de personalización. Sólo puede mostrarse texto simple, no podemos formatearlo mediante etiquetas HTML ni poner saltos de línea donde queramos.

Elementos de un tooltip

Los conceptos para crear un tooltip propio son sencillos, básicamente el mensaje no es más que un elemento HTML, normalmente un span o un div, con un texto informativo dentro.

<div class="tooltip">
This is a tooltip
</div>
Despues, mediante estilos CSS, le damos el aspecto que queramos y lo posicionamos donde corresponda con posicionamiento absoluto para que no desplace a otros elementos de la página al mostrarse:

.tooltip {
  position: absolute;
  padding: 10px;
  background-color: gray;
  color: white;
  top: 100%;
  left: 95%;
}

Jugando con los estilos podemos crear ejemplos espectaculares como estos, sacados del post [CSS3 tooltips de Catalin Rosu] (http://www.red-team-design.com/css3-tooltips):


Tooltips usando sólo CSS. El método tradicional

Para crear el mensaje utilizando sólo CSS vamos a incluir el texto de información en un span que inicialmente estará oculto ( display: none). Para que se muestre cuando colocamos el ratón encima (hover) definiremos un estilo diferente para la pseudo-clase :hover con display: block.
<button>Press
    <span class="tooltip">This is a tooltip</span>
</button>
El siguiente paso es definir el CSS para darle el aspecto que queramos, colocarlo en el lugar donde debe mostrarse y definirlo como oculto por defecto y visible sólo en el :hover .
button {
  position: relative;
}

.tooltip {
  position: absolute;
  display: none;
  border: 1px solid black;
  padding: 3px;
  background-color: gray;
  color: white;
  width: 100px;
  left: 100%;
  top: 100%;
}

button:hover .tooltip {
    display:block;
}

Nos quedaría:



Para que el mensaje aparezca siempre en la esquina inferior izquierda del elemento, hemos definido la posición como:

position: absolute;
top: 100%;
left: 100%;

El problema de este método es que el HTML queda confuso y poco semántico porque el span del mensaje se incluye dentro del elemento. Si queremos ponerlo fuera tenemos que "envolverlos" en un div o span extra de forma que sea este elemento el que muestra el tooltip al hacer hover con el ratón y nos sirva como marco para que aparezca posicionado con respecto al elemento.

//HTML
<div class="tooltipWrapper">
<button>Press</button>
<span class="tooltip">This is a tooltip</span>
</div>
//CSS

.tooltipWrapper {
  display: inline-block;
  position: relative;
}

.tooltip {
  position: absolute;
  display: none;
  border: 1px solid black;
  padding: 5px;
  background-color: gray;
  color: white;
  width: 100px;
  left: 50%;
  top: 100%;
}

.tooltipWrapper:hover .tooltip {
    display:block;
}

El display: inline-block se pone para que el div se ajuste al tamaño del contenido. Si no lo ponemos tendría todo el ancho del navegador. En la siguiente imagen se puede ver cómo quedaría. El div.tooltipWrapper está marcado con un borde rojo para verlo:



En esta ocasión hemos puesto left: 50% para que aparezca más centrado.
Si queremos mostrar el mensaje sobre una imagen necesitamos este div envolvente ( wrapper ) porque la etiqueta <img> no encierra contenido:

<div class="tooltipWrapper">
<img src="myImage.jpg" />
    <span class=".tooltip">This is a tooltip</span>
</div>
Con el mismo CSS del ejemplo anterior obtenemos:



Jugando con los estilos podemos darle el aspecto que queramos, incluso incluir transiciones o animaciones para para que se muestre más lentamente o vaya variando la opacidad ( en los navegadores que lo soporten ). En el siguiente ejemplo utilizamos el pseudo elemento :before para crear el pequeño triángulo que apunta al elemento y hacerlo similar al de Google que mostramos en la primera imagen:
//HTML
This is a tooltip
//CSS .tooltipWrapper { display: inline-block; position: relative; } .tooltip { position: absolute; display: none; border: 1px solid black; padding: 5px; background-color: gray; color: white; width: 100px; left: -50%; bottom: -37px; } .tooltip:before{ content: ' '; display: block; position: absolute; left: 45px; top: -8px; width: 14px; height: 14px; border-color: black; border-width: 1px; border-style: solid none none solid; background-color: gray; transform: rotate(45deg); -webkit-transform: rotate(45deg); -ms-transform: rotate(45deg); -moz-transform: rotate(45deg); } .tooltipWrapper:hover .tooltip { display:block; }
El resultado es:


Limitaciones del método tradicional

Como hemos visto no es muy semántico porque tenemos un elemento extraño dentro del contenido del botón (el span) o un elemento extra que envuelva el elemento y el tooltip. Podemos solucionarlo utilizando CSS3 para coger el valor de un atributo HTML y colocarlo como contenido de un elemento.

Tooltips con CSS3 utilizando un atributo de HTML para el contenido

Este método es mucho más semántico porque no necesitamos el span para el contenido del tooltip ni el div extra que lo envuelve. Utilizamos un atributo personalizado de HTML del tipo data- para el mensaje y el pseudo-elemento ::before para construir el tooltip:
//HTML
<button class="tooltip2" data-tooltip="this is the tooltip!">
    Press
</button>    

//CSS
.tooltip2 {
  position: relative;
}

.tooltip2::before {
    position:absolute;
    display: none;
    content: attr(data-tooltip) ;
    border: 1px solid black;
    padding: 3px;
    background-color:gray;
    color: white;
    white-space:nowrap;
    left: 50%;
    top: 100%;
 }

.tooltip2:hover::before {
    display: block;

 }




El texto del tooltip se coge del atributo data-tooltip mediante la línea CSS3:
content: attr(data-tooltip) ;
Podriamos utilizar title como vimos al principio, pero aparecería nuestro mensaje y el del navegador al mismo tiempo.
Recordemos que tenemos aún un pseudo-elemento extra disponible ( ::after ) por si queremos añadir también el pequeño triángulo que apunta al elemento.

El inconveniente de tener el texto en el atributo es que deben ser textos sencillos. No podemos crear textos complicados  con formato o imágenes.

Fuentes:
W3 Org title definiton
Simple cross-browser tooltip with CSS
Tooltips courtesy of HTML5 data attributes

domingo, 2 de diciembre de 2012

Memoization en JavaScript

Se trata de una técnica que mejora el rendimiento de una función utilizando una caché para guardar los resultados de invocaciones previas. De esta forma, cuando se vuelve a llamar a la función con el mismo parámetro, se devuelve el resultado de la caché, sin necesidad de recalcular de nuevo.

En realidad estamos mejorando el tiempo de respuesta de la funcion a cambio de ocupar más espacio de memoria. Los resultados se van guardando en un tabla hash (un objeto) indexados por el parámetro o parámetros con los que se ha producido ese resultado.

Podemos limitar el tamaño del objeto de resultados a un máximo e incluir algun algoritmo de sustitución de entradas. En algunos casos puede ser conveniente tener una tabla de resultados fija, inicializada de antemano, con los argumentos que sabemos que se van a dar en una amplia mayoría de las llamadas.

Ejemplo. Factorial de un número

Como sabemos el factorial se define por:

0! = 1
n! = n * (n-1)!

Podemos hacerlo con una función recursiva muy sencilla:


function factorial (n) {
  if (n <= 0) return 1;
  return n * factorial(n-1);
}

Este es un ejemplo de función que se beneficiaría enormemente si utilizaramos memoization para cachear resultados de operaciones anteriores.

Si hacemos factorial(3)la función tendrá que hacer el calculo, pero si luego hacemos factorial(5) de nuevo tendra que calcular ( recursivamente ) el factorial de 4, de 3, de 2 y de 1. Como el factorial de 3 ya lo hemos calculado antes, podriamos aprovecharlo ahorrando procesamiento.

Para aprovechar los calculos anteriores introducimos una caché local que almacenará los resultados.


var cache = {};

function factorial (n) {
    
    if (n <= 0) return 1;
    
    if ( cache[n] ) {
        return cache[n];
    }
    else {  
        cache[n] =  n * factorial(n-1);
        return n * factorial(n-1);
    }   
}

Hemos utilizado una cache externa porque debe permanecer cuando la función haya terminado. Podemos aprovechar que en JavaScript las funciones son también objetos para definir la caché como una propiedad de la función:


function factorial (n) {

    //creamos nuestra cache solamente si no está ya definida
    if ( !factorial.cache ) factorial.cache = {};
    
    if (n <= 0) return 1;
  
    if (factorial.cache[n]) {
        return factorial.cache[n];
    }
    else {
        factorial.cache[n] =  n * factorial(n-1);
        return factorial.cache[n];
  }     
}

Cuando la función tiene varios parámetros tenemos que combinarlos de alguna forma para formar un indice único y válido en la tabla de resultados.

En proyectos grandes, con muchos métodos apropiadas para aplicarles esta técnica, es util crear una función memoizadora a la que pasamos nuestra función y nos devuelve una nueva, igual que la original, pero con memoization.

Fuentes:
Implementing memoization in JavaScript
JavaScript Memoization
Speed-up your JavaScript
Memoization. Una primera mirada

domingo, 4 de noviembre de 2012

Hacer un mosaico/galería con HTML y CSS

Un mosaico o galería de imágenes es un elemento básico que utilizado en multitud de aplicaciones web:

galería de fotos de Picasa
Esta distribución de elementos es muy sencilla de realizar con HTML y CSS. Vamos a ver a continuación algunos ejemplos y algunas de las particularidades que nos podemos encontrar.

El ejemplo que vamos a hacer va a ser el siguiente:

Siempre vamos a tener un contenedor, que en la imagen anterior vemos con un borde punteado, dentro del cual que colocan los elementos.

Los elementos pueden ser cualquier cosa, desde una simple imagen, como en la galería de Picasa, hasta un div conteniendo textos, imágenes y otros div's interiores, como en la galería de películas de Youzee:


En nuestro ejemplo el elemento es un div con otro div interno, con los bordes redondeados hasta hacer un círculo, y un número. Como el contenido del elemento no es importante para este tutorial, simplemente lo representaremos como:

1


El HTML para nuestros 8 elementos sería:
1
2
3
4
5
6
7
8
No tiene ninguna dificultad, es un div contenedor envolviendo a los elementos que formarán la galería. También es muy común ver esta misma estructura con una lista <ul> como contenedor y los items <li> de la lista como elementos. Lo veremos más adelante.

Con lo que realmente construimos la galería es con el CSS:
#container {
  border: 1px dotted black;
  overflow: hidden;
  width: 450px;
}

.element {
  width: 100px;
  height: 150px;
  float: left;
  margin : 5px;
}


Lo más importante en estas pocas líneas de CSS es que los elementos tienen float: left. Esto es lo que hace que se coloquen como un mosaico. El hecho de que sean float nos obliga a poner overflow:hidden en el contenedor para que los envuelva y no se quede vacío (aunque hay otras soluciones ).

El margin de 5px de los elementos sirve para que no queden pegados y tengan esa pequeña separación entre ellos, tanto por arriba como a los lados.

También vemos que hemos dado una anchura fija al contenedor para que quepan justo cuatro elementos, pero no tiene que ser necesariamente así. Es una galería autoajustable y podemos dejarlo sin anchura fija para que ocupe todo el ancho disponible, los elementos se redistribuirán automáticamente:


Podemos fijar una anchura mínima y/o máxima para que se quede siempre dentro de unos márgenes aceptables para el diseño gráfico de nuestra aplicación:

#container {
  border: 1px dotted black;
  overflow: hidden;
  min-width: 230px;
  max-width: 700px;
}

Con los valores anteriores conseguimos que el diseño sea líquido pero que nunca se expanda más de seis columnas ( con sus respectivos márgenes ) o se contraiga hasta contener menos de dos columnas.

También podríamos utilizar display: inline-block para los elementos, pero presenta un pequeño inconveniente cuando tenemos que hacer diseños con unas especificaciones muy concretas (márgenes, dimensiones, etc). El problema es que dejan un pequeño margen extra entre elementos. Esto lo veremos más detalladamente en otra entrada.

Utilizando una lista

Como hemos comentado antes, se puede utilizar una lista para hacer la galería. Algunos programadores argumentan que el HTML resultante es más semántico, aunque tengo mis dudas sobre este punto.

El código es muy parecido al anterior. El HTML sería:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
Y el CSS:
#container {
  border: 1px dotted black;
  overflow: hidden;
  width: 450px;
  padding: 0px;

}

.element {
  list-style: none;
  width: 100px;
  height: 150px;
  float: left;
  margin : 5px;
}

El CSS tiene dos novedades importantes. Primero tenemos que resetear el padding de nuestra lista <ul>, con el id #container, porque algunos navegadores fijan un padding izquierdo por defecto que haría que el mosaico quedara un poco desplazado a la derecha.

El segundo detalle a tener en cuenta es que tenemos que eliminar el 'disco' que normalmente tienen a la izquierda los elementos de una lista. Para esto hacemos list-style: none.

El resultado es el mismo que en el ejemplo anterior.

viernes, 26 de octubre de 2012

Tutorial de JSON

JSON (JavaScript Object Notation) es un formato estándar para intercambio de datos que prácticamente ha sustituido a XML como el formato preferido para traspaso de datos entre cliente y servidor web.


Es una sintaxis estandarizada, un conjunto de reglas en el que nos hemos puesto de acuerdo para poder pasar información sabiendo como codificarla y decodificarla.

Se basa en la notación literal de JavaScript para estructurar datos en arrays y en objetos. Es una forma sencilla de 'empaquetar' información para enviarla:

{
"title": "The wonderful guide",
"pages": 348,
"price": 15
}

A pesar de llevar JavaScript integrado en el acrónimo, es un estándar independiente definido en la RFC 4627 y puede utilizarse en cualquier lenguaje.

El sustituto de XML

AJAX revolucionó la forma en la que se desarrollan aplicaciones web. En vez de pedir al servidor páginas completas cada vez que algo cambia, se pide sólo la información que ha cambiado. Es decir, se piden datos.

Se hizo necesario un formato para intercambio de datos, para enviarlos desde el servidor, que tiene la base de datos, al cliente que se encarga de presentarlos. El formato elegido fue, en principio, XML. Parecía tan apropiado que incluso forma parte de las siglas AJAX (Asynchronous Javascript And XML).

Douglas Crockford propuso, en el año 2001, JSON como una alternativa más ligera, sencilla y eficiente porque encontraba que XML no era muy apropiado para esta tarea. El nuevo formato se popularizó rápidamente y se ha convertido en el estándar de facto para este tipo de transferencias.

JSON es más sencillo de codificar y más facil de leer, pero probablemente el factor más importante para su éxito es que utiliza unas estructuras de datos que son nativas para JavaScript, el lenguaje del cliente (no olvidemos que es la 'J' de AJAX).

¿Cómo codifico datos en JSON?

Para enviar datos en JSON debemos organizarlos de alguna de estas dos formas:

  • Un conjunto de pares clave/valor entre llaves { clave1: valor1, clave2: valor2 } (un objeto en JavaScript)
  • Una lista de valores entre corchetes [valor1, valor2] (un array en JavaScript)

Sólo existen estas dos opciones, cualquier dato tiene que enviarse de esta forma (En realidad lo que se envía es un string con esta estructura).

Por ejemplo, supongamos que sacamos de la base de datos la información de un empleado: nombre, edad y cargo. Para enviarlo como JSON válido tendriamos que colocarlo con la siguiente estructura:

{
 "nombre": "Pepe",
 "edad": 22,
 "cargo": "botones"

}

También podríamos ponerlo en forma de array, pero en este caso tiene menos sentido:


["Pepe", 22, "botones"]

Lo que sí tiene sentido es utilizar un array para enviar los datos de varios empleados:

[
    {
      "nombre": "Pepe",
      "edad": 22,
      "cargo": "botones"
    },
    {
      "nombre": "Julio",
      "edad": 32,
      "cargo": "albañil"
    },
    {
      "nombre": "Tim O'Theo",
      "edad": 28,
      "cargo": "detective"
    }
]   

Objetos y arrays se pueden anidar hasta crear estructuras muy complejas.

Aunque tengamos un único dato debe enviarse tambien de esta forma, como un objeto de una sóla propiedad o un array de un sólo elemento:


["Pepe"]

o

{ "nombre": "Pepe"}

Como podemos ver en estos ejemplos, las claves de los pares clave/valor tienen que ir siempre entre comillas dobles.

Los valores pueden ser: objetos, arrays, números, strings, booleanos o null.

Usando JSON en JavaScript

No olvidemos que lo que se envía/recibe es siempre un string. Por eso cuando enviamos del cliente al servidor, aunque JavaScript ya tiene los objetos en el formato correcto (nativo), hay que serializarlos para enviarlos.

Lo que se envía es:

'{ "nombre": "Pepe", "edad": 22, "cargo": "botones" }'

Al recibir JSON en JavaScript (de una petición AJAX, por ejemplo), debemos convertir el string en un objeto/array real. Como la sintaxis es idéntica a la de un objeto/array en JavaScript, lo único que tenemos que hacer es 'decirle' al motor de JS que lo interprete. Esto se hace mediante eval("my string") ( por motivos de seguridad es mejor utilizar el objeto JSON como veremos más adelante).

Por ejemplo, si tenemos recibido el JSON del ejemplo anterior mediante AJAX, en la variable ajaxData, basta con hacer:

var empleado = eval ('(' + ajaxData + ')') 

Los dos paréntesis que añadimos son necesarios para que JavaScript interprete correctamente las llaves con las que puede comenzar un string JSON. Lo veremos más detalladamente en otro post.

Ahora ya podemos acceder a todos los datos internos de la forma habitual:

alert ( empleado.name );
alert ( empleado.edad);

Debido a los problemas de seguridad que puede provocar el uso de eval(), se ha incluido en los navegadores, de forma nativa, el objeto JSON. Para 'evaluar' un string JSON que hemos recibido sólo tenemos que hacer:

var empleado = JSON.parse(ajaxData);

Y ya tenemos el objeto nativo en la variable empleado.

Si queremos convertir un objeto/array en un string para enviarlo como JSON hacemos:


JSON.stringify( empleado );

Si tenemos que dar soporte algún navegador antiguo que no tenga el objeto JSON ( IE6 e IE7 ) podemos utilizar alguna de las múltiples librerias que hacen lo mismo, como JSON2 o jQuery ( que proporciona el método .parseJSON() ).

Fuentes:
JSON
JSON, wikipedia
JSON: The Fat-Free Alternative to XML
Tutorial JSON

domingo, 30 de septiembre de 2012

Preservando el contexto *this*. $.proxy() y bind()

Como hemos visto en algún post anterior, el valor de this en una función representa el contexto en el que se ejecuta y depende de cómo se la llame. Cuando utilizamos la función como un callback, es muy probable que el valor de this no sea el que esperamos cuando finalmente se ejecuta.
Supongamos que tenemos el siguiente objeto:
var myObject = {
    myName: "Hulk",
    showMyName: function() {  alert(  "My Name Is: " + this.myName );}
}
Es muy importante que, cuando utilicemos showMyName() como callback, this siga haciendo referencia al objeto myObject, para que this.myName sea “Hulk”. Sin embargo, la mayoría de las veces no será así. Al ejecutarse desde otro contexto, el valor de this será diferente. Por ejemplo:

//como callback en setTimeout
setTimeout( myObject.showMyName, 1000); 

//como callback en el click de un botón
$(".button1").click( myObject.showMyName );


En los dos casos perdemos el contexto original y el resultado es:

“My Name Is: undefined”

En el primer caso this será el objeto global window y en el segundo caso será el elemento button del DOM. Ninguno de estos dos objetos tiene una propiedad llamada myName, por lo que this.myName es undefined en ese contexto.

A veces necesitamos asegurarnos de que un callback se va a ejecutar con el contexto que nosotros necesitamos. Es decir, que el valor de this no nos va a dar sorpresas y tendrá el valor que queramos. En cualquier proyecto grande necesitaremos una función que nos asegure esto.

Muchas librerias y frameworks proporcionan una solución propia a este problema, por ejemplo jQuery tiene la función $.proxy(), Prototype tiene una función bind(), Ext JS tiene createDelegate(), Dojo tiene hitch(), etc. La última versión de JavaScript, ECMAscript 5, ha incluido también el método .bind() como solución nativa.

Si tenemos que dar soporte a navegadores que no soportan bind() de ECMAscript 5 y no tenemos necesidad de incluir ninguna de estas librerías o frameworks, podemos crear nosotros mismos nuestra utilidad para esto.

Creando nuestra función proxy

Lo que tenemos que hacer es sencillo, envolver la función original en otra que nos asegure el contexto y que se pueda utilizar como callback en lugar del método original. Es decir, una función intermediaria ( proxy ).

Vamos a crear una función createProxyFunction( context, originalFunc ) con dos argumentos: el contexto que debemos preservar ( this ) y la función original. Devolverá una función que puede usarse exactamente igual que la original, pero asegurando el valor de this. El uso sería tan sencillo como:

var myProxyFunc = createProxyFunction( myObject,  myObject.showMyName ); 
$(".button1").click( myProxyFunc );


o, ahorrandonos la variable intermedia:

$(".button1").click( createProxyFunction( myObject,  myObject.showMyName) );


Podemos hacer esto simplemente utilizando los métodos call() o apply() que tienen todas las funciones en JavaScript. Devolveremos una función que llamará a la original utilizando apply() para asignar el contexto.

Una implementación simple sería:

function createProxyFunction ( context, originalFunc ) {
    var proxyFunction = function() {
        return originalFunc.apply( context, arguments );
};
return proxyFunction;
} 


Podríamos complicarlo un poco para permitir añadir argumentos ‘locales’ que se sumarían a los argumentos con los que se llamará al callback cuando se ejecute:

function createProxyFunction ( context, originalFunc ) {
    //store additional arguments (if any) appart from context and originalFunc 
    var proxyArgs = Array.prototype.slice.call(arguments, 2);
    var proxyFunction = function() {
        var allArgs = proxyArgs.concat ( Array.prototype.slice.call(arguments) );
        return originalFunc.apply( context, allArgs );
};
return proxyFunction;
} 


Utilizamos la función slice() de la ‘clase’ Array porque arguments es un pseudo-array y no la tiene. El valor que devuelve sí es un array real y por eso podemos utilizar concat().

La función que se devuelve en este caso tiene concatenados los argumentos que pasemos a createProxyFunction() (despues de context y originalFunc ) con los que se incluyan en la función original por parte de quien ejecute el callback. Es lo mismo que hace $.proxy() de jQuery.

¿Para qué sirven los argumentos extra?

La opción de añadir parámetros extra es muy útil porque podemos estar manejando información, externa al objeto que enviamos como contexto, que necesitaremos conocer cuando recibamos la llamada a nuestro callback.

Los parámetros que recibimos normalmente en un callback estarán fijados por un API. Por ejemplo, para un evento click, jQuery lanzará nuestro callback con eventObject como único argumento:

eventHandler( eventObject)

Si utilizamos la función $.get() de jQuery para pedir unos datos por AJAX, nuestro callback será invocado cuando los datos estén listos con los argumentos:

myAjaxCallback( data, textStatus, jqXHR )

Puede ocurrir que estemos manejando unos datos concretos y, en función de estos, hagamos una llamada AJAX. La respuesta de la llamada AJAX nos llegará cuando se invoque nuestro callback, pero necesitamos seguir teniendo acceso a los datos que estábamos manejando antes para terminar la tarea. La solución sería añadir estos datos como parámetros extra:

$.get('ajax/test.html', createProxyFunction( myObject, myAjaxCallback, extraParam1, extraParam2 );

Por supuesto la función myAjaxCallback tiene que estar preparada para recibir todos estos parámetros:

myAjaxCallback( extraParam1, extraParam2, data, textStatus, jqXHR ); 


ECMAScript 5 bind()

En la implementación de ECMAScript 5, bind() es un método nativo de las funciones que devuelve una nueva función con el contexto fijado. En nuestro ejemplo lo utilizaríamos así:

$(".button1").click( myObject.showMyName.bind( myObject ) );


También pueden añadirse parámetros extra si los necesitamos:

$(".button1").click( myObject.showMyName.bind( myObject, extraParam1, extraParam2 ) );


Recibiremos los parámetros extra delante:

callbackFunction ( extraParam1, extraParam2, param1, param2 );


En que se diferencia de myObject.showMyName.call( myObject )?

A veces se plantea la pregunta de si podríamos usar directamente:

$(".button1").click( myObject.showMyName.call( myObject ) );


en vez de :

$(".button1").click( myObject.showMyName.bind( myObject ) );


No es lo mismo. La diferencia es que bind() devuelve una función que es la que actúa como callback. Utilizando call() estamos ejecutando directamente el método, no sirve como callback. Las dos expresiones no son equivalentes.