Quản lý lịch sử trình duyệt với Window History trong JavaScript

JavaScript HTML DOM | by Học Javascript

Trong quá trình sử dụng trình duyệt web, người dùng thường xuyên tương tác với các thao tác như quay lại trang trước, tiến tới trang sau, hoặc chuyển đổi giữa các trạng thái nội dung mà không cần tải lại toàn bộ trang. Những hành động này được hỗ trợ nhờ vào một tính năng mạnh mẽ trong JavaScript – đó là Window History.

Window History là một phần của Browser Object Model (BOM), cho phép lập trình viên truy cập và thao tác với lịch sử duyệt web của người dùng trong tab hiện tại. Việc sử dụng đúng cách Window History không chỉ giúp cải thiện trải nghiệm người dùng mà còn đóng vai trò quan trọng trong việc xây dựng các ứng dụng web hiện đại như Single Page Applications (SPA), nơi điều hướng nội dung được xử lý hoàn toàn bằng JavaScript mà không cần tải lại trang.

Trong bài viết này, mình sẽ cùng tìm hiểu chi tiết về Window History, các phương thức thường dùng, ứng dụng thực tế và lưu ý khi sử dụng trong phát triển web.

Giới thiệu về Window History trong JavaScript

window.history là gì?

window.history là một đối tượng trong JavaScript, thuộc Browser Object Model (BOM), cho phép lập trình viên truy cập và thao tác với lịch sử duyệt web của trình duyệt. Đối tượng này cho phép người dùng điều hướng qua các trang trước đó mà không cần tải lại trang web hoàn toàn. Với window.history, bạn có thể thực hiện các thao tác như quay lại trang trước, tiến tới trang sau, hoặc thao tác với lịch sử của các trang động mà không làm mới trang hiện tại.

Lịch sử của trình duyệt được lưu trữ dưới dạng một stack (ngăn xếp), với mỗi bước truy cập vào trang web sẽ được thêm vào ngăn xếp này. Điều này giúp người dùng dễ dàng quay lại các trang trước đó thông qua các chức năng tiêu chuẩn như nút Back hoặc Forward trong trình duyệt.

Vị trí của history trong đối tượng window

Trong đối tượng window, history là một thuộc tính có sẵn và được sử dụng để truy cập vào các thao tác liên quan đến lịch sử duyệt web. Cấu trúc đơn giản của đối tượng này trong JavaScript sẽ giống như sau:

window.history

window.history có thể được truy cập trực tiếp từ đối tượng window, không cần phải khai báo thêm bất kỳ thuộc tính nào. Đối tượng này không cần được khởi tạo, và nó có sẵn trong mọi trang web khi truy cập thông qua JavaScript.

Mục đích sử dụng: Di chuyển qua các bước truy cập trước đó mà không cần tải lại toàn bộ trang

Mục đích chính của window.history là giúp người dùng điều hướng qua các bước truy cập trang trước mà không cần tải lại trang web hoàn toàn. Điều này rất hữu ích trong các ứng dụng web hiện đại, đặc biệt là trong các Single Page Applications (SPA), nơi mà việc thay đổi nội dung hoặc điều hướng giữa các trang không cần phải tải lại toàn bộ trang mà chỉ thay đổi phần nội dung động.

Ví dụ, khi người dùng nhấn vào một liên kết trong một ứng dụng SPA, nội dung của trang có thể thay đổi mà không tải lại trang, nhưng lịch sử của trình duyệt vẫn được cập nhật. Điều này có thể được thực hiện thông qua các phương thức của window.history như pushState(), replaceState() hoặc sử dụng các thao tác như back(), forward() để di chuyển qua các trang trước đó mà không cần tải lại trang.

Như vậy, việc sử dụng window.history giúp tạo ra các trải nghiệm người dùng mượt mà hơn và dễ dàng quản lý lịch sử điều hướng trong các ứng dụng web động.

Các phương thức chính của Window History trong JavaScript

history.back()

history.back() là một phương thức trong đối tượng window.history dùng để quay lại trang trước đó trong lịch sử duyệt web của trình duyệt. Khi gọi phương thức này, trình duyệt sẽ tự động tải lại trang web mà người dùng đã truy cập trước đó.

