Insertar HTML con JavaScript y priorizar el contenido visible

Modificando páginas HTML estáticas

Página de inicio de Wextensible con estructura que ya no estoy usando
Figura. Estructura de página anterior modificada a finales del año 2013

Una web puede basarse en páginas estáticas cuando el contenido principal no se modifica en el momento de servir esa página. Creamos la página y la subimos al servidor y a partir de ahí servimos siempre el mismo contenido. Cualquier modificación conlleva volver a reescribir la página y volverla a subir. Parte de ese contenido estático es también la estructura HTML que se basa en una plantilla que repetimos en todas las páginas.

El problema es cuando queremos modificar algo en esa estructura HTML. A finales del año 2013 modifiqué la estructura anterior (ver Figura), lo que supuso modificar todas las páginas de este sitio para que tuvieran el aspecto y funcionalidad que ahora tienen. Una vez modificadas no es mucho trabajo subirlas, pero luego requerirá un tiempo para que se actualicen cachés de navegadores y buscadores. Este no es un sitio grande, pero actualmente son unas 204 páginas que tendría que reeditar y reemplazar aunque el propósito fuese modificar o agregar sólo un poco de HTML de la estructura. Y eso precisamente es el problema que ahora planteo: agregar un botón HTML en la parte superior de todas las páginas sin tener que reescribirlas y volver a subirlas.

Menú de enlaces y botones de Wextensible
Figura. En el menú que aparece oculto inicialmente está el botón de compartir al final.

En la nueva estructura tengo un menú que se despliega ofreciendo una serie de enlaces y botones. El último de ellos es para compartir en redes sociales (ver Figura). Cuando se pulsa inserta los botones me gusta de Google+, Twitter y Facebook. Cuando cambié la estructura de página decidí no insertarlos con la carga de la página pues la entorpecen, especialmente en redes móviles. Mi opinión es que si a un usuario no le interesa compartir ni saber cuántos han compartido no tenemos porque insertarle esa información extra, con la consiguiente carga adicionales de recursos.

De todas formas consideré que ese botón está demasiado oculto y decidí poner otro entre el encabezado y el contenido. Si no lo hacía modificando el HTML tendría que hacerlo con JavaScript. También es posible insertar contenidos desde el servidor con PHP. De hecho ahora estoy usando CSS-FOLD para insertar trozos de CSS antes y después de la carga. Pero no son contenidos HTML y no me agrada la idea de que parte de la estructura HTML se inserte con PHP.

Otra forma de insertar elementos en el DOM es usando los pseudo-elementos CSS before y after. Pero no sabría como insertar un elemento BUTTON de esa forma, que se comporte como un botón y además luego adjudicarle el evento click. Puede que sea posible, no lo sé, pero finalmente he decidido seguir insertando con JavaScript. Y para preservar la velocidad de carga debería (supuestamente) hacerlo en el evento window.onload.

Botón compartir en redes sociales con letras g,t,f de Google, Twitter y Facebook
Figura. Botón compartir insertado con JavaScript.

Al final llevé a cabo el cambio y el nuevo botón quedaba insertado entre la barra de menú y el contenido, tal como se aprecia en la Figura. En lugar de usar el clásico icono de compartir o incluso los de Google+, Twitter y Facebook opté por dar estilo de fuente y colores a las tres letras g+ t f que identifican esas redes sociales. Todo parecía correcto pues ese botón aparecía tras la carga de la página insertado con JavaScript. Si el usuario quería podía pulsarlo y se le cargarían los botones me gusta correspondientes. Esta modificación sólo afectaba al JavaScript del archivo general.js y al CSS del archivo base.css, recursos genéricos del sitio. No tenía que tocar ningún HTML.

En principio todo parecía que iba a ir bien, pero al final no resultó así. Porque tocar la parte superior de la página es un tema crítico tal como veremos a continuación.

Un error: incrementar en exceso el above-the-fold con JS

Recorte del sitio PageSpeed Insights mostrando puntuación 91/100
Figura. PageSpeed Insights detecta que hay que priorizar el contenido visible.

Como hago siempre que subo páginas al sitio, le paso PageSpeed Insights on-line para ver si hay algún problema. Y efectivamente lo había, pues antes conseguía una puntuación 100 en la página de inicio y ahora estaba dando 91 como se observa en la Figura. El mensaje decía que tenía que priorizar el contenido visible. Esto sólo pasaba en móviles pues en ordenador seguía dando la máxima puntuación.

Para más información sobre esa y otras cuestiones de optimización puedes consultar en este sitio las Reglas PageSpeed HTML, que también incluye un enlace al documento de PageSpeed Insights que propone reducir el tamaño del contenido de la mitad superior de la página.

Analicemos con detalle que nos está diciendo PageSpeed en el informe para móviles. El primer párrafo dice: Prioriza el contenido visible. Tu página requiere indicaciones completas de red adicionales para procesar el contenido destacado en la parte superior de la página. Para obtener un rendimiento óptimo, reduce la cantidad de HTML necesario para procesar dicho contenido.

¿Cómo que reducir la cantidad de HTML?. Si lo único que he hecho es inyectar con JavaScript un pequeño trozo de HTML en window.onload. Para entender esto hay que ver la captura de pantalla que hace PageSpeed de la presentación de la página en un móvil así como el resto del mensaje:

Recorte del sitio PageSpeed Insights mostrando una captura de pantalla en la carga
Figura. Capturas de PageSpeed Insights en móviles.

