NovaMonoFix
Errores PHP
X
Usuario
Password
0 FPS

Webs de contenido dinámico

19 de Noviembre del 2015 por Josep Antoni Bover, 0 visitas, 0 comentarios, 0 votos
Categorías : JavaScript, JQuery, PHP, HTML, Programación.
La web está en segundo plano, animación en pausa.
Cargando animación...
Webs de contenido dinámico

Desde que se empezó a usar Ajax se podría decir que empezaron a existir webs con contenido dinámico, al principio se utilizaba para cosas simples, y estaba pensado mas bien como una herramienta para consultar pequeñas cantidades de datos. Con el paso de los años Ajax se ha vuelto prácticamente imprescindible en cualquier proyecto web medio/grande.

Por poner un ejemplo claro, Facebook carga prácticamente todo su contenido mediante Ajax, seguro que todos conocéis su scroll infinito.

También se pueden encontrar paginas tipo blog/revistas que hacen uso del scroll infinito, y que además son capaces de modificar su URL sin tener que re-cargar toda la web.

A mí personalmente el tema del scroll infinito no me hace mucha gracia, pero sí que me gusta mucho la idea de tener una web eficiente, y que sea capaz de entregar los datos justos al usuario, además de darle una buena experiencia en la navegación.

En este tutorial os voy mostrar cómo hacer una web muy simple con un menú estático, que pueda cargar todos los documentos de forma dinámica en un marco. Antes de nada me gustaría que vieras un poco como he dividido los datos de mi web, ya que tenéis que tener muy claro que partes son esenciales y que partes no. Tal y como veis mi web ahora mismo, se podría dividir en 2 partes :

En una web normal (estática), el servidor nos mandará cada vez que abramos un enlace tanto la plantilla como el documento. Pero que es lo que pasa si navegamos entre 50 documentos de la misma web? pues que el servidor nos manda 50 veces la plantilla junto al documento, y considerando que la plantilla es siempre la misma, aquí estamos derrochando el ancho de banda tanto del servidor como del usuario.

En una web dinámica se evita ese derroche de ancho de banda, ya que si el usuario entra en 50 urls durante la misma sesión, recibirá la plantilla en la primera petición y luego recibirá los 50 documentos. Por lo que nos ahorramos los datos de un total de 49 plantillas respecto al modelo estático.

Lo que vamos a hacer en este tutorial será crear una web muy sencilla con un menú que nos dejara navegar por 6 documentos. Lo que nos permitirá ver cómo trabajar con Ajax y como debemos gestionar el historial de documentos de la pestaña (para modificar la url).


Creación de la plantilla

Lo primero que necesitamos es la plantilla básica para nuestros documentos. La idea principal es que la plantilla se mande una sola vez al principio, y que luego si el usuario carga mas documentos solo reciba los datos de ellos pero sin mandar otra vez la plantilla.

La forma mas fácil de hacernos una plantilla es crear un php con una función IniciarPlantilla, y una función TerminarPlantilla, que utilizaremos al principio y al final de nuestros documentos respectivamente.

IniciarPlantilla
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Función para iniciar la plantilla (requiere el nombre del archivo, y el titulo del documento)
function IniciarPlantilla($Archivo, $Titulo) {
// Si no existe el parámetro SinPlantilla es porque se está cargando la web por primera vez
if (!isset($_POST["SinPlantilla"])) {
echo "<!DOCTYPE HTML>".
"<html lang='es'>".
"<head>".
"<title>".$Titulo."</title>". // Titulo
"<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />". // Codificación UTF8
"<link rel='stylesheet' href='TutoWebDinamica.css'>". // Archivo con el CSS para la web
"<script src='//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js'></script>". // jQuery
"<script src='TutoWebDinamica.js'></script>". // Archivo con el JS para la web
"</head>".
// El body empieza con el atributo cargando (para la animación de carga entre documentos)
"<body cargando='true'>".
// Menu superior fijo para navegar por los enlaces
"<div id='MenuSuperior'>".
/* El atributo archivo se utiliza para saber en que documento estamos
y de esta forma poder resaltar el botón del documento actual (todo mediante css) */
"<ul archivo='".$Archivo."'>".
"<li><a href='Amarillo.php'>Amarillo</a></li>".
"<li><a href='Azul.php'>Azul</a></li>".
"<li><a href='Verde.php'>Verde</a></li>".
"<li><a href='Rojo.php'>Rojo</a></li>".
"<li><a href='Blanco.php'>Blanco</a></li>".
"<li><a href='Negro.php'>Negro</a></li>".
"</ul>".
"</div>".
// Marco donde se cargaran los documentos dinámicos mediante JavaScript
"<div id='MarcoDinamico'>";
}
// Datos del archivo que esta usando la plantilla (siempre quedan al principio del MarcoDinamico)
echo "<div id='InformacionDinamica' archivo='".$Archivo."' titulo='".$Titulo."'></div>";
}