Cách sử dụng:

window.history.back();
  • Ví dụ: Nếu người dùng đang ở trang page2.html và gọi history.back(), trình duyệt sẽ chuyển đến page1.html (trang trước đó trong lịch sử duyệt web).

  • Lưu ý: Phương thức này tương tự với hành động nhấn nút "Back" trong trình duyệt. Nếu không còn trang nào trong lịch sử trước đó, nó sẽ không làm gì và không gây lỗi.

history.forward()

history.forward() là phương thức giúp di chuyển đến trang tiếp theo trong lịch sử duyệt web của trình duyệt, nếu có. Đây là phương thức ngược lại với history.back().

Cách sử dụng:

window.history.forward();

Ví dụ: Nếu người dùng đang ở trang page1.html và sau đó quay lại trang trước đó (ví dụ, page2.html), phương thức history.forward() sẽ giúp người dùng di chuyển trở lại page1.html.

Lưu ý: Phương thức này tương tự với hành động nhấn nút "Forward" trong trình duyệt. Nếu không còn trang nào phía trước, nó sẽ không làm gì và không gây lỗi.

history.go(n)

history.go(n) là một phương thức linh hoạt cho phép bạn di chuyển đến một vị trí cụ thể trong lịch sử duyệt web. Tham số n là một số nguyên, trong đó:

  • n > 0: Di chuyển tới n bước tiến trong lịch sử.

  • n < 0: Di chuyển tới n bước lùi trong lịch sử (tương tự như history.back()).

  • n = 0: Làm mới lại trang hiện tại.

Cách sử dụng:

window.history.go(n);

Ví dụ:

  • history.go(-1): Tương đương với history.back(). Quay lại trang trước đó.

  • history.go(1): Tương đương với history.forward(). Tiến tới trang tiếp theo.

  • history.go(0): Làm mới trang hiện tại.

Lưu ý: Phương thức go() có thể được sử dụng để nhảy tới bất kỳ vị trí nào trong lịch sử của trình duyệt, nếu bạn biết chỉ số của bước bạn muốn di chuyển tới.

history.length

history.length là một thuộc tính của đối tượng window.history trả về số lượng các mục trong lịch sử trình duyệt của phiên hiện tại. Điều này cho phép bạn biết trình duyệt đã lưu trữ bao nhiêu bước trong lịch sử duyệt web.

  • Cách sử dụng:

let historyLength = window.history.length;
console.log(historyLength);

Giải thích:

  • Thuộc tính này trả về một số nguyên, là số lượng các trang đã được truy cập trong phiên hiện tại của trình duyệt.

  • Ví dụ: Nếu người dùng đã duyệt qua 5 trang trong một phiên, history.length sẽ trả về giá trị 5.

Lưu ý: history.length chỉ báo cáo số lượng mục trong lịch sử của phiên duyệt web hiện tại. Nếu bạn điều hướng đến một trang mới mà không thay đổi lịch sử (ví dụ, bằng cách sử dụng history.replaceState()), số lượng mục trong lịch sử có thể thay đổi, nhưng phương thức này vẫn sẽ trả về số lượng mục trước đó.

Thao tác với lịch sử bằng History API nâng cao trong JavaScript

History API cung cấp một số phương thức mạnh mẽ giúp bạn có thể thao tác với lịch sử trình duyệt mà không cần tải lại trang, hỗ trợ tốt cho việc xây dựng các ứng dụng web hiện đại như Single Page Applications (SPA).

history.pushState(state, title, url)

history.pushState() cho phép bạn thêm một mục mới vào lịch sử trình duyệt mà không làm tải lại trang. Phương thức này rất hữu ích khi bạn muốn thay đổi trạng thái của ứng dụng mà không cần phải thực hiện một yêu cầu tải lại trang, như khi điều hướng giữa các trang trong SPA.

  • Cú pháp:

history.pushState(state, title, url);

state: Một đối tượng đại diện cho trạng thái mới. Đối tượng này có thể lưu trữ bất kỳ thông tin gì bạn muốn, ví dụ như dữ liệu người dùng đã nhập vào form hoặc trạng thái của ứng dụng.

  • title: Tên của trang. Tuy nhiên, tham số này thường không ảnh hưởng nhiều trong các trình duyệt hiện đại, nên có thể truyền giá trị trống hoặc null.

  • url: URL mới mà bạn muốn lưu vào lịch sử trình duyệt. Điều này có thể là một URL mới mà bạn muốn người dùng thấy trong thanh địa chỉ mà không tải lại trang.

