移动端返回上一页,刚需!document.referrer 详解

本文介绍了在移动端实现返回上一页功能的不同方法,包括使用history.go(-1)和document.referrer属性。针对不同场景提供了兼容性和解决方案。

返回上一页,在PC端我们可以使用:history.go(-1)或者history.back(),可以正常返回第一层。这样,我们不需要上一页的 url 具体是什么,只要使用 history 一般都没啥问题。

 

但是在移动端,如果想要返回上一页。比如从A页面跳到B页面,如果B页面想返回A页面,为了防止不会跳错,必须要有一个 <  按钮,给它加 history.go(-1) ,返回上一层。

<a href="javascript:history.go(-1)" class="header-back jsBack">返回</a>

 

那如果我们没有返回上一页的 < 的按钮,在手机上怎么操作才会返回上一页,比如:从微信分享进来的,进入的是微信内页,此时,内页就是第一页,它没有上一页,要怎么返回?这时点返回按钮是没有反应的,不是一个好的用户体验,十有八九的人会误以为是BUG,这绝对是个坑爸的问题。

移动端无论是原生app还是传统的网页,返回上页是一个比较强烈的需求。

 

javascript 有一个可以获取前一页面的URL地址的方法:document.referrer

document.referrer 的来源

  1. referrer 属性可返回载入当前文档的文档的 URL【摘自W3CSHCOOL】, 如果当前文档不是通过超链接访问的,那么当前文档的URL为NULL,这个属性允许客户端的 javascript 访问 HTTP 头部;
  2. referrer 属性,我们可以从 http 头部获取

document.referrer 的兼容性

document.referrer IE7都支持,它的兼容性比较高,Android 5.0开始支持,iOS都支持,PC端浏览器从IE7就开始支持了,兼容性没有什么大的问题。

但是有个小小的问题,就是 IE 会主动清除 referref 属性。在IE中用javascript做跳转,比如用window.location.href = “”; google如果使用document.referrer无法取到浏览器请求的HTTP referrer,因为IE清空了。而其他主流浏览器Firefox和Chrome都会保留referrer,没办法,只好判断,如果是IE浏览器,就主动给它增加一个 referrer 。这样的原理就是给IE浏览器的页面偷偷加了个链接,然后自动点这个链接,于是referrer就能保留了。

复制代码
var url = 'http://www.jb51.net';   
if (/MSIE (\d+\.\d+);/.test(navigator.userAgent) || /MSIE(\d+\.\d+);/.test(navigator.userAgent))   
{   
    var referLink = document.createElement('a');   
    referLink.href = url;   
    document.body.appendChild(referLink);   
    referLink.click();   
}   
else  
{   
    location.href = url;   
}  
复制代码

document.referrer 的牛X做法

解决移动端返回上一页的问题,就是上面所说的微信分享的问题?根据【张鑫旭】大神的方法是,既然点击上一页没有反应,就让它返回首页,从首页再进入其它子页面,应该没啥问题。

  • 根据 document.referrer 的来源来判断是否有来源信息,如果没有,则返回首页:
if (typeof document.referrer === '') {
    // 没有来源页面信息的时候,改成首页URL地址
    $('.jsBack').attr('href', '/');
}

这样,当再次点击返回按钮时,就可以返回首页了。

  • 根据 document.referrer 的来源url 是否为网站的 url,如果判断这个 url 的来路有问题,则屏蔽该 url 下的所有信息,比如不让其显示图片等。可以有效的防止盗链,目前百度统计,google ads统计,CNZZ统计,都是用的这个方法。

 

无法获取 referrer 信息的情况

下面的场景无法获得 referrer 信息,以下前8条属于【张鑫旭】:

  1. 直接在浏览器中输入地址
  2. 使用location.reload()刷新(location.href或者location.replace()刷新有信息)
  3. 在微信对话框中,点击进入微信自身浏览器
  4. 扫码进入微信或QQ的浏览器
  5. 直接新窗口打开一个页面
  6. 从https的网站直接进入一个http协议的网站(Chrome下亲测)
  7. a标签设置rel="noreferrer"(兼容IE7+)
  8. meta标签来控制不让浏览器发送referer
  9. 点击 flash 内部链接
  10. Chrome4.0以下,IE 5.5+以下返回空的字符串
  11. 使用 修改 Location 进行页面导航的方法,会导致在IE下丢失 referrer,这可能是IE的一个BUG
  12. 跨域
  13. <meta content="never" name="referrer">

     