La función IniciarPlantilla imprime toda la cabecera del documento HTML y el menú, siempre y cuando la variable $_POST["SinPlantilla"] no exista. La idea es que el usuario la primera vez que entra, lo hace mediante un GET y por lo tanto no puede existir la variable SinPlantilla en el POST. Luego en el código de la web vamos a tener que re-direccionar todos los enlaces que apunten a nuestro servidor para hacer un POST con la variable SinPLantilla, para que de esta forma nos devuelva solo el documento sin la plantilla.

Otra cosa a remarcar es la línea 33, que siempre imprime un div con la ID InformacionDinamica, este div lo vamos a utilizar para pasar variables de nuestro PHP al código JavaScript. En principio mandaremos el título y el nombre del archivo, pero se pueden añadir tantas variables como necesitéis.

Para pasar variables de PHP a JavaScript no hay que hacerlo necesariamente así, pero para este ejemplo me ha parecido la forma mas sencilla.

TerminarPlantilla
1
2
3
4
5
6
7
8
// Función para terminar la plantilla
function TerminarPlantilla() {
if (!isset($_POST["SinPlantilla"])) {
echo "</div>". // Final del MarcoDinamico
"</body>".
"</html>";
}
}

La función TerminarPlantilla mira si la variable $_POST["SinPlantilla"] no existe, y en ese caso imprime el código HTML para cerrar el documento correctamente.

Lo que nos tiene que quedar bien claro, es que cuando carguemos un documento de forma dinámica, lo tenemos que cargar con un post que contenga el parámetro SinPlantilla. Si no especificamos el parámetro SinPlantilla al cargar la web el servidor nos mandara toda la plantilla entera y el documento.


JavaScript

Ahora que ya tenemos la plantilla, vamos a por el código en java script. Necesitaremos lo siguiente :

Por comodidad se utilizara jQuery para casi todo.


Función para cargar enlaces dinámicos

Antes de nada debo remarcar que al cargar dinámicamente el documento, el navegador seguirá con la primera URL, y vamos a tener que decirle la nueva URL manualmente. Para ello vamos a tener que toquetear el historial de la pestaña.

Para tener acceso al historial de la pestaña tenemos el objeto window.history, del cual solo vamos a utilizar la función pushState. Echad un vistazo a la función CargarURL :

CargarURL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Función que carga una URL dinámicamente
function CargarURL(URL) {
// Si hay una petición ajax pendiente, la cancelamos
if (PeticionAjax != 0) PeticionAjax.abort();
// Inicio la animación de la carga
$("body").attr({ "cargando" : true });
console.log("CargarURL", URL, document.title);
// Petición ajax para obtener el documento dinámico
PeticionAjax = $.post(URL, { "SinPlantilla" : "true" }).done(function(data) {
// Asigno el nuevo HTML para el marco dinámico
$("#MarcoDinamico").html(data);
// Re-direcciono todas las etiquetas 'a' a la funcion CargarURL, y actualizo el titulo y el menú.
ActualizarContenido();
// Añado la nueva URL al historial de la pestaña, y esta queda como la URL actual.
window.history.pushState(null, document.title, URL);
// Borramos la petición ajax de la memória
PeticionAjax = 0;
// Error en la petición ajax
}).fail(function(jqXHR, textStatus, tError) {
// Termino la animación de la carga
$("body").removeAttr("cargando");
// Si hay un error
alert("Error cargando " + URL);
// Borramos la petición ajax de la memória
PeticionAjax = 0;
});
}

Para empezar en la línea 6 asigno el atributo cargando a la etiqueta body. De esta forma se iniciara una pequeña animación.

En la línea 9 se utiliza la funcion post para mandar un post con el parámetro SinPlantilla, lo que debería devolvernos si todo va bien el documento sin la plantilla.

Lo mas destacable de la función es la línea 15 donde llamamos a window.history.pushState, dicha función nos permite insertar una URL nueva en la cola del historial. Al ser la primera de la cola, es la que se mostrará en el navegador.

En la MDN nos dice que en Firefox no se usa el segundo parámetro de la función pushState (title), pero que no se use en Firefox no quiere decir que otros navegadores no lo usen, y de paso decir que el título debe ser el titulo del anterior documento (ya que en la MDN ni se menciona).


CallBack del historial

Para poder controlar cuando el usuario va hacia atrás o hacia adelante en el historial, y de esta forma pasarle solo el documento sin la plantilla, debemos sobre-escribir el callback del historial.