Ví dụ:

// Thêm một mục mới vào lịch sử
history.pushState({ page: 1 }, "Page 1", "page1.html");

Ở ví dụ trên, khi gọi pushState(), trang không tải lại mà chỉ thay đổi URL trên thanh địa chỉ thành page1.html. Điều này cho phép người dùng di chuyển giữa các "trang" trong ứng dụng mà không cần phải tải lại toàn bộ trang.

Ứng dụng trong SPA:

  • Với SPA, khi bạn thay đổi trạng thái ứng dụng mà không tải lại trang, pushState() giúp bạn điều hướng giữa các phần khác nhau của ứng dụng mà vẫn giữ cho người dùng ở một URL duy nhất, tránh làm mới trang.

history.replaceState(state, title, url)

history.replaceState() tương tự như pushState(), nhưng thay vì thêm một mục mới vào lịch sử, nó thay thế mục hiện tại trong lịch sử. Điều này rất hữu ích khi bạn cần thay đổi trạng thái của trang mà không muốn tạo một bước mới trong lịch sử, chẳng hạn như khi bạn cập nhật một URL mà không thay đổi trạng thái của ứng dụng.

Cú pháp:

history.replaceState(state, title, url);

state: Đối tượng trạng thái mới mà bạn muốn lưu vào lịch sử.

  • title: Tên của trang (có thể bỏ qua hoặc truyền giá trị trống).

  • url: URL mới mà bạn muốn thay thế mục hiện tại.

Ví dụ:

// Thay thế mục hiện tại trong lịch sử với URL mới
history.replaceState({ page: 2 }, "Page 2", "page2.html");

Trong ví dụ này, replaceState() sẽ thay thế mục lịch sử hiện tại với page2.html mà không tạo một bước mới trong lịch sử. Điều này có nghĩa là người dùng không thể nhấn nút "Back" để quay lại trang trước đó.

Ứng dụng trong SPA:

  • Trong các ứng dụng một trang, khi thay đổi trạng thái của một phần mà không muốn tạo một bước mới trong lịch sử (ví dụ: khi bạn cập nhật một tham số trong URL nhưng không muốn người dùng quay lại trang trước đó), replaceState() sẽ giúp thay thế mục lịch sử mà không tạo ra sự gián đoạn.

window.onpopstate

window.onpopstate là sự kiện được kích hoạt khi người dùng di chuyển qua các mục trong lịch sử trình duyệt (khi nhấn nút "Back" hoặc "Forward"). Sự kiện này rất hữu ích khi bạn muốn theo dõi sự thay đổi trong lịch sử và cập nhật giao diện người dùng tương ứng, ví dụ như khi người dùng quay lại một trang trước đó trong một SPA.

Cú pháp:

window.onpopstate = function(event) {
  // Xử lý sự kiện khi lịch sử thay đổi
  console.log("State changed: ", event.state);
};

Giải thích:

  • event.state: Trạng thái của đối tượng được lưu trong lịch sử tại thời điểm sự kiện xảy ra. Đây là đối tượng state mà bạn đã gán khi sử dụng pushState() hoặc replaceState().

  • Sự kiện popstate sẽ được kích hoạt khi người dùng nhấn nút "Back" hoặc "Forward", và bạn có thể sử dụng thông tin trạng thái này để thay đổi giao diện người dùng.

Ví dụ:

// Thêm một mục vào lịch sử
history.pushState({ page: 1 }, "Page 1", "page1.html");

// Lắng nghe sự kiện "popstate"
window.onpopstate = function(event) {
  alert("You are now at: " + location.pathname + ", state: " + JSON.stringify(event.state));
};

// Sau khi nhấn "Back" hoặc "Forward", sự kiện sẽ được kích hoạt

Trong ví dụ trên, khi người dùng nhấn nút "Back" hoặc "Forward", sự kiện onpopstate sẽ kích hoạt và bạn có thể xử lý trạng thái của trang hoặc cập nhật giao diện người dùng dựa trên event.state.

