11月01, 2018

一个小弹窗的故事

TLDR;

本文解决了一个问题:在浏览器中,一个URL只对应一个打开的弹窗,而且反复调出弹窗不会刷新页面。

大家在写前端应用的时候,应该都用window.open做过打开新窗口的功能。

笔者最近做的项目里,也有类似的功能。原先只需要用window.open(url)打开一个URL就可以了。有一天产品经理提需求:从网站任意地方弹窗打开一个URL,必须保证浏览器只为这个URL开一个窗口。

HTML标准文档中,window.open API的定义如下:

window.open([ url[, target [, features]]])

target参数指定了浏览该地址的上下文。通常情况下,如果浏览器中已经存在一个上下文(即,已经打开一个窗口),那么就会用这个上下文打开URL。target参数完美匹配了笔者的需求。只要将target参数设置成url的值,就能保证弹窗的唯一性。代码这样写:

function uniqueWindowOpen(urll, winName = url) {
  window.open(url, winName)
}

但是,以上代码会导致一次页面刷新。如果这个页面有正在进行中的任务,则会不幸地丢失未保存的数据和状态。hmmm……需要解决两个问题:

  • 根据target检测窗口是否已经打开

  • 检测到窗口已经打开后,聚焦到窗口

一番搜索后,终于找到解决办法。代码如下:

function uniqueWindowOpenWithoutReload(url, winName = url) {
  const winRef = window.open('', winName)
  const regExp = new RegExp(url) // url可能是路径,没包含域名。此处还可定义更严格的检测规则。
  if (winRef == null || !regExp.test(winRef.document.location.href)) {
    winRef.location = url
  }
}

在以上代码中,window.open方法会让新窗口聚焦。根据标准文档,url参数为空的话,新窗口指向的资源是about:blank,浏览器经过一系列判断条件之后,不会做加载动作,也就不会刷新页面。这时候检测一下新窗口的路径跟我们传的url是否匹配。如果是的话,不做任何动作;否则,用window.location = url让其加载url指定的页面。

以上方法在Chrome和Safari中测试有效:如果未打开过URL,则会在新窗口中打开URL,如果已经打开了,则只会让窗口聚焦。

本文链接:http://imhxl.com/post/unique-window-open-without-reload.html

-- EOF --

Comments