Cargando la web de forma asíncrona pero sin URL – javascript php jquery

Pregunta:


Estoy desarrollando una web para la gestión de pacientes.

La web tiene un solo punto de entrada (digamos que es index.php). Ese landing dibuja un cierto HTML que contiene un menú y un div de contenido.

Dependiendo del elemento del menú que es activado, se dispara una llamada por Ajax que llena el div de contenido.

$(document).ready(function(){

    $.ajax({ url: "formulario.php", 
        dataType: 'text',
        success: function(data) {
            $("#sentences").html(data); 
        }, 
        error: function() {
            alert("error"); 
        }
    });
});

Como todo esto ocurre sin cambiar la URL, no tengo cómo definir una URL única para una sección en particular. Cualquier visitante que llegue al sitio verá simplemente el landing por defecto.

¿Hay alguna solución práctica para mantener el refresco dinámico del contenido, pero al mismo tiempo darle a cada sección una URL única?

Preguntado por: sergibarca

Respuesta Alvaro Montoro:

Usa almohadillas/hash (el símbolo #) para diferenciar cada sección de las demás. Así por ejemplo si tienes las secciones “formulario”, “contacto”, “historia”, etc. Tendrían URLs únicas:

  • index.php#formulario
  • index.php#contacto
  • index.php#historia

Y seguirías cargando tu menú de la misma manera a como lo estás haciendo ahora (o podrías cambiarlo para que fueran enlaces a #formulario, #contacto, etc. y detectar el cambio usando un controlador del evento onhashchange).

Lo único sería que ahora los usuarios podrían acceder a una sección particular usando su “deep link“. Entonces deberías cambiar un poco el código que se ejecuta al principio para que si hay un hash lo detecte y cargue la página correcta en lugar de la página por defecto. El cambio sería sencillo: leerías el hash (usando window.location.hash) y con un switch elegirías la url a cargar. Algo como esto:

$(document).ready(function(){

    var myURL = "formulario.php";

    if (window.location.hash) {
       switch (window.location.hash) {
            case "#historia": myURL = "historia.php"; break;
            case "#contacto": myURL = "contacto.php"; break;
       }
    }

    $.ajax({ url: myURL, 
        dataType: 'text',
        success: function(data) {
            $("#sentences").html(data); 
        }, 
        error: function() {
            alert("error"); 
        }
    });
});

Ejemplo funcionando

Voy a hacer un ejemplo básico con 4 páginas (la principal y 3 subpáginas que se cargaran via AJAX). Dejo todo el código aquí, pero como no ejecuta PHP, he creado una demo online en un servidor propio para que lo puedas ver funcionando.

index.php (página principal):

<!doctype html>
<html>
    <head>
        <title>Test Hash</title>
    </head>
    <body>
        <div id="opciones">
            <a href="#formulario">Formulario</a>
            <a href="#contacto">Contacto</a>
            <a href="#historia">Historia</a>
        </div>
        <div id="sentences"></div>

        <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
        <script>

            function hashChange() {

                var myURL = "formulario.php";

                if (window.location.hash) {
                    switch (window.location.hash) {
                        case "#contacto": myURL = "contacto.php"; break;
                        case "#historia": myURL = "historia.php"; break;
                        case "#formulario":
                        default: myURL = "formulario.php"; break;
                    }
                }

                $.ajax({ 
                    url: myURL,
                    dataType: 'text',
                    success: function(data) {
                        $("#sentences").html(data); 
                    }, 
                    error: function() {
                        alert("error"); 
                    }
                });
            }

            $(document).ready(function(){
                hashChange();
                $(window).on("hashchange", function() {
                    hashChange();
                });
            });
        </script>
    </body>
</html>

formulario.php:

<h1>Titulo Formulario</h1>
<p>Contenido formulario</p>

historia.php:

<h1>Historia</h1>
<p>Contenido Historia</p>

contacto.php:

<h1>Contacto</h1>
<p>Contenido Contacto</p>

Puedes ver que cuando se carga la página, se hará con el contenido de la sección indicada en el hash (si ninguno se especifica, se mostrará el formulario). Además, al cambiar la URL con cada interacción del menú, puedes pulsar los botones de adelante y atrás porque se guardarán los cambios en el historial del navegador.

Se me ocurren unas cuántas formas, una es la que te dijo @AlvaroMontoro; otra es mediante la History API y otra es usando un router en la parte cliente.

History API

Esta API es muy práctica e intuitiva de usar. Tienes algunos métodos a disposición:

  • pushState
  • replaceState

El método que nos interesa más es pushState. Este método añade una entrada a la historia del navegador. Supongamos que haces click en Home, luego en Portafolio y por último en Contáctame, entonces, la pila de historia para tu dominio será:

  • Home -> Portafolio -> Contáctame

Y si haces click en atrás del navegador seguirás ese orden en modo reverso.

El método pushState no comprueba si la URL o página que se le indica existe o no. Este método solo la añade al stack de historia. Este método recibe tres parámetros: objeto de estado (para almacenar información respecto a la URL a cargar), el título del documento (ignorado por algunos navegadores) y la nueva URL (relativa) a mostrar.

$(document).ready(function () {
  $('#menu a').on('click', function (e) {
    e.preventDefault();
    var menuName = $(this).text();

    $.ajax({
      url: "formulario.php?page" + menuName,
      dataType: 'text',
    })
    .done(function (html) {
      $('#sentences').html(html);
      history.pushState(
        {},
        menuNames.charAt(0).toUpperCase() + string.slice(1),
        menuName
      );
    })
    .fail(function (xhr, status, error) {
      // hacer algo
    });
  });
});

El código anterior hace lo siguiente: cada vez que se haga click en un menú, se obtiene le HTML de la sección que indica el menú por medio del parámetro ?page el cual se recibe en el backend y se envía una vista de acuerdo a ella. Finalmente, actualiza la URL de modo que si:

  • Vas a Home: la URL será /tuapp/home.
  • Vas a Portafolio: la URL será /tuapp/portafolio.
  • Vas a Contáctame: la URL será tuapp/contáctame

Usando un Router del lado cliente

Tienes algunas opciones aquí, como Page.js y Navigo. Me gusta más Page.js porque se parece mucho al router de Vue.js. El mismo ejemplo que el anterior puede hacerse con Page.js:

$(document).ready(function () {
  page.base('/app'); // nombre de tu app
  page('/home', render);
  page('/portafolio', render);
  page('/contactame', render);
  page(); // inicializa page.js
});

function render (ctx, next) {
  let pageName = ctx.pathname.split('/').reverse()[0];

  $.ajax({
    url: "formulario.php?page=" + pageName,
      dataType: 'text',
  })
  .done(function (html) {
    $('#sentences').html(html);
  })
  .fail(function (xhr, status, error) {
    // hacer algo
  });
});

En este caso, no es necesario prevenir las acciones por defecto de los links ya que de eso se encarga Page.js. Solo debes definir tu ruta base (<base href="/tuapp/">) y encargarte de traer el HTML e insertarlo en el documento.

Demo

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
  <title>Demo Page.js</title>
  <base href="/app/">
</head>
<body>

  <a href="home">Home</a>
  <a href="portafolio">Portafolio</a>
  <a href="contactame">Contactame</a>
  
  <div id="view-router"></div>
  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/page.js/1.7.1/page.min.js"></script>
  <script>
    document.addEventListener('DOMContentLoaded', function () {
      page.base('/app');
      page('/home', render);
      page('/portafolio', render);
      page('/contactame', render);
      page();
    });

    function render (ctx, next) {
      let viewRouter = document.getElementById('view-router');
      let pageName = ctx.pathname.split('/').reverse()[0];
      viewRouter.innerHTML = `Estás viendo ${pageName}`;
    }
  </script>
</body>
</html>

Fuente

Related Posts:

¿Cómo obtener la respuesta de una llamada asíncrona (AJAX) fuera de ella? – javascript jquery asincrónico
Pregunta: Actualmente tengo esta función: function devuelveButaca(posicion){ var array = posicion.split('_'); var row = array; var column ...
Evitar múltiples toggle con misma clase – javascript
Pregunta: Para un evento toggle con puro javascript lo desarrollo de la siguiente forma: Javascript // Esta clase lo expande. const expandButton = document.querySelector('.expand-button'); expandButton.addEventListener('click', ()=> { ...
Validación de datos nulos en una función – javascript
Pregunta: Tengo la siguiente función pero al momento validar todos mis datos me los registra todos a excepción del los "containers" que son grids y ...
Como poner una imagen de fondo a pantalla completa – javascript jquery html5
Pregunta: Como puedo hacer que la imagen abarque todo el tamaño de la pantalla. Se que de las propiedad min-width y min-heigth pero ellas no ...
array push con keys php – php array
Pregunta: Buen día. Estoy tratando de crear un array con valores agrupados por categorías, mediante AJAX recibo dos variables una contiene un string con el nombre ...
Error de: Uncaught TypeError: url.indexOf is not a function por Jquery – javascript jquery
Pregunta: En el navegador, reviso en inspeccionar elemento, y me sale el siguiente error: Uncaught TypeError: url.indexOf is not a function Me imagino que es ...
Actualizar información con ajax php mysql – php jquery mysql
Pregunta: Tengo esta simple consulta que lo que simplemente me muestra las visitas de la web, pero quisiera que fuera automático con ajax ya que ...
¿Cómo insertar código en las entradas de WordPress? – php wordpress plugin
Pregunta: En algunos blogs de programación he visto algo como esto en las entradas: Me gustaría poder hacer lo mismo. Sin embargo, no solo quiero que ...
Como validar array checkbox – javascript php jquery
Pregunta: Que tal buen dia, alguien me podria decir como se puede hacer para que cuando presione el boton "BAJAR DATOS" si este esta vacio ...
listar datos en una vista parcial pasandolos desde una ventana modal – ajax asp.net-mvc asp.net-mvc-5
Pregunta: Tengo una ventana modal con un formulario del cual quiero retornar los datos ingresados a una vista parcial y ponerlos en lista como se ...
Problema con UTF_8 – php sql sql-server
Pregunta: Buen día, mi problema es el siguiente, Php me lanza el siguiente Warning Warning: utf8_decode() expects parameter 1 to be string, object given ...
Uso de js y c# en unity. ¿Igual rendimiento? – javascript c# unity3d
Pregunta: Estoy comenzando con unity . Y veo que se pueden usar c# y js para programar. Mi pregunta es si tanto c# como js ...
Obtener las fechas de los días Lunes dependiendo de un rango de fechas PHP – php datetime time
Pregunta: Dependiendo de un rango de fechas quiero obtener sólo las fechas de los días Lunes, tengo la siguiente función que funciona pero me trae ...
Como cambiar una propiedad html dependiendo del valor de un perfil recogido de clase java – javascript jquery html
Pregunta: Tengo una caja de texto en la que me gustaría poder escribir o no dependiendo del perfil que utilice la aplicación. Esta caja de ...
Agregar marcador especifico a una localización Google Maps – javascript google-maps
Pregunta: ¿Cómo podría añadir un marcador personalizado en Google maps? Actualmente el código que tengo es el siguiente: var locations = [ ['Bondi Beach', -33.890542, ...

One Comment

Add a Comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *