icono_twiter
Jose Manuel Sánchez Suárez

Consultor tecnológico de desarrollo de proyectos informáticos.

Puedes encontrarme en Autentia: Ofrecemos servicios de soporte a desarrollo, factoría y formación

Somos expertos en Java/J2EE

Ver todos los tutoriales del autor

Fecha de publicación del tutorial: 2010-10-15

Tutorial visitado 3.456 veces Descargar en PDF
IAQ (Interesting Asked Questions), implementado una interfaz SPI con jQuery.

IAQ (Interesting Asked Questions), implementado una interfaz SPI con jQuery


0. Índice de contenidos


1. Introducción

Continuando con la sección que abrió Alejandro sobre "Preguntas interesantes" en adictos, este tampoco es realmente un tutorial, es una pregunta que nos ha realizado uno de vosotros y que, además de tener la misma temática que el primer IAQ de Alejandro sobre spi, nos ha parecido también interesante publicar.

Se trata de poner en práctica el concepto de spi con el soporte de jQuery en cliente, sin ninguna tecnología de generación dinámica de páginas o soporte alguno del lado del servidor, salvo el de servir contenido estático.


2. La pregunta (by Renzo)

Al iniciar un nuevo proyecto, pensé en hacer una web más al estilo de una aplicación de escritorio... es decir "que cuando le presione un enlace, la página no tenga que cargarse por completo, sino sólo la parte que se necesita".

Y con esa premisa me puse a investigar y me topé con AJAX (del cual prácticamente sabía nada). Conocía algo de Jquery y también sabía que se podía utilizar AJAX mediante esta librería y su método load.() entonces sólo necesitaba un html que haga la llamada para que en él, se cargaran otros archivos html... todo estaría listo¡¡¡ pero fue justo en este momento que empezaron los problemas, paso a explicar mi problema:

Tengo un HTML llamado inicio.html que sería mi index:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<script type="text/javascript" src="js/jquery.js"></script>
 
<script type="text/javascript">
$(document).ready(function() {
$('#content1').load('home.html');
$('#nav01 a').click(function() {
        var url=$(this).attr('href');
        $('#content1').load(url);
        return false;
    });
});
</script>
</head>

<body>
<div id="menu">
  <ul id="nav01">
<li><a href="home.html">Uno</a></li>
<li><a href="fotos.html">Dos</a></li>
<li><a href="agenda.html">Cuatro</a></li>
<li><a href="otros.html">Tres</a></li>
</ul>
 
</div>  
<div id="content1">
</div>
</body>
</html>

Supuestamente este simple código me permitiría hacer lo que tanto había soñado, y bueno... siendo sinceros vaya que lo logra, pero sólo cuando el archivo llamado a cargarse en mi index (inicio.html) sólo contenga html y css; cuando a ésto se le suma plugins de javascript, simplemente o los lee confusamente o no los lee.

Para ser precisos 2 de mis html "externos" contienen plug-ins y me dan problemas:

  • Uno de ellos usa el famoso plug-in de jQuery FancyBox, y el problema con éste es que funciona correctamente cuando es llamado "la primera vez", pero sin embargo si llamamos a otro archivo y luego volvemos al que contiene este plug-in, deja de funcionar.
  • Y el siguiente otro archivo que me da problemas contiene el plug-in MOPSLIDER, el cual es un slider de fotos que se "ejecuta al cargarse la web", el problema con éste es que no funciona a la primera, pero si se vuelve a presionar el enlace ya funciona...

Por último tengo que decir que cada uno de los archivos que son llamados a cargarse en mi index (inicio.html) funcionan correctamente por sí solos.



3. La respuesta

Nuestro amigo nos remitía un enlace a su web y a cada una de las páginas que cargan en la página inicial y efectivamente, funcionan de forma aislada, pero no si se cargan desde la página inicial; no incluimos los enlaces, pero sí, como ejemplo, la cabecera de una de las páginas que se carga de forma dinámica, porque uno de los problemas estaba precisamente ahí.

La cabecera de la página fotos.html es la siguiente:


<link rel="stylesheet" type="text/css" href="css/jquery.fancybox-1.3.1.css" media="screen" />
<script type="text/javascript" src="js/jquery.js">
<script type="text/javascript" src="js/jquery.fancybox-1.3.1.pack.js">
<script type="text/javascript" src="js/jquery.easing-1.3.pack.js">

