On - window.location.hash - ¿Cambiar?

558

Estoy usando Ajax y hash para la navegación.

¿Hay alguna manera de verificar si ha window.location.hashcambiado así?

http://example.com/blah # 123 a http://example.com/blah # 456

Funciona si lo reviso cuando se carga el documento.

Pero si tengo una navegación basada en #hash no funciona cuando presiono el botón Atrás en el navegador (así que salto de blah # 456 a blah # 123).

Se muestra dentro del cuadro de dirección, pero no puedo detectarlo con JavaScript.

MilMike
fuente
99
History.js es compatible con la funcionalidad de administración de estado HTML5 (por lo que ya no necesita usar hashes) y lo degrada con gracia a los navegadores HTML4 utilizando hashchanges. Es compatible con jQuery, MooTools y Prototype de fábrica.
Balupton
@balupton, En realidad, todavía necesitamos usar hashes para proporcionar comentarios al usuario de que se ha insertado una "nueva página" en su historial, a menos que use el cambio de URL como comentario.
Pacerier
1
[Hasher] github.com/millermedeiros/hasher
Vishnoo Rath
hmm ... creo que necesitas moar jQuery
RaisingAgent

Respuestas:

617

La única forma de hacer esto realmente (y es cómo la 'historia realmente simple' hace esto), es estableciendo un intervalo que siga verificando el hash actual y comparándolo con lo que era antes, hacemos esto y permitimos que los suscriptores se suscriban a un cambio evento que activamos si el hash cambia ... no es perfecto, pero los navegadores realmente no admiten este evento de forma nativa.


Actualice para mantener esta respuesta fresca:

Si está utilizando jQuery (que hoy debería ser algo fundamental para la mayoría), entonces una buena solución es utilizar la abstracción que le proporciona jQuery utilizando su sistema de eventos para escuchar eventos de cambio de hash en el objeto de la ventana.

$(window).on('hashchange', function() {
  //.. work ..
});

Lo bueno aquí es que puede escribir código que ni siquiera tiene que preocuparse por el soporte de hashchange, sin embargo, necesita hacer algo de magia, en forma de una característica jQuery algo menos conocida de eventos especiales jQuery .

Con esta característica, esencialmente puede ejecutar algún código de configuración para cualquier evento, la primera vez que alguien intenta usar el evento de alguna manera (como el enlace al evento).

En este código de configuración, puede verificar la compatibilidad con el navegador nativo y, si el navegador no lo implementa de forma nativa, puede configurar un solo temporizador para sondear los cambios y activar el evento jQuery.

Esto libera por completo su código de la necesidad de comprender este problema de soporte, la implementación de un evento especial de este tipo es trivial (para obtener una versión de trabajo simple del 98%), pero ¿por qué hacerlo cuando alguien más ya lo ha hecho ?

meandmycode
fuente
77
IE8 lo hace. Más por venir
Sergey Ilinsky
28
La última versión de Firefox (3.6 alpha) ahora también admite el evento de cambio hash nativo: developer.mozilla.org/en/DOM/window.onhashchange Sin duda vale la pena hacer una comprobación para este evento, pero tenga en cuenta que IE8 le informará el evento existe cuando se ejecuta en modo de compatibilidad con IE7 ... lamentablemente, el evento no se dispara ... deberá verificar el evento y que el navegador no parece ser IE7 ... suspiro (o intentar activar el evento) con el método fireEvent de IE).
meandmycode
8
Al momento de escribir, WebKit también dispara hashchangeeventos, mientras que Safari (estable) aún no.
jholster
51
Solo para agregar otra actualización, el hashchangeevento ahora es ampliamente compatible: caniuse.com/#search=hash
Paystey
19
¿Soy el único que piensa que las respuestas no solicitadas de jQuery son un dolor?
Luc
290

