關於 Safari back 按鈕在 iOS 16 不能按照期望工作的問題分析

JerryWang_汪子熙 發表於 2023-01-24
iOS
  • 裝置: iOS: 16.1.1
  • User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 16_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Mobile/15E148 Safari/604.1

問題重現步驟:

  • 使用 iOS 16.1.1 Safari 開啟 test1.html
  • 點選 Link 超連結跳轉到 test2.html
  • 在 test2.html 回退到 test1.html

期望的結果是看到 alert 對話方塊。

問題在 iOS 15 不能重現。

test1.html 的原始碼:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Test1</title>
  </head>
  <body>
    <a href="test2.html">Link</a>
  </body>
</html>

test2.html 的原始碼:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Test2</title>
  </head>
  <body>
    <button id="btn">history.back()</button>
    <script>
      history.pushState(null, null, null)

      window.addEventListener("popstate", _ => alert(1))

      document.getElementById("btn").addEventListener("click", _ => {
        history.back()
      }, false)
    </script>
  </body>
</html>

我在好幾個瀏覽器上做了測試:

我剛試過,也可以在 macOS 上重現。

我在單擊 history.back() 按鈕時看到 alert,但在單擊後退按鈕(或向後滑動)時看不到 alert。

對於 history.back() 和透過瀏覽器 UI 向後導航之間的行為差異,我還沒有任何解釋(儘管我懷疑這與我們所做的一些後退/前進列表劫持預防工作有關)。

我還注意到我們的行為似乎與 Chrome 一致。 因此,如果有錯誤,那不是 WebKit 特有的。

Firefox 似乎始終如一地顯示 alert,正如 Web 開發人員所期望的那樣。

StackOverflow 上相關的討論

結論

哦,我只是更仔細地檢視了測試用例,我明白了現在發生了什麼。

與 Chrome 類似,WebKit 最近做了一些安全加固,以防止不良的 JavaScript 劫持後退/前進列表。 這意味著由 JS 新增的歷史條目(例如透過 history.pushState())在使用者導航時會被跳過,除非它們是透過使用者互動新增的。

在測試用例中,test2.html 在沒有使用者互動的情況下呼叫 history.pushState()。 結果,建立的歷史記錄項被標記了一個特殊的標誌。 如果使用者向後滑動或按下後退按鈕,我們將跳過這個“虛擬”歷史記錄項,因此不會觸發 popstate 事件。 這是新的故意行為,應該與 Blink 保持一致。

如果不希望跳過歷史記錄項,則在呼叫 history.pushState() 時需要使用者手勢/啟用(例如,由於使用者單擊按鈕而呼叫 history.pushState())。

相關文章