Як виявити подію задньої кнопки браузера - перехресний браузер


219

Як ви остаточно визначите, натиснув чи не користувач кнопку назад у веб-переглядачі?

Як примусити використання кнопки повернення на сторінці у веб-програмі однієї сторінки за допомогою #URLсистеми?

Чому на землі кнопки повернення браузера не запускають власні події !?


2
Я хочу, щоб колись були додані події навігації (назад / вперед). Поки що це щось у роботах developer.mozilla.org/en-US/docs/Web/API/…
llaaalu

Відповіді:


184

(Примітка. Відповідно до відгуків Шаркі, я включив код для виявлення зворотних просторів)

Таким чином, я часто бачив ці питання в програмі SO, і останнім часом я сам стикався з проблемою контролю функцій кнопки назад. Після кількох днів пошуку найкращого рішення для мого застосування (Single-Page with Hash Navigation), я створив просту, крос-браузерну, без бібліотеку систему виявлення кнопки "назад".

Більшість людей рекомендують використовувати:

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

Однак ця функція також буде викликана, коли користувач використовує вбудований елемент, який змінює хеш місцеположення. Не найкраща робота з користувачем, коли користувач клацає, а сторінка переходить назад або вперед.

Щоб дати вам загальний контур моєї системи, я заповнюю масив попередніми хешами, коли мій користувач рухається по інтерфейсу. Це виглядає приблизно так:

function updateHistory(curr) {
    window.location.lasthash.push(window.location.hash);
    window.location.hash = curr;
}

Досить прямо вперед. Я роблю це для забезпечення крос-браузерної підтримки, а також підтримки для старих браузерів. Просто перенесіть новий хеш у функцію, і він збереже його для вас, а потім змінить хеш (який потім буде введено в історію браузера).

Я також використовую кнопку повернення на сторінці, яка переміщує користувача між сторінками за допомогою lasthashмасиву. Це виглядає приблизно так:

function goBack() {
    window.location.hash = window.location.lasthash[window.location.lasthash.length-1];
    //blah blah blah
    window.location.lasthash.pop();
}

Таким чином, це перемістить користувача назад до останнього хешу та видалить останній хеш із масиву (у мене зараз немає кнопки переадресації).

Так. Як визначити, користувався чи не користувач моєю кнопкою повернення на сторінці чи кнопкою браузера?

Спочатку я подивився window.onbeforeunload, але безрезультатно - це викликається лише у випадку, якщо користувач збирається змінювати сторінки. Це не відбувається в односторінковому додатку, що використовує хеш-навігацію.

Отже, після ще одного копання я побачив рекомендації щодо спроби встановити змінну прапора. Проблема з цим у моєму випадку полягає в тому, що я б спробував встановити це, але оскільки все асинхронно, воно не завжди буде встановлено вчасно для заяви if у хеші. .onMouseDownне завжди викликали натискання, і додавання його до onclick ніколи не призведе до цього досить швидко.

Це коли я почав роздивлятися різницю між documentі window. Моє остаточне рішення - встановити прапор за допомогою document.onmouseoverта відключити його з використанням document.onmouseleave.

Що трапляється, це те, що, якщо миша користувача знаходиться в області документа (читайте: візуалізована сторінка, але виключаючи рамку браузера), мій булевий параметр встановлений на true. Як тільки миша покидає область документа, булева повертається до false.

Таким чином я можу змінити своє window.onhashchangeна:

window.onhashchange = function() {
    if (window.innerDocClick) {
        window.innerDocClick = false;
    } else {
        if (window.location.hash != '#undefined') {
            goBack();
        } else {
            history.pushState("", document.title, window.location.pathname);
            location.reload();
        }
    }
}

Ви помітите чек на #undefined. Це тому, що якщо в моєму масиві немає історії, вона повертається undefined. Я використовую це, щоб запитати користувача, чи хочуть вони вийти, використовуючи window.onbeforeunloadподію.

Отже, коротше, і для людей, які не обов’язково використовують кнопку повернення на сторінці або масив для зберігання історії:

document.onmouseover = function() {
    //User's mouse is inside the page.
    window.innerDocClick = true;
}

document.onmouseleave = function() {
    //User's mouse has left the page.
    window.innerDocClick = false;
}

window.onhashchange = function() {
    if (window.innerDocClick) {
        //Your own in-page mechanism triggered the hash change
    } else {
        //Browser back button was clicked
    }
}

І ось у вас це є. простий, з трьох частин спосіб виявити використання кнопок "назад" та елементів на сторінці, що стосується навігації по хешу.

Редагувати:

Щоб користувач не використовував backspace для запуску події назад, ви також можете включити наступне (Спасибі @thetoolman у цьому запитанні ):

$(function(){
    /*
     * this swallows backspace keys on any non-input element.
     * stops backspace -> back
     */
    var rx = /INPUT|SELECT|TEXTAREA/i;

    $(document).bind("keydown keypress", function(e){
        if( e.which == 8 ){ // 8 == backspace
            if(!rx.test(e.target.tagName) || e.target.disabled || e.target.readOnly ){
                e.preventDefault();
            }
        }
    });
});

5
+1 приємна ідея, але я думаю, що це не вдасться, якщо користувач використовує будь-який ярлик клавіатури для "назад" (клавіша зворотного простору на firefox), поки його миша знаходиться у вікні браузера
Шаркі


7
Що з мобільним (наприклад, ipad)
базаратом

5
А як щодо розгортання подій із трекпада в MAC. Чи можна це захопити також?
Sriganesh Navaneethakrishnan

6
Як я можу розрізняти кнопки вперед і назад?
Mr_Perfect

79

Ви можете спробувати popstateобробник подій, наприклад:

window.addEventListener('popstate', function(event) {
    // The popstate event is fired each time when the current history entry changes.

    var r = confirm("You pressed a Back button! Are you sure?!");

    if (r == true) {
        // Call Back button programmatically as per user confirmation.
        history.back();
        // Uncomment below line to redirect to the previous page instead.
        // window.location = document.referrer // Note: IE11 is not supporting this.
    } else {
        // Stay on the current page.
        history.pushState(null, null, window.location.pathname);
    }

    history.pushState(null, null, window.location.pathname);

}, false);

Примітка. Для найкращих результатів слід завантажувати цей код лише на певні сторінки, де ви хочете реалізувати логіку, щоб уникнути будь-яких інших несподіваних проблем.

Подія "popstate" запускається кожного разу, коли змінюється поточна запис історії (користувач переходить у новий стан). Це відбувається , коли користувач натискає на кнопки Назад браузера вперед / назад або коли history.back(), history.forward(), history.go()методи програмно називається.

event.stateЦе властивість події одно історія стану об'єкта.

Для синтаксису jQuery оберніть його (щоб додати рівного слухача після того, як документ буде готовий):

(function($) {
  // Above code here.
})(jQuery);

Дивіться також: window.onpopstate при завантаженні сторінки


Дивіться також приклади на сторінках програм для одних сторінок та HTML5 pushState :

<script>
// jQuery
$(window).on('popstate', function (e) {
    var state = e.originalEvent.state;
    if (state !== null) {
        //load content with ajax
    }
});

// Vanilla javascript
window.addEventListener('popstate', function (e) {
    var state = e.state;
    if (state !== null) {
        //load content with ajax
    }
});
</script>

Це має бути сумісним із Chrome 5+, Firefox 4+, IE 10+, Safari 6+, Opera 11.5+ та подібними.


2
Kenorb, ти прав, що popstate працює, щоб схопити зміни, але це не відрізняє між програмним викликом події і тим, коли користувач натискає кнопки браузера.
Xarus

1
Відмінна публікація у програмах на одній сторінці та HTML5 pushState . Ідеально підходить для сучасних "макетів на одній сторінці" або "додатків для однієї сторінки". Дякую за посилання та вашу відповідь!
lowtechsun

1
Наприклад, сучасні браузери завжди не визначені
FAjir

Я пропоную вам поставити свій код у фрагмент, щоб можна було запустити його безпосередньо тут.
FourCinnamon0

16

Я досить довго боровся з цією вимогою і прийняв деякі рішення вище, щоб здійснити її. Однак я натрапив на спостереження, і, здається, він працює у веб-переглядачах Chrome, Firefox та Safari + Android та iPhone

На завантаженні сторінки:

window.history.pushState({page: 1}, "", "");

window.onpopstate = function(event) {

  // "event" object seems to contain value only when the back button is clicked
  // and if the pop state event fires due to clicks on a button
  // or a link it comes up as "undefined" 

  if(event){
    // Code to handle back button or prevent from navigation
  }
  else{
    // Continue user action through link or button
  }
}

Повідомте мене, якщо це допомагає. Якщо чогось мені не вистачає, я буду радий зрозуміти.


1
Неправда. eventмає значення навіть для кнопки вперед
Даніель Біровський Попескі

14

У javascript тип навігації 2означає натиснення кнопки "назад" або "вперед", і браузер фактично бере вміст із кешу.

if(performance.navigation.type == 2)
{
    //Do your code here
}

