javascript - replacestate - ¿Cómo recibir notificaciones sobre cambios en el historial a través de history.pushState?
window history replacestate({} (8)
5.5.9.1 Definiciones de eventos
El evento de estado emergente se activa en ciertos casos cuando se navega hacia una entrada en el historial de la sesión.
De acuerdo con esto, no hay ninguna razón para que se active el estado emergente cuando usa pushState
. Pero un evento como pushstate
sería útil. Como la history
es un objeto host, debes tener cuidado con ella, pero Firefox parece ser bueno en este caso. Este código funciona bien:
(function(history){
var pushState = history.pushState;
history.pushState = function(state) {
if (typeof history.onpushstate == "function") {
history.onpushstate({state: state});
}
// ... whatever else you want to do
// maybe call onhashchange e.handler
return pushState.apply(history, arguments);
};
})(window.history);
Su jsfiddle se becomes :
window.onpopstate = history.onpushstate = function(e) { ... }
Puedes aplicar un parche a la window.history.replaceState
de la misma manera.
Nota: por supuesto, puede agregar onpushstate
simplemente al objeto global, e incluso puede hacer que maneje más eventos a través de add/removeListener
Entonces, ahora que HTML5 introduce history.pushState
para cambiar el historial de los navegadores, los sitios web comienzan a usar esto en combinación con Ajax en lugar de cambiar el identificador de fragmento de la URL.
Lamentablemente eso significa que esas llamadas ya no pueden ser detectadas por onhashchange
.
Mi pregunta es: ¿existe alguna forma confiable (¿hack?;) Para detectar cuándo un sitio web usa history.pushState
? La especificación no dice nada sobre los eventos que se plantean (al menos no pude encontrar nada).
Traté de crear una fachada y reemplacé window.history
con mi propio objeto JavaScript, pero no tuvo ningún efecto.
Más explicación: estoy desarrollando un complemento de Firefox que necesita detectar estos cambios y actuar en consecuencia.
Sé que hubo una pregunta similar hace unos días que preguntaba si escuchar algunos eventos DOM sería eficiente, pero preferiría no confiar en eso porque estos eventos pueden generarse por muchos motivos diferentes.
Actualizar:
Aquí hay un jsfiddle (usa Firefox 4 o Chrome 8) que muestra que onpopstate
no se activa cuando se llama a pushState
(¿o estoy haciendo algo mal? ¡Siéntete libre de mejorarlo!).
Actualización 2:
Otro problema (lateral) es que window.location
no se actualiza cuando se usa pushState
(pero ya lo he leído aquí en SO, creo).
Bueno, veo muchos ejemplos de pushState
reemplazar la propiedad pushState
de la history
pero no estoy seguro de que sea una buena idea. Prefiero crear un evento de servicio basado en una API similar a la historia. De esta forma puedes controlar no solo el estado del empuje, sino también reemplaza el estado y abre puertas para muchas otras implementaciones que no dependen de la API de historial global. Por favor verifique el siguiente ejemplo:
function HistoryAPI(history) {
EventEmitter.call(this);
this.history = history;
}
HistoryAPI.prototype = utils.inherits(EventEmitter.prototype);
const prototype = {
pushState: function(state, title, pathname){
this.emit(''pushstate'', state, title, pathname);
this.history.pushState(state, title, pathname);
},
replaceState: function(state, title, pathname){
this.emit(''replacestate'', state, title, pathname);
this.history.replaceState(state, title, pathname);
}
};
Object.keys(prototype).forEach(key => {
HistoryAPI.prototype = prototype[key];
});
Si necesita la definición EventEmitter
, el código anterior se basa en el emisor del evento NodeJS: https://github.com/nodejs/node/blob/36732084db9d0ff59b6ce31e839450cd91a156be/lib/events.js . utils.inherits
implementación de utils.inherits
se puede encontrar aquí: https://github.com/nodejs/node/blob/36732084db9d0ff59b6ce31e839450cd91a156be/lib/util.js#L970
Como estados estándar:
Tenga en cuenta que simplemente al llamar a history.pushState () o history.replaceState () no se activará un evento de estado emergente. El evento popstate solo se desencadena al realizar una acción del navegador, como hacer clic en el botón Atrás (o invocar history.back () en JavaScript)
necesitamos llamar a history.back () para trigeer WindowEventHandlers.onpopstate
Muy insensible a:
history.pushState(...)
hacer:
history.pushState(...)
history.pushState(...)
history.back()
Creo que este tema necesita una solución más moderna.
Estoy seguro de que nsIWebProgressListener
existía en aquel momento. Me sorprende que nadie lo haya mencionado.
Desde un marco de lectura (para compatibilidad con e10s):
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress);
webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW | Ci.nsIWebProgress.NOTIFY_LOCATION);
Luego escucha en el cambio de onLoacationChange
onLocationChange: function onLocationChange(webProgress, request, locationURI, flags) {
if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT
Aparentemente capturará todos los de PushState. Pero hay un comentario que advierte que "TAMBIÉN activa los estados pushState". Así que tenemos que hacer un poco más de filtrado aquí para asegurarnos de que solo se trata de cosas del estado de empuje.
Y: resource: //gre/modules/WebNavigationContent.js
Podría vincularse al evento window.onpopstate
?
https://developer.mozilla.org/en/DOM%3awindow.onpopstate
De los documentos:
Un controlador de eventos para el evento popstate en la ventana.
Un evento de estado emergente se envía a la ventana cada vez que cambia la entrada del historial activo. Si la entrada del historial que se está activando se creó mediante una llamada a history.pushState () o se vio afectada por una llamada a history.replaceState (), la propiedad de estado del evento popstate contiene una copia del objeto de estado de la entrada del historial.
Solía usar esto:
var _wr = function(type) {
var orig = history[type];
return function() {
var rv = orig.apply(this, arguments);
var e = new Event(type);
e.arguments = arguments;
window.dispatchEvent(e);
return rv;
};
};
history.pushState = _wr(''pushState''), history.replaceState = _wr(''replaceState'');
window.addEventListener(''replaceState'', function(e) {
console.warn(''THEY DID IT AGAIN!'');
});
Es casi lo mismo que los galambalazs .
Sin embargo, suele ser exagerado. Y podría no funcionar en todos los navegadores. (Solo me importa mi versión de mi navegador).
(Y deja una var _wr
, por lo que es posible que desee envolverlo o algo así. No me importó eso).
Ya que estás preguntando por un complemento de Firefox, aquí está el código que tengo para trabajar. El uso de unsafeWindow
ya no se recomienda , y los errores se producen cuando se llama a pushState desde un script del cliente después de ser modificado:
Permiso denegado para acceder a la propiedad history.pushState
En cambio, hay una API llamada exportFunction que permite que la función se inyecte en window.history
esta manera:
var pushState = history.pushState;
function pushStateHack (state) {
if (typeof history.onpushstate == "function") {
history.onpushstate({state: state});
}
return pushState.apply(history, arguments);
}
history.onpushstate = function(state) {
// callback here
}
exportFunction(pushStateHack, unsafeWindow.history, {defineAs: ''pushState'', allowCallbacks: true});
La respuesta de window.history.pushState
parchea window.history.pushState
y window.history.replaceState
, pero por alguna razón dejó de funcionar para mí. Aquí hay una alternativa que no es tan buena porque usa encuestas:
(function() {
var previousState = window.history.state;
setInterval(function() {
if (previousState !== window.history.state) {
previousState = window.history.state;
myCallback();
}
}, 100);
})();