martes, 28 de agosto de 2012

Los scripts externos pueden bloquear una web

Cuando cargamos un fichero JavaScript externo mediante una etiqueta <script> incluida en nuestro HTML, estamos corriendo un riesgo importante. Ese script externo a nuestra aplicación, probablemente un banner de publicidad o un widget, puede bloquear nuestra página y dejarla en blanco durante varios minutos o incluso evitar que llegue a cargarse. Steve Souders explica el problema en su presentación “Your script just killed my site” y proporciona una demostración online.

En la siguiente imagen podemos ver al menos tres elementos que se cargan mediante javascript externo en una página web real:



Si el código que incluimos para cargar estos elementos (normalmente proporcionado también por la empresa externa) no es apropiado, puede bloquear nuestra web.

¿Porqué un simple script puede bloquear nuestra página?

Un archivo JavaScript puede cargarse de manera síncrona (bloqueante) o asíncrona (no-bloqueante). Veamos que implica cada una:


Carga síncrona (bloqueante)

Decimos que se carga de forma síncrona cuando el navegador tiene que esperar a que el archivo se haya descargado y ejecutado para continuar presentando elementos en la página. Esto ocurre cuando utilizamos una etiqueta <script> incluida en el HTML. Al encontrarla, el navegador no continua calculando y mostrando los siguientes elementos de la página, este proceso se para hasta la ejecución del fichero. Esto ocurre para todos los navegadores.

La razón por la que se bloquea es que este script, al estar incluido de forma estática en el HTML, se considera parte de la página que se está presentando y, por lo tanto, el javascript que contiene puede crear nuevos elementos en la página ( o modificarlos si se carga en el body). Se espera a ejecutarlo completo para poder presentar la página correctamente.

Carga asíncrona (no-bloqueante)

La carga es asíncrona cuando el proceso de carga se realiza sin bloquear la presentación de otros elementos de la página.

Podemos conseguir la carga no-bloqueante mediante la inserción dinámica de la etiqueta <script> o utilizando atributos como  “defer” o “async”  (HTML5). Veremos después en detalle estas opciones.

El problema con los scripts externos que pueden "matar" nuestra página, es que a veces se incluyen de forma síncrona. Si el archivo es grande va a retrasar siempre la carga de la web que lo incluye. Aunque el archivo no sea pesado, su servidor puede ser lento, estar caído, o incluso estar bloqueado ( en China, por ejemplo ). Hasta que el script no se reciba o se produzca un timeout (si lo hay) nuestra página aparecerá en blanco.


¿Qué significa exactamente no-bloqueante?


Puesto que javascript no es multi-hilo, es imposible que dos scripts se ejecuten en paralelo. Este thread es además el mismo que se encarga de renderizar los elementos de la página. Cuando hablamos de un script no-bloqueante significa que no bloquea la página (puede seguir con el renderizado de elementos del DOM) mientras se descarga, la ejecución siempre se va a realizar como tarea única, nunca en paralelo.

Tenemos entonces que el navegador tiene que realizar dos tareas con el archivo:
  1.  Descargarlo de la URL que se le indique 
  2.  Ejecutarlo
El punto 1 es normalmente el que más tiempo requiere y es el único que podemos optimizar mediante estas técnicas. La ejecución siempre va a ser bloqueante puesto que tenemos un solo hilo que se encarga de ejecutar y de presentar elementos del UI.

Formas de carga no-bloqueante


Etiqueta <script> generada dinámicamente

Lo más habitual es utilizar una etiqueta <script> que se genera dinámicamente con JavaScript. Cuando se crean de este modo, la descarga se realiza inmediatamente pero no bloquea el renderizado de otros elementos. El fichero se ejecuta cuando se ha descargado completamente:
var script = document.createElement("script");
script.type = "text/javascript";
script.src = “/path/to/script.js";
document.getElementsByTagName("head")[0].appendChild(script);
Hay que tener en cuenta que esta técnica no respeta el orden de inclusión de varios ficheros JavaScript (en algunos navegadores). No se garantiza que el fichero que se incluye primero sea el primero en ejecutarse.