CALLBACK Historial
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* CALLBACK para el historial */
window.addEventListener('popstate', function(event) {
// Si hay una petición ajax pendiente, la cancelamos
if (PeticionAjax != 0) PeticionAjax.abort();
// Inicio la animación de la carga
$("body").attr({ "cargando" : true });
console.log("CALLBACK_Histroial", window.location.href, event);
// Petición ajax para obtener el documento dinámico
PeticionAjax = $.post(window.location.href, { "SinPlantilla" : "true" }).done(function(data) {
// Asigno el nuevo HTML para el marco dinámico
$("#MarcoDinamico").html(data);
// Re-direcciono todas las etiquetas 'a' a la funcion CargarURL, y actualizo el titulo y el menú.
ActualizarContenido();
// Borramos la petición ajax de la memória
PeticionAjax = 0;
// Error en la petición ajax
}).fail(function(jqXHR, textStatus, tError) {
// Termino la animación de la carga
$("body").removeAttr("cargando");
// Si hay un error
alert("Error cargando " + window.location.href);
// Borramos la petición ajax de la memória
PeticionAjax = 0;
});
}, false);

En esencia es prácticamente el mismo código de la función CargarURL con la diferencia de que no llamamos a window.history.pushState, ya que estamos navegando por el historial y no es una nueva entrada.

Hay que remarcar que en la línea 4 miramos si ya hay una petición ajax en proceso, y de ser ese el caso la abortamos. Si por ejemplo navegais por 20 paginas de la misma web, y luego pulsais muy rápido la flecha atrás del historial, se abren 20 periciones Ajax y nos interesa abortarlas todas excepto la última.

Sobretodo hay que asignar a la variable 'PeticionAjax' el valor '0' una vez terminada la petición, tal y como podéis ver en la línea 15 y en la línea 23.

El navegador actualiza la URL automáticamente antes de pasar por nuestro evento 'popstate', por lo que window.location.href contiene la nueva URL.


Re-direccionar etiquetas a href

Necesitamos capturar todos los eventos OnClick de los enlaces de la página, para evitar que el navegador salte a dicho enlace, y para que podamos cargarlo utilizando la función CargarURL.

Es muy importante mantener las etiquetas a para los buscadores, y a la vez poder utilizarlas para cargar contenido dinámico de nuestra web.

Vamos a crear una función para enlazar los eventos OnClick, y ya de paso también para actualizar el título de la página y el botón del menú seleccionado.

ActualizarContenido
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Esta función re-emplaza el evento click de las etiquetas "a" para cargar el enlace dinamicamente.
function ActualizarContenido() {
// Solo se redireccionan etiquetas sin el atributo target
$("a[href]:not([target])").off("click").on("click", function(e) {
// Evito que el evento llegue a la función OnClick por defecto de los enlaces
e.preventDefault();
// Cargo la URL del enlace utilizando Ajax
CargarURL($(this).attr("href"));
// Devuelvo false para que el evento no se extienda a sus padres.
return false;
});
/* El titulo y el nombre del archivo los podemos encontrar en el div #InformacionDinamica
que se ha creado expresamente en la plantilla para esta finalidad. */
// Asigno el nuevo titulo
document.title = $("#InformacionDinamica").attr("titulo");
// Asigno el boton actual del menú superior
$("#MenuSuperior > ul").attr({ "archivo" : $("#InformacionDinamica").attr("archivo") });
// Termino la animación de la carga
$("body").removeAttr("cargando");
}

En la línea 4 el selector apunta a todas las etiquetas a que no tengan el atributo target establecido. Lo que tenemos que tener en cuenta, es que los enlaces que apunten a ventanas/pestañas nuevas deberán ser aquellos que apunten fuera de nuestro servidor. Para los enlaces que apunten a nuestro servidor usaremos la etiqueta a sin el atributo target

Fijaros que se utiliza la función off antes de la función on, para eliminar cualquier evento anterior que hubiera asignado. Esto es necesario, ya que el menú de la plantilla también contiene enlaces, y este nunca desaparece.

En la línea 16 asignamos el titulo para el documento desde el atributo archivo del div #InformacionDinamica, que si os acordáis este div se ha creado única y exclusivamente para pasar datos desde PHP a JavaScript. En la línea 18 asignamos el nombre del documento al atributo archivo del ul que contiene el menú, de esta forma el botón quedara marcado en verde.

Por ultimo en la línea 20 elimino el atributo cargando de la etiqueta body, lo que finalizará la animación de la carga. La animación a decir verdad es una chorrada, y prácticamente es imperceptible ya que el ejemplo tiene muy poco contenido, lo que hace que el tiempo de carga sea mínimo. De todas formas si os habéis fijado en la animación de carga de devildrey33, os puedo decir que funciona de un modo muy similar.


Y esto es todo por hoy, como siempre podéis ver el ejemplo o descargarlo. Un saludo!