Cambie la URL en el navegador sin cargar la nueva página usando JavaScript

297

¿Cómo podría tener una acción de JavaScript que pueda tener algunos efectos en la página actual, pero también cambiaría la URL en el navegador, de modo que si el usuario presiona recargar o marcar, se usa la nueva URL?

También sería bueno que el botón Atrás volviera a cargar la URL original.

Estoy tratando de registrar el estado de JavaScript en la URL.

Steven Noble
fuente
1
Esto sería muy lindo Por supuesto, se limitaría a modificaciones del mismo dominio. Pero cierto control del camino por parte del cliente (y no solo hash) es un paso lógico ahora que las recargas de páginas son una especie de "último recurso" para muchas aplicaciones.
harpo
1
Un "tipo de" buen uso de pushState:for(i=1;i<50;i++){var txt="..................................................";txt=txt.slice(0,i)+"HTML5"+txt.slice(i,txt.length);history.pushState({}, "html5", txt);}
Derek 朕 會 功夫
ejemplo de este efecto en acción: dujour.com
Ben
2
Ejemplo de este efecto: facebook.com (al abrir imágenes en la caja de luz)
Esta es una buena pregunta. Sin embargo, es una situación triste que si esta pregunta se hubiera formulado de esta manera hoy, habría sido rechazada y apoyada por el "este no es el tipo de sitio en el que hace que la gente escriba todo su código por usted".
Rocío

Respuestas:

118

Con HTML 5, use la history.pushStatefunción . Como ejemplo:

<script type="text/javascript">
var stateObj = { foo: "bar" };
function change_my_url()
{
   history.pushState(stateObj, "page 2", "bar.html");
}
var link = document.getElementById('click');
link.addEventListener('click', change_my_url, false);
</script>

y un href:

<a href="#" id='click'>Click to change url to bar.html</a>

Si desea cambiar la URL sin agregar una entrada a la lista de botones de retroceso, use history.replaceStateen su lugar.

