Làm thế nào để phát hiện sự kiện nhấp vào nút quay lại của trình duyệt bằng cách sử dụng góc?


79

Có thể phát hiện người dùng đã vào một trang thông qua nút quay lại lịch sử trong trình duyệt của họ không? Tốt hơn là tôi muốn phát hiện hành động này bằng cách sử dụng angle.js.

Tôi không muốn sử dụng định tuyến theo góc. Nó cũng sẽ hoạt động nếu người dùng gửi biểu mẫu và sau khi gửi thành công đến máy chủ và chuyển hướng lại, nó cũng có thể hoạt động nếu người dùng quay lại biểu mẫu bằng nút quay lại của trình duyệt.

Câu trả lời:


120

Đây là một giải pháp với Angular.

myApp.run(function($rootScope, $route, $location){
   //Bind the `$locationChangeSuccess` event on the rootScope, so that we dont need to 
   //bind in induvidual controllers.

   $rootScope.$on('$locationChangeSuccess', function() {
        $rootScope.actualLocation = $location.path();
    });        

   $rootScope.$watch(function () {return $location.path()}, function (newLocation, oldLocation) {
        if($rootScope.actualLocation === newLocation) {
            alert('Why did you use history back?');
        }
    });
});

Tôi đang sử dụng một khối chạy để bắt đầu điều này. Đầu tiên, tôi lưu trữ vị trí thực tế trong $ rootScope.actualLocation, sau đó tôi lắng nghe $ locationChangeSuccess và khi điều đó xảy ra, tôi cập nhật realLocation với giá trị mới.

Trong $ rootScope, tôi theo dõi các thay đổi trong đường dẫn vị trí và nếu vị trí mới bằng với vị trí trước đó là do $ locationChangeSuccess không được kích hoạt, có nghĩa là người dùng đã sử dụng lại lịch sử.


1
Cảm ơn! Trông rất tuyệt. Phải kiểm tra chi tiết vào ngày mai. Đặc biệt là tôi phải kiểm tra xem $ rootScope.actualLocation cũng được cập nhật nếu vị trí được thay đổi mà không sử dụng các tuyến đường góc cạnh, mà là các siêu liên kết "bình thường". Bạn có nghĩ rằng nó cũng sẽ hoạt động bằng cách sử dụng các siêu liên kết bình thường?
Thomas Kremmel

4
à vâng. chưa bao giờ sử dụng các tuyến đường góc trước đây, vì vậy nhìn vào ví dụ của bạn, có vẻ như không có siêu liên kết bình thường và không bình thường (loại đường góc). vì vậy chỉ cần quên bình luận của tôi ..
Thomas Kremmel

2
$ LocationProvider.html5Mode (true) chỉ được sử dụng để làm cho các liên kết hoạt động trong jsFiddle, bạn sẽ không cần nó trong dự án của mình. Chỉ cần nhớ các liên kết cần được đặt trước bằng # / để góc cạnh có thể nắm bắt chúng (trong fiddle thì không). Nếu bạn sử dụng liên kết "bình thường", chẳng hạn như / sách, góc cạnh sẽ không nắm bắt được nó và trang sẽ được chuyển hướng đến url đó, điều này sẽ đặt lại ứng dụng của bạn vì tất cả các tập lệnh sẽ được tải lại, đó là lý do tại sao nó sẽ không hoạt động .
Bertrand

19
Đối với bất kỳ ai (như tôi) không hiểu nó hoạt động như thế nào: Khi url thay đổi theo cách thông thường (không sử dụng nút quay lại / chuyển tiếp), thì $locationChangeSuccesssự kiện sẽ kích hoạt sau khi $watch gọi lại. Nhưng khi url thay đổi bằng cách sử dụng nút quay lại / chuyển tiếp hoặc history.back()api, thì $locationChangeSuccesssự kiện sẽ kích hoạt trước khi $watch gọi lại. Vì vậy, khi kích hoạt lệnh gọi lại đồng hồ actualLocationđã bằng với newLocation. Nó chỉ là cách góc hoạt động bên trong và giải pháp này chỉ sử dụng hành vi bên trong này.
Ruslan Stelmachenko

5
Đoạn mã trên không phát hiện các thay đổi chuỗi truy vấn URL, như từ /#/news/?page=2đến /#/news/?page=3. Để nắm bắt chúng, bạn có thể sử dụng $location.absUrl()thay vì $location.path()(ở cả hai nơi mà nó được sử dụng ở trên).
Michael

11

Nếu bạn muốn giải pháp chính xác hơn (phát hiện lùi và chuyển tiếp), tôi đã mở rộng giải pháp do Bertrand cung cấp :

$rootScope.$on('$locationChangeSuccess', function() {
    $rootScope.actualLocation = $location.path();
});


$rootScope.$watch(function () {return $location.path()}, function (newLocation, oldLocation) {

    //true only for onPopState
    if($rootScope.actualLocation === newLocation) {

        var back,
            historyState = $window.history.state;

        back = !!(historyState && historyState.position <= $rootScope.stackPosition);

        if (back) {
            //back button
            $rootScope.stackPosition--;
        } else {
            //forward button
            $rootScope.stackPosition++;
        }

    } else {
        //normal-way change of page (via link click)

        if ($route.current) {

            $window.history.replaceState({
                position: $rootScope.stackPosition
            });

            $rootScope.stackPosition++;

        }

    }

 });

