Come posso aggiornare window.location.hash senza saltare il documento?


163

Ho un pannello scorrevole impostato sul mio sito Web.

Quando ha finito di animare, ho impostato l'hash in questo modo

function() {
   window.location.hash = id;
}

(questo è un callback, e idviene assegnato in precedenza).

Funziona bene, per consentire all'utente di aggiungere un segnalibro al pannello e anche il funzionamento della versione non JavaScript.

Tuttavia, quando aggiorno l'hash, il browser passa alla posizione. Immagino che questo sia un comportamento previsto.

La mia domanda è: come posso impedirlo? Vale a dire come posso modificare l'hash della finestra, ma il browser non può scorrere fino all'elemento se l'hash esiste? Una sorta event.preventDefault()di cosa?

Sto usando jQuery 1.4 e il plugin scrollTo .

Grazie molto!

Aggiornare

Ecco il codice che cambia il pannello.

$('#something a').click(function(event) {
    event.preventDefault();
    var link = $(this);
    var id = link[0].hash;

    $('#slider').scrollTo(id, 800, {
        onAfter: function() {

            link.parents('li').siblings().removeClass('active');
            link.parent().addClass('active');
            window.location.hash = id;

            }
    });
});

2
Presumo che tu abbia provato event.preventDefault():)
Marko

@Marko Non so dove posizionarlo!
alex,

Puoi pubblicare il resto del tuo codice?
Marko,

@Marko Ivanovski Non credo sia rilevante, ma vedrò cosa posso fare.
alex,

3
@Gareth Non penso che ci sia posto, perché succede non appena aggiorno l'hash.
alex,

Risposte:


261

Esiste una soluzione alternativa utilizzando l'API di cronologia sui browser moderni con fallback su quelli vecchi:

if(history.pushState) {
    history.pushState(null, null, '#myhash');
}
else {
    location.hash = '#myhash';
}

Il merito va a Lea Verou


Questa è la risposta migliore, secondo me.
Greg Annandale,

10
Notare che pushState ha l'effetto laterale (rientrato) di aggiungere uno stato allo stack della cronologia del browser. In altre parole, quando l'utente fa clic sul pulsante Indietro, non accadrà nulla se non aggiungi anche un listener di eventi popstate.
David Cook,

