chrome彈窗在雙屏情況下left居中定位異常分析

毛靜文發表於2019-01-24

背景

使用 window.open 進行彈窗顯示,實現微信二維碼彈窗功能 在雙屏情況下,chrome瀏覽器位於副屏彈窗時,會存在彈窗位置異常問題。目前網上相關解析及解決方案几乎沒有,故寫此文章以作分享。

圖片

文章重點

雙屏情況下,chrome瀏覽器彈窗位置問題

  • 多螢幕時,chrome瀏覽器位於非主屏進行彈窗顯示時,設定彈窗的left,top將會異常
  • 本文將分析其顯示異常的原因,並給出解決方案

解決該問題的分析過程

  • 這是本文分享的另一個重點
  • 除了解決方案,希望能通過本文和大家分享筆者解決該問題時的思路和方法。
  • 這些方法可能不是最優的,但希望能給大家帶來一點觸動或者啟示。在解決到其他問題的時候也用得上。
    圖片

window.open的第三個引數及其相容性介紹

  • window.open方法相信大家都不會陌生,通常用於傳遞一個地址引數,新建一個瀏覽器tab頁面。
  • 但除了第一個地址引數,window.open還另外接收兩個引數,分別是「strWindowName(新視窗的名稱)」,「strWindowFeatures(新視窗特性)」
  • 這強調的是第三個引數,當設定了第三個引數後,新開的彈窗將會在原頁面的基礎上,已非tab頁面的形式進行顯示,有以下幾個特點
    • 在原頁面上進行彈窗顯示,而不是新起瀏覽器tab頁面進行跳轉。其顯示方式類似alert彈窗,屬於原頁面的一個功能模組,而不是跳轉至新頁面。
    • 非tab頁面,這意味著它不像其他tab頁面那樣可以放在瀏覽器tab欄中,它是摺疊不進去了,是以彈窗的形式呈現。
  • 第三個引數「strWindowFeatures」可以設定新視窗特性,例如寬度,高度,距頂,距左,是否顯示滾動條等等。本文不做詳細介紹,引數詳情可以參考這篇文章
  • 需要注意的是,strWindowFeatures裡的特效並不是每個瀏覽器都支援的,不同於「dom」,這屬於「bom(borwser Object Model)」的內容。具體相容性這裡也不講了,網上也有相關文章
    圖片

chrome的相容性與坑(重點一)

異常的顯示

  • 即使看完上面的相容性文章,當你使用chrome瀏覽器,位於非主屏進行彈窗時,依然會存在位置設定異常的問題。
  • 實現居中顯示彈窗,一般程式碼會這樣寫
    const windowWidth = window.screen.width // 螢幕寬度
    const windowHeight = window.screen.height  // 螢幕高度
    const pageWidth = 600 // 彈出視窗的寬度
    const pageHeight = 550 // 彈出視窗的高度
    let pageTop = (windowHeight - pageHeight) / 2 // 視窗的垂直位置
    let pageLeft = (windowWidth - pageWidth) / 2 // 視窗的水平位置;
    window.open('xxx', 'xxx', `width=${pageWidth},height=${pageHeight},top=${pageTop},left=${pageLeft}`) // 實現居中彈窗
    複製程式碼
  • 這段程式碼在主螢幕顯示沒有問題,可以居中顯示,但如果將頁面移換到副螢幕進行彈窗時。你會發現,無論引數怎麼設定,彈窗都會在螢幕最左側或螢幕最右側進行顯示,並不是水平居中。點選這裡檢視示例