8

Đối với định tuyến ui, tôi đang sử dụng đoạn mã dưới đây.

Bên trong App.run(), sử dụng

 $rootScope.$on("$stateChangeStart", function (event, toState, toParams, fromState, fromParams) 
 {

    if (toState.name === $rootScope.previousState )
       { 
          // u can any 1 or all of below 3 statements
         event.preventDefault();  // to Disable the event
         $state.go('someDefaultState'); //As some popular banking sites are using 
         alert("Back button Clicked");

       }
 else
      $rootScope.previousState= fromState.name;
   });

Bạn có thể lấy giá trị 'beforeState' trong sự kiện '$ stateChangeSuccess' cũng giống như sau nếu bạn muốn.

    $rootScope.$on("$stateChangeSuccess", function (event, toState, toParams, fromState, fromParams) 
   {

        $rootScope.previousStatus = fromState.name;
    });

Điều này không phát hiện các nhấp chuột vào nút quay lại - điều này chỉ phát hiện xem người dùng có đang chuyển đổi giữa hai tên trạng thái - thông qua nút quay lại hoặc bằng cách nhấp vào giữa hai trang. Ngoài ra, điều này không nhìn vào các thông số của trạng thái.
Amy B

1

Tôi biết đó là một câu hỏi cũ. Dù sao :)

Câu trả lời của Bema trông rất tuyệt. Tuy nhiên, nếu bạn muốn làm cho nó hoạt động với các url 'bình thường' (tôi đoán không có hàm băm), bạn có thể so sánh đường dẫn tuyệt đối, thay vì đường được trả về bởi $location.path():

myApp.run(function($rootScope, $route, $location){
//Bind the `$locationChangeSuccess` event on the rootScope, so that we dont need to 
//bind in induvidual controllers.

    $rootScope.$on('$locationChangeSuccess', function() {
        $rootScope.actualLocation = $location.absUrl();
    });      

    $rootScope.$watch(function () {return $location.absUrl()}, function (newLocation, oldLocation) {
        if($rootScope.actualLocation === newLocation) {
            alert('Why did you use history back?');
        }
    });
});

0

JavaScript có một đối tượng lịch sử gốc.

window.history

Kiểm tra MDN để biết thêm thông tin; https://developer.mozilla.org/en-US/docs/DOM/window.history?redirectlocale=en-US&redirectslug=window.history

Không chắc nó tốt như thế nào khi hỗ trợ đa trình duyệt tho.

Cập nhật

Có vẻ như những gì tôi đã gọi ở trên chỉ dành cho các trình duyệt kiểu Gecko.

Đối với các trình duyệt khác, hãy thử sử dụng history.js; https://github.com/browserstate/history.js


Cám ơn bạn đã góp ý. Vì tôi thích một cách làm điều đó theo góc cạnh hơn, tôi sẽ thử câu trả lời của Bertrands.
Thomas Kremmel

0

Vì vậy, tôi đã phiên âm câu trả lời của @Bema thành TypeScript và nó trông như thế này:

namespace MyAwesomeApp {
    // ReSharper disable once Class
    function detectBackButton(
        $rootScope: ng.IRootScopeService,
        $location: ng.ILocationService
    ) {
        let actualLocation: string = '';

        $rootScope.$on('$locationChangeSuccess',
            () => {
                actualLocation = $location.path();
            });

        $rootScope.$watch(() => $location.path(),
            (newLocation: string, oldLocation: string) => {
                if (actualLocation === newLocation) {
                    //$rootScope.$broadcast('onBackButtonPressed', null);
                }
            });
    }

    detectBackButton.$inject = [
        '$rootScope',
        '$location'
    ];

    angular
        .module('app')
        .run(detectBackButton);
}

Chúng tôi không phải tạo thuộc tính ngoài $rootScopedịch vụ, chúng tôi có thể có mã 'khi thay đổi vị trí thành công' và mã 'thay đổi vị trí' của chúng tôi đều đóng trên actualLocationbiến cục bộ. Từ đó, bạn có thể làm bất cứ điều gì bạn thích, giống như trong mã gốc. Về phần mình, tôi sẽ cân nhắc việc phát sóng một sự kiện để những người điều khiển riêng lẻ có thể làm bất cứ điều gì họ phải làm, nhưng bạn có thể bao gồm các hành động toàn cầu nếu bạn phải làm .

Cảm ơn vì câu trả lời tuyệt vời và tôi hy vọng điều này sẽ giúp ích cho những người dùng trình đánh chữ khác.


-2

Solutio của tôi cho vấn đề này là

app.run(function($rootScope, $location) {
    $rootScope.$on('$locationChangeSuccess', function() {
        if($rootScope.previousLocation == $location.path()) {
            console.log("Back Button Pressed");
        }
        $rootScope.previousLocation = $rootScope.actualLocation;
        $rootScope.actualLocation = $location.path();
    });
});

-7

khi nhấn nút quay lại, góc chỉ kích hoạt sự kiện $ routeChangeStart , $ locationChangeStart không kích hoạt.

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.