Ứng dụng thực tế của History API trong JavaScript

History API rất mạnh mẽ khi xây dựng các ứng dụng web hiện đại, đặc biệt là Single Page Applications (SPA). Dưới đây là một số ứng dụng thực tế của các phương thức trong History API:

Điều hướng trang trong SPA mà không cần reload

Một trong những ứng dụng chính của History API là cho phép điều hướng giữa các "trang" trong SPA mà không cần phải tải lại trang. Khi người dùng tương tác với một phần của ứng dụng, bạn có thể thay đổi URL và trạng thái của trang mà không làm mới toàn bộ trang.

Ứng dụng thực tế: Giả sử bạn đang phát triển một ứng dụng danh mục sản phẩm. Khi người dùng chọn một sản phẩm từ danh sách, bạn có thể cập nhật URL bằng pushState() mà không cần tải lại trang. Việc này sẽ giúp trang web duy trì trạng thái và dễ dàng điều hướng giữa các phần mà không làm gián đoạn trải nghiệm của người dùng.

Ví dụ:

// Khi người dùng chọn một sản phẩm
function viewProduct(productId) {
  history.pushState({ productId: productId }, "Product Details", "/product/" + productId);
  // Cập nhật giao diện mà không tải lại trang
  loadProductDetails(productId);
}

Ở ví dụ trên, khi người dùng chọn sản phẩm, URL sẽ thay đổi thành /product/{id} và phần chi tiết sản phẩm sẽ được tải động mà không làm mới toàn bộ trang.

Tạo trang chi tiết động có URL riêng nhưng không tải lại

Khi phát triển các ứng dụng web động, đặc biệt là khi cần tạo trang chi tiết cho từng mục (như chi tiết sản phẩm, bài viết, v.v.), bạn có thể sử dụng History API để thay đổi URL và lưu trữ trạng thái mà không phải tải lại trang.

Ứng dụng thực tế: Bạn có thể tạo trang chi tiết cho từng bài viết trong blog mà không cần phải tải lại trang. Mỗi khi người dùng nhấp vào bài viết, bạn sẽ thay đổi URL và trạng thái mà không gây gián đoạn trải nghiệm người dùng.

Ví dụ:

// Khi người dùng nhấp vào bài viết
function viewPost(postId) {
  history.pushState({ postId: postId }, "Post " + postId, "/post/" + postId);
  // Cập nhật nội dung trang mà không tải lại
  loadPostContent(postId);
}

Trong ví dụ này, mỗi khi người dùng mở một bài viết khác, URL sẽ thay đổi và trạng thái được lưu mà không làm mới trang. Điều này tạo ra cảm giác như người dùng đang duyệt qua nhiều trang, trong khi thực tế ứng dụng chỉ có một trang duy nhất.

Thêm trạng thái vào lịch sử để lưu vị trí cuộn, bộ lọc, tab đang mở...

Một ứng dụng phổ biến khác của History API là lưu trữ trạng thái của trang, chẳng hạn như vị trí cuộn của trang, các bộ lọc đang được áp dụng hoặc các tab đang mở. Điều này giúp người dùng trở lại trang trước đó mà vẫn duy trì trạng thái ban đầu của trang, mang lại trải nghiệm liên tục và mượt mà.

Ứng dụng thực tế: Khi người dùng tìm kiếm hoặc lọc các mục trong một bảng dữ liệu, bạn có thể lưu trạng thái của bộ lọc và vị trí cuộn trong lịch sử. Điều này giúp người dùng quay lại trang mà không mất trạng thái đã chọn.

Ví dụ:

// Lưu trạng thái bộ lọc và vị trí cuộn
function updateFilter(filter) {
  history.pushState({ filter: filter, scrollPosition: window.scrollY }, "Filtered Results", "?filter=" + filter);
  applyFilter(filter);
}

// Khi người dùng quay lại trang, khôi phục trạng thái
window.onpopstate = function(event) {
  if (event.state) {
    applyFilter(event.state.filter);
    window.scrollTo(0, event.state.scrollPosition);
  }
};