異常的原因及其解決方案

  • 原因可能很多同學都難以想到,這是因為彈窗的left和top引數,並不是基於當前頁面作為原點進行計算的,而是以主螢幕作為原點進行計算

  • 所以進行位置設定時,需要計算其基於主螢幕的偏移值。

  • 那怎麼知道當前是否處於主螢幕上呢?可以通過window.screen.availLeft引數來解決,該引數返回瀏覽器可用空間左邊距離螢幕(系統桌面)左邊界的距離。

  • 通過該引數,甚至不需要知道目前處於哪個螢幕上,直接加上該引數即可基於當前螢幕進行定位。修改後的程式碼如下

    const {
      availLeft, // 返回瀏覽器可用空間左邊距離螢幕(系統桌面)左邊界的距離。
      availHeight, // 瀏覽器在螢幕上的可用高度,即當前螢幕高度
      availWidth, // 瀏覽器在螢幕上的可用寬度,即當前螢幕寬度
    } = window.screen
    const pageWidth = 600 // 彈出視窗的寬度
    const pageHeight = 550 // 彈出視窗的高度
    let pageTop = (availHeight - pageHeight) / 2 // 視窗的垂直位置
    let pageLeft = (availWidth - pageWidth) / 2 // 視窗的水平位置;
    left += availLeft // 加上螢幕偏移值
    window.open('xxx', 'xxx', `width=${pageWidth},height=${pageHeight},top=${pageTop},left=${pageLeft}`) // 實現居中彈窗
    複製程式碼
  • 「top」引數的設定同樣存在這個問題

  • 如果主螢幕和副螢幕並不是處於相同的高度,「top」值的設定同樣會由於距系統主螢幕定位,而發生定位異常的顯示。看下面這張圖可能更好地理解

    圖片

  • 另外目前筆者發現,這個相容性問題,僅會在chrome核心的瀏覽器存在,safari上執行是不存在該問題的。綜上所述,得出最終的解決方案為

    const {
      availTop, // 返回瀏覽器可用空間左邊距離螢幕(系統桌面)左邊界的距離。
      availLeft, // 返回瀏覽器可用空間左邊距離螢幕(系統桌面)左邊界的距離。
      availHeight, // 瀏覽器在螢幕上的可用高度,即當前螢幕高度
      availWidth, // 瀏覽器在螢幕上的可用寬度,即當前螢幕寬度
    } = window.screen
    const pageWidth = 600 // 彈出視窗的寬度
    const pageHeight = 550 // 彈出視窗的高度
    let pageTop = (availHeight - pageHeight) / 2 // 視窗的垂直位置
    let pageLeft = (availWidth - pageWidth) / 2 // 視窗的水平位置;
    if (navigator.userAgent.indexOf('Chrome') !== -1) { // 相容chrome的bug
      top += availTop // 距頂偏移值
      left += availLeft // 距左偏移值
    }
    window.open('xxx', 'xxx', `width=${pageWidth},height=${pageHeight},top=${pageTop},left=${pageLeft}`) // 實現居中彈窗
    複製程式碼

問題解決過程(重點二)

筆者遇到該問題是通過如下方式一一尋找解決方案

百度

最基礎,成本最低的一步,筆者進行過以下關鍵字的搜尋(這裡主要突出關鍵字提取)

  • window.open 居中顯示
  • window.open left chrome
  • window.open left 異常
  • window.open 定位 異常
  • window.open chrome 相容性
  • window.open 雙屏顯示異常
  • 搜尋結果,找到了相關的問題,但未能找到真正有效的解決方案。

問答論壇

  • stackoverflow,國外著名的程式設計問答網站,純英文,內容全。
  • segmentfault,國內的stackoverflow,內容也不錯。

MDN官網

維基百科:MDN Web Docs(舊稱Mozilla Developer Network、Mozilla Developer Center,簡稱MDN)是一個彙集眾多Mozilla基金會產品和網路技術開發文件的免費網站。

  • 一般可以看作前端基礎函式的官方說明文件,具有一定的權威性,當然一定程度上會更為難懂

其他頁面程式碼分析

尋找網上實現了該功能的網站,下載其頁面程式碼進行分析。 網上的程式碼都是加密過的,雖然不直觀,但能推測或猜出一些端倪

  • 各關鍵詞搜尋
    • 首先,通過chrome除錯工具,找到觸發彈窗的按鈕
    • ctrl+s,下載整個頁面,
    • 通過IDE全域性搜尋整個頁面中關於該按鈕的資訊,如class,id,及其他屬性值,能定位到該按鈕的屬性都全域性搜尋一遍
    • 逐檔案檢視,有無相關配置
  • window.open 函式名搜尋
    • 開啟彈窗肯定需要通過該語句,全域性搜尋,如果window沒被覆蓋的話應該能找到
  • 第三個引數搜尋
    • 根據 strWindowFeatures 可配置專案進行全域性搜尋,
    • 提取其特點,如「scrollbars」,「titlebar」這些變數
    • 以及其字串形式傳參的特點,搜尋「,left=」「,height=」
  • 重置函式
    • 終極大招,函式重置,及通過在chrome控制檯重置該函式,來觀察其傳參情況
    • 開啟chrome控制檯,找到Console欄,拷貝如下程式碼
      window.open = function () {
        console.log(arguments)
      }
      複製程式碼
    • 再此進行登入彈窗操作,觸發函式執行

筆者是在前三個方法都失敗的情況下,通過第四個方法找到的問題所在。

  • 發現其left值傳參為負數,
  • 在自己專案中設定為負數也能實現居中效果
  • 從而推測出原因

感謝閱讀,祝好

圖片

相關文章