新开窗口的那些事:拦截、安全、target

简介: 前端开发经常会遇到需要新开窗口的需求,而某些时候,新窗口的地址需要通过接口返回,经常就会遇到新开窗口被拦截的情况,这里说一下新开窗口的几种方式、被拦截的原因以及如何避免被拦截、新窗口安全、target 的秘密。

网络异常,图片无法展示
|

前端开发经常会遇到需要新开窗口的需求,而某些时候,新窗口的地址需要通过接口返回,经常就会遇到新开窗口被拦截的情况,这里说一下新开窗口的几种方式、被拦截的原因以及如何避免被拦截、新窗口安全、target 的秘密。

方式

主流的新开方式分为两种:

window.open

window.open 也是比较常用的一种弹窗方式,可以直接脚本打开新窗口,且可控制窗口是否弹出、大小等。

window.open(url, name, features);
复制代码

模拟 a 标签

第三种方式是创建一个 a 标签,然后通过触发它的点击事件来打开新窗口,也算是比较常用的方式。

const openLink = (link: string) => {
    const a = document.createElement('a');
    a.href = link;
    a.target = '_blank';
    a.click();
};
复制代码

除了上述几种常用方式,还有可以使用弹窗提示用户再次点击跳转、预先请求链接完成后再渲染按钮等,这里不做讨论。

拦截和避免

然而无论是 window.open 还是模拟 a 标签点击,都会遇到被拦截的情况,原因是很多黑产滥用弹窗功能,导致浏览器对这块功能收紧,当浏览器弹窗不是由用户发起时,便会拦截该操作。那浏览器是怎么判定是否由用户发起的呢?其实是当用户进行了点击行为一段时间内,浏览器都会认为该操作是用户的行为。并且该时间段内只能触发一次合法弹窗,多次弹窗依旧会被拦截。同时如果该时间段内触发的 setTimeout,会以 setTimeout 调用的时间为准,即只需要 setTimeout 在指定时间内调用,而不管 setTimeout 中的 window.open 或模拟点击的时间。

通过上面的分析,我们可以知道,如果用户点击后,一段时间内还没拿到 url 进行新窗口打开的操作,浏览器就会进行拦截,这个时间段各浏览器中好像并不一致。

通过上面的信息,为了避免被拦截,可以想到以下方案:

通过 setTimeout 定时一段足够请求返回的时间段,获取请求到的数据,打开新窗口,这种方式过于死板,不推荐。

在点击后直接打开窗口,然后等待请求返回后,再将新窗口重定向到返回的地址,这也是使用最为广泛的方式。

const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
const getUrl = async () => {
    await sleep(1000);
    return 'https://www.baidu.com';
};
const newWindow = window.open('about:blank', '_blank');
newWindow.location.href = await getUrl();
复制代码

不过该方式体验上也有点问题,因为打开窗口触发后窗口便会弹出,而等待请求的这段时间里该窗口都会是一片空白,还有接口报错后窗口依旧保留,这里可以做一些小小的优化。

const newWindow = window.open('about:blank', '_blank');
newWindow.document.write('waiting...');
try {
    newWindow.location.href = await getUrl();
} catch (error) {
    newWindow.close();
    alert('open failed');
}
复制代码

其它的还有通过将请求改为同步请求等方式,这里不做讨论。

安全

通过 window.open 或者 a 链接打开的标签可以通过 window.opener 拿到原页面的 window 引用,如果新窗口为外部页面,可能会导致信息泄漏、被钓鱼等安全问题。为了避免该情况,需要在 a 标签中添加 rel='noopener'rel='noreferrer',在 window.open 中添加 noopenernoreferrerfeatures

注意 noreferrer 包含 noopener 的作用,并且会影响到窗口请求的 referrer 头,会影响到一些导流分析统计数据。

不过经测试最新 chrome 中使用 a 标签点击时,opener 默认为 null,必须要显示使用 rel='opener' 才能拿到 opener 引用。

target

在打开窗口时我们也经常用到 target,一般包含四个内置值:

  • _self 当前窗口打开
  • _blank 新窗口打开
  • _parent 父窗口打开,iframe 的父窗口
  • _top 顶层窗口,同样是 iframe 中的最顶层窗口

其实除了这几个常用值,target 还可以为任何字符串,该字符串会作为窗口的名称也就是 window.name,如果存在同名窗口,则会在对应窗口中打开链接,如果不存在则会创建一个新窗口。

当某些窗口全局只能存在一个时,该属性会非常有效,比如登陆页面,可以设置其 namemy_login,当在其它页面需要登录时,直接将 target 设置为 my_login,就可以直接进入登陆页面,避免用户打开一堆新窗口。

相关文章
ElementPlus菜单如何默认打开第一个,router-view里替换变的,menu菜单没有跳转怎么办,开启路由:router=“true“,如何设置点击空格就调用方法
ElementPlus菜单如何默认打开第一个,router-view里替换变的,menu菜单没有跳转怎么办,开启路由:router=“true“,如何设置点击空格就调用方法
ElementPlus菜单如何默认打开第一个,router-view里替换变的,menu菜单没有跳转怎么办,开启路由:router=“true“,如何设置点击空格就调用方法
|
Java Android开发 C++
Framework 全局监听屏幕点击事件 INPUT_EVENT_INJECTION
Framework 全局监听屏幕点击事件 INPUT_EVENT_INJECTION
324 0
|
容器
使用Fragmentation,start跳转到嵌套viewpager页面出现返回键重写失效原因。
最近在写项目时,采用的是单Activity+多Fragment的架构,用的Fragmentation的库。我的主页面是一个BootomFragment的抽象类(当然它还有一个管理类),其又继承自最大的LatteDelegate,LatteDelegate又继承自Fragment并实现Fragmentation库的接口,当然,当然这只是其中一部分,所以简略的讲了一下,大概知道层次就行。
107 0
使用Fragmentation,start跳转到嵌套viewpager页面出现返回键重写失效原因。
新窗口打开链接target = “_blank“
新窗口打开链接target = “_blank“
133 0
|
前端开发
前端面试题:1.页面加载完成(onload)之前触发的事件;2.History,Location,Window,Navigation的区别;3.e.target和e.currentTarget的区别
★Navagator:提供有关浏览器的信息 ★Window: Window对象处于对象层次的最顶层, 它提供了处理Navagator窗口的方法和属性 ★Location:提供了与当前打开的URL-工作的方 法和属性,是一个静态的对象 ★History:提供了与历史清单有关的信息 ★Document:包含与文档元素一起工作的对象,它将这些元素封装起来供编程人员使用
276 0
|
JavaScript
js实现动态添加具有相同name的input+动态添加的input绑定事件+保存前判断所有name为空阻断提交
js实现动态添加具有相同name的input+动态添加的input绑定事件+保存前判断所有name为空阻断提交
472 0
js实现动态添加具有相同name的input+动态添加的input绑定事件+保存前判断所有name为空阻断提交
WebBrowser 的 DocumentCompleted事件不执行的解决方法
原文:WebBrowser 的 DocumentCompleted事件不执行的解决方法 WebBrowser 的 DocumentCompleted事件不执行的解决方法: 使用WebBrowser的ProgressChanged事件,在时间中判断((WebBrowser)sender).ReadyState == WebBrowserReadyState.Complete是否成立,若成立则执行DocumentCompleted的处理。
1533 0