1
Це не працює на одній сторінці програми, результат завжди 1 (що означає перезавантажити). Більше того, це не робить різниці між кнопкою "назад" та "вперед", що дуже прикро.
папільйон

"Більше того, це не робить різниці між кнопкою" назад "та" вперед ", що дуже прикро." ​​Так, тому що щоразу, коли дані надходять із кешу, цей час performance.navigation.type становить 2
Hasan Badshah

Це не гарантовано працювати у всіх браузерах! тож вам слід написати щось подібне: якщо (window.performance) {// код, пов’язаний з продуктивністю}, також краще використовувати TYPE_BACK_FORWARD замість 2 для більшої читабельності.
blank94

7

Це обов'язково спрацює (Для виявлення натискання кнопки "назад")

$(window).on('popstate', function(event) {
 alert("pop");
});

6

Дивіться це:

history.pushState(null, null, location.href);
    window.onpopstate = function () {
        history.go(1);
    };

це чудово працює ...


Не працює в Chrome 78.0.3904.70 (Офіційна збірка) (64-розрядна)
Jeffz

Підтверджено роботу над Brave v1.3.118 >> Хром: 80.0.3987.116 (Офіційна збірка) (64-розрядна)
ZeroThe2,

Не працює, якщо ви прийшли на сторінку із зовнішнього сайту.
Мілан Влах

5
if (window.performance && window.performance.navigation.type == window.performance.navigation.TYPE_BACK_FORWARD) {
  alert('hello world');
}

Це єдине рішення, яке працювало для мене (це не веб-сторінка з однією сторінкою). Він працює з Chrome, Firefox та Safari.


1
window.performance.navigation застарілий. Ось документи розробника.mozilla.org
en-US/

Це працювало для мене на моїй .cshtml сторінці. Успішно працювали в Chrome та IE. Спасибі
Джаджіде

5

Правильна відповідь вже є, щоб відповісти на запитання. Я хочу згадати новий JavaScript API PerformanceNavigationTiming , він замінює застарілу performance.navigation .

Наступний код увійде в консоль "back_forward", якщо користувач приземлився на вашу сторінку за допомогою кнопки назад або вперед. Перегляньте таблицю сумісності, перш ніж використовувати її у своєму проекті.

var perfEntries = performance.getEntriesByType("navigation");
for (var i = 0; i < perfEntries.length; i++) {
    console.log(perfEntries[i].type);
}

Це не спосіб визначити, чи натиснуто кнопку назад на цій сторінці . Він повідомляє, чи було натиснуто на останню сторінку .
Seph Reed

Але це було справжньою відповіддю, яку я шукав, оскільки проблема, яку я отримую, - це після того, як я натиснув кнопку "назад", а не раніше, ніж я маю на увазі, що я можу створити код відповіді, який зупиняє мої дублікати запитів. Дякую.
Андрій

1
Це може не відповісти на питання, але саме те, що мені було потрібно! Гарний - я про це не знав ...
Hogsmill

4

Браузер: https://jsfiddle.net/Limitlessisa/axt1Lqoz/

Для мобільного управління: https://jsfiddle.net/Limitlessisa/axt1Lqoz/show/

$(document).ready(function() {
  $('body').on('click touch', '#share', function(e) {
    $('.share').fadeIn();
  });
});

// geri butonunu yakalama
window.onhashchange = function(e) {
  var oldURL = e.oldURL.split('#')[1];
  var newURL = e.newURL.split('#')[1];

  if (oldURL == 'share') {
    $('.share').fadeOut();
    e.preventDefault();
    return false;
  }
  //console.log('old:'+oldURL+' new:'+newURL);
}
.share{position:fixed; display:none; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,.8); color:white; padding:20px;
<!DOCTYPE html>
<html>

<head>
    <title>Back Button Example</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

</head>

<body style="text-align:center; padding:0;">
    <a href="#share" id="share">Share</a>
    <div class="share" style="">
        <h1>Test Page</h1>
        <p> Back button press please for control.</p>
    </div>
</body>

</html>


Це не відповідає на поставлене запитання. Питання полягає у виявленні використання кнопки повернення на сторінці в порівнянні з нативною кнопкою повернення браузера.
Xarus

2
Питання позначено тегами javascript, а не jquery.
skwny

3

Ось мій погляд на це. Припущення полягає в тому, що коли URL-адреса змінюється, але не documentвиявляється натискання в межах виявленого, це браузер назад (так, або вперед). Клік користувача скидається через 2 секунди, щоб зробити цю роботу на сторінках, які завантажують вміст через Ajax:

(function(window, $) {
  var anyClick, consoleLog, debug, delay;
  delay = function(sec, func) {
    return setTimeout(func, sec * 1000);
  };
  debug = true;
  anyClick = false;
  consoleLog = function(type, message) {
    if (debug) {
      return console[type](message);
    }
  };
  $(window.document).click(function() {
    anyClick = true;
    consoleLog("info", "clicked");
    return delay(2, function() {
      consoleLog("info", "reset click state");
      return anyClick = false;
    });
  });
  return window.addEventListener("popstate", function(e) {
    if (anyClick !== true) {
      consoleLog("info", "Back clicked");
      return window.dataLayer.push({
        event: 'analyticsEvent',
        eventCategory: 'test',
        eventAction: 'test'
      });
    }
  });
})(window, jQuery);

2

Мені вдалося використати деякі відповіді в цій темі та інші, щоб змусити її працювати в IE та Chrome / Edge. історія.pushState для мене не підтримувалася в IE11.

if (history.pushState) {
    //Chrome and modern browsers
    history.pushState(null, document.title, location.href);
    window.addEventListener('popstate', function (event) {
        history.pushState(null, document.title, location.href);
    });
}
else {
    //IE
    history.forward();
}

Щойно спробував це на Chrome 78.0.3904.70 (Official Build) (64-розрядний), і це не спрацювало.
Джефф

2

Повноцінний компонент може бути реалізований лише в тому випадку, якщо ви перегляньте API (зміните методи "історії" об'єкта), я поділюсь тільки що написаному класу. Тестовано на Chrome і Mozilla Підтримують лише HTML5 та ECMAScript5-6

class HistoryNavigation {
    static init()
    {
        if(HistoryNavigation.is_init===true){
            return;
        }
        HistoryNavigation.is_init=true;

        let history_stack=[];
        let n=0;
        let  current_state={timestamp:Date.now()+n};
        n++;
        let init_HNState;
        if(history.state!==null){
            current_state=history.state.HNState;
            history_stack=history.state.HNState.history_stack;
            init_HNState=history.state.HNState;
        } else {
            init_HNState={timestamp:current_state.timestamp,history_stack};
        }
        let listenerPushState=function(params){
            params=Object.assign({state:null},params);
            params.state=params.state!==null?Object.assign({},params.state):{};
            let h_state={ timestamp:Date.now()+n};
            n++;
            let key = history_stack.indexOf(current_state.timestamp);
            key=key+1;
            history_stack.splice(key);
            history_stack.push(h_state.timestamp);
            h_state.history_stack=history_stack;
            params.state.HNState=h_state;
            current_state=h_state;
            return params;
        };
        let listenerReplaceState=function(params){
            params=Object.assign({state:null},params);
            params.state=params.state!==null?Object.assign({},params.state):null;
            let h_state=Object.assign({},current_state);
            h_state.history_stack=history_stack;
            params.state.HNState=h_state;
            return params;
        };
        let desc=Object.getOwnPropertyDescriptors(History.prototype);
        delete desc.constructor;
        Object.defineProperties(History.prototype,{

            replaceState:Object.assign({},desc.replaceState,{
                value:function(state,title,url){
                    let params={state,title,url};
                    HistoryNavigation.dispatchEvent('history.state.replace',params);
                    params=Object.assign({state,title,url},params);
                    params=listenerReplaceState(params);
                    desc.replaceState.value.call(this,params.state,params.title,params.url);
                }
            }),
            pushState:Object.assign({},desc.pushState,{
                value:function(state,title,url){
                    let params={state,title,url};
                    HistoryNavigation.dispatchEvent('history.state.push',params);
                    params=Object.assign({state,title,url},params);
                    params=listenerPushState(params);
                    return desc.pushState.value.call(this, params.state, params.title, params.url);
                }
            })
        });
        HistoryNavigation.addEventListener('popstate',function(event){
            let HNState;
            if(event.state==null){
                HNState=init_HNState;
            } else {
                HNState=event.state.HNState;
            }
            let key_prev=history_stack.indexOf(current_state.timestamp);
            let key_state=history_stack.indexOf(HNState.timestamp);
            let delta=key_state-key_prev;
            let params={delta,event,state:Object.assign({},event.state)};
            delete params.state.HNState;
            HNState.history_stack=history_stack;
            if(event.state!==null){
                event.state.HNState=HNState;
            }
            current_state=HNState;
            HistoryNavigation.dispatchEvent('history.go',params);
        });

    }
    static addEventListener(...arg)
    {
        window.addEventListener(...arg);
    }
    static removeEventListener(...arg)
    {
        window.removeEventListener(...arg);
    }
    static dispatchEvent(event,params)
    {
        if(!(event instanceof Event)){
            event=new Event(event,{cancelable:true});
        }
        event.params=params;
        window.dispatchEvent(event);
    };
}
HistoryNavigation.init();

// exemple

HistoryNavigation.addEventListener('popstate',function(event){
    console.log('Will not start because they blocked the work');
});
HistoryNavigation.addEventListener('history.go',function(event){
    event.params.event.stopImmediatePropagation();// blocked popstate listeners
    console.log(event.params);
    // back or forward - see event.params.delta

});
HistoryNavigation.addEventListener('history.state.push',function(event){
    console.log(event);
});
HistoryNavigation.addEventListener('history.state.replace',function(event){
    console.log(event);
});
history.pushState({h:'hello'},'','');
history.pushState({h:'hello2'},'','');
history.pushState({h:'hello3'},'','');
history.back();

    ```

Гарний підхід! Дякую за те, що додали до цього (я все ще вважаю дивовижним те, що є розмова в цій темі через стільки років)
Xarus

Це здається розумним механізмом відстеження історії. Однак код коментований, тому дотримуватися його досить складно. Якщо хтось відходить від поточної сторінки, код не виявить натискання цієї кнопки, що не відповідає на початкове запитання "Як остаточно визначити, чи натиснув користувач кнопку назад у браузері чи ні". Натискання кнопок і відстеження історії - це дві різні речі, хоча одна може викликати іншу. Те, що все ще є розмова, вказує на головний недолік у дизайні веб-браузера.
CubicleSoft

1

Document.mouseover не працює для IE та FireFox. Однак я спробував це:

$(document).ready(function () {
  setInterval(function () {
    var $sample = $("body");
    if ($sample.is(":hover")) {
      window.innerDocClick = true;
    } else {
      window.innerDocClick = false;
    }
  });

});

window.onhashchange = function () {
  if (window.innerDocClick) {
    //Your own in-page mechanism triggered the hash change
  } else {
    //Browser back or forward button was pressed
  }
};

Це працює для Chrome і IE, а не FireFox. Все ще працюю, щоб FireFox правильно. Будь-який простий спосіб виявлення натискання кнопки назад / вперед браузера вітається, особливо не в JQuery, а також AngularJS або звичайному Javascript.


1
 <input style="display:none" id="__pageLoaded" value=""/>


 $(document).ready(function () {
        if ($("#__pageLoaded").val() != 1) {

            $("#__pageLoaded").val(1);


        } else {
            shared.isBackLoad = true;
            $("#__pageLoaded").val(1);  

            // Call any function that handles your back event

        }
    });

Наведений вище код працював для мене. У мобільних браузерах, коли користувач натискав кнопку назад, ми хотіли відновити стан сторінки відповідно до його попереднього відвідування.


Наведений вище код працював для мене, у мобільних браузерах, коли користувачі натискали кнопку назад, ми хотіли відновити стан сторінки відповідно до його попереднього відвідування
Ешвін

0

Я вирішив це, відслідковуючи оригінальну подію, яка викликала hashchange(чи то пальцем, клацанням чи колесом), щоб подія не помилилася з простою посадкою на сторінці та використовуючи додатковий прапор у кожне з моїх подій. Під falseчас натискання кнопки "Назад " браузер знову не встановить прапор :

var evt = null,
canGoBackToThePast = true;

$('#next-slide').on('click touch', function(e) {
    evt = e;
    canGobackToThePast = false;
    // your logic (remember to set the 'canGoBackToThePast' flag back to 'true' at the end of it)
}

-21

Я спробував перераховані вище варіанти, але жоден з них не працює на мене. Ось рішення

if(window.event)
   {
        if(window.event.clientX < 40 && window.event.clientY < 0)
        {
            alert("Browser back button is clicked...");
        }
        else
        {
            alert("Browser refresh button is clicked...");
        }
    }

Перейдіть за цим посиланням http://www.codeproject.com/Articles/696526/Solution-to-Browser-Back-Button-Click-Event-Handli для отримання більш детальної інформації


8
@MunamYousuf Цілком очевидно, чому цей фрагмент коду насправді не працює. Він відстежує кнопку "назад" браузера, але кнопка знаходиться поза вікном перегляду, отже, координати clientX та clientY не повинні бути отримані. Якщо припустити, що він працює, як щодо iOS? Кнопка "Назад" знаходиться внизу ... Крім того, як написано, це дуже неефективно, це умовно для кожної події, що спрацьовує ... Боже, це так погано, що я збираюся голосувати за неї.
Джеймс Вонг - Відновіть Моніку

Це жахливо! чому б ти робив таке? : / і це навіть не буде працювати, як сказав Джеймс.
msqar
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.