<script type="text/javascript">

  jQuery(document).ready(function() {

  ...
		

El primer error está en el hecho de que las páginas que se cargan en la página inicial funcionen de forma aislada, cuando no deberían; puesto que su objetivo es que funcionen dentro de la propia página inicial.

Y una de las causas es la siguiente: en las páginas que se cargan vía Ajax, también se está importando la librería de jQuery, con lo que:

  • al cargar el inicio.html se carga el javascript del fichero jQuery que se importa en inicio.html,
  • cuando se pulsa sobre cualquiera de los enlaces (agenda), se carga el fichero jQuery que se importa en las páginas individuales junto con los plugins, con lo que se sobreescribe la carga del jQuery que se realizó en el inicio.html,
  • al pulsar sobre un nuevo enlace (fotos), se vuelve a cargar el fichero jQuery, el que se importa en la página individual junto con sus plugins, con lo que el jQuery cargado en el paso anterior y sus plugins se sobreescriben,
  • al llegar a este punto, el javascript de jQuery se habría cargado 3 veces sobreescribiéndose 2, y tendríamos disponibles únicamente los plugins cargados en el último load de la página individual,
  • si volvemos a pulsar sobre el primero de los enlaces (agenda) se debería volver a recargar el contenido con lo que se deberían cargar nuevamente todos los scripts, el de jQuery y sus plugins asociados, en la página de nuestro amigo le debería funcionar porque en la página de inicio no utiliza ningún plugin de jQuery que se sobreescriba, por casualidad, pero el caso es que no le funciona.
  • ¿por qué no funciona?, más abajo lo veremos.

Para ilustrar el error hemos preparado el siguiente ejemplo con una página que carga un plugin propio y una segunda página que carga un carrusel de imágenes con fancyBox:

para reproducir el error

  • pulsar primero sobre el enlace que carga la página de fancyBox, y comprobad que funciona el carrusel,
  • pulsar después sobre el enlace que carga la página de nuestro plugin (es sólo una función de alerta), y comprobad que funciona el botón,
  • si volvemos a cargar la página de fancyBox, el carrusel ya no funciona y suelta el siguiente error

fancyBox: U is undefined

Hemos puesto una traza para ver la de veces que se llega a cargar el mismo recurso.

Bueno, ¿Y si quitamos la carga del script de jQuery de las páginas que se recargan vía Ajax?, la página inicial ya lo hace con lo que no serían necesarias, no funcionarán de forma aislada, pero ese no es su objetivo.

Pues el caso es que tampoco llega a funcionar, porque el error se encuentra en el propio plugin de fancyBox, no puede cargarse dos veces en la misma página.

¿Qué nos queda?, pues no vamos a analizar el por qué del error, quizás hace uso de variables de ámbito global... el caso es que vamos a tratar de evitar que, efectivamente, se cargue dos veces y, para ello, sólo tenemos que incluir la solución que ya proponíamos en el tutorial de retrasar la carga de Javascript con jQuery.getScript(), pero de otra forma, incluyendo una simple comprobación, que el plugin no esté cargado ya en jQuery:

<script type="text/javascript">
$("#log").append("jQuery de fancyBox cargado.").append("
"); if (typeof jQuery.fancybox == 'undefined') { $.getScript('http://fancybox.net/js/fancybox/jquery.easing-1.3.pack.js', function() { $("#log").append("fancyBox easing cargado.").append("
"); }); $.getScript('http://fancybox.net/js/fancybox/jquery.fancybox-1.3.1.pack.js', function() { $("#log").append("fancyBox cargado.").append("
"); }); } </script>

A continuación el mismo ejemplo de antes con las correcciones aplicadas, nos podemos mover entre páginas y el carrusel seguirá funcionando.

Para que funcionen y fallen los ejemplos en esta misma página se han incluido como iframes, sino nos pasaría como a nuestro amigo, que se pisarían.



4. Extrapolando el error a otros ámbitos

Una página que se recarga vía Ajax hay que verla como un todo; si se lleva a cabo la carga de recursos, éstos sólo se deberían cargar una única vez. En caso contrario podemos estar sobreescribiendo funcionalidad (véase el caso de los plugins).

Pero, ¿Qué ocurre cuando estamos trabajando con páginas que se generan de forma dinámica?, pues la problemática es la misma, si hacemos uso de un framework como JSF con el sistema de plantillas de facelets o con el soporte de richFaces de Jboss Seam, lo tenemos cubierto con la carga dinámica de recusos, la comprobación de si ya está cargado la hace el framework (no lo incluye dos veces en la cabecera).

Otra cuestión es la de la compatibilidad entre librerías de javascript, jQuery tiene un modo de compatibilidad con otras librerías http://docs.jquery.com/Using_jQuery_with_Other_Libraries que permite hacer uso de prototype, junto con script.aculo.us, en una misma página en la que tenemos plugins de jQuery, y hay que tenerlo en cuenta.

Esto último, junto a lo anterior, nos lleva a pensar en el uso de diferentes tecnologías en el servidor para generar piezas de código en el cliente: como pueden ser los portlets. ¿Funcionarían dos portlets que hacen uso de distintas tecnologías en el cliente susceptibles de ser incompatibles?, pues depende de cómo estén desarrollados y de si hacen uso o no de sus propios espacios de nombres.

Javascript estará siempre ahí, aunque intentemos encapsularlo haciendo uso de tecnologías que nos abstraen en el servidor; sigue teniendo un peso específico y no deberíamos menospreciar su conocimiento.

Parafraseando a @izaera: toda la potencia de ayer, hoy!!!, y más ahora con lo que nos viene en html5, ¿te imaginas ejecutando sentencias SQL en javascript?, hablaremos de ello en breve!

Un saludo.

Jose

jmsanchez@autentia.com

A continuación puedes evaluarlo:

Regístrate para evaluarlo

Por favor, vota +1 o compártelo si te pareció interesante

Share |
Anímate y coméntanos lo que pienses sobre este TUTORIAL: