Cách phát hiện nếu URL đã thay đổi sau khi băm trong JavaScript


160

Làm cách nào để kiểm tra xem URL có thay đổi trong JavaScript không? Ví dụ: các trang web như GitHub, sử dụng AJAX, sẽ nối thêm thông tin trang sau ký hiệu # để tạo URL duy nhất mà không cần tải lại trang. Cách tốt nhất để phát hiện nếu URL này thay đổi là gì?

  • onloadsự kiện được gọi lại?
  • Có một trình xử lý sự kiện cho URL?
  • Hoặc phải kiểm tra URL mỗi giây để phát hiện thay đổi?

1
Đối với khách truy cập trong tương lai, câu trả lời mới của @aljeim từ năm 2018 là giải pháp tốt nhất: stackoverflow.com/a/52809105/151503
Redzarf

Câu trả lời:


106

Trong các trình duyệt hiện đại (IE8 +, FF3.6 +, Chrome), bạn chỉ có thể nghe hashchangesự kiện trên window.

Trong một số trình duyệt cũ, bạn cần một bộ đếm thời gian liên tục kiểm tra location.hash. Nếu bạn đang sử dụng jQuery, có một plugin thực hiện chính xác điều đó.


132
Điều này, theo tôi hiểu, chỉ hoạt động cho sự thay đổi của phần sau dấu # (do đó là tên sự kiện)? Và không phải để thay đổi URL đầy đủ, dường như được ngụ ý bởi tiêu đề của câu hỏi.
NPC

13
@NPC Bất kỳ trình xử lý nào để thay đổi URL đầy đủ (không có thẻ neo)?
Neha Choudhary

Bạn hiếm khi cần các sự kiện hết thời gian: sử dụng chuột và bàn phím để kiểm tra.
Sjeiti

Nếu đường dẫn thay đổi, không phải là hàm băm thì sao?
SuperUberDuper

@SuperUberDuper Nếu đường dẫn thay đổi do người dùng bắt đầu điều hướng / nhấp vào liên kết, v.v., thì bạn sẽ chỉ thấy một beforeunloadsự kiện. Nếu mã của bạn bắt đầu thay đổi URL, nó biết rõ nhất.
phihag

92

Tôi muốn có thể thêm locationchangengười nghe sự kiện. Sau khi sửa đổi bên dưới, chúng tôi sẽ có thể làm điều đó, như thế này

window.addEventListener('locationchange', function(){
    console.log('location changed!');
})

Ngược lại, window.addEventListener('hashchange',()=>{})sẽ chỉ kích hoạt nếu phần sau khi hashtag trong url thay đổi và window.addEventListener('popstate',()=>{})không phải lúc nào cũng hoạt động.

Sửa đổi này, tương tự như câu trả lời của Christian , sửa đổi đối tượng lịch sử để thêm một số chức năng.

Theo mặc định, có một popstatesự kiện, nhưng không có sự kiện nào cho pushstatereplacestate.

Này sửa đổi ba chức năng để tất cả lửa một phong tục locationchangesự kiện cho bạn để sử dụng, và cũng có thể pushstatereplacestatesự kiện nếu bạn muốn sử dụng những:

/* These are the modifications: */
history.pushState = ( f => function pushState(){
    var ret = f.apply(this, arguments);
    window.dispatchEvent(new Event('pushstate'));
    window.dispatchEvent(new Event('locationchange'));
    return ret;
})(history.pushState);

history.replaceState = ( f => function replaceState(){
    var ret = f.apply(this, arguments);
    window.dispatchEvent(new Event('replacestate'));
    window.dispatchEvent(new Event('locationchange'));
    return ret;
})(history.replaceState);

window.addEventListener('popstate',()=>{
    window.dispatchEvent(new Event('locationchange'))
});

2
Tuyệt vời, những gì tôi cần Cảm ơn
eslamb

Cảm ơn, thực sự hữu ích!
Thomas Lang

1
Có cách nào để làm điều này trong IE không? Vì nó không hỗ trợ =>
joshuasc Bông

1
@joshuac Bông => là một hàm mũi tên, bạn có thể thay thế f => hàm fname () {...} bằng hàm (f) {return function fname () {...}}
aljeim

1
Điều này rất hữu ích và hoạt động như một lá bùa. Đây phải là câu trả lời được chấp nhận.
Kunal Parekh

77

sử dụng mã này

window.onhashchange = function() { 
     //code  
}

với jQuery

$(window).bind('hashchange', function() {
     //code
});

7
Điều này phải được đánh dấu câu trả lời. Đó là một dòng, sử dụng mô hình sự kiện của trình duyệt và không phụ thuộc vào thời gian chờ tiêu tốn tài nguyên vô tận
Nick Mitchell