HTML5 especifica un hashchangeevento . Este evento ahora es compatible con todos los navegadores modernos . Se agregó soporte en las siguientes versiones del navegador:

  • Internet Explorer 8
  • Firefox 3.6
  • Chrome 5
  • Safari 5
  • Opera 10.6
Millas
fuente
20
Actualización: FF 5, Safari 5 y Chrome 12 admiten este evento a partir de junio de 2011.
james.garriss
2
Aquí está la página CanIUse para hashchange . Aquí hay cambio de hash en quirksmode . El soporte de IE es defectuoso con respecto a mayúsculas y minúsculas.
Tobu
3
@todos los demás, no es necesario que sigas agregando la respuesta en la sección de comentarios, para eso está el botón "Editar". :)
Michael Martin-Smucker
15
uso:window.onhashchange = function() { doYourStuff(); }
Chris
44
Documentación MDN del evento hashchange .
Dabowheel
52

Tenga en cuenta que en el caso de Internet Explorer 7 e Internet Explorer 9, la ifdeclaración dará verdadero (para "onhashchange" en Windows), pero window.onhashchangenunca se disparará, por lo que es mejor almacenar hash y verificarlo después de cada 100 milisegundos, ya sea que haya cambiado o no. para todas las versiones de Internet Explorer.

    if (("onhashchange" in window) && !($.browser.msie)) {
         window.onhashchange = function () {
              alert(window.location.hash);
         }
         // Or $(window).bind( 'hashchange',function(e) {
         //       alert(window.location.hash);
         //   });
    }
    else {
        var prevHash = window.location.hash;
        window.setInterval(function () {
           if (window.location.hash != prevHash) {
              prevHash = window.location.hash;
              alert(window.location.hash);
           }
        }, 100);
    }

EDITAR: desde jQuery 1.9, $.browser.msieno es compatible. Fuente: http://api.jquery.com/jquery.browser/

Khan Salahuddin
fuente
14

Hay muchos trucos para tratar con History y window.location.hash en los navegadores IE:

  • Como decía la pregunta original, si va de la página a.html # b a a.html # c, y luego presiona el botón Atrás, el navegador no sabe que la página ha cambiado. Permítanme decirlo con un ejemplo: window.location.href será 'a.html # c', sin importar si está en a.html # b o a.html # c.

  • En realidad, a.html # by a.html # c se almacenan en el historial solo si los elementos '<a name="#b">' y '<a name="#c">' existen previamente en la página.

  • Sin embargo, si coloca un iframe dentro de una página, navegue de a.html # b a a.html # c en ese iframe y luego presione el botón Atrás, iframe.contentWindow.document.location.href cambia como se esperaba.

  • Si usa 'document.domain = something ' en su código, entonces no puede acceder a iframe.contentWindow.document.open () '(y muchos administradores de historia lo hacen)

Sé que esta no es una respuesta real, pero quizás las notas de IE-History sean útiles para alguien.

Sergio Cinos
fuente
11

Ben Alman tiene un excelente complemento de jQuery para hacer frente a esto: http://benalman.com/projects/jquery-hashchange-plugin/

Si no está utilizando jQuery, puede ser una referencia interesante para diseccionar.

CJ.
fuente
El complemento de Ben Alman parece que ya no se mantiene. Sin embargo, hay varias horquillas.
Mnebuerquo
9

Puede implementar fácilmente un observador (el método "watch") en la propiedad "hash" del objeto "window.location".

Firefox tiene su propia implementación para observar los cambios de objeto , pero si usa alguna otra implementación (como Observar cambios de propiedades de objetos en JavaScript ), para otros navegadores, eso será el truco.

El código se verá así:

window.location.watch(
    'hash',
    function(id,oldVal,newVal){
        console.log("the window's hash value has changed from "+oldval+" to "+newVal);
    }
);

Entonces puedes probarlo:

var myHashLink = "home";
window.location = window.location + "#" + myHashLink;

Y, por supuesto, eso activará su función de observador.