32
@DavidCook - Potremmo anche usare history.replaceState(che, per le modifiche all'hash, potrebbe avere più senso) per evitare la necessità di un popstatelistener di eventi.
Jack,

Questo ha funzionato per me. Mi piace l'effetto del cambio di storia. Voglio aggiungere l'avvertenza che questo non attiverà l' hashchangeevento. Era qualcosa su cui ho dovuto lavorare.
Giordania,

avvertimento! questo non attiverà un hashchangeevento
santiago arizti,

52

Il problema è che stai impostando window.location.hash sull'attributo ID di un elemento. È il comportamento previsto per il browser passare a quell'elemento, indipendentemente dal fatto che "preventDefault ()" o meno.

Un modo per aggirare questo è il prefisso dell'hash con un valore arbitrario come questo:

window.location.hash = 'panel-' + id.replace('#', '');

Quindi, tutto ciò che devi fare è controllare l'hash prefissato al caricamento della pagina. Come bonus aggiuntivo, puoi persino scorrere fino ad esso poiché ora hai il controllo del valore di hash ...

$(function(){
    var h = window.location.hash.replace('panel-', '');
    if (h) {
        $('#slider').scrollTo(h, 800);
    }
});

Se hai bisogno che funzioni sempre (e non solo sul caricamento della pagina iniziale), puoi utilizzare una funzione per monitorare le modifiche al valore di hash e passare all'elemento corretto al volo:

var foundHash;
setInterval(function() {
    var h = window.location.hash.replace('panel-', '');
    if (h && h !== foundHash) {
        $('#slider').scrollTo(h, 800);
        foundHash = h;
    }
}, 100);

2
Questa è l'unica soluzione che risponde effettivamente alla domanda - consentendo l'hashing senza saltare
amosmos,

Questo risponde davvero alla domanda spiegando l'aggiunta e la cancellazione del valore arbitrario per l'hash per evitare il salto secondo il comportamento predefinito del browser.
lowtechsun,

1
buona soluzione, ma indebolisce la soluzione dei PO poiché nega l'uso delle sezioni dei segnalibri
Samus,

27

Soluzione economica e cattiva .. Usa il brutto #! stile.

Per impostarlo:

window.location.hash = '#!' + id;

Per leggerlo:

id = window.location.hash.replace(/^#!/, '');

Dal momento che non corrisponde e ancora o ID nella pagina, non salterà.


3
Ho dovuto preservare la compatibilità con IE 7 e alcune versioni mobili del sito. Questa soluzione è stata la più pulita per me. Generalmente sarei in tutte le soluzioni pushState.
Eric Goodwin,

1
impostando l'hash con: window.location.hash = '#/' + id;e poi sostituendolo con: window.location.hash.replace(/^#\//, '#');prettificherà un po 'l'URL -> projects/#/tab1
braitsch

13

Perché non ottieni la posizione di scorrimento corrente, inseriscila in una variabile, quindi assegna l'hash e rimetti la pagina indietro dove era:

var yScroll=document.body.scrollTop;
window.location.hash = id;
document.body.scrollTop=yScroll;

questo dovrebbe funzionare


1
hm, ha funzionato per me per un po ', ma negli ultimi mesi ha smesso di funzionare su Firefox ....
matchew,

10

Ho usato una combinazione di Attila Fulop (Lea Verou) per i browser moderni e Gavin Brock per i vecchi browser come segue:

if (history.pushState) {
    // IE10, Firefox, Chrome, etc.
    window.history.pushState(null, null, '#' + id);
} else {
    // IE9, IE8, etc
    window.location.hash = '#!' + id;
}

Come osservato da Gavin Brock, per catturare nuovamente l'id dovrete trattare la stringa (che in questo caso può avere o meno il "!") Come segue:

id = window.location.hash.replace(/^#!?/, '');

Prima di allora, ho provato una soluzione simile a quella proposta da user706270, ma non ha funzionato bene con Internet Explorer: poiché il suo motore Javascript non è molto veloce, puoi notare l'aumento e la riduzione dello scorrimento, che produce un brutto effetto visivo.


2

Questa soluzione ha funzionato per me.

Il problema con l'impostazione location.hashè che la pagina passerà a quell'id se viene trovata sulla pagina.

Il problema window.history.pushStateè che aggiunge una voce alla cronologia per ogni scheda su cui l'utente fa clic. Quindi, quando l'utente fa clic sul backpulsante, passa alla scheda precedente. (Questo può essere o meno quello che vuoi. Non era quello che volevo).

Per me, è replaceStatestata l'opzione migliore in quanto sostituisce solo la cronologia corrente, quindi quando l'utente fa clic sul backpulsante, passa alla pagina precedente.

$('#tab-selector').tabs({
  activate: function(e, ui) {
    window.history.replaceState(null, null, ui.newPanel.selector);
  }
});

Consulta i documenti dell'API di cronologia su MDN.


1

Non sono sicuro di poter modificare l'elemento originale, ma che ne dici di passare dall'uso dell'id attr a qualcos'altro come id-dati? Quindi leggi il valore di data-id per il tuo valore di hash e non salterà.


1

Questa soluzione ha funzionato per me

// store the currently selected tab in the hash value
    if(history.pushState) {
        window.history.pushState(null, null, '#' + id);
    }
    else {
        window.location.hash = id;
    }

// on load of the page: switch to the currently selected tab
var hash = window.location.hash;
$('#myTab a[href="' + hash + '"]').tab('show');

E il mio codice js completo è

$('#myTab a').click(function(e) {
  e.preventDefault();
  $(this).tab('show');
});

// store the currently selected tab in the hash value
$("ul.nav-tabs > li > a").on("shown.bs.tab", function(e) {
  var id = $(e.target).attr("href").substr(1);
    if(history.pushState) {
        window.history.pushState(null, null, '#' + id);
    }
    else {
        window.location.hash = id;
    }
   // window.location.hash = '#!' + id;
});

// on load of the page: switch to the currently selected tab
var hash = window.location.hash;
// console.log(hash);
$('#myTab a[href="' + hash + '"]').tab('show');

0

Quando ho usato il framework laravel, ho avuto dei problemi con l'uso della funzione route-> back () poiché ha cancellato il mio hash. Per mantenere il mio hash, ho creato una semplice funzione:

$(function() {   
    if (localStorage.getItem("hash")    ){
     location.hash = localStorage.getItem("hash");
    }
}); 

e l'ho impostato nell'altra mia funzione JS in questo modo:

localStorage.setItem("hash", myvalue);

Puoi nominare i valori di archiviazione locale come preferisci; il mio di nome hash.

Pertanto, se l'hash è impostato su PAGE1 e si passa a PAGE2; l'hash verrà ricreato su PAGINA 1 quando si fa clic su Indietro su PAGINA2.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.