Jak mogę zaktualizować window.location.hash bez przeskakiwania dokumentu?

163

Mam ustawiony przesuwany panel na mojej stronie internetowej.

Po zakończeniu animacji ustawiłem hash w ten sposób

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

(to jest oddzwonienie, które idzostało przypisane wcześniej).

Działa to dobrze, aby umożliwić użytkownikowi dodanie zakładek do panelu, a także aby działała wersja bez JavaScript.

Jednak kiedy aktualizuję skrót, przeglądarka przeskakuje do lokalizacji. Myślę, że jest to oczekiwane zachowanie.

Moje pytanie brzmi: jak mogę temu zapobiec? To znaczy, jak mogę zmienić hash okna, ale nie pozwolić przeglądarce przewinąć do elementu, jeśli hash istnieje? event.preventDefault()Coś w tym rodzaju?

Używam jQuery 1.4 i wtyczki scrollTo .

Wielkie dzięki!

Aktualizacja

Oto kod, który zmienia panel.

$('#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;

            }
    });
});
Alex
źródło
2
Zakładam, że próbowałeś event.preventDefault():)
Marko
@Marko Nie wiem, gdzie to umieścić!
alex
Czy możesz opublikować resztę swojego kodu?
Marko
@Marko Ivanovski Nie sądzę, żeby to miało znaczenie, ale zobaczę, co da się zrobić.
alex
3
@Gareth Myślę, że nie ma na to miejsca, ponieważ dzieje się tak, jak tylko zaktualizuję hash.
alex

Odpowiedzi:

261

Można obejść ten problem, używając interfejsu API historii w nowoczesnych przeglądarkach ze starszymi przeglądarkami:

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

Kredyt trafia do Lea Verou

Attila Fulop
źródło
Moim zdaniem to lepsza odpowiedź.
Greg Annandale
10
Zauważ, że pushState ma boczny (wcięty) efekt dodawania stanu do stosu historii przeglądarki. Innymi słowy, gdy użytkownik kliknie przycisk Wstecz, nic się nie stanie, chyba że dodasz również odbiornik zdarzeń popstate.
David Cook,
32
@DavidCook - Moglibyśmy również użyć history.replaceState(co w przypadku zmian skrótu może mieć więcej sensu), aby uniknąć potrzeby popstatenasłuchiwania zdarzeń.
Jack
To zadziałało dla mnie. Podoba mi się efekt zmiany historii. Chcę dodać zastrzeżenie, że nie spowoduje to hashchangezdarzenia. Musiałem to obejść.
Jordania
ostrzeżenie! to nie zapoczątkuje hashchangewydarzenia
santiago arizti
52

Problem polega na tym, że ustawiasz window.location.hash na atrybut ID elementu. Jest to oczekiwane zachowanie przeglądarki, aby przeskoczyć do tego elementu, niezależnie od tego, czy użyjesz metody „disableDefault ()”, czy nie.

Jednym ze sposobów obejścia tego jest poprzedzenie skrótu dowolną wartością, taką jak:

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

Następnie wszystko, co musisz zrobić, to sprawdzić prefiksowany skrót podczas ładowania strony. Jako dodatkowy bonus, możesz nawet płynnie przewijać do niego, ponieważ masz teraz kontrolę nad wartością skrótu ...

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

Jeśli chcesz, aby to działało przez cały czas (a nie tylko podczas początkowego ładowania strony), możesz użyć funkcji do monitorowania zmian wartości skrótu i ​​przeskakiwania do właściwego elementu w locie:

var foundHash;
setInterval(function() {
    var h = window.location.hash.replace('panel-', '');
    if (h && h !== foundHash) {
        $('#slider').scrollTo(h, 800);
        foundHash = h;
    }
}, 100);
Derek Hunziker
źródło
2
To jedyne rozwiązanie, które faktycznie odpowiada na pytanie - zezwolenie na mieszanie bez skakania
amosmos
To naprawdę odpowiada na pytanie wyjaśniające dodanie i usunięcie dowolnej wartości dla skrótu, aby uniknąć przeskoku zgodnie z domyślnym zachowaniem przeglądarki.
lowtechsun
1
dobre rozwiązanie, ale osłabia rozwiązanie PO, ponieważ neguje użycie sekcji zakładek
Samus
27