gion_13
fuente
Mejor uso: window.location.href en lugar de window.location.
Codebeat
3
Él está mirando window.location.hash, no window.location.
indefinido el
1
@BrianMortenson: de acuerdo con los documentos ( developer.mozilla.org/en-US/docs/JavaScript/Reference/… ) debe aplicar watchal objeto que posee la propiedad que está cambiando y desea observarlo.
gion_13
@ gion_13 Sí, eso es exactamente lo que estaba tratando de señalar. Por "Él" me refería a ti, y estaba dirigido al comentario de Erwinus. Debería haber sido más claro. Gracias por tu comentario aclaratorio.
indefinido el
7

Estaba usando esto en una aplicación de reacción para hacer que la URL muestre diferentes parámetros dependiendo de la vista en la que se encontraba el usuario.

Vi el parámetro hash usando

window.addEventListener('hashchange', doSomethingWithChangeFunction());

Entonces

doSomethingWithChangeFunction () { 
    // Get new hash value
    let urlParam = window.location.hash;
    // Do something with new hash value
};

Trabajó de maravilla, funciona con los botones del navegador hacia adelante y hacia atrás y también en el historial del navegador.

Sprose
fuente
1
en su addEventListenerllamada, se debe quitar la ()dedoSomethingWithChangeFunction
Jason S
¿Puede proporcionar una razón para eliminar el ()? Sé que funcionará con cualquiera de los dos, y menos código es en su mayoría la mejor opción, pero esto parece exigente a menos que haya una razón sustancial para respaldar esto.
Sprose
66
? no se debe trabajar con (). La addEventListenerfunción requiere que pases una función. doSomethingWithChangeFunctionEs una función. doSomethingWithChangeFunction()es el valor de retorno de esa función, que en este caso no es una función.
Jason S
6

Se puede encontrar una implementación decente en http://code.google.com/p/reallysimplehistory/ . El único (pero también) problema y error que tiene es: en Internet Explorer, la modificación manual del hash de ubicación restablecerá toda la pila del historial (este es un problema del navegador y no se puede resolver).

Tenga en cuenta que Internet Explorer 8 tiene soporte para el evento "hashchange", y dado que se está convirtiendo en parte de HTML5, puede esperar que otros navegadores se pongan al día.

Sergey Ilinsky
fuente
1

Otra gran implementación es jQuery History, que utilizará el evento onhashchange nativo si es compatible con el navegador; de lo contrario, utilizará un iframe o intervalo apropiado para que el navegador garantice que toda la funcionalidad esperada se emule con éxito. También proporciona una buena interfaz para vincularse a ciertos estados.

Otro proyecto que vale la pena destacar también es jQuery Ajaxy, que es más o menos una extensión para jQuery History para agregar ajax a la mezcla. Como cuando comienzas a usar ajax con hashes, ¡es bastante complicado !

Balupton
fuente
1
var page_url = 'http://www.yoursite.com/'; // full path leading up to hash;
var current_url_w_hash = page_url + window.location.hash; // now you might have something like: http://www.yoursite.com/#123

function TrackHash() {
    if (document.location != page_url + current_url_w_hash) {
        window.location = document.location;
    }
    return false;
}
var RunTabs = setInterval(TrackHash, 200);

Eso es todo ... ahora, cada vez que presionas los botones hacia atrás o hacia adelante, la página se volverá a cargar según el nuevo valor hash.

hombre murciélago
fuente
no use cadenas eval
Vitim.us
1

He estado usando path.js para mi enrutamiento del lado del cliente. He descubierto que es bastante sucinto y ligero (también se ha publicado en NPM), y hace uso de la navegación basada en hash.

path.js NPM

path.js GitHub

Tom
fuente
0

Usé un complemento jQuery, HUtil , y escribí un YUI interfaz similar a la de History.

Compruébalo una vez. Si necesitas ayuda, puedo ayudarte.

moha297
fuente