javascript - react - window history replacestate({}
Prevenir el desplazamiento del navegador en el historial de HTML5 popstate (11)
¿Podría querer probar esto?
window.onpopstate = function(event) {
return false;
};
¿Es posible evitar el comportamiento predeterminado de desplazar el documento cuando se produce un evento de estado emergente?
Nuestro sitio utiliza el desplazamiento animado jQuery y History.js, y los cambios de estado deben desplazar al usuario a diferentes áreas de la página ya sea a través de pushstate o popstate. El problema es que el navegador restaura automáticamente la posición de desplazamiento del estado anterior cuando ocurre un evento de estado emergente.
Intenté usar un conjunto de elementos de contenedor al 100% de ancho y alto del documento y desplazar el contenido dentro de ese contenedor. El problema con eso que he encontrado es que no parece ser tan fácil como desplazar el documento; especialmente si se usa mucho css3 como sombras de caja y degradados.
También intenté almacenar la posición de desplazamiento del documento durante un desplazamiento iniciado por el usuario y restaurarlo después de que el navegador recorra la página (en estado emergente). Esto funciona bien en Firefox 12 pero en Chrome 19 hay un parpadeo debido a que la página se desplaza y se restaura. Supongo que esto tiene que ver con un retraso entre el desplazamiento y el evento de desplazamiento que se dispara (donde se restaura la posición de desplazamiento).
Firefox desplaza la página (y desencadena el evento de desplazamiento) antes de que el estado emergente se dispare y Chrome dispara el estado emergente primero y luego se desplaza por el documento.
Todos los sitios que he visto que usan la API de historial usan una solución similar a las anteriores o simplemente ignoran el cambio de posición de desplazamiento cuando un usuario retrocede o avanza (por ejemplo, GitHub).
¿Es posible evitar que el documento se dispare en absoluto en eventos popstate?
Ahora puedes hacer
history.scrollRestoration = ''manual'';
y esto debería evitar el desplazamiento del navegador. Esto solo funciona ahora en Chrome 46 y versiones superiores, pero parece que Firefox está planeando apoyarlo también
Al igual que otros dijeron, no hay una forma real de hacerlo, solo feo estilo hacky. Eliminar el oyente de eventos de desplazamiento no funcionó para mí, así que esta es mi fea manera de hacerlo:
/// GLOBAL VARS ////////////////////////
var saveScrollPos = false;
var scrollPosSaved = window.pageYOffset;
////////////////////////////////////////
jQuery(document).ready(function($){
//// Go back with keyboard shortcuts ////
$(window).keydown(function(e){
var key = e.keyCode || e.charCode;
if((e.altKey && key == 37) || (e.altKey && key == 39) || key == 8)
saveScrollPos = false;
});
/////////////////////////////////////////
//// Go back with back button ////
$("html").bind("mouseout", function(){ saveScrollPos = false; });
//////////////////////////////////
$("html").bind("mousemove", function(){ saveScrollPos = true; });
$(window).scroll(function(){
if(saveScrollPos)
scrollPosSaved = window.pageYOffset;
else
window.scrollTo(0, scrollPosSaved);
});
});
Funciona en Chrome, FF e IE (parpadea la primera vez que vuelve en IE). ¡Cualquier sugerencia de mejora es bienvenida! Espero que esto ayude.
Cree un elemento ''span'' en algún lugar en la parte superior de la página y establezca el foco en esto en la carga. El navegador se desplazará al elemento enfocado. Entiendo que esto es una solución alternativa y que centrarse en ''span'' no funciona en todos los navegadores (uhmmm .. Safari). Espero que esto ayude.
Establecer scrollRestoration en manual no funcionó para mí, esta es mi solución.
window.addEventListener(''popstate'', function(e) {
var scrollTop = document.body.scrollTop;
window.addEventListener(''scroll'', function(e) {
document.body.scrollTop = scrollTop;
});
});
Este ha sido un problema informado con el núcleo de desarrolladores de mozilla desde hace más de un año . Desafortunadamente, el ticket realmente no progresó. Creo que Chrome es el mismo: no hay una manera confiable de abordar la posición de desplazamiento en estado de onpopstate
través de js, ya que es el comportamiento del navegador nativo.
Sin embargo, hay esperanza para el futuro, si nos fijamos en la especificación del historial de HTML5 , que explícitamente desea que la posición de desplazamiento se represente en el objeto de estado:
Los objetos de historial representan el historial de la sesión de su contexto de navegación como una lista plana de las entradas del historial de la sesión. Cada entrada del historial de sesión consiste en una URL y, opcionalmente, un objeto de estado, y puede además tener un título, un objeto de documento, datos de formulario, una posición de desplazamiento y otra información asociada a él.
Esto, y si lee los comentarios en el boleto de mozilla mencionado anteriormente, da alguna indicación de que es posible que en un futuro próximo la posición de desplazamiento ya no se restablezca en estado onpopstate
, al menos para las personas que usan el estado de pushState
.
Desafortunadamente, hasta entonces, la posición de desplazamiento se almacena cuando se usa replaceState
, y replaceState
no reemplaza la posición de desplazamiento. De lo contrario, sería bastante fácil, y podría usar replaceState
para establecer la posición de desplazamiento actual cada vez que el usuario haya desplazado la página (con algún controlador onscroll prudente).
Desafortunadamente, la especificación HTML5 no especifica cuándo se debe disparar exactamente el evento de estado popstate
; simplemente dice: «se dispara en ciertos casos cuando se navega a una entrada en el historial de la sesión», que no indica claramente si es anterior o posterior; si fue siempre antes, una solución para manejar el evento de desplazamiento que ocurre después del estado emergente sería posible.
Cancelar el evento de desplazamiento?
Además, también sería fácil, si el evento scroll fuera cancelable, lo cual no es posible. Si fuera así, simplemente podría cancelar el primer evento de desplazamiento de una serie (los eventos de desplazamiento del usuario son como lemmings, vienen en docenas, mientras que el evento de desplazamiento disparado por el reposicionamiento del historial es uno solo), y usted estaría bien.
No hay solución por ahora
Por lo que veo, lo único que recomendaría por ahora es esperar a que la especificación HTML5 se implemente por completo y aplicar el comportamiento del navegador en este caso, es decir: animar el desplazamiento cuando el navegador te permite hacerlo y permita que el navegador vuelva a colocar la página cuando haya un evento de historial. Lo único que puede influir en la posición es que use pushState
cuando la página esté en una buena posición para volver. Cualquier otra solución está destinada a tener errores, o ser demasiado específica del navegador, o ambas cosas.
Esto es lo que he implementado en un sitio que quería que la posición de desplazamiento se centrara en un elemento específico cuando se dispara el post-estado (botón Atrás):
$(document).ready(function () {
if (window.history.pushState) {
//if push supported - push current page onto stack:
window.history.pushState(null, document.title, document.location.href);
}
//add handler:
$(window).on(''popstate'', PopStateHandler);
}
//fires when the back button is pressed:
function PopStateHandler(e) {
emnt= $(''#elementID'');
window.scrollTo(0, emnt.position().top);
alert(''scrolling to position'');
}
Probado y funciona en Firefox.
Chrome se desplazará a la posición, pero luego volverá a su ubicación original.
La siguiente solución debería funcionar en todos los navegadores.
Puede establecer la posición de desplazamiento en 0 en el evento de descarga. Puede leer sobre este evento aquí: https://developer.mozilla.org/en-US/docs/Web/Events/unload . Esencialmente, el evento de descarga se dispara justo antes de salir de la página.
Si configura scrollPosition en 0 al momento de la descarga significa que cuando abandona la página con un conjunto de pushState, establece scrollPosition en 0. Cuando regrese a esta página al actualizar o al presionar, no se desplazará automáticamente.
//Listen for unload event. This is triggered when leaving the page.
//Reference: https://developer.mozilla.org/en-US/docs/Web/Events/unload
window.addEventListener(''unload'', function(e) {
//set scroll position to the top of the page.
window.scrollTo(0, 0);
});
La solución es usar la position: fixed
y especificar la top
igual a la posición de desplazamiento de la página.
Aquí hay un ejemplo:
$(window).on(''popstate'', function()
{
$(''.yourWrapAroundAllContent'').css({
position: ''fixed'',
top: -window.scrollY
});
requestAnimationFrame(function()
{
$(''.yourWrapAroundAllContent'').css({
position: ''static'',
top: 0
});
});
});
Sí, en su lugar, recibe una barra de desplazamiento parpadeante, pero es menos malvada.
Tendrás que usar algún tipo de navegador horrible olfateando aquí. Para Firefox, me gustaría ir con la solución de almacenar la posición de desplazamiento y restaurarla.
Pensé que tenía una buena solución de Webkit basada en tu descripción, pero lo intenté en Chrome 21, y parece que Chrome se desplaza primero, luego activa el evento de estado emergente y luego desencadena el evento de desplazamiento. Pero como referencia, esto es lo que se me ocurrió:
function noScrollOnce(event) {
event.preventDefault();
document.removeEventListener(''scroll'', noScrollOnce);
}
window.onpopstate = function () {
document.addEventListener(''scroll'', noScrollOnce);
};
La velocidad de repintado de la pantalla también descarta la magia negra, como pretender que la página se desplaza moviendo un elemento de posición absolute
.
Así que estoy 99% seguro de que la respuesta es que no puedes, y vas a tener que usar uno de los compromisos que has mencionado en la pregunta. Ambos navegadores se desplazan antes de que JavaScript sepa algo al respecto, por lo que JavaScript solo puede reaccionar después del evento. La única diferencia es que Firefox no pinta la pantalla hasta después de que se haya disparado el Javascript, razón por la cual hay una solución viable en Firefox pero no en WebKit.
if (''scrollRestoration'' in history) {
history.scrollRestoration = ''manual'';
}
( Anunciado por Google el 2 de septiembre de 2015)
Soporte del navegador:
Chrome: compatible (desde 46)
Firefox: será compatible desde el 19 de abril de 2016 (versión 46.0)
IE / Edge: solicite ayuda (luego, deje el enlace en comentario)
Opera: compatible (desde 33)