Toda la respuesta HTML no era suficiente para visualizar el contenido de la mitad superior de la página. Esto suele indicar que eran necesarios recursos adicionales, que se cargan después del análisis de HTML, para presentar este contenido. Prioriza el contenido visible que se necesita para visualizar el contenido de la mitad superior de la página incluyéndolo directamente en la respuesta HTML. Solo se ha podido procesar cerca de un 48 % del contenido final de la mitad superior de la página con la respuesta de HTML completa.

Lo que vemos en la Figura son las capturas de pantalla que hace PageSpeed en los momentos de antes y después del window.onload. Vemos que hay dos contenidos adicionales que se inyectan con JavaScript. Uno es un trozo de texto con el aviso relacionado con la Ley de Cookies, insertado a continuación de la barra de menú. El otro contenido adicional es el nuevo botón con las letras "g+tf" antes del cuerpo de contenido. También el contenido de la imagen aparece después del evento Load, pero no es quién está ocasionando el problema.

Realmente hay más trozos HTML que actualmente inserto en el window.onload:

  • Un botón para agrandar el ancho de pantalla que se inserta en la barra de cabecera. Sólo es insertado en dispositivos iguales o mayores de 800px. Está motivado por la necesidad de limitar el ancho máximo de la página.
  • Un aviso de actualización de página que se inserta al inicio del contenido y que se origina en el subproceso de control de versión con revalidación desde caché. Sólo se inserta si hay contenidos previos en caché y están caducados.
  • Una sección de comentarios que se inserta antes del pie de página.

Las dos primeras inserciones afectan a la parte superior de la página. Pero PageSpeed para móviles no las tiene en cuenta porque el ancho del viewport es menor de 800px y además hace la petición con caché vacía. Si lo desea puede ver en el siguiente desplegable un diagrama de flujo del proceso de poscarga. También puede verlo en una imagen del diagrama de poscarga en caso de que no se presente adecuadamente.

En todo caso lo único que he modificado es el nuevo botón "g+tf", porque ese aviso de cookies ya existía antes y obtenía una puntuación 100. Al agregar más espacio vertical e insertar el nuevo botón da como resultado que se incrementa el contenido que no es visible inicialmente sólo con el HTML. Por lo tanto cualquier modificación posterior que afecte a un porcentaje importante del contenido final será considerada como un problema de priorizar el contenido visible. Y esto no pasa en ordenador pues ese porcentaje es inferior dada su mayores dimensiones de pantalla.

Es obvio que estos contenidos HTML adicionales deberían estar ya en el HTML y no insertarlos posteriormente con JavaScript, consiguiendo así priorizar el contenido visible. Pero tanto el aviso de cookies como el nuevo botón "g+tf" he tenido que insertarlos así debido a que inicialmente no los contemplé en la estructura y ahora no sería el momento de modificar el HTML de todas las páginas existentes.

Insertar contenidos con la primera interacción

La lección aprendida es que debemos restringuir la inserción de contenidos con JavaScript. Por supuesto, antes del evento Load la evitaremos siempre que podamos, pues bloquea el proceso de renderizado. Y después intentaremos insertar el menor contenido posible. Por lo tanto la solución para mi problema pasa por insertar ese nuevo botón en la parte inferior de la página (below the fold). O insertarlo en la parte superior (above the fold) con la primera interacción del usuario, cuando haga click o scroll por primera vez. O las dos cosas, que es por lo que finalmente he optado, tal como se observa en la siguiente serie de capturas de pantalla.

Insertar HTML en poscarga
Página presentada en un emulador de móviles.Imagen no disponible
En el emulador de móviles podemos ver como aparece la página en un viewport de 320 píxeles de ancho.

En la primera imagen se observa el emulador de móviles que viene con el Developer Tools de Chrome. Con un viewport de 320 píxeles de ancho se presenta la página con el párrafo de aviso de cookies, pues previamente eliminé la cookie de aceptación y, a los efectos, es como si un usuario visitara por primera vez la página. En la segunda imagen verá una captura de PageSpeed Insights recuperando ahora la puntuación 100 en móviles. Se observa que también usa un móvil de un ancho de viewport similar.

La tercera imagen muestra la página para un usuario que ya hubiese aceptado las cookies, con lo que no le saldría a partir de ese momento el párrafo de aviso. He tenido que ampliar la separación entre la barra de menú y el contenido, incrementando la sombra de la barra para disminuir la sensación de espacio en blanco. También he agregado un borde ancho superior al contenido. Estos incrementos de espacios son para poder albergar el botón de compartir, que ya vemos en la cuarta imagen tras una primera interacción del usuario. Uso transiciones CSS para disminuir la sombra y que el botón aparezca gradualmente. También he agregado un boton igual a pie de página, como se observa en la quinta imagen.

Cuando el usuario pulse el botón del pie le llevará al de la parte superior. Ambos ejecutarán la inserción de los botones de las redes sociales, como se observa en la última imagen. Vemos que el espacio que he abierto entre barra de menú y contenido es suficiente para albergar verticalmente estos botones. Al especificar un valor para height al contenedor exterior evitamos el flujo normal y que ese contenedor empuje el resto de la página hacia abajo, lo que produce una sensación de repintado no agradable.

En el siguiente desplegable puede ver el código completo de estos nuevos botones de compartir.

Código completo de este ejemplo