1
Đây là ý kiến ​​của tôi câu trả lời tốt nhất ở đây. Không có plugin và mã tối thiểu
agDev 04/11/2016

14
Không hoạt động trên các thay đổi url không băm dường như rất phổ biến, chẳng hạn như thay đổi được thực hiện bởi Slack
NycCompSci

26
Làm thế nào đây là câu trả lời tốt nhất nếu điều này chỉ được kích hoạt khi có một hàm băm trong url?
Aaron

1
Bạn nên sử dụng addEventListener thay vì trực tiếp thay thế giá trị onhashchange, trong trường hợp có điều gì khác muốn lắng nghe.
hỏng-

55

EDIT sau một chút nghiên cứu:

Dường như tôi đã bị đánh lừa bởi tài liệu có trên tài liệu Mozilla. Sự popstatekiện (và chức năng gọi lại của nó onpopstate) không được kích hoạt bất cứ khi nào pushState()hoặc replaceState()được gọi trong mã. Do đó, câu trả lời ban đầu không áp dụng trong mọi trường hợp.

Tuy nhiên, có một cách để khắc phục điều này bằng cách vá khỉ các chức năng theo @ alpha123 :

var pushState = history.pushState;
history.pushState = function () {
    pushState.apply(history, arguments);
    fireEvents('pushState', arguments);  // Some event-handling function
};

Câu trả lời gốc

Cho rằng tiêu đề của câu hỏi này là " Cách phát hiện thay đổi URL " câu trả lời, khi bạn muốn biết khi nào đường dẫn đầy đủ thay đổi (và không chỉ là mỏ neo băm), là bạn có thể lắng nghe popstatesự kiện:

window.onpopstate = function(event) {
  console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));
};

Tham chiếu cho popstate trong Mozilla Docs

Hiện tại (tháng 1 năm 2017) có hỗ trợ cho popstate từ 92% trình duyệt trên toàn thế giới.


11
Không thể tin được rằng chúng ta vẫn phải dùng đến những vụ hack như vậy vào năm 2018.

1
Điều này làm việc đối với trường hợp sử dụng của tôi - nhưng cũng giống như @goat nói - đó là không thể tin được rằng không có nguồn gốc hỗ trợ cho điều này ...
wasddd_

1
lập luận gì? Làm thế nào tôi có thể thiết lập fireEvents?
SeanMC

2
Lưu ý rằng điều này cũng không đáng tin cậy trong nhiều trường hợp. Ví dụ: nó sẽ không phát hiện thay đổi URL khi bạn nhấp vào các biến thể sản phẩm khác nhau của Amazon (các ô bên dưới giá).
thdoan

2
điều này sẽ không phát hiện thay đổi từ localhost / foo sang localhost / baa nếu không sử dụng location.back ()
SuperUberDuper

53

Với jquery (và một trình cắm thêm), bạn có thể làm

$(window).bind('hashchange', function() {
 /* things */
});

http://benalman.com/projects/jquery-hashchange-plugin/

Nếu không, bạn sẽ phải sử dụng setInterval và kiểm tra sự thay đổi trong sự kiện băm (window.location.hash)

Cập nhật! Một bản nháp đơn giản

function hashHandler(){
    this.oldHash = window.location.hash;
    this.Check;

    var that = this;
    var detect = function(){
        if(that.oldHash!=window.location.hash){
            alert("HASH CHANGED - new has" + window.location.hash);
            that.oldHash = window.location.hash;
        }
    };
    this.Check = setInterval(function(){ detect() }, 100);
}

var hashDetection = new hashHandler();

Tôi có thể phát hiện sự thay đổi của (window.location) và xử lý nó không? (không có jquery)
BergP

6
Bạn có thể @BergP, Sử dụng trình nghe javascript đơn giản: window.addEventListener("hashchange", hashChanged);
Ron

1
Là khoảng thời gian ngắn như vậy tốt cho ứng dụng? Đó là, không phải nó giữ cho trình duyệt quá bận rộn trong việc thực thi detect()chức năng sao?
Hasib Mahmud

4
@HasibMahmud, mã đó đang thực hiện kiểm tra 1 đẳng thức cứ sau 100ms. Tôi chỉ điểm chuẩn trong trình duyệt của mình rằng tôi có thể thực hiện 500 kiểm tra bình đẳng trong vòng dưới 1ms. Vì vậy, mã đó đang sử dụng 1/50000 sức mạnh xử lý của tôi. Tôi sẽ không lo lắng quá nhiều.
z5h

24

Thêm một người nghe sự kiện thay đổi băm!

window.addEventListener('hashchange', function(e){console.log('hash changed')});

Hoặc, để lắng nghe tất cả các thay đổi URL:

window.addEventListener('popstate', function(e){console.log('url changed')});

