最近遇到这样一个需求:

  • 打开当前页面时,可能会带上一个参数pid
  • 页面初始化时,如果发现存在此参数,就立刻跳转到其对应的外部页面。
  • 当用户从外部页面后退时,可以回到当前页面,此时正常显示页面内容。

我的实现方式是:

  • hash传递pid
  • 在页面入口脚本中,如果hash中没有pid,就正常渲染页面。
  • 如果发现存在pid,就不渲染页面内容,而是通过ajax从后台获取pid对应的URL。然后从location.hash里去掉pid,再修改location.href进行跳转。

在PC端Chrome下测试正常通过,但在手机浏览器中出现了问题。

跳转到新页面后,按后退时不会回到当前页。在微信浏览器里,后退会直接关闭,在小米自带浏览器、夸克浏览器里,后退会回到标签页的初始页,移动端Chrome还是正常。

上百度搜了下,尝试了以下方案,均无效:

  • setTimeout延时再跳转
  • 使用location.assign()代替location.href赋值
  • 使用history.pushState手动写入当前页面URL后再跳转
  • window.onload里执行判断并延时跳转
  • window.onload里延时执行一个模拟点击事件并在该元素的点击回调中执行跳转

这其中倒是发现一个奇怪的现象,就是在延时跳转前,如果手动点击过页面任意位置,表现就会一切正常,新页面后退可以正常返回当前页,完全符合需求预期。

但是,使用dispatchEvent模拟出来的点击事件不行。

与之前做全屏api时遇到的问题类似,应该是浏览器有某种策略,要求history的操作只能在用户的真实点击操作中执行。

但奇怪的是搜了很多中文页,没有一个提到这样的问题。

最后在StackOverflow上找到了一些解答(类似问题同样很少,但总算有答案):

HTML标准 文档对history的使用有一些规范:

If any of the following conditions are met, let replacement flag be unset; otherwise, let it be set:

  • This Location object's relevant Document has completely loaded, or
  • In the task in which the algorithm is running, an activation behavior is currently being processed whose click event's isTrusted attribute is true, or
  • In the task in which the algorithm is running, the event listener for a click event, whose isTrusted attribute is true, is being handled.

大致的意思是:

如果满足以下任意条件之一, location对象的replacement标记置为默认,否则置为true。

(换句话说,如果不满足以下条件,对location进行任何操作,都不会产生新的历史记录)

  • location对象所在的文档已经完全加载
  • location的修改操作由click事件触发,且事件的isTrusted属性为true(也就是用户的真实点击操作)

英文不行,没有找到完全加载的精确定义。但我推测对遵循HTML标准的浏览器来说,只有用户与页面产生互动,才表示页面真的已经“完全加载”了。

在用户对页面进行操作前,只有最终展现的页面才被认为是有效页面,之前的所有中间跳转都应该被忽略,不计入历史记录,这样才能避免后退行为出现死锁。

我对这种设计表示理解,这段HTML标准的定义证实了我的推测。因此,原本预想的方案是走不通的,我只能调整需求了。

最终方案是增加一个跳转中间页,由用户点击来进行跳转。

话说Chrome没这个问题,说明Chrome在这一条上其实没有遵循HTML标准规范?


mirari
209 声望4 粉丝