Tanie i paskudne rozwiązanie .. Użyj brzydkiego #! styl.

Aby to ustawić:

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

Aby to przeczytać:

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

Ponieważ nie pasuje i nie zakotwicza ani nie ma identyfikatora na stronie, nie przeskoczy.

Gavin Brock
źródło
3
Musiałem zachować kompatybilność z IE 7 i niektórymi mobilnymi wersjami strony. To rozwiązanie było dla mnie najczystsze. Generalnie byłbym we wszystkich rozwiązaniach pushState.
Eric Goodwin
1
ustawienie hasha na :, window.location.hash = '#/' + id;a następnie zastąpienie go: window.location.hash.replace(/^#\//, '#');poprawi nieco adres URL -> projects/#/tab1
braitsch
13

Dlaczego nie uzyskasz aktualnej pozycji przewijania, umieść ją w zmiennej, a następnie przypisz skrót i przewiń stronę z powrotem do miejsca, w którym się znajdował:

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

to powinno działać

user706270
źródło
1
hm, to działało przez jakiś czas, ale kiedyś w ciągu ostatnich kilku miesięcy przestało to działać w przeglądarce Firefox ...
matchew
10

Użyłem kombinacji rozwiązania Attila Fulop (Lea Verou) dla nowoczesnych przeglądarek i rozwiązania Gavin Brock dla starszych przeglądarek w następujący sposób:

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

Jak zauważył Gavin Brock, aby odzyskać id, będziesz musiał potraktować łańcuch (który w tym przypadku może mieć lub nie "!") W następujący sposób:

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

Wcześniej wypróbowałem rozwiązanie podobne do tego, które zaproponował user706270, ale nie działało dobrze z Internet Explorerem: ponieważ jego silnik Javascript nie jest bardzo szybki, możesz zauważyć wzrost i spadek przewijania, co daje nieprzyjemny efekt wizualny.

aldemarcalazans
źródło
2

To rozwiązanie zadziałało dla mnie.

Problem z ustawieniem location.hashpolega na tym, że strona przeskoczy do tego identyfikatora, jeśli zostanie znaleziony na stronie.

Problem window.history.pushStatepolega na tym, że dodaje wpis do historii dla każdej karty, którą klika użytkownik. Następnie, gdy użytkownik kliknie backprzycisk, przechodzi do poprzedniej karty. (to może być, ale nie musi to być to, czego chcesz. nie tego chciałem).

Dla mnie replaceStatebyła lepsza opcja, ponieważ zastępuje tylko bieżącą historię, więc gdy użytkownik kliknie backprzycisk, przechodzi do poprzedniej strony.

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

Sprawdź dokumentację History API na MDN.

Mike Vallano
źródło
1

Nie jestem pewien, czy możesz zmienić oryginalny element, ale co powiesz na zmianę z używania atrybutu id na coś innego, jak data-id? Następnie po prostu przeczytaj wartość data-id dla swojej wartości skrótu i ​​nie przeskoczy.

Jon B.
źródło
1

To rozwiązanie zadziałało dla mnie

// 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');

A mój pełny kod js to

$('#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');
Rohit Dhiman
źródło
0

Używając laravel framework, miałem pewne problemy z używaniem funkcji route-> back (), ponieważ kasowała ona mój hash. Aby zachować mój hash, stworzyłem prostą funkcję:

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

i ustawiłem to w mojej innej funkcji JS w ten sposób:

localStorage.setItem("hash", myvalue);

Możesz dowolnie nazwać wartości lokalnej pamięci masowej; mój nazwanyhash .

Dlatego też, jeśli hash jest ustawiony na STRONIE1, a następnie przejdziesz do STRONY2; skrót zostanie odtworzony na STRONIE1 po kliknięciu Wstecz na STRONIE2.

Andrzej
źródło