Trong ví dụ này, khi người dùng thay đổi bộ lọc, trạng thái bộ lọc và vị trí cuộn sẽ được lưu vào lịch sử. Khi người dùng nhấn nút "Back" hoặc "Forward", trạng thái sẽ được khôi phục mà không làm mất đi các thay đổi trước đó.

Lưu ý khi sử dụng History API trong JavaScript

Mặc dù History API mang lại nhiều tiện ích cho việc điều hướng và quản lý trạng thái trang, bạn cần lưu ý một số điểm quan trọng để sử dụng API này hiệu quả và tránh các vấn đề không mong muốn:

Không hỗ trợ hoàn toàn trên trình duyệt cũ

Mặc dù History API được hỗ trợ trên các trình duyệt hiện đại, nhưng một số trình duyệt cũ hoặc các phiên bản trình duyệt không hỗ trợ đầy đủ các phương thức như pushState()replaceState(). Nếu người dùng sử dụng trình duyệt không hỗ trợ, các phương thức này sẽ không hoạt động như mong đợi.

  • Giải pháp: Sử dụng các công cụ kiểm tra tính tương thích của History API, như Modernizr, để xác định liệu tính năng này có được hỗ trợ trên trình duyệt của người dùng hay không. Nếu không hỗ trợ, có thể sử dụng các phương thức thay thế như điều hướng bằng hash (ví dụ: sử dụng # trong URL).

Cần xử lý cẩn thận để tránh "lịch sử rối loạn"

Khi sử dụng History API, bạn có thể dễ dàng tạo ra các mục mới trong lịch sử trình duyệt mỗi khi trạng thái của trang thay đổi. Tuy nhiên, nếu không sử dụng cẩn thận, việc thêm quá nhiều mục vào lịch sử có thể gây rối loạn và tạo ra một lịch sử trình duyệt khó điều hướng.

  • Giải pháp: Hãy chắc chắn rằng bạn chỉ gọi pushState() khi thực sự có sự thay đổi trong trang. Nếu trạng thái của trang không thay đổi nhưng bạn muốn thay thế một mục trong lịch sử, sử dụng replaceState() thay vì pushState() để không tạo thêm một bước mới.

Tránh tạo quá nhiều entry trong lịch sử một cách không cần thiết

Việc tạo quá nhiều mục trong lịch sử trình duyệt có thể khiến người dùng không thể quay lại trang trước đó một cách dễ dàng. Điều này có thể xảy ra khi bạn gọi pushState() quá nhiều lần mà không thực sự thay đổi trạng thái của trang. Người dùng sẽ phải duyệt qua nhiều mục trong lịch sử và cảm thấy trải nghiệm không mượt mà.

  • Giải pháp: Đảm bảo rằng bạn chỉ gọi pushState() khi có sự thay đổi thực sự về trạng thái hoặc nội dung của trang. Nếu bạn chỉ muốn thay đổi trạng thái mà không tạo mục mới trong lịch sử, hãy sử dụng replaceState(). Điều này giúp giảm số lượng mục trong lịch sử và tạo ra trải nghiệm người dùng tốt hơn.

Kết bài

Trong quá trình phát triển ứng dụng web, việc quản lý lịch sử trình duyệt là một phần quan trọng giúp tạo ra trải nghiệm người dùng mượt mà và liền mạch. History API của JavaScript mang lại khả năng thay đổi URL và lưu trạng thái của trang mà không phải tải lại trang, giúp tối ưu hóa hiệu suất và duy trì các thao tác của người dùng. Bằng cách sử dụng các phương thức như pushState(), replaceState(), và go(), các nhà phát triển có thể dễ dàng tạo ra các ứng dụng Single Page Application (SPA) với khả năng điều hướng linh hoạt.

Tuy nhiên, khi làm việc với History API, chúng ta cần phải chú ý đến việc sử dụng hợp lý các phương thức này, tránh tạo quá nhiều mục trong lịch sử và xử lý sự kiện một cách cẩn thận để không gây ảnh hưởng đến trải nghiệm người dùng. Việc hiểu rõ và sử dụng History API sẽ giúp tối ưu hóa ứng dụng của bạn, đặc biệt trong các ứng dụng web hiện đại, nơi điều hướng và quản lý trạng thái là rất quan trọng.

Bài viết liên quan