Atributo ‘defer’ en <script>

Incluyendo el atributo defer la descarga comienza inmediatamente pero de forma no-bloqueante. La ejecución se retrasa hasta que la página se ha parseado completamente.


Atributo ‘async’ en <script> ( HTML5 )

En HTML5 se ha creado un atributo nuevo precisamente para este propósito:
de esta forma la etiqueta <script> se comporta igual que si la hubiéramos generado dinámicamente. El script de descarga inmediatamente sin bloquear la presentación de la página y se ejecuta en cuanto está descargado (esta es la principal diferencia respecto a defer).

Es importante señalar que todas estas técnicas retrasan el evento onload hasta que los scripts se han ejecutado. Existen otras técnicas que utilizan XHR (AJAX) o iframes, pero no aportan ventajas con respecto a las anteriores y prácticamente han sido desplazadas por estas.


Referencias:
Best way to load external javascript
Cargar JavaScript. Blocking vs non-blocking
Loading Scripts Without Blocking
What is a non-blocking script?


viernes, 24 de agosto de 2012

CSS: elementos de bloque y elementos en línea

Un elemento HTML se puede mostrar como elemento de bloque ( display: block ) o como elemento en línea ( display: inline ). Comprender exactamente la diferencia entre estos dos tipos de presentación es fundamental para un desarrollador.

Elementos de bloque ( block )

Elementos de bloque son, por ejemplo: <p>, <div>, <ul>, <li>, <form>, etc (siempre que no cambiemos su display por defecto).
Se caracterizan por:
  • Se colocan siempre en su propia línea, debajo de los elementos anteriores
  • Se expanden hasta ocupar toda la anchura disponible ( la del elemento padre )
  • La altura se ajusta al contenido
  • Pueden contener otros elementos inline o block
  • Se les puede fijar altura y anchura con CSS ( width y height )
  • Se les puede asignar margin y padding con CSS
  • Ignoran la propiedad CSS vertical-align


Elementos en línea ( inline )

Elementos en línea son, por ejemplo: <a>, <span>, <b>, <em>, etc (siempre que no cambiemos su display por defecto).
Se caracterizan por:
  • No crean una nueva línea. Se colocan ‘en línea’ con otros elementos
  • Su anchura y altura se ajusta al contenido y no podemos cambiarlas con CSS ( ignora widht y height )
  • Sólo podemos fijar margin-left y margin-right. Ignoran margin-top y margin-bottom 
  • Sólo pueden contener otros elementos inline
  • Debe respetar la propiedad white-space de CSS
  • Respeta vertical-align


Particularidades de margin y padding para elementos inline

El padding y el margin para elementos inline puede aplicarse en los cuatro lados, pero el que se define arriba y abajo no va a afectar a otros elementos, no los desplaza. Es como si ignorara estos valores, pero podemos apreciar el padding aplicado si definimos un borde o un color de fondo:


Imágenes y elementos inline-block

El elemento <img>(imagen) es un poco especial porque se comporta como una mezcla de elemento en línea y elemento de bloque:


Realmente es un elemento inline, pero de un tipo especial que admite altura y anchura. En el estándar se define como ”elemento en línea reemplazado” ( replaced inline element ) y para estos elementos width y height sí aplica.

Se define como un elemento reemplazado porque no tiene contenido por si mismo, sino que será despues reemplazado por el archivo que se obtiene de su URL.

Existe un tipo de display para hacer que cualquier elemento se comporte como lo hacen las imágenes, como un elemento de bloque que se coloca en línea:

display: inline-block

Para elementos reemplazados ( como <img> ) display: inline-block es exactamente igual al display: inline que tienen por defecto.

Referencias:
Inline elements and padding
W3C CSS2 Visual formatting model