Zmień adres URL w przeglądarce bez ładowania nowej strony za pomocą JavaScript

297

W jaki sposób miałbym działanie JavaScript, które może mieć wpływ na bieżącą stronę, ale zmieniłoby również adres URL w przeglądarce, więc jeśli użytkownik kliknie ponownie załadować lub utworzyć zakładkę, to używany jest nowy adres URL?

Byłoby również miło, gdyby przycisk Wstecz przeładował oryginalny adres URL.

Próbuję zapisać stan JavaScript w adresie URL.

Steven Noble
źródło
1
To by było takie miłe. Oczywiście byłoby to ograniczone do modyfikacji w tej samej domenie. Ale kontrola ścieżki po stronie klienta (a nie tylko skrót) jest logicznym krokiem, ponieważ ponowne ładowanie strony jest dla wielu aplikacji swego rodzaju „ostatecznością”.
harpo
1
„W pewnym sensie” dobre użycie pushState:for(i=1;i<50;i++){var txt="..................................................";txt=txt.slice(0,i)+"HTML5"+txt.slice(i,txt.length);history.pushState({}, "html5", txt);}
Derek 朕
przykład tego efektu w akcji: dujour.com
Ben
2
Przykład tego efektu: facebook.com (podczas otwierania zdjęć w lightbox)
to dobre pytanie. jest to jednak smutna sytuacja, ponieważ gdyby dziś zadawano to pytanie w ten sposób, zostałoby to zanegowane i przeskoczone przez „to nie jest strona, na której można napisać cały kod za ciebie”.
rosa

Odpowiedzi:

118

W HTML 5 użyj history.pushStatefunkcji . Jako przykład:

<script type="text/javascript">
var stateObj = { foo: "bar" };
function change_my_url()
{
   history.pushState(stateObj, "page 2", "bar.html");
}
var link = document.getElementById('click');
link.addEventListener('click', change_my_url, false);
</script>

i href:

<a href="#" id='click'>Click to change url to bar.html</a>

Jeśli chcesz zmienić adres URL bez dodawania wpisu do listy przycisków wstecz, użyj history.replaceStatezamiast tego.