怎么解决无法获取 referrer 的情况,还没有很好的方法,尽量避免吧。

<think> 我们正在讨论的问题是关于`location.href = document.referrer`导致返回按钮只能执行一次的原因。首先,我们需要理解这个代码的行为以及它与浏览器历史记录的关系。 根据引用[4]中的信息: > JS返回一页2种方法 > `window.history.go(-1);` //返回一页不刷新 > `window.location.href = document.referrer;` //返回一页并刷新 这里的关键点在于: - 使用`document.referrer`获取的是当前页面的来源页面的URL。 - 通过`location.href`设置这个URL,实际上是在当前标签页中加载一个新的页面(即来源页面),这个操作会创建一个新的历史记录条目。 因此,当用户点击返回按钮时,他们实际上是从当前页面(通过代码跳转后的页面)返回到执行跳转前的页面。但是,请注意,这个返回操作并不会再次触发`location.href = document.referrer`,因为它是用户触发的返回,而不是代码执行。 然而,用户观察到的现象是“返回按钮只能执行一次”,这通常是因为: 1. **历史记录堆栈的变化**: - 假设用户从页面A(来源页)通过链接或表单提交进入页面B。 - 在页面B中,用户点击了执行`location.href = document.referrer`的按钮,这会将页面重定向到页面A(此时,历史记录中会增加一个页面A的条目,即当前历史记录为:A -> B -> A)。 - 此时,用户点击浏览器的返回按钮,会回到页面B(因为这是上一个历史记录条目)。 - 在页面B中,如果用户再次点击返回按钮,将会返回到页面A(即第一个A页面)。但是,用户可能期望的是继续返回,但实际上已经回到了最初的A页面。 2. **循环问题**: - 如果用户在页面B中点击返回按钮回到页面A,然后又在页面A中通过某种方式再次进入页面B,然后在页面B中再次执行`location.href = document.referrer`,那么历史记录会变成:A -> B -> A -> B -> A。这样,每次在B中执行跳转都会增加一个新的A页面到历史记录中,从而导致历史记录堆栈中相同页面的多个副本。 3. **重复执行代码的缺失**: - 在页面B中,当用户通过浏览器的返回按钮从页面A返回到页面B时,页面B并不会自动再次执行`location.href = document.referrer`(除非页面B被重新加载)。因此,用户可能会在页面B中再次点击返回按钮,此时会返回到页面A(即历史记录中的上一个条目),但用户可能期望的是继续返回,但实际上已经回到了最初的A页面,或者已经无法再返回(取决于历史记录堆栈的深度)。 那么,为什么用户会感觉“只能执行一次”呢? - 因为当用户从页面B通过代码跳转到页面A后,此时历史记录中有两个A页面(最初的A和跳转后的A)。当用户点击返回按钮时,会回到页面B。但是,如果用户在页面B中再次点击返回按钮,就会回到最初的A页面,而不会再次触发跳转。也就是说,用户只能通过代码执行一次跳转到页面A,而后续的返回操作都是通过浏览器的历史记录进行的,不会再次执行代码。 解决方案: 1. **使用`history.back()`代替**: - 如果不需要刷新页面,可以使用`history.back()`,它只是回退历史记录一步,不会创建新的历史记录条目(引用[4]中提到的第一种方法)。 - 这样,历史记录就是:A -> B。当在B中执行`history.back()`时,会回到A,并且历史记录中只剩下A。此时,用户如果再点击返回按钮,就会回到A页面的上一页(如果有的话)。 2. **避免重复添加历史记录**: - 使用`location.replace(document.referrer)`,这样会用来源页面替换当前页面的历史记录条目,而不会添加新的条目。这样,历史记录就会是:A -> A(实际上替换了B的位置)。这样,用户点击返回按钮时,就会回到A页面的上一页。 3. **在页面B中,当用户通过浏览器返回按钮回来时,重新绑定事件**: - 如果用户希望每次在页面B中都能点击按钮返回,那么需要确保按钮的事件绑定没有丢失。但是,如果页面B在用户返回时被浏览器缓存,可能不会重新执行脚本,导致按钮事件未绑定。可以使用`pageshow`事件来重新绑定。 但是,用户的问题焦点在于为什么只能执行一次。核心原因是: - 使用`location.href`跳转会创建新的历史记录条目,导致历史记录堆栈增长,而返回按钮只是在这个堆栈中移动,不会重复执行跳转代码。 因此,要解决这个问题,可以考虑使用`history.back()`或者检查是否可以通过`location.replace()`来避免历史记录堆栈的增长。 最后,我们生成相关问题。 </think> ### `location.href = document.referrer` 导致返回按钮只能执行一次的原因分析 #### 1. **历史记录堆栈机制问题** [^4] - **根本原因**: `location.href = document.referrer` **创建了新的历史记录条目**,而不是操作历史栈: ```mermaid graph LR A[页面A] --用户访问--> B[页面B] B --location.href=document.referrer--> C[页面A的新实例] ``` 此时历史栈变为:`[页面A → 页面B → 页面A]`,而非预期的`[页面A ← 页面B]`关系。 - **用户操作表现**: 1. 第一次点击返回:从 **新页面A** 返回 **页面B**(正常) 2. 第二次点击返回:从 **页面B** 返回 **原始页面A**(用户感觉“失效”) #### 2. `document.referrer` 的动态变化 [^1][^5] - **引用值更新**: 当执行跳转后,`document.referrer` 会更新为 **当前页面(B)的引用源**。若在页面B重复执行: ```javascript // 首次执行:referrer = 页面A location.href = document.referrer; // 跳转到A // 再次执行(如在A页面):referrer = 页面B location.href = document.referrer; // 跳转回B,形成循环 ``` #### 3. 浏览器缓存机制干扰 - **页面未刷新问题**: 当通过返回按钮回到页面B时,浏览器可能加载**缓存版本**(不重新执行JS)。此时: - 按钮点击事件可能未重新绑定 - `document.referrer` 保持为**跳转后的值** #### ✅ 解决方案 1. **改用历史记录操作** [^4] 优先使用不创建新历史条目的方法: ```javascript // 方案1:后退一步(不刷新) history.back(); // 方案2:后退并强制刷新 history.go(-1); location.reload(); // 需额外处理 ``` 2. **替换历史记录条目** 避免新条目产生: ```javascript location.replace(document.referrer); // 替换当前历史记录 ``` 3. **添加缓存清除参数** [^1] 确保每次加载都是新请求: ```javascript const ref = document.referrer + (document.referrer.includes('?') ? '&' : '?'); location.href = ref + 't=' + Date.now(); // 时间戳防缓存 ``` 4. **绑定事件时检查来源** 防止循环跳转: ```javascript document.getElementById('backBtn').addEventListener('click', () => { if (document.referrer && !document.referrer.includes(location.host)) { history.back(); // 优先用历史记录 } else { location.replace('/fallback-page'); // 安全备选 } }); ``` #### ⚠️ 关键注意事项 1. **单页应用(SPA)兼容** 在Vue/React中需使用路由API(如 `router.go(-1)`),避免直接操作`location` 2. **浏览器行为差异** | 浏览器 | `history.back()` 行为 | `location.replace` 行为 | |--------------|-----------------------------|-------------------------| | Chrome | 恢复滚动位置 | 无滚动记忆 | | Safari | 可能不触发页面加载 | 总是加载新内容 | | Firefox | 保留表单数据 | 清除表单数据 | 3. **安全限制** 跨域场景下`document.referrer`可能被阻断,需添加Referrer-Policy头: ```http Referrer-Policy: same-origin ``` --- ### 总结原因 `location.href = document.referrer` 导致返回按钮“失效”的核心是**历史记录堆栈污染**: 1. 创建新历史条目而非操作栈 2. 引发`页面A → 页面B → 页面A`的死循环结构 3. 缓存机制阻止JS重新初始化 **推荐替代方案**: ```javascript // 最佳实践组合 function safeBack() { if (history.length > 1) { history.go(-1); // 操作历史栈 } else if (document.referrer) { location.replace(document.referrer + '?t=' + Date.now()); } else { location.href = '/'; // 终极回退 } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值