replacestate refrescar onpopstate mdn example event change cambiar html5 google-chrome javascript-events browser-history

html5 - refrescar - Popstate en la carga de la página en Chrome



window history pushstate refresh (15)

Aquí está mi solución:

var _firstload = true; $(function(){ window.onpopstate = function(event){ var state = event.state; if(_firstload && !state){ _firstload = false; } else if(state){ _firstload = false; // you should pass state.some_data to another function here alert(''state was changed! back/forward button was pressed!''); } else{ _firstload = false; // you should inform some function that the original state returned alert(''you returned back to the original state (the home state)''); } } })

Estoy usando History API para mi aplicación web y tengo un problema. Hago llamadas a Ajax para actualizar algunos resultados en la página y uso history.pushState () para actualizar la barra de ubicación del navegador sin recargar la página. Luego, por supuesto, uso window.popstate para restaurar el estado anterior cuando se hace clic en el botón de retroceso.

El problema es bien conocido: Chrome y Firefox tratan ese evento popstate de manera diferente. Si bien Firefox no lo activa en la primera carga, Chrome sí lo hace. Me gustaría tener el estilo de Firefox y no activar el evento con carga, ya que solo actualiza los resultados con exactamente los mismos en la carga. ¿Hay una solución alternativa excepto el uso de History.js ? La razón por la que no tengo ganas de usarlo es que necesita demasiadas bibliotecas JS por sí mismo y, como necesito que se implemente en un CMS con demasiadas JS, me gustaría minimizar el JS que estoy poniendo en él. .

Por lo tanto, me gustaría saber si hay una forma de que Chrome no inicie ''popstate'' en la carga o, tal vez, alguien intente utilizar History.js como todas las bibliotecas combinadas en un solo archivo.


El evento onpopstate inicial de Webkit no tiene un estado asignado, por lo que puede usarlo para verificar el comportamiento no deseado:

window.onpopstate = function(e){ if(e.state) //do something };

Una solución integral, que permite la navegación de vuelta a la página original, se basaría en esta idea:

<body onload="init()"> <a href="page1" onclick="doClick(this); return false;">page 1</a> <a href="page2" onclick="doClick(this); return false;">page 2</a> <div id="content"></div> </body> <script> function init(){ openURL(window.location.href); } function doClick(e){ if(window.history.pushState) openURL(e.getAttribute(''href''), true); else window.open(e.getAttribute(''href''), ''_self''); } function openURL(href, push){ document.getElementById(''content'').innerHTML = href + '': '' + (push ? ''user'' : ''browser''); if(window.history.pushState){ if(push) window.history.pushState({href: href}, ''your page title'', href); else window.history.replaceState({href: href}, ''your page title'', href); } } window.onpopstate = function(e){ if(e.state) openURL(e.state.href); }; </script>

Si bien esto aún podría disparar dos veces (con alguna navegación ingeniosa), se puede manejar simplemente con un control contra el href anterior.


En Google Chrome en la versión 19, la solución de @spliter dejó de funcionar. Como @johnnymire señaló, history.state en Chrome 19 existe, pero es nulo.

Mi solución es agregar window.history.state! == null para verificar si el estado existe en window.history:

var popped = (''state'' in window.history && window.history.state !== null), initialURL = location.href;

Lo probé en todos los principales navegadores y en las versiones de Chrome 19 y 18. Parece que funciona.


En caso de que no desee tomar medidas especiales para cada controlador que agregue a onpopstate, mi solución puede ser interesante para usted. Una gran ventaja de esta solución es que los eventos en estado de porpop se pueden manejar antes de que la carga de la página haya finalizado.

Simplemente ejecute este código una vez antes de agregar cualquier controlador onpopstate y todo debería funcionar como se espera (también conocido como Mozilla ^^) .

(function() { // There''s nothing to do for older browsers ;) if (!window.addEventListener) return; var blockPopstateEvent = document.readyState!="complete"; window.addEventListener("load", function() { // The timeout ensures that popstate-events will be unblocked right // after the load event occured, but not in the same event-loop cycle. setTimeout(function(){ blockPopstateEvent = false; }, 0); }, false); window.addEventListener("popstate", function(evt) { if (blockPopstateEvent && document.readyState=="complete") { evt.preventDefault(); evt.stopImmediatePropagation(); } }, false); })();

Cómo funciona:

Chrome , Safari y probablemente otros navegadores webkit activan el evento onpopstate cuando el documento se ha cargado. Esto no es intencionado, por lo que bloqueamos los eventos de estado emergente hasta que se haya cargado el primer ciclo de ciclo de evento después de que se haya cargado el documento. Esto se hace mediante las llamadas preventDefault y stopImmediatePropagation (a diferencia de stopPropagation stopImmediatePropagation detiene todas las llamadas al controlador de eventos al instante).

Sin embargo, dado que el readyState del documento ya está "completo" cuando Chrome activa el estado de error erróneamente, permitimos eventos opopstate, que se han activado antes de que la carga del documento haya finalizado para permitir llamadas de estado público antes de que el documento se haya cargado.

Actualización 2014-04-23: Se corrigió un error por el cual se bloquearon los eventos de estado emergente si el script se ejecuta después de que se cargó la página.


En caso de usar event.state !== null volver al historial de la primera página cargada no funcionará en navegadores que no sean móviles. Yo uso sessionStorage para marcar cuando realmente comienza la navegación ajax.

history.pushState(url, null, url); sessionStorage.ajNavStarted = true; window.addEventListener(''popstate'', function(e) { if (sessionStorage.ajNavStarted) { location.href = (e.state === null) ? location.href : e.state; } }, false);


Esta es mi solución.

window.setTimeout(function() { window.addEventListener(''popstate'', function() { // ... }); }, 1000);


Esto funcionó para mí en Firefox y Chrome

window.onpopstate = function(event) { //back button click console.log("onpopstate"); if (event.state) { window.location.reload(); } };


Esto resolvió mi problema. Todo lo que hice fue establecer una función de tiempo de espera que retrasa la ejecución de la función el tiempo suficiente para perder el evento de estado emergente que se activa en la carga de la página.

if (history && history.pushState) { setTimeout(function(){ $(window).bind("popstate", function() { $.getScript(location.href); }); },3000); }



La solución se ha encontrado en jquery.pjax.js líneas jquery.pjax.js :

// Used to detect initial (useless) popstate. // If history.state exists, assume browser isn''t going to fire initial popstate. var popped = (''state'' in window.history), initialURL = location.href // popstate handler takes care of the back and forward buttons // // You probably shouldn''t use pjax on pages with other pushState // stuff yet. $(window).bind(''popstate'', function(event){ // Ignore inital popstate that some browsers fire on page load var initialPop = !popped && location.href == initialURL popped = true if ( initialPop ) return var state = event.state if ( state && state.pjax ) { var container = state.pjax if ( $(container+'''').length ) $.pjax({ url: state.url || location.href, fragment: state.fragment, container: container, push: false, timeout: state.timeout }) else window.location = location.href } })


Las soluciones presentadas tienen un problema en la recarga de la página. Lo siguiente parece funcionar mejor, pero solo he probado Firefox y Chrome. Utiliza la realidad, que parece haber una diferencia entre e.event.state y window.history.state .

window.addEvent(''popstate'', function(e) { if(e.event.state) { window.location.reload(); // Event code } });


Puede crear un evento y activarlo después de su controlador de carga.

var evt = document.createEvent("PopStateEvent"); evt.initPopStateEvent("popstate", false, false, { .. state object ..}); window.dispatchEvent(evt);

Tenga en cuenta que esto está un poco roto en Chrome / Safari, pero he enviado el parche a WebKit y debería estar disponible pronto, pero es la forma "más correcta".


Sé que preguntaste en contra, pero en realidad deberías usar History.js ya que borra un millón de incompatibilidades del navegador. Pasé por la ruta de reparación manual solo para descubrir más adelante que había más y más problemas que solo descubrirías en el futuro. Realmente no es tan difícil hoy en día:

<script src="//cdnjs.cloudflare.com/ajax/libs/history.js/1.8/native.history.min.js" type="text/javascript"></script>

Y lea la API en https://github.com/browserstate/history.js


Una solución más directa que reimplementar pjax es establecer una variable en pushState, y verificar la variable en popState, por lo que el popState inicial no dispara incoherentemente en la carga (no es una solución específica de jquery, solo la usa para eventos):

$(window).bind(''popstate'', function (ev){ if (!window.history.ready && !ev.originalEvent.state) return; // workaround for popstate on load }); // ... later ... function doNavigation(nextPageId) { window.history.ready = true; history.pushState(state, null, ''content.php?id=''+ nextPageId); // ajax in content instead of loading server-side }


Usar setTimeout solo no es una solución correcta porque no tienes idea de cuánto tiempo tardará en cargarse el contenido, por lo que es posible que el evento de estado emergente se emita después del tiempo de espera.

Aquí está mi solución: https://gist.github.com/3551566

/* * Necessary hack because WebKit fires a popstate event on document load * https://code.google.com/p/chromium/issues/detail?id=63040 * https://bugs.webkit.org/process_bug.cgi */ window.addEventListener(''load'', function() { setTimeout(function() { window.addEventListener(''popstate'', function() { ... }); }, 0); });