IOS微信浏览器 回退按钮事件监听和不刷新问题

本文介绍了一种使用JavaScript实现的网页导航逻辑,通过监听浏览器的popstate事件并结合history API来实现页面间的平滑过渡及历史记录管理,确保用户在不同页面间切换时能够返回到正确的状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//跳转上一页的路径,否则会直接跳到第一页
$(window).attr('location','/mobile/kyk/wrongdetail.html?subject=' + refsubject);

function pushHistory(){
window.addEventListener("popstate", function(e){
	getwrongtllist(); //调用列表Ajax接口
	$(window).attr('location','/mobile/kyk/wrongdetail.html?subject=' + refsubject);//跳转上一页的路径
}, false);
var state = {
	title:"",
	url: "#"
};
window.history.pushState(state, "", "");
};
pushHistory();
<think>我们正在处理一个Vue项目中拦截微信浏览器左上角返回按钮的需求。根据引用资料,我们可以知道一些关键点:1.微信浏览器左上角返回按钮的默认行为是返回上一页,但有时我们需要先执行一些逻辑(比如关闭蒙层)而是直接返回。2.我们可以通过监听`popstate`事件或`pageshow`事件来实现拦截逻辑。3.在Vue中,我们可以在组件的mounted生命周期钩子中添加事件监听,并在beforeDestroy钩子中移除,以避免内存泄漏。根据引用[3][4]中的示例,我们可以在第二个页面(需要拦截返回的页面)中这样做:-在mounted中,我们添加一个`popstate`事件监听器,当事件触发时,我们执行自定义逻辑(例如隐藏蒙层),并且通过`history.pushState`方法向当前历史记录中添加一个状态,这样用户需要再次点击返回才会真正返回上一页。-注意:在iOS微信浏览器中,history的前进后退会重新加载页面,所以我们需要监听页面展示事件来执行逻辑。实现步骤:1.在进入需要拦截返回的页面时,先向history中添加一个空状态(使用`history.pushState`),这样当前页面就有两个状态:初始状态我们添加的状态。当用户点击返回按钮时,首先会回到我们添加的状态(此时页面刷新),这时我们触发自定义逻辑(如关闭蒙层),然后移除我们添加的状态,这样再次点击返回就会返回上一页。2.我们监听`popstate`事件,在事件处理函数中判断是否应该执行拦截逻辑。具体代码实现思路(以Vue组件为例):```javascript//在需要拦截返回的页面组件中exportdefault{data(){return{isShowMask:true//假设有一个蒙层需要显示}},mounted(){//添加一个历史记录,这样当前页面就有两个状态:初始状态pushState添加的状态history.pushState(null,null,document.URL);//监听popstate事件window.addEventListener('popstate',this.handlePopstate);},beforeDestroy(){//组件销毁前移除事件监听window.removeEventListener('popstate',this.handlePopstate);},methods:{handlePopstate(event){//在这里进行拦截处理if(this.isShowMask){//如果蒙层是显示的,则隐藏蒙层,并阻止默认的返回行为(通过再次pushState)this.isShowMask=false;//再次pushState,这样用户再次点击返回时才会返回上一页history.pushState(null,null,document.URL);//注意:这里我们并没有调用event.preventDefault(),因为popstate事件无法通过preventDefault阻止返回//我们的策略是:第一次返回时隐藏蒙层并添加一个新的历史记录,这样用户需要再点一次返回才会真正返回}//如果蒙层已经隐藏,那么做额外处理,让浏览器执行默认的返回行为(返回上一页)}}}```但是,上述代码有一个问题:在安卓微信中,可能一次返回按钮点击就会触发两次`popstate`事件。因此,我们需要更健壮的方案。改进方案(参考引用[4]):我们可以通过一个标记来控制。另外,引用[3][4]的示例中,在隐藏蒙层后再次pushState,这样当用户再次点击返回时,由于没有蒙层,就会返回上一页。注意:在Vue项目中,我们也可以使用VueRouter的导航守卫,但是微信浏览器左上角的返回按钮触发VueRouter的导航守卫,所以我们只能使用原生的popstate事件。另外,引用[2]中提到,iOS中可以使用`pageshow`事件,但这里我们主要使用`popstate`事件。另一种实现方式(更常见):1.在进入页面时,先pushState一个状态。2.当用户点击返回时,触发popstate事件,此时我们隐藏蒙层,并且再次pushState一个状态(这样当前页面仍然停留在当前页),同时设置一个标记表示蒙层已经关闭。3.如果用户再次点击返回,由于蒙层已经关闭,我们就让页面返回(此时我们可以使用history.back()或者什么都做,因为popstate事件触发后,如果我们没有再次pushState,就会返回上一页)。但是,注意我们第二次pushState后,历史记录中又多了一条,所以我们需要在返回时移除我们添加的状态?或者我们可以使用history.go(-1)直接返回?实际上,我们可以在第一次popstate事件中,在隐藏蒙层并pushState之后,再添加一个popstate事件监听(或使用同一个),当再次触发popstate时,我们执行history.go(-1)返回上一页。但这样可能造成循环。因此,更安全的做法是:在第一次触发popstate时,我们隐藏蒙层,然后pushState,这样历史记录中有三个状态:初始状态、我们第一次pushState的状态、第二次pushState的状态。然后,当用户第二次点击返回时,会触发popstate事件,此时我们判断蒙层已经关闭,那么我们就移除事件监听,并执行history.back()(或者history.go(-1))返回上一页。但这样需要精确控制历史记录。实际上,我们可以简化:在第一次返回时,我们隐藏蒙层,并pushState,这样用户再点一次返回,就会回到我们第一次pushState的状态,然后再次触发popstate事件,这时我们做拦截,直接返回上一页?但是这样我们会在历史记录中留下两个无用的状态。所以,我们采用以下策略:-进入页面时:history.pushState(state1)-第一次返回(点击返回按钮):-触发popstate事件,我们隐藏蒙层,然后history.pushState(state2)->这样历史记录中有三个状态:初始、state1、state2-此时用户再次点击返回,会触发popstate事件(对应state2被弹出,当前状态变为state1),我们在这个事件中再次pushState(state3)?这样会陷入循环。为了避免循环,我们只在蒙层显示的时候拦截,蒙层隐藏后就再拦截。所以,在第一次popstate事件中,我们隐藏蒙层并pushState,然后当用户再次点击返回时,由于蒙层已经隐藏,我们拦截,让浏览器返回上一页(即state1被弹出,当前状态为初始状态),然后再点一次返回,就会返回上一页?这样用户需要点三次返回?显然行。因此,我们调整思路:我们只拦截一次,在第一次返回时隐藏蒙层,然后我们手动后退到上一页?行,这样会直接离开页面。所以,我们采用以下方法:在第一次popstate事件中,我们执行:1.隐藏蒙层(或其他操作)2.阻止浏览器默认的返回行为(实际上popstate事件中无法阻止,所以我们需要改变历史记录)3.我们使用setTimeout,在下一个事件循环中,使用history.go(-1)来抵消我们之前添加的历史记录?这样历史记录就恢复了,然后用户再次点击返回就会直接返回上一页。但是,history.go(-1)会触发popstate事件,所以我们需要在调用history.go(-1)之前先移除事件监听,执行完后再重新添加?这样会有些复杂。参考常见做法:```javascriptmethods:{handlePopstate(event){if(this.isShowMask){this.isShowMask=false;//再次pushState,这样当前历史记录中多了一条,用户需要再点一次返回才会触发返回history.pushState(null,null,document.URL);}else{//如果蒙层已经关闭,我们移除事件监听并且什么都做,让浏览器返回上一页//但是注意,我们之前pushState了两次,所以需要回退两次?对,我们第一次进入页面时pushState一次,然后第一次返回时又pushState一次,所以当前历史记录有三条:初始、第一次pushState、第二次pushState。//当用户第二次点击返回时,我们期望返回上一页,但实际上只是回退到第一次pushState的状态(仍然是当前页),然后第三次点击返回才会返回上一页。//所以,我们需要在蒙层关闭后,将历史记录恢复到进入页面时的状态?我们可以使用history.go(-2)直接回退两页?这样就直接返回上一页了,但用户会看到页面直接返回了,没有过渡。//所以,我们在蒙层关闭后,移除事件监听,然后手动回退一次(因为我们添加了两个状态,需要回退两次?)//但这样会直接返回上一页,用户可能没有感知。//更好的做法是:在第一次拦截时,我们pushState,然后当蒙层关闭后,我们等待用户再次点击返回,这时我们再拦截,但是因为历史记录中还有两个状态(初始第一次pushState),所以用户需要再点两次返回?这样体验好。//因此,我们可以在蒙层关闭后,立即用history.back()或history.go(-1)来移除我们第一次pushState的状态,这样历史记录就只剩下初始状态,用户再点一次返回就会返回上一页。//但是,在popstate事件中执行history.back()会再次触发popstate事件,所以我们需要在执行前移除事件监听。//移除事件监听window.removeEventListener('popstate',this.handlePopstate);//回退一次,移除我们第一次拦截时pushState的状态history.go(-1);}}}```但是,这个处理在第一次触发popstate时(蒙层显示)我们pushState,然后当蒙层关闭后,用户再次点击返回,进入else分支,我们移除监听并执行history.go(-1)(这又会触发popstate事件,但此时我们已经移除了监听,所以会再次进入handlePopstate),然后页面回退一次(移除我们第一次pushState的状态),然后用户再点一次返回,就会返回上一页。这样用户需要点击三次返回按钮(第一次隐藏蒙层,第二次无反应(实际上执行了history.go(-1)),第三次返回上一页)。但用户第二次点击返回时,由于我们执行了history.go(-1),页面状态会回到初始状态(即我们刚进入页面时的状态),然后用户第三次点击返回,才离开页面。这样体验好,用户会感到困惑。因此,我们采用另一种思路:在第一次触发popstate事件时,我们隐藏蒙层,然后立即使用history.back()来模拟返回,但是history.back()会触发popstate事件,所以我们需要在隐藏蒙层后,在事件处理函数中移除事件监听,然后执行history.back(),这样就会返回上一页?对,这样会直接返回上一页,没有达到拦截的目的。所以,最终我们采用最常用的方法:在蒙层显示时,拦截返回并隐藏蒙层,改变历史记录的次数,而是通过再次pushState让用户需要再点一次返回才能返回。这样用户需要点击两次返回按钮:第一次隐藏蒙层,第二次返回上一页。虽然多了一次点击,但逻辑清晰。代码实现:```javascriptexportdefault{data(){return{isShowMask:true}},mounted(){//记录当前历史记录的状态history.pushState(null,null,document.URL);window.addEventListener('popstate',this.handlePopstate);},beforeDestroy(){window.removeEventListener('popstate',this.handlePopstate);},methods:{handlePopstate(event){if(this.isShowMask){//隐藏蒙层this.isShowMask=false;//再次pushState,这样用户再点一次返回才会返回上一页history.pushState(null,null,document.URL);}else{//如果蒙层已经隐藏,那么我们就让页面返回//但这里我们什么都做,因为浏览器会执行默认的返回行为(返回上一页)//但是,由于我们之前pushState了两次,所以需要移除两次?实际上,我们第一次进入页面时pushState一次,第一次返回时又pushState一次,所以当前历史记录有三条。//当用户第二次点击返回时,会触发popstate,此时我们什么都做,那么浏览器会返回上一个历史记录(即我们第一次pushState的状态),然后用户需要再点一次返回才会返回上一页(总共三次)。//所以我们需要在蒙层隐藏后,手动移除一个历史记录?//注意:我们无法在popstate事件中再次pushState,否则会无限循环。//因此,我们在else中做任何操作,而是让浏览器按历史记录返回。这样用户需要点击三次返回按钮(第一次隐藏蒙层,第二次返回进入当前页面的初始状态,第三次返回上一页)。但这样体验好。//解决方案:在蒙层隐藏后,我们移除事件监听,然后手动执行history.go(-2)直接返回上一页?但是这样会直接跳转,没有过渡。//权衡之下,我们接受用户需要点击两次返回按钮才能离开页面(第一次隐藏蒙层,第二次离开页面)。但是,按照上面的代码,用户需要点击三次,所以我们需要调整。//实际上,在第一次pushState后,我们有一个历史记录,然后我们进入页面时的历史记录是一个(比如A),然后pushState(变成AB),当用户点击返回,触发popstate(从B回到A),然后我们再次pushState(变成AC)。然后当用户再次点击返回,从C回到A,再次触发popstate事件,这时我们进入else,然后我们移除事件监听,并执行history.back(),这样就会从A回到上一页(A之前的状态)。这样用户点击两次返回:第一次从B->A(触发拦截,隐藏蒙层并pushState到C),第二次从C->A(触发popstate,进入else,执行history.back(),回到上一页)。但是,在第二次触发popstate时,我们执行history.back()又会触发popstate事件?所以我们需要在移除监听后执行。//所以,我们修改:在else分支中,我们移除事件监听,然后执行history.back(),这样就会返回上一页。但是注意,执行history.back()会触发popstate事件,但我们已经移除了监听,所以会处理。window.removeEventListener('popstate',this.handlePopstate);history.back();}}}}```这样,当用户第一次点击返回时,蒙层隐藏,并添加一个新的历史记录(当前页面URL)。然后用户第二次点击返回时,触发popstate事件,进入else分支,我们移除事件监听并执行history.back(),这样页面就会返回上一页。但是,在安卓微信中,可能一次返回按钮点击会触发两次popstate事件,所以我们需要在handlePopstate中加入防抖或标记。因此,我们可以添加一个标记,表示是否正在处理返回事件。改进后的代码:```javascriptexportdefault{data(){return{isShowMask:true,isHandling:false//防止多次触发}},mounted(){history.pushState(null,null,document.URL);window.addEventListener('popstate',this.handlePopstate);},beforeDestroy(){window.removeEventListener('popstate',this.handlePopstate);},methods:{handlePopstate(event){if(this.isHandling){return;}this.isHandling=true;if(this.isShowMask){this.isShowMask=false;history.pushState(null,null,document.URL);//处理完成后,重置标记this.isHandling=false;}else{window.removeEventListener('popstate',this.handlePopstate);history.back();}}}}```但是,在else分支中,我们移除了监听并执行history.back(),此时会再次触发handlePopstate(因为已经移除了),所以需要标记。而且,在history.back()之后,组件可能被销毁,所以会有问题。但是,在安卓中,一次返回按钮点击可能连续触发两次popstate事件,所以我们需要在第一次触发时设置标记,防止第二次触发时再次执行。所以,我们在handlePopstate开头检查isHandling,如果正在处理就跳过。另外,引用[4]中的代码只添加了一次pushState,并在popstate事件中隐藏蒙层,并没有处理后续的返回。所以用户需要再点一次返回才能离开,这就是两次点击。综上所述,我们采用以下方案(两次点击方案):1.进入页面时,pushState一个状态。2.在popstate事件中,如果蒙层显示,则隐藏蒙层,并再次pushState一个状态,然后设置标记防止重复处理(针对安卓的双次触发问题)。3.如果蒙层已经隐藏,则移除事件监听,并执行history.back()返回上一页。注意:在隐藏蒙层后再次pushState,是为了让用户再次点击返回按钮时能够触发popstate事件,从而进入else分支进行返回操作。但是,执行history.back()会立即返回上一页,用户可能没有机会再次点击返回按钮对,我们的else分支是在用户第二次点击返回按钮时触发的,所以此时执行history.back()就会返回上一页。然而,在else分支中执行history.back(),会触发浏览器的返回操作,页面会立即跳转,所以用户会感知到中间状态。因此,我们最终代码如下:```javascriptexportdefault{data(){return{isShowMask:true,isHandling:false}},mounted(){//添加历史记录history.pushState(null,null,document.URL);window.addEventListener('popstate',this.handlePopstate);},beforeDestroy(){window.removeEventListener('popstate',this.handlePopstate);},methods:{handlePopstate(event){//防止安卓微信中一次返回触发两次事件if(this.isHandling)return;this.isHandling=true;if(this.isShowMask){//隐藏蒙层this.isShowMask=false;//再次pushState,保证用户再次点击返回时能触发事件history.pushState(null,null,document.URL);//重置标记,允许下一次处理this.isHandling=false;}else{//蒙层已隐藏,移除监听并返回上一页window.removeEventListener('popstate',this.handlePopstate);history.back();}}}}```但是,在iOS中,可能需要防抖,但为了兼容安卓,我们加上。另外,注意:在VueRouter中使用history模式时,可能会VueRouter的历史记录冲突,但微信浏览器中我们通常使用hash模式,所以影响大。最后,如果蒙层隐藏后,用户是点击返回按钮,而是点击页面内的返回按钮,我们也要能够返回上一页。所以,我们可以在页面内的返回按钮事件中,执行:```javascriptthis.$router.go(-1);```这样,我们既拦截了微信左上角的返回按钮,又提供了页面内的返回按钮。总结:以上代码实现了在Vue项目中拦截微信浏览器左上角返回按钮的功能,第一次点击返回时隐藏蒙层,第二次点击返回时返回上一页。</think>在 Vue 项目中拦截微信浏览器左上角返回按钮的实现方法如下,我将分步骤说明关键实现逻辑: ### 核心实现步骤 1. **添加历史记录状态** 在页面加载时,使用 `history.pushState()` 添加一个空历史记录: ```javascript mounted() { // 添加历史记录,使当前页面有可返回的状态 history.pushState(null, null, document.URL); } ``` 2. **监听 popstate 事件** 在 Vue 组件的 `mounted` 钩子中添加事件监听: ```javascript mounted() { history.pushState(null, null, document.URL); window.addEventListener('popstate', this.handleBackButton); } ``` 3. **实现拦截逻辑** 在事件处理函数中控制返回行为: ```javascript methods: { handleBackButton(event) { if (this.isShowMask) { // 1. 如果蒙层显示,则隐藏蒙层 this.isShowMask = false; // 2. 重新添加历史记录,保持当前页面 history.pushState(null, null, document.URL); } else { // 3. 无蒙层时正常返回 window.removeEventListener('popstate', this.handleBackButton); history.back(); } } } ``` 4. **销毁事件监听** 在组件销毁前移除监听器: ```javascript beforeDestroy() { window.removeEventListener('popstate', this.handleBackButton); } ``` ### 完整 Vue 组件示例 ```vue <template> <div> <!-- 蒙层元素 --> <div v-if="isShowMask" class="mask"> <button @click="closeMask">关闭蒙层</button> </div> <!-- 页面内容 --> <div v-else> <h1>当前是二级页面</h1> <button @click="goBack">手动返回</button> </div> </div> </template> <script> export default { data() { return { isShowMask: true // 初始显示蒙层 } }, mounted() { // 关键步骤1:添加历史记录 history.pushState(null, null, document.URL); // 关键步骤2:监听返回事件 window.addEventListener('popstate', this.handleBackButton); }, beforeDestroy() { // 关键步骤4:清理监听 window.removeEventListener('popstate', this.handleBackButton); }, methods: { // 关键步骤3:拦截逻辑 handleBackButton(event) { if (this.isShowMask) { this.closeMask(); history.pushState(null, null, document.URL); // 维持当前页 event.preventDefault(); // 阻止默认返回 } }, closeMask() { this.isShowMask = false; }, goBack() { this.$router.go(-1); // 手动返回 } } } </script> <style scoped> .mask { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); } </style> ``` ### 关键注意事项 1. **iOS 特殊处理** iOS 微信浏览器会重新加载页面,建议在 `pageshow` 事件中同步状态: ```javascript mounted() { window.addEventListener('pageshow', this.syncState); }, methods: { syncState() { // 同步页面状态 } } ``` 2. **安卓双触发问题** 部分安卓机型会连续触发两次 `popstate`,需添加防抖: ```javascript let isHandling = false; handleBackButton() { if (isHandling) return; isHandling = true; // ...拦截逻辑 setTimeout(() => isHandling = false, 500); } ``` 3. **路由兼容性** 使用 Vue Router 时,需确保与原生历史记录操作兼容: ```javascript this.$router.push('/current-page'); // 替代 history.pushState ``` ### 实现效果 1. 首次点击返回按钮 → 关闭蒙层(仍停留当前页) 2. 再次点击返回按钮 → 正常返回上一页 3. 页面内手动返回 → 直接触发路由跳转 > **引用说明**:该方法基于微信浏览器特性实现,通过操作历史记录栈改变返回行为[^3][^4]。iOS 需注意页面状态同步问题[^2],安卓需处理事件多次触发[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值