天天看點

移動端h5監聽浏覽器傳回操作(目前在react項目中用到)

前言

1.主要是傳回是預設的浏覽器傳回事件是傳回上一個頁面。

2.處理頁面各種彈窗,點選實體傳回應該隐藏這些彈窗而不是直接傳回頁面。

3.總結下問題,h5應該希望能監聽到傳回事件并且做一些處理。

相關知識

1、利用

popstate

事件,點選浏覽器前進,後退會觸發popstate事件。

2、利用

hashchange

事件,頁面hash改變是會觸發此事件(适合react,vue單頁面應用)。

一、window.onpopstate

每當處于激活狀态的曆史記錄條目發生變化時,popstate

事件就會在

對應window

對象上觸發. 如果目前

處于激活狀态的曆史記錄條目是由history.pushState()

方法建立,或者由

history.replaceState()方法修改過

的, 則

popstate事件對象的state

屬性包含了這個曆史記錄條目的state對象的一個拷貝.

調用

history.pushState()

或者

history.replaceState()

不會觸發popstate事件.

popstate

事件隻會在浏覽器某些行為下觸發, 比如點選後退、前進按鈕(或者在JavaScript中調用

history.back()、history.forward()、history.go()

方法).

history.pushState()

不會重新整理頁面,往曆史記錄中添加一條記錄。

history.replaceState()

不會重新整理頁面,替換目前頁面記錄,不會在曆史記錄中新增。

相容性:當網頁加載時,各浏覽器對

popstate

事件是否觸發有不同的表現,Chrome 和 Safari會觸發

popstate

事件, 而Firefox不會.

可參考相關文檔。

假如目前網頁位址為example.com/example.htm…,則運作下述代碼後:

window.onpopstate = () => {} 等同于 window.addEventListener('popstate',() => {});
​
window.onpopstate = function(event) {
  alert("location: " + document.location + ", state: " + JSON.stringify(event.state));
};
//綁定事件處理函數. 
history.pushState({page: 1}, "title 1", "?page=1");    //添加并激活一個曆史記錄條目 http://example.com/example.html?page=1,條目索引為1   目前記錄有兩條記錄(包括最開始的)
history.pushState({page: 2}, "title 2", "?page=2");    //添加并激活一個曆史記錄條目 http://example.com/example.html?page=2,條目索引為2   目前記錄有三條記錄
history.replaceState({page: 3}, "title 3", "?page=3"); //修改目前激活的曆史記錄條目 http://ex..?page=2 變為 http://ex..?page=3,條目索引為3  目前記錄有三條(replaceState會替換第三條條)
history.back(); // 彈出 "location: http://example.com/example.html?page=1, state: {"page":1}"
history.back(); // 彈出 "location: http://example.com/example.html, state: null
history.go(2);  // 彈出 "location: http://example.com/example.html?page=3, state: {"page":3}
​複制代碼
           
移動端h5監聽浏覽器傳回操作(目前在react項目中用到)

如上圖,假設目前頁面是A頁面,跳轉到B頁面,B頁面打開dialong彈窗,這個時候點選傳回,預設是傳回到A頁面。這種體驗是很差的,優化體驗,這個時候需要優化這種體驗性能問題。

場景:在B頁面中有個彈窗,點選傳回,要關閉彈窗,再次點選,傳回到A頁面。

代碼如下:

// 假設A頁面為http://www.example.com/a.html 
// B頁面是http://www.example.com/b.html 
// 下面以react生命周期為例
componentDidMount: function() {
    window.addEventListener('popstate',(state) => {
        // 監聽到傳回事件,注意,隻有觸發了傳回才會執行這個方法
        console.log(state);
        this.back();
    })
}
// 假設點選打開對話窗,利用pushState添加一天記錄
openDialong: function() {
    // 此時頁面位址為b.html?page=1,但是頁面沒有重新整理。并且不會觸發popstate方法
    history.pushState({page: 1}, "title 1", "?page=1");
    // TODO
    
}
// 彈窗已存在,并且點選了傳回事件,此時頁面b.html?page=1--->b.html,并觸發popstate事件
back: function() {
    // 頁面在使用者看來是沒有重新整理的,此時是關閉dialong
    // TODO
    
}
// 離開頁面的時候取消監聽popstate
componentWillUnmount: function() {
    window.removeEventListener('popstate',(state) => {
        this.back();
    }) 
}複制代碼
           

