记录---当window.open被ios安全机制拦截,我掏出3种方案,终于跳转成功!

🧑‍💻 写在开头
点赞 + 收藏 === 学会🤣🤣🤣

一、前言

今天在开发H5的时候,遇到了一个bug,就是在ios环境,在某些情况下执行window.open不生效,所以正好趁此机会研究了一下window.open。

二、window.open介绍

从open方法的调用方式可以看出,open方法是定义在Window接口上,正因为如此它有三个参数:

window.open(url, target, windowFeatures)
  1. url:「可选参数」,表示你要加载的资源URL或路径,如果不传,则打开一个url地址为about:blank的空白页。
    在这里插入图片描述
    顺便介绍一下,about:blank是chrome浏览器的一个命令,该指令会打开浏览器的一个内建空白页面,而不是从网络上下载,类似的命令还有about:downloads、about:extensions、about:history等等,具体chrom浏览器提供了哪些命令,你可以通过在浏览器地址栏输入about:about查看。
  2. target:「可选参数」,它可以给以下两种值。

第一种是target关键字

_self:当前标签页加载;
_blank(默认值):新标签页打开;
_parent:作为当前浏览环境的父级浏览上下文打开,没有父级浏览上下文,效果与_self相同;
_top:作为最顶级的浏览上下文打开,没有顶级浏览上下文,效果与_self相同。

第二种是一个字符串:表示加载资源的浏览上下文的名称,也就是标签页的名称,如果这个名称在现有的标签页中不存在,则会开启一个新的标签页,如果存在,会跳转到这个标签页。
这里顺便提一下,我在平时开发中曾经写出这样的代码:

const handleClick = () => {
    window.open(url, 'blank');
}

这个方法是绑定在一个点击按钮上面的,结果我的同事在点击多次这个按钮时,第一次会打开一个新窗口,而后续的点击都跳转到第一次点击打开的那个窗口,于是我去排查了一下,发现自己把_blank写成了blank,于是浏览器把它解析成了标签页的名字,而不是target关键词,我那时候感觉挺有意思,第一次详细去了解了这个target。

  1. windowFeatures:「可选参数」,它是一个字符串,用来描述窗口的特性,其格式是"key1=value1,
    key2=value2",即将key和value以=号连接拼接成字符串,多个key
    value以逗号隔开,比如我们要打开一个宽为500,高为600的窗口可以这么写:
window.open(url, 'new-window', 'width=500,height=600');

它可以描述如下窗口特性:

width:内容区域宽度,最小值为100,
height:内容区域高度,最小值100,
left:距离用户操作系统工作区左侧的距离,
top:距离用户操作系统工作区顶部的距离。

这四个应该是最常用的,其它的不太常用我这里就不列举了。
至于这个windowFeatures的作用呢,平常开发中用的不太多,我能想到的场景就是比如你要通过url打开一个预览页面,让用户看里面的一些内容,就可以用这个试试。

三、bug复现

先写一个能复现问题的demo:

async function jump() {
  await fetch('/xxx');
  window.open('https://www.xxx.cn');
}

正常情况下执行window.open是能正常新标签页打开传入的url的,但是一旦前面用await做了异步操作后,再执行window.open,就不生效了。

然后我又尝试了a标签,发现效果也是一样的,无法打开新标签页。

async function jump() {
  await fetch('/xxx');
  let a = document.createElement('a');
  a.setAttribute('target', 'blank');
  a.href = 'https://www.xxx.cn';
  a.click()
  a = null;
}

四、原因分析

安全机制拦截:IOS的Safari浏览器为了防止恶意网站通过window.open/a标签打开其他网站,于是对它们的调用有所限制,如果不是由用户直接交互触发的,而是由程序自动触发的,Safari会拦截这个操作。
异步操作:在AJAX回调中执行window.open/a标签跳转,被浏览器认为是非用户交互行为,所以被拦截。

五、解决方案

方案1:改用location.href
既然window.open和a标签跳转不行,那就换成location.href就好了,因为safari不会拦截location.href。