clu3
fuente
Tienes razón, la última vez que experimenté con Chrome. Acabo de probar con Firefox 3.6 en mi Ubuntu, no funcionó. Supongo que tendremos que esperar hasta que HTML5 sea totalmente compatible con FF
clu3
15
El código de muestra se corta y pega desde developer.mozilla.org/en/DOM/Manipulating_the_browser_history . Lo que realmente molesta al explicar qué significan foo y bar en este caso.
mikemaccana 01 de
2
Foo y bar no significan nada, es un objeto de estado definido arbitrariamente que puede recuperar más tarde.
Paul Dixon
3
@Paul: sí, el artículo anterior proporciona esa información.
mikemaccana 01 de
2
A partir de la publicación de este comentario, funciona en Firefox Chrome y Safari. Sin embargo, no hubo suerte con el safari móvil en el ipad o el iphone :(
Sid
187

Si desea que funcione en navegadores que no son compatibles history.pushStatey history.popState, sin embargo, la forma "antigua" es establecer el identificador de fragmento, lo que no provocará una recarga de la página.

La idea básica es establecer la window.location.hashpropiedad en un valor que contenga cualquier información de estado que necesite, luego usar el evento window.onhashchange o para navegadores más antiguos que no son compatibles onhashchange(IE <8, Firefox <3.6), verifique periódicamente para vea si el hash ha cambiado (usando, setIntervalpor ejemplo) y actualice la página. También deberá verificar el valor hash en la carga de la página para configurar el contenido inicial.

Si está utilizando jQuery, hay un complemento de hashchange que utilizará cualquier método que admita el navegador. Estoy seguro de que también hay complementos para otras bibliotecas.

Una cosa a tener en cuenta es colisionar con identificadores en la página, porque el navegador se desplazará a cualquier elemento con una identificación coincidente.

Matthew Crumley
fuente
2
¿Los motores de búsqueda no ignoran estos enlaces internos (hashes)? En mi caso, también quiero que las arañas se den cuenta de estos enlaces.
Drew Noakes
3
@Drew, que yo sepa, no hay forma de que los motores de búsqueda traten una parte de una página como un resultado separado.
Matthew Crumley
8
Ahora hay una propuesta para hacer que los motores de búsqueda vean hashes: googlewebmastercentral.blogspot.com/2009/10/…
LKM
55
En lugar de usar setIntervalpara inspeccionar, document.location.hashuno puede usar el hashchangeevento, ya que es mucho más adoptado. Verifique aquí qué navegadores admiten cada estándar . Mi enfoque actual es usar push / popState en los navegadores que lo admiten. En los demás configuro el hash y solo miro los hashchangenavegadores compatibles. Esto deja a IE <= 7 sin soporte de historial, pero con un enlace permanente útil. Pero, ¿a quién le importa la historia de IE <= 7?
Irae Carvalho
2
@gabeDel Sí, aunque no creo que Analytics registre el hash, por lo que tendría la misma URL. Una mejor manera sería llamar manualmente _trackPageviewcon la URL "real" de la nueva página.
Matthew Crumley
37

window.location.href contiene la URL actual. Puede leerlo, puede agregarlo y puede reemplazarlo, lo que puede provocar una recarga de la página.

Si, como parece, desea registrar el estado de JavaScript en la URL para que se pueda agregar a favoritos, sin volver a cargar la página, añádala a la URL actual después de un # y haga que un elemento de JavaScript activado por el evento de carga analice el actual URL para ver si contiene estado guardado.

Si usas un? en lugar de un #, forzará una recarga de la página, pero dado que analizará el estado guardado en la carga, esto puede no ser realmente un problema; y esto hará que los botones de avance y retroceso funcionen correctamente también.

sombra de Luna
fuente
2
grabar el estado de javascript en la URL es exactamente lo que estoy tratando de hacer
Steven Noble
21

Sospecharía firmemente que esto no es posible, porque sería un problema de seguridad increíble si lo fuera. Por ejemplo, podría hacer una página que pareciera una página de inicio de sesión bancaria, ¡y hacer que la URL en la barra de direcciones se parezca al banco real !

Quizás si explican por qué quieren hacer esto, la gente podría sugerir enfoques alternativos ...

[Editar en 2011: desde que escribí esta respuesta en 2008, ha salido a la luz más información sobre una técnica HTML5 que permite modificar la URL siempre que sea del mismo origen]

Paul Dixon
fuente
66
No es un gran problema si los cambios que no son de recarga se restringieron a la ruta, la cadena de consulta y el fragmento, es decir, no a la autoridad (dominio y tal).
Ollie Saunders
2
youtube.com/user/SilkyDKwan#p/u/2/gTfqaGYVfMg es un ejemplo de que es posible, intente cambiar videos :)
Spidfire
Solo puede cambiar la ubicación.hash (todo después del #), como Matthew Chumley señala en la respuesta aceptada.
Paul Dixon
2
¿Usas Facebook? Facebook cambia la URL sin recargar, usando history.pushState().
Derek 朕 會 功夫
Paul Dixon, debe notar la bandeja de entrada de Facebook, cuando hace clic en los nombres en el lado izquierdo, solo se cambia la URL y se carga el nuevo chat en el cuadro de chat, la página no se actualiza. también hay muchos más sitios en WebGL que hacen lo mismo
Dheeraj Thedijje
8

La configuración de seguridad del navegador evita que las personas modifiquen la URL mostrada directamente. Podrías imaginar las vulnerabilidades de phishing que causarían.

La única forma confiable de cambiar la URL sin cambiar las páginas es usar un enlace interno o hash. por ejemplo: http://site.com/page.html se convierte en http://site.com/page.html#item1 . Esta técnica se usa a menudo en hijax (AJAX + preservar historial).

Al hacer esto, a menudo solo usaré enlaces para las acciones con el hash como href, luego agregaré eventos de clic con jquery que usan el hash solicitado para determinar y delegar la acción.

Espero que eso te ponga en el camino correcto.

Jethro Larson
fuente
Lo siento pero tu respuesta no es cierta. Sí, puedes cambiar la URL.
Derek 朕 會 功夫
1
Sí, puede usar la API de historial html5, pero no es un navegador cruzado, aunque puede usar un relleno de polietileno que recurrirá a las URL hash.
Jethro Larson
8

jQuery tiene un gran complemento para cambiar la URL de los navegadores, llamado jQuery-pusher .

JavaScript pushStatey jQuery podrían usarse juntos, como:

history.pushState(null, null, $(this).attr('href'));

Ejemplo:

$('a').click(function (event) {

  // Prevent default click action
  event.preventDefault();     

  // Detect if pushState is available
  if(history.pushState) {
    history.pushState(null, null, $(this).attr('href'));
  }
  return false;
});

Usando solo JavaScript history.pushState() , que cambia el referente, que se usa en el encabezado HTTP para los objetos XMLHttpRequest creados después de cambiar el estado.

Ejemplo:

window.history.pushState("object", "Your New Title", "/new-url");

El método pushState ():

pushState()toma tres parámetros: un objeto de estado, un título (que actualmente se ignora) y (opcionalmente) una URL. Examinemos cada uno de estos tres parámetros con más detalle:

  1. objeto de estado: el objeto de estado es un objeto de JavaScript que está asociado con la nueva entrada del historial creada por pushState(). Cada vez que el usuario navega al nuevo estado, se dispara un evento popstate y la propiedad de estado del evento contiene una copia del objeto de estado de la entrada del historial.

    El objeto de estado puede ser cualquier cosa que se pueda serializar. Debido a que Firefox guarda los objetos de estado en el disco del usuario para que puedan restaurarse después de que el usuario reinicie su navegador, imponemos un límite de tamaño de 640k caracteres en la representación serializada de un objeto de estado. Si pasa un objeto de estado cuya representación serializada es más grande que esto pushState(), el método arrojará una excepción. Si necesita más espacio que este, le recomendamos que use sessionStorage y / o localStorage.

  2. title : Firefox actualmente ignora este parámetro, aunque puede usarlo en el futuro. Pasar la cadena vacía aquí debería estar a salvo de futuros cambios en el método. Alternativamente, puede pasar un título corto para el estado al que se está mudando.

  3. URL : este parámetro proporciona la URL de la nueva entrada del historial. Tenga en cuenta que el navegador no intentará cargar esta URL después de una llamada a pushState(), pero podría intentar cargar la URL más tarde, por ejemplo, después de que el usuario reinicie su navegador. La nueva URL no necesita ser absoluta; si es relativo, se resuelve en relación con la URL actual. La nueva URL debe ser del mismo origen que la URL actual; de lo contrario, pushState()arrojará una excepción. Este parámetro es opcional; si no se especifica, se establece en la URL actual del documento.

Ilia Rostovtsev
fuente
3

La galería de fotos de Facebook hace esto usando un #hash en la URL. Aquí hay algunos ejemplos de URL:

Antes de hacer clic en 'siguiente':

/photo.php?fbid=496429237507&set=a.218088072507.133423.681812507&pid=5887027&id=681812507

Después de hacer clic en "siguiente":

/photo.php?fbid=496429237507&set=a.218088072507.133423.681812507&pid=5887027&id=681812507#!/photo.php?fbid=496435457507&set=a.218088072507.133423.681812507&pid=5887085&id=681812507

Tenga en cuenta el hash-bang (#!) Seguido inmediatamente por la nueva URL.

Jonathon Hill
fuente
2

Lo que funciona para mí es - history.replaceState()función que es la siguiente -

history.replaceState(data,"Title of page"[,'url-of-the-page']);

Esto no volverá a cargar la página, puede usarla con el evento de javascript

Veshraj Joshi
fuente
1

Me preguntaba si será posible siempre que la ruta principal en la página sea la misma, solo se le agrega algo nuevo.

Entonces, digamos que el usuario está en la página: http://domain.com/site/page.html entonces el navegador puede dejarme hacerlo location.append = new.html y la página se vuelve: http://domain.com/site/page.htmlnew.htmly el navegador no la cambia.

O simplemente permita que la persona cambie el parámetro get, así que vamos location.get = me=1&page=1.

Entonces la página original se vuelve http://domain.com/site/page.html?me=1&page=1y no se actualiza.

El problema con # es que los datos no se almacenan en caché (al menos no lo creo) cuando se cambia el hash. Por lo tanto, es como cada vez que se carga una nueva página, mientras que los botones de retroceso y avance en una página que no es Ajax pueden almacenar datos en caché y no pasan tiempo en volver a cargar los datos.

Por lo que vi, la historia de Yahoo ya carga todos los datos a la vez. No parece estar haciendo ninguna solicitud de Ajax. Entonces, cuando divse usa a para manejar horas extras de métodos diferentes, esos datos no se almacenan para cada estado del historial.

usuario58670
fuente
1

mi código es:

//change address bar
function setLocation(curLoc){
    try {
        history.pushState(null, null, curLoc);
        return false;
    } catch(e) {}
        location.hash = '#' + curLoc;
}

y acción:

setLocation('http://example.com/your-url-here');

y ejemplo

$(document).ready(function(){
    $('nav li a').on('click', function(){
        if($(this).hasClass('active')) {

        } else {
            setLocation($(this).attr('href'));
        }
            return false;
    });
});

Eso es todo :)

Tusko Trush
fuente
1

Una respuesta más simple que presento,

window.history.pushState(null, null, "/abc")

esto se agregará /abcdespués del nombre de dominio en la URL del navegador. Simplemente copie este código y péguelo en la consola del navegador y vea que la URL cambia a " https://stackoverflow.com/abc "

Sam
fuente
0

He tenido éxito con:

location.hash="myValue";

Simplemente se agrega #myValuea la URL actual. Si necesita activar un evento en la carga de la página, puede usar el mismo location.hashpara verificar el valor relevante. Solo recuerde eliminar #el valor devuelto por location.hashej.

var articleId = window.location.hash.replace("#","");
Adam Hey
fuente