chrome弹窗在双屏情况下left居中定位异常分析

3,388 阅读7分钟

背景

使用 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值传参为负数,
  • 在自己项目中设置为负数也能实现居中效果
  • 从而推测出原因

感谢阅读,祝好

图片