Điều này tốt hơn một cái gì đó giống như mã dưới đây vì chỉ có một thứ có thể tồn tại trong window.onhashchange và bạn có thể sẽ ghi đè mã của người khác.

// Bad code example

window.onhashchange = function() { 
     // Code that overwrites whatever was previously in window.onhashchange  
}

1
trạng thái pop chỉ kích hoạt khi bạn bật một trạng thái, không đẩy một trạng thái
SeanMC

8

giải pháp này hiệu quả với tôi:

var oldURL = "";
var currentURL = window.location.href;
function checkURLchange(currentURL){
    if(currentURL != oldURL){
        alert("url changed!");
        oldURL = currentURL;
    }

    oldURL = window.location.href;
    setInterval(function() {
        checkURLchange(window.location.href);
    }, 1000);
}

checkURLchange();

18
Đây là một phương pháp khá thô sơ, tôi nghĩ chúng ta có thể nhắm mục tiêu cao hơn.
Carles Alcolea

8
Mặc dù tôi đồng ý với @CarlesAlcolea rằng điều này cảm thấy cũ, nhưng theo kinh nghiệm của tôi, đây vẫn là cách duy nhất để nắm bắt 100% tất cả các thay đổi url.
Trev14

5
@ahofmann đề xuất (trong một chỉnh sửa nên là một nhận xét) thay đổi setIntervalthành setTimeout: "sử dụng setInterval () sẽ khiến Trình duyệt tạm dừng sau một thời gian, bởi vì nó sẽ tạo một cuộc gọi mới để checkURLchange () mỗi giây. là giải pháp chính xác, bởi vì nó chỉ được gọi một lần. "
divibisan

Đây là giải pháp duy nhất để nắm bắt tất cả các thay đổi URL nhưng việc tiêu thụ tài nguyên đang buộc tôi phải tìm kiếm một số giải pháp khác.
ReturnTable

3
Hoặc thay vì sử dụng setTimeoutnhư @divibisan gợi ý, hãy di chuyển setIntervalbên ngoài hàm. checkURLchange();cũng trở thành tùy chọn.
aristidesfl

4

Mặc dù là một câu hỏi cũ, dự án thanh vị trí rất hữu ích.

var LocationBar = require("location-bar");
var locationBar = new LocationBar();

// listen to all changes to the location bar
locationBar.onChange(function (path) {
  console.log("the current url is", path);
});

// listen to a specific change to location bar
// e.g. Backbone builds on top of this method to implement
// it's simple parametrized Backbone.Router
locationBar.route(/some\-regex/, function () {
  // only called when the current url matches the regex
});

locationBar.start({
  pushState: true
});

// update the address bar and add a new entry in browsers history
locationBar.update("/some/url?param=123");

// update the address bar but don't add the entry in history
locationBar.update("/some/url", {replace: true});

// update the address bar and call the `change` callback
locationBar.update("/some/url", {trigger: true});

Đó là cho nodeJs, chúng ta cần sử dụng browserify để sử dụng phía máy khách. Chúng ta đừng
hack4mer

1
Không, không. Hoạt động trong trình duyệt
Ray Booysen

3

Để nghe thay đổi url, xem bên dưới:

window.onpopstate = function(event) {
  console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));
};

Sử dụng phong cách này nếu bạn có ý định dừng / loại bỏ người nghe sau một số điều kiện nhất định.

window.addEventListener('popstate', function(e) {
   console.log('url changed')
});

2

Trong khi thực hiện một phần mở rộng chrome nhỏ, tôi đã gặp phải vấn đề tương tự với một vấn đề bổ sung: Đôi khi, trang thay đổi nhưng không phải là URL.

Chẳng hạn, chỉ cần truy cập Trang chủ Facebook và nhấp vào nút 'Trang chủ'. Bạn sẽ tải lại trang nhưng URL sẽ không thay đổi (kiểu ứng dụng một trang).

99% thời gian, chúng tôi đang phát triển các trang web để chúng tôi có thể nhận các sự kiện đó từ các Khung như Angular, React, Vue, v.v.

NHƯNG , trong trường hợp tiện ích mở rộng của Chrome (trong Vanilla JS), tôi đã phải nghe một sự kiện sẽ kích hoạt cho mỗi "thay đổi trang", thường có thể bị bắt bởi URL đã thay đổi, nhưng đôi khi không.

Giải pháp tự chế của tôi là như sau:

listen(window.history.length);
var oldLength = -1;
function listen(currentLength) {
  if (currentLength != oldLength) {
    // Do your stuff here
  }

  oldLength = window.history.length;
  setTimeout(function () {
    listen(window.history.length);
  }, 1000);
}

Về cơ bản, giải pháp leoneckert, được áp dụng cho lịch sử cửa sổ, sẽ thay đổi khi một trang thay đổi trong một ứng dụng trang.

