本文详细讲解如何利用HTML5的history.pushState和popstate事件,解决新标签页无法点击返回的问题。通过向历史记录插入指定URL和空状态,实现点击返回时跳转至首页。文章包含完整代码示例,并分析了该方案在移动端微信环境下的兼容性及用户体验缺陷。
HTML5 History API实现点击返回跳转指定页面
此前曾简要提及此功能,本文将深入剖析其实现原理及潜在问题。由于该方案依赖于HTML5提供的新API,因此存在浏览器兼容性问题,建议仅在移动端环境中使用此方法。
功能描述:
当在浏览器中新建标签页并指定特定网址时,网页加载完成后,按照正常流程用户无法点击“返回”按钮。这是因为当前标签页的历史记录中不存在之前的页面,因此没有可供返回的记录。
根据客户需求,需要在此类场景的历史记录中手动添加一个链接(例如首页)。这样,当用户在新打开的页面点击返回时,即可跳转至首页,从而展示系统各项功能,达到推广平台的目的。
HTML5 History API核心知识
一、知识要点
HTML5引入了history.pushState()和history.replaceState()方法,允许开发者逐条添加或修改历史记录条目。这些方法需与window.onpopstate事件配合使用。
案例:
假设访问 http://mozilla.org/foo.html 并执行以下JavaScript代码:
var stateObj = { foo: "bar" };
history.pushState(stateObj, "page 2", "bar.html");
上述操作会使浏览器地址栏显示为 http://mozilla.org/bar.html,但不会实际加载bar.html页面,也不会检查该文件是否存在。
若用户随后导航至 http://google.com 并点击后退按钮,地址栏将显示 http://mozilla.org/bar.html,同时页面触发popstate事件。该事件的状态对象(state object)包含stateObj的一个副本。尽管页面看起来仍像foo.html,但其内容可能在popstate事件处理中被修改。
若再次点击后退按钮,URL将恢复为 http://mozilla.org/foo.html,并触发另一个popstate事件,此次的状态对象为null。此回退操作同样不会改变文档内容。
pushState()方法 pushState()接收三个参数:状态对象、标题(当前被忽略)以及可选的URL地址。以下分别解析这三个参数的细节:
状态对象(state object) — 这是一个JavaScript对象,与通过pushState()创建的新历史记录条目相关联。当用户导航至新创建的状态时,将触发popstate事件,且事件对象的state属性包含该历史记录条目的状态对象副本。
任何可序列化的对象均可作为状态对象。由于Firefox浏览器会将状态对象保存至用户硬盘以便重启后还原,因此强制限制状态对象大小为640k。若向pushState()传递超过此限额的状态对象,该方法将抛出异常。如需存储大量数据,建议使用sessionStorage或localStorage。
标题(title) — Firefox浏览器目前忽略此参数,尽管未来可能会支持。考虑到未来可能的修改,传递空字符串较为安全。或者,也可传入简短标题以标明即将进入的状态。
地址(URL) — 新历史记录条目的地址。调用pushState()后浏览器不会加载该地址,但后续可能会尝试加载,例如用户重启浏览器时。新URL不一定是绝对路径;若是相对路径,则基于当前URL解析;传入的URL必须与当前URL同源,否则pushState()将抛出异常。该参数为可选,若未指定则默认为当前文档URL。
注意: 在Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1) 至 Gecko 5.0 (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2) 版本中,传入对象通过JSON进行序列化。从Gecko 6.0 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3)开始,对象采用结构化拷贝算法进行序列化。这使得更多类型的对象能够安全传入。 某种程度上,调用pushState()类似于设置window.location='#foo',两者均会在当前文档内创建并激活新的历史记录条目。但pushState()具备以下优势:
1、新URL可以是任意同源URL,而使用window.location方法时,仅修改hash值才能保证停留在同一document中。
2、可根据个人需求决定是否修改URL。相反,设置window.location='#foo'仅在当前hash值不为foo时才创建新历史记录。
3、可在新的历史记录条目中添加抽象数据。若使用基于hash的方法,只能将相关数据编码为极短的字符串。
注意pushState()方法永远不会触发hashchange事件,即使新地址仅变更了hash。
popstate事件
每当激活的历史记录发生变化时,都会触发popstate事件。若被激活的历史记录条目由pushState创建或被replaceState影响,popstate事件的状态属性将包含该历史记录状态对象的副本。
replaceState()方法
history.replaceState()的操作类似于history.pushState(),不同之处在于replaceState()用于修改当前历史记录条目,而非创建新条目。
当需要响应用户操作并更新当前历史记录条目的状态对象或URL时,使用replaceState()方法尤为合适。
利用pushState和popstate实现返回跳转
二、实现思路
1. 利用popstate事件监听返回按钮的点击操作。
2. 触发事件时,判断当前页面的历史记录中是否存在可返回的页面。
3. 若无可返回页面,则插入两条记录:
1)、指定的跳转页面。
2)、空记录。(确保当前页面视觉上不发生变化)
三、实现方法
//返回之前没页面则返回首页
function pushHistory() {
if (history.length < 2) {
var state = {
title: "index",
url: getHttpPrefix + "index.html"
};
window.history.pushState(state, "index", location.href);
state = {
title: "index",
url: ""
};
window.history.pushState(state, "index", "");
}
//lll("history.state" + history.state)
//console.log(history.state)
}
判断当前history中的记录个数。由于页面加载时浏览器会自动push一条记录,因此需判断长度是否小于2。
插入的state对象用于获取对应的URL链接。
注意点:
第一个pushState将跳转URL放入state对象,以便于后续跳转操作。第二个参数无实际意义,因为现代浏览器基本不使用该参数。 第三个参数会替换地址栏显示的链接,但页面不会发生跳转。(此前曾犯错误,将第三个参数设为首页链接,导致地址栏变更为首页链接,进而使当前页所有链接均以首页为基础进行跳转,造成页面链接全部错误。)
setTimeout(function () {
pushHistory()
window.addEventListener("popstate", function (e) {
lll("popstate"+window.history.state)
if (window.history.state != null && window.history.state.url != "") {
location.href = window.history.state.url
}
});
}, 300);
这段代码放置在页面的ready事件中执行,延迟300毫秒是为了将操作滞后,防止与系统pop事件冲突。 if语句用于判断history是否存在state对象,因为只有满足我们要求的记录才会有我们添加的state对象,因此可根据这一点进行页面跳转操作。 如此即可实现预期效果。
方案局限性与总结
四、写在最后
缺点:
1. 如开头所述,该方案仅适用于支持HTML5的浏览器。
2. 由于插入了两条记录,因此在类似微信的移动端环境中,用户需点击两次返回才能退出页面回到微信聊天窗口,用户体验较差。
总结:
此方法仍有优化和完善的空间,受限于当前技术水平,尚无法达到完美程度。
希望阅读本文的朋友能获得启发,或找到更好的实现方案。