二、window.onhashchange

當 一個視窗的 hash (URL 中 # 後面的部分)改變時就會觸發 hashchange 事件(參見

location.hash

)。
if ("onhashchange" in window) {
    alert("該浏覽器支援 hashchange 事件!");
}
​
function locationHashChanged() {
    if (location.hash === "#somecoolfeature") {
        somecoolfeature();
    }
}
​
window.onhashchange = locationHashChanged;複制代碼
           

還是以上面場景為例:

// 假設A頁面為http://www.example.com/a.html#/a 
// B頁面是http://www.example.com/a.html#/b 
// 下面以react生命周期為例
componentDidMount: function() {
    window.addEventListener('hashchange',(state) => {
        // hash改變就會觸發
        const href = location.href;
        // 目前hash中不存在?page=1是觸發(初始化剛進來是不會觸發這個方法的)
        if (href.indexOf('?page=1') < 0) {
          this.back();
        }
    })
}
// 假設點選打開對話窗,利用pushState添加一天記錄
openDialong: function() {
    // 此時頁面位址為a.html?page=1,但是頁面沒有重新整理。并且不會觸發popstate方法
    // history.pushState({page: 1}, "title 1", "?page=1");
    // 不過此時會觸發hashchange,但是加個判斷;
    this.props.history.push('/b?page=1'); 
    // TODO
    
}
// 彈窗已存在,并且點選了傳回事件,此時頁面a.html?page=1--->a.html,并觸發hashchange事件
back: function() {
    // 頁面在使用者看來是沒有重新整理的,此時是關閉dialong
    // TODO
    
}
// 離開頁面的時候取消監聽hashchange
componentWillUnmount: function() {
    window.removeEventListener('hashchange',(state) => {
        this.back();
    }) 
}
​複制代碼
           

三、優化**

移動端h5監聽浏覽器傳回操作(目前在react項目中用到)

還是這個圖檔

假設A是首頁,B詳情頁面,C是支付頁面,D是結果頁面。(我們希望D不能傳回到C頁面)。

假設要從D頁面跳轉到B頁面(B,C,D也一樣), 從結果頁面D中點選按鈕跳轉到詳情B頁面,可再次購買。

有以下方法:

方法一:location.href = 'http://www.example/b.html';
方法二:history.go(-2);複制代碼
           

主要差別:

方法一會往曆史記錄中添加新的記錄,變成A—>B—>C—>D—>B。

此時點選實體傳回,就會回到D頁面。而這不是我們要的結果。

方法二是回到曆史記錄中,不會新增記錄,鍊路還是A—>B—>C—>D。

D頁面不應該回到C頁面

此時我們就可以用到上面的方法,點選傳回利用history.go()的方法,回到我們想到的頁面。

// D頁面---->B頁面
componentDidMount: function() {
    this.props.history.push('/b?page=1'); 
    // 如果有問題,加個setTimeout
    window.addEventListener('hashchange',(state) => {
        // hash改變就會觸發
        const href = location.href;
        // 目前hash中不存在?page=1是觸發(初始化剛進來是不會觸發這個方法的)
        if (href.indexOf('?page=1') < 0) {
          this.back();
        }
    })
}
// 并且點選了傳回事件,此時頁面d.html?page=1--->d.html,并觸發hashchange事件
back: function() {
    // 頁面在使用者看來是沒有重新整理的,此時是回到B頁面
    // TODO
    this.props.history.go(-2);
}
// 離開頁面的時候取消監聽hashchange
componentWillUnmount: function() {
    window.removeEventListener('hashchange',(state) => {
        this.back();
    }) 
}複制代碼
           

相容性可檢視。

有問題歡迎指正。

作者:_forever

連結:https://juejin.im/post/5b603c94e51d4519700f84a1

來源:掘金

著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

繼續閱讀