clu3
źródło
Masz rację, ostatnim razem eksperymentowałem z Chrome. Właśnie próbowałem z Firefoksem 3.6 na moim Ubuntu, to nie działało. Chyba będziemy musieli poczekać, aż HTML5 będzie w pełni obsługiwany w FF
clu3
15
Przykładowy kod jest wycinany i wklejany z developer.mozilla.org/en/DOM/Manipicing_the_browser_history . Co tak naprawdę przeszkadza w wyjaśnieniu, co w tym przypadku oznaczają foo i bar.
mikemaccana
2
Foo i bar nic nie znaczą, to arbitralnie zdefiniowany obiekt stanu, który można odzyskać później.
Paul Dixon
3
@Paul: tak, powyższy artykuł zawiera te informacje.
mikemaccana
2
Po opublikowaniu tego komentarza działa on na Firefox Chrome i Safari. Jednak bez powodzenia w mobilnym safari na iPadzie lub iPhonie :(
Sid
187

Jeśli chcesz pracować w przeglądarkach, które nie obsługują history.pushStatei history.popStatejeszcze „stare” sposobem jest ustawiony identyfikator fragmentu, który nie spowoduje przeładowanie strony.

Podstawowym pomysłem jest ustawienie window.location.hashwłaściwości na wartość, która zawiera dowolne potrzebne informacje o stanie, a następnie użycie zdarzenia window.onhashchange lub w przypadku starszych przeglądarek, które nie obsługują onhashchange(IE <8, Firefox <3.6), okresowo sprawdzaj, czy sprawdź, czy skrót się zmienił (używając setIntervalna przykład) i zaktualizuj stronę. Będziesz także musiał sprawdzić wartość skrótu przy ładowaniu strony, aby skonfigurować początkową zawartość.

Jeśli używasz jQuery, istnieje wtyczka hashchange, która będzie używać dowolnej metody obsługiwanej przez przeglądarkę. Jestem pewien, że istnieją również wtyczki do innych bibliotek.

Jedną z rzeczy, na które należy uważać, jest kolizja z identyfikatorami na stronie, ponieważ przeglądarka przewinie do dowolnego elementu o pasującym identyfikatorze.

Matthew Crumley
źródło
2
Czy wyszukiwarki nie ignorują tych wewnętrznych linków (skrótów)? W moim scenariuszu chcę, aby pająki również zbierały te linki.
Drew Noakes
3
@Drew, o ile mi wiadomo, wyszukiwarki nie mogą traktować części strony jako osobnego wyniku.
Matthew Crumley,
8
Jest teraz propozycja, aby wyszukiwarki zobaczyły skróty: googlewebmastercentral.blogspot.com/2009/10/…
LKM
5
Zamiast korzystać setIntervalz inspekcji document.location.hashmożna skorzystać ze hashchangezdarzenia, ponieważ jest ono o wiele bardziej przyjęte. Sprawdź tutaj, jakie przeglądarki obsługują każdy standard . Moje obecne podejście polega na użyciu push / popState w przeglądarkach, które go obsługują. W pozostałych ustawiam skrót i oglądam tylko hashchangew obsługiwanych przeglądarkach. To pozostawia IE <= 7 bez obsługi historii, ale z użytecznym łączem bezpośrednim. Ale kogo obchodzi historia IE <= 7?
Irae Carvalho
2
@gabeDel Tak, choć nie sądzę, że Analytics rejestruje skrót, więc miałby ten sam adres URL. Lepszym sposobem byłoby ręczne wywołanie _trackPageviewz „prawdziwym” adresem URL nowej strony.
Matthew Crumley,
37

window.location.href zawiera bieżący adres URL. Możesz z niego czytać, możesz do niego dołączyć i możesz go wymienić, co może spowodować ponowne załadowanie strony.

Jeśli, jak się wydaje, chcesz zapisać stan javascript w adresie URL, aby można go było dodać do zakładek, bez ponownego ładowania strony, dołącz go do bieżącego adresu URL po znaku # i uruchom fragment javascript wywołany przez zdarzenie onload parsuj bieżący URL, aby zobaczyć, czy zawiera zapisany stan.

Jeśli używasz? zamiast # wymusisz przeładowanie strony, ale ponieważ przeanalizujesz zapisany stan przy ładowaniu, może to nie być problemem; dzięki temu przyciski do przodu i do tyłu będą również działać poprawnie.

księżycowy cień
źródło
2
rejestrowanie stanu javascript w adresie URL jest dokładnie tym, co próbuję zrobić
Steven Noble,
21

Zdecydowanie podejrzewałbym, że nie jest to możliwe, ponieważ byłoby to niesamowitym problemem bezpieczeństwa. Na przykład mógłbym stworzyć stronę, która wyglądałaby jak strona logowania do banku, a URL w pasku adresu wyglądałby jak prawdziwy bank !

Być może, jeśli wyjaśnisz, dlaczego chcesz to zrobić, ludzie mogą zaproponować alternatywne podejście ...

[Edycja w 2011 r .: odkąd napisałem tę odpowiedź w 2008 r., Pojawiło się więcej informacji na temat techniki HTML5, która umożliwia modyfikację adresu URL, pod warunkiem, że pochodzi z tego samego źródła]

Paul Dixon
źródło
6
Nie stanowi to większego problemu, jeśli zmiany niezwiązane z przeładowaniem ograniczały się do ścieżki, ciągu zapytania i fragmentu - tj. Nie władzy (domeny i tym podobnych).
Ollie Saunders
2
youtube.com/user/SilkyDKwan#p/u/2/gTfqaGYVfMg to przykład, że jest to możliwe, spróbuj zmienić filmy :)
Spidfire
Możesz zmienić tylko lokalizację. Skrót (wszystko po numerze), jak zauważa Matthew Chumley w przyjętej odpowiedzi.
Paul Dixon,
2
Czy używasz Facebooka? Facebook zmienia adres URL bez ponownego ładowania, używając history.pushState().
Derek 朕 會 功夫
Paul Dixon, powinieneś zauważyć skrzynkę odbiorczą Facebooka, kiedy klikniesz nazwy po lewej stronie, zmieni się tylko adres URL i nowy czat zostanie załadowany w polu czatu, strona nie zostanie odświeżona. jest też o wiele więcej stron na WebGL, robią to samo
Dheeraj Thedijje
8

Ustawienia zabezpieczeń przeglądarki zapobiegają bezpośredniej modyfikacji wyświetlanego adresu URL. Można sobie wyobrazić podatne na ataki phishing.

Jedynym niezawodnym sposobem zmiany adresu URL bez zmiany stron jest użycie wewnętrznego linku lub skrótu. np .: http://site.com/page.html staje się http://site.com/page.html#item1 . Ta technika jest często stosowana w hijax (AJAX + zachowaj historię).

Robiąc to, często używam linków do akcji z haszem jako href, a następnie dodam zdarzenia kliknięcia z jquery, które używają żądanego skrótu do ustalenia i delegowania akcji.

Mam nadzieję, że postawi cię na właściwej drodze.

Jethro Larson
źródło
Przepraszamy, ale twoja odpowiedź nie jest prawdziwa. Tak, możesz zmienić adres URL.
Derek 朕 會 功夫
1
Tak, możesz użyć interfejsu API historii HTML5, ale nie jest to przeglądarka, ale możesz użyć wypełnienia wielokrotnego, które zastąpi adresy URL mieszania.
Jethro Larson
8

jQuery ma świetną wtyczkę do zmiany adresu URL przeglądarki, o nazwie jQuery-pusher .

JavaScript pushStatei jQuery mogą być używane razem, na przykład:

history.pushState(null, null, $(this).attr('href'));

Przykład:

$('a').click(function (event) {

  // Prevent default click action
  event.preventDefault();     

  // Detect if pushState is available
  if(history.pushState) {
    history.pushState(null, null, $(this).attr('href'));
  }
  return false;
});


Używanie tylko JavaScript history.pushState() , który zmienia odnośnik, który jest używany w nagłówku HTTP dla obiektów XMLHttpRequest utworzonych po zmianie stanu.

Przykład:

window.history.pushState("object", "Your New Title", "/new-url");

Metoda pushState ():

pushState()przyjmuje trzy parametry: obiekt stanu, tytuł (który jest obecnie ignorowany) i (opcjonalnie) adres URL. Przeanalizujmy każdy z tych trzech parametrów bardziej szczegółowo:

  1. obiekt stanu - Obiekt stanu to obiekt JavaScript powiązany z nowym wpisem historii utworzonym przez pushState(). Ilekroć użytkownik przechodzi do nowego stanu, uruchamiane jest zdarzenie popstate, a właściwość state tego zdarzenia zawiera kopię obiektu stanu wpisu historii.

    Obiekt stanu może być dowolnym obiektem, który może być szeregowany. Ponieważ Firefox zapisuje obiekty stanu na dysku użytkownika, aby można je było przywrócić po ponownym uruchomieniu przeglądarki przez użytkownika, na serializowaną reprezentację obiektu stanu nakładamy limit wielkości 640k znaków. Jeśli przekażesz obiekt stanu, którego reprezentacja szeregowa jest większa niż to pushState(), metoda zgłosi wyjątek. Jeśli potrzebujesz więcej miejsca, zachęcamy do skorzystania z sessionStorage i / lub localStorage.

  2. tytuł - Firefox obecnie ignoruje ten parametr, chociaż może go używać w przyszłości. Przekazanie tutaj pustego ciągu powinno być bezpieczne przed przyszłymi zmianami metody. Alternatywnie możesz przekazać krótki tytuł stanu, do którego się przeprowadzasz.

  3. URL - ten parametr podaje adres URL nowego wpisu historii. Pamiętaj, że przeglądarka nie podejmie próby załadowania tego adresu URL po wywołaniu pushState(), ale może spróbować załadować adres URL później, na przykład po ponownym uruchomieniu przeglądarki przez użytkownika. Nowy adres URL nie musi być bezwzględny; jeśli jest względny, jest rozwiązany względem bieżącego adresu URL. Nowy adres URL musi być tego samego pochodzenia co bieżący adres URL; w przeciwnym razie pushState()zgłosi wyjątek. Ten parametr jest opcjonalny; jeśli nie jest określony, jest ustawiony na bieżący adres URL dokumentu.

Ilia Rostowcew
źródło
3

Galeria zdjęć Facebooka robi to za pomocą # skrótu w adresie URL. Oto kilka przykładowych adresów URL:

Przed kliknięciem „Dalej”:

/photo.php?fbid=496429237507&set=a.218088072507.133423.681812507&pid=5887027&id=681812507

Po kliknięciu „Dalej”:

/photo.php?fbid=496429237507&set=a.218088072507.133423.681812507&pid=5887027&id=681812507#!/photo.php?fbid=496435457507&set=a.218088072507.133423.681812507&pid=5887085&id=681812507

Zwróć uwagę na hash-bang (#!), Po którym następuje nowy adres URL.

Jonathon Hill
źródło
2

Dla mnie działa - history.replaceState()funkcja, która wygląda następująco -

history.replaceState(data,"Title of page"[,'url-of-the-page']);

Ta strona nie zostanie ponownie załadowana, możesz z niej skorzystać w przypadku javascript

Veshraj Joshi
źródło
1

Zastanawiałem się, czy będzie to możliwe, dopóki ścieżka nadrzędna na stronie będzie taka sama, tylko coś nowego zostanie do niej dołączone.

Powiedzmy, że użytkownik jest na stronie: http://domain.com/site/page.html wtedy przeglądarka pozwala mi to zrobić, location.append = new.html a strona staje się: http://domain.com/site/page.htmlnew.htmla przeglądarka jej nie zmienia.

Lub po prostu pozwól tej osobie zmienić parametr get, więc pozwólmy location.get = me=1&page=1.

Oryginalna strona staje się http://domain.com/site/page.html?me=1&page=1i nie jest odświeżana.

Problem z # polega na tym, że dane nie są buforowane (przynajmniej tak mi się nie wydaje) po zmianie skrótu. To tak, jak za każdym razem, gdy ładowana jest nowa strona, podczas gdy przyciski wstecz i do przodu na stronie innej niż Ajax są w stanie buforować dane i nie tracą czasu na ponowne ładowanie danych.

Z tego, co widziałem, historia Yahoo już ładuje wszystkie dane naraz. Wygląda na to, że nie wykonuje żadnych żądań Ajax. Kiedy więc divużywa się a do obsługi innej metody w godzinach nadliczbowych, dane te nie są przechowywane dla każdego stanu historii.

użytkownik58670
źródło
1

mój kod to:

//change address bar
function setLocation(curLoc){
    try {
        history.pushState(null, null, curLoc);
        return false;
    } catch(e) {}
        location.hash = '#' + curLoc;
}

i akcja:

setLocation('http://example.com/your-url-here');

i przykład

$(document).ready(function(){
    $('nav li a').on('click', function(){
        if($(this).hasClass('active')) {

        } else {
            setLocation($(this).attr('href'));
        }
            return false;
    });
});

To wszystko :)

Tusko Trush
źródło
1

Przedstawiam prostszą odpowiedź,

window.history.pushState(null, null, "/abc")

doda to /abcpo nazwie domeny w adresie URL przeglądarki. Po prostu skopiuj ten kod i wklej go w konsoli przeglądarki i zobacz, jak adres URL zmienia się na „ https://stackoverflow.com/abc

Sam
źródło
0

Odniosłem sukces z:

location.hash="myValue";

Po prostu dodaje #myValuesię do bieżącego adresu URL. Jeśli chcesz wywołać zdarzenie na stronie Załaduj, możesz użyć tego samego, location.hashaby sprawdzić odpowiednią wartość. Pamiętaj tylko, aby usunąć #wartość zwróconą przez location.hashnp

var articleId = window.location.hash.replace("#","");
Adam Hej
źródło