async function jump() {
  await fetch('/xxx');
  location.href = 'https://www.xxx.cn';
}

当然并不是所有场景下都适合用location.href,因为location.href会刷新页面,所以需要根据具体场景来选择。
方案2:先打开一个空标签页
通过window.open(“”, “_blank”)先打开一个空标签页,然后等待请求完成后,修改这个新标签页的url。

async function jump() {
  const newWin = window.open("", "_blank"); // 提前打开一个窗口
  const { jumpUrl } = await fetch('/xxx');
  if (jumpUrl) {
    newWin.location = jumpUrl;
  } else {
    newWin.close();
    // ... 
  }
}

但这里有个体验问题,我这里根据有没有jumpUrl进行跳转,如果没有jumpUrl,我需要调用close方法关闭刚才提前打开的那个窗口,而这样用户就会体验到的流程就是,先出来一个新窗口,随后被秒关闭,这样用户体验很差。

方案3:setTimeout/requestAnimationFrame

在我的业务场景中,是必须要用window.open的,所以只能另寻他法,最终找到了一个解决方案,就是在window.open之前加一个setTimeout,在回调中执行window.open,这样就能避免被safari拦截。

async function jump() {
  await fetch('/xxx');
  setTimeout(() => {
    window.open('https://www.xxx.cn');
  }, 0)
}

后面测试了一下,发现requestAnimationFrame也可以。

async function jump() {
  await fetch('/xxx');
  requestAnimationFrame(() => {
    window.open('https://www.xxx.cn');
  })
}

六、最终我采取的方案

我最终是通过方案3的setTimeout解决了问题,如果setTimeout不生效,可以尝试加点延时看看,比如100毫秒,我这边实测的ios机型都能生效,所以就没加延时。

七、小结

本文主要介绍了window.open的用法,以及我自己在平时开发中踩的坑,希望对大家平常开发有帮助!

本文转载于:https://juejin.cn/post/7461206556300607525
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

在这里插入图片描述

定义:上下文无关文法是一种描述形式语言的数学模型,由四元组 G=(V,Σ,R,S) 构成。其中,V 是非终结符集合,Σ 是终结符集合,R 是产生式规则集合,S 是起始符号。 示例:在文档中,有 G(E) 和 G(S) 等上下文无关文法,用于描述表达式的结构。例如,G(E) 的定义如下: E→T∣E+T∣E−T T→F∣T∗F∣T/F F→(E)∣i 这里,E、T、F 是非终结符,而 +、−、∗、/、(、) 和 i 是终结符。该文法用于描述算术表达式的构造方式。 推导是根据文法规则从起始符号逐步生成句子的过程,分为两种类型: 最左推导:始终扩展当前最左边的未展开非终结符。 最右推导:始终扩展当前最右边的未展开非终结符。 例如,在 G(N) 的上下文无关文法中,数字串的最左推导过程可以表示为: N⇒ND⇒NDD⇒⋯⇒DDD⇒0DDD⇒01DD⇒012D⇒0127 语法树是通过图形方式展示字符串如何根据文法规则进行推导的结构。它清晰地反映了推导过程中的层次关系。例如,对于表达式 i+i∗i,其语法树可以直观地展示操作符和操作数之间的层次结构。 如果一个句子存在多个不同的语法树,即可以通过多种推导过程生成,那么这个文法就被认为是二义性的。例如,句子 iiiei 有两个可能的语法树,这表明该文法存在二义性。 在自动机理论中,确定化是将非确定有限自动机(NFA)转换为确定有限自动机(DFA),以确保每个状态在读取输入符号时只有一个确定的转移路径。最小化则是去除 DFA 中的冗余状态,以获得更简洁的模型。文档中提供了 DFA 确定化和最小化的详细步骤示例。 正则表达式是一种用于匹配字符串模式的工具。文档中给出了许多正则表达式的例子,例如 (0∣1)∗01,用于匹配所有以“01”结尾的由 0 和 1 组成的字符串。正则表达式在文本处理和模式匹配中具有广泛应用。 综上所述,编译原理不仅涉
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值