Không phải khoa học tên lửa, nhưng giải pháp sạch nhất tôi tìm thấy, vì chúng tôi chỉ kiểm tra một đẳng thức nguyên ở đây, và không phải là các đối tượng lớn hơn hoặc toàn bộ DOM.


Giải pháp của bạn rất đơn giản và hoạt động rất tốt cho các tiện ích mở rộng chrome. Tôi muốn đề xuất sử dụng id video YouTube thay vì độ dài. stackoverflow.com/a/3452617/808901
Rod Lima

2
bạn không nên sử dụng setInterval vì mỗi lần bạn gọi nghe (xy), một Interval mới được tạo và bạn kết thúc với hàng ngàn khoảng.
Stef Chäser

1
Bạn nói đúng tôi nhận thấy rằng sau và không thay đổi bài viết của mình, tôi sẽ chỉnh sửa nó. Quay lại thời gian tôi thậm chí đã gặp phải sự cố của Google Chrome vì rò rỉ RAM. Cảm ơn bạn đã nhận xét
Alburkerk

window.historycó độ dài tối đa là 50 (ít nhất là bằng Chrome 80). Sau thời điểm đó, window.history.lengthluôn trả về 50. Khi điều đó xảy ra, phương thức này sẽ không nhận ra bất kỳ thay đổi nào.
Collin Krawll

1

Nhìn vào hàm unload jQuery. Nó xử lý tất cả mọi thứ.

https://api.jquery.com/unload/

Sự kiện dỡ tải được gửi đến thành phần cửa sổ khi người dùng điều hướng khỏi trang. Điều này có thể có nghĩa là một trong nhiều điều. Người dùng có thể đã nhấp vào một liên kết để rời khỏi trang hoặc nhập một URL mới trong thanh địa chỉ. Các nút chuyển tiếp và quay lại sẽ kích hoạt sự kiện. Đóng cửa sổ trình duyệt sẽ khiến sự kiện được kích hoạt. Ngay cả một trang tải lại đầu tiên sẽ tạo ra một sự kiện dỡ tải.

$(window).unload(
    function(event) {
        alert("navigating");
    }
);

2
Khi nội dung được Ajaxed, url có thể thay đổi mà không tải được cửa sổ. Tập lệnh này không phát hiện thay đổi url, mặc dù nó vẫn có thể hữu ích cho một số người dùng không tải cửa sổ trên mỗi thay đổi url.
Deborah

giống như câu hỏi @ranbuch, đây chỉ dành riêng cho các trang không phải là ứng dụng trang đơn và sự kiện này chỉ xem sự kiện cửa sổ dỡ tải, không thay đổi url.
ncubica

0
window.addEventListener("beforeunload", function (e) {
    // do something
}, false);

11
điều này sẽ không hoạt động trong ngữ cảnh của các ứng dụng trang đơn vì sự kiện dỡ tải sẽ không bao giờ kích hoạt
ncubica

0

Câu trả lời dưới đây xuất phát từ đây (với cú pháp javascript cũ (không có chức năng mũi tên, hỗ trợ IE 10+)): https://stackoverflow.com/a/52809105/9168962

(function() {
  if (typeof window.CustomEvent === "function") return false; // If not IE
  function CustomEvent(event, params) {
    params = params || {bubbles: false, cancelable: false, detail: null};
    var evt = document.createEvent("CustomEvent");
    evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
    return evt;
  }
  window.CustomEvent = CustomEvent;
})();

(function() {
  history.pushState = function (f) {
    return function pushState() {
      var ret = f.apply(this, arguments);
      window.dispatchEvent(new CustomEvent("pushState"));
      window.dispatchEvent(new CustomEvent("locationchange"));
      return ret;
    };
  }(history.pushState);
  history.replaceState = function (f) {
    return function replaceState() {
      var ret = f.apply(this, arguments);
      window.dispatchEvent(new CustomEvent("replaceState"));
      window.dispatchEvent(new CustomEvent("locationchange"));
      return ret;
    };
  }(history.replaceState);
  window.addEventListener("popstate", function() {
    window.dispatchEvent(new CustomEvent("locationchange"));
  });
})();

0

Một cách đơn giản khác mà bạn có thể thực hiện là bằng cách thêm một sự kiện nhấp chuột, thông qua tên lớp vào thẻ neo trên trang để phát hiện khi nào nó được nhấp, sau đó bạn có thể sử dụng window.location.href để lấy dữ liệu url bạn có thể sử dụng để chạy yêu cầu ajax của bạn đến máy chủ. Đơn giản và dễ dàng.


-1

Bạn đang bắt đầu một setIntervalcuộc gọi mới ở mỗi cuộc gọi mà không hủy cuộc gọi trước đó - có lẽ bạn chỉ muốn có một cuộc gọisetTimeout

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.