浏览器的History、Location对象,及使用js控制网页的前进后退和加载,刷新当前页面总结!

这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战

History 对象

History 对象是浏览器模型中的一个对象,通过 window.history 属性获取,表示的是当前窗口的浏览历史。

在一个浏览器窗口中,所有访问过的页面地址,都会保存在 History 对象中

URL 的锚点值变了,对应的也会在 History 对象中创建一条浏览记录。

无法通过js读取浏览过的具体地址

处于安全的原因,浏览器不允许js读取具体的地址,但是可以在不同地址之间导航。比如 back、forward、go 等方法。

lengthstate 属性

  • history.length:当前窗口访问过的网址数量(包括当前网页)。
  • history.state:History 堆栈最上层的状态值,即当前状态,通常为 undefined,未设置。

history.back() 回退到上一个网页

history.back() 返回前一个浏览的页面。也就是浏览器中最常见的回退功能。

history.forward() 前进到下一个网址

history.forward() 移动到下一个网址,当前窗口显示下一个页面。前进功能。

history.go(num) 移动到基于当前位置的网址页面

history.go() 的参数为整数,基于的是当前网址的位置,比如 go(1) 相当于 forward()go(-1) 相当于 back()

go(-2) 移动到当前页面往后的第2个网址。如果指定的参数,超过是实际的范围,则不会有任何效果。

不指定参数,或指定参数 0,会刷新当前页面。

history.pushState() 在浏览历史中添加一条记录。

window.history.pushState(state, title, url)

  • state:一个与添加的记录相关联的状态对象,会在 popstate 事件中传入回调函数。它是一个单独的对象,可以在重新载入该页面时进行使用。如果不需要,设置为 null 即可。
  • title:新页面的标题,一般不起作用(比如 FireFox浏览器),填空字符串即可。
  • url:新的网址,必须与当前页面处在同一个域中。也就是,pushState 不允许添加跨域的地址

history.pushState() 添加记录后,会在浏览器地址栏中显示,但是不会跳转到该页面,只是作为浏览历史中的一个最新记录。

即,pushState() 不会触发页面进入刷新。该方法执行后,可以通过 history.state 读出传入的状态值。

pushState 不允许添加跨域的网址。这样做是因为,防止恶意代码让用户以为他们是在另一个网站上,因为pushState()不会导致页面跳转

history.replaceState() 修改当前的记录

history.replaceState() 的用法 和 pushState() 一样,只不过 replaceState() 用于修改当前的记录。

比如,当前的网址是 example.com/example.html。执行如下操作后,对应地址栏中url的显示为:

history.pushState({page: 1}, 'title 1', '?page=1')
// URL 显示为 http://example.com/example.html?page=1

history.pushState({page: 2}, 'title 2', '?page=2');
// URL 显示为 http://example.com/example.html?page=2

history.replaceState({page: 3}, 'title 3', '?page=3');
// URL 显示为 http://example.com/example.html?page=3

history.back()
// URL 显示为 http://example.com/example.html?page=1

history.back()
// URL 显示为 http://example.com/example.html

history.go(2)
// URL 显示为 http://example.com/example.html?page=3
复制代码

popstate 和 hashchange 事件

popstate 事件

当同一个文档的浏览历史发生变化,并无刷新进入改变的url时,就会触发 popstate 事件。

注意,是同一个文档的浏览历史改变,如果产生文档的加载(加载不同的文档或重新加载当前文档),则不会触发该事件。也就是,

浏览历史发生变化,并且页面没有加载时,才会触发 popstate 事件。 并且,页面(重新)加载刷新时(包括第一次加载),浏览器不会触发 popstate 事件

window.addEventListener('popstate',function(e){
            console.log('location: ' + document.location);
            console.log('state: ' + JSON.stringify(event.state));
        })
复制代码

回调函数中 event 事件对象的 state 属性,指向的是 pushState 和 replaceState 方法中的状态对象(第一个参数)。该 state 对象,也可以通过 history.state 获取。

何时会触发 popstate 事件?

正如上面提到的一点,popstate 事件只在浏览器没有发生页面加载时才会触发。而,通过 history.pushState()/history.replaceState() 方法、修改 url 的 hash 是,才会出现修改url的同时而又不会发生页面的加载。

也就是,在页面加载完成后,调用了 history.pushState()/history.replaceState() 方法之后发生页面的前进后退(或调用 history.back()history.forward()history.go())时、或者修改 url 的 hash 时,才会触发 popstate 事件。

要模拟触发 popstate 事件,可以通过此处介绍的方式,修改浏览记录地址栏中的url地址或hash值,测试 popstate 事件的触发执行。

hashchange 事件

hash 即url中 # 后面的内容(包括#)。hashchange 事件就是对url中hash值改变进行监听的事件

hash 也可以称作 片段标识符(fragment identifier)。

window.addEventListener('hashchange', function() {
  console.log('The hash has changed!');
  if (location.hash === '#cool-feature') {
    console.log("You're visiting a cool feature!");
  }
}, false);
复制代码

hashchange 的事件对象包含hash改变前后的URL:oldURL 和 newURL 属性。

window.addEventListener('hashchange', function(e) {
  console.log(e.oldURL);
  console.log(e.newURL);
}, false);
复制代码

popstate 和 hashchange 的事件顺序

如下,通过修改 hash 值,测试 popstate 和 hashchange 事件发生的顺序。

window.location.href+='#cool-feature'
复制代码

也可以通过浏览器中的前进后退键测试。如下,是这两个事件发生输出内容的顺序:

location: http://127.0.0.1:5500/code/popstateAndhashchange.html#cool-feature
state: null
The hash has changed!
You're visiting a cool feature!
复制代码

修改hash时,会先触发 popstate 事件,然后再触发 hashchange 事件。

popstate 事件的一个优点时,可以使用 pushState/replaceState 方法传递的状态对象,可以独立保存和传递一些数据。

popstate、hashchange 事件也是处理前端路由的基石。

popstate 仅仅监听 history 的改变,并不能阻止浏览器的前进后退,不过,可以在事件方法中,通过实现跳转、加载其他页面等各种方法,阻止默认的后退操作。

突然想到,应该记录一下 hash 的作用。当进入带hash的url中,或修改hash值时,页面会定位到id和hash值一致的元素的所在位置(或滚动到)。因此也称为锚点。 单纯的改变hash会不引起页面的重新加载,不会发送get请求。在单页应用中很流行,也更有用。

hashchange事件在处理移动端页面效果中的妙用

在移动端,处理页面中的图片时,有一个很常见的功能,点击小图在当前页面上显示大图

来源于下面参考文章中的截图

对于手机端来说,需要考虑,当显示大图时,如何返回?在大图展示状态下,我们通常会按手机下方的返回按键,退出大图,然后之前的小图状态,继续浏览。

但是,但是,对浏览器,返回表示的是返回上一页,即返回到上一个url页面,而不是退出大图显示。

那么,可以利用 URL 的 hash 改变,页面不会重新加载,但是会被记录到浏览器的历史记录中。这一特性,来处理点击返回键,大图显示退出,而不是返回上一页。

借助 hashchange 事件,在进入大图展示状态时,加上一个特定的hash,点击返回键触发hashchange时,退出大图状态。

以下为主要处理的代码:

// 假设大图展示状态的hash为imgSlider
window.addEventListener('hashchange', function(e) {
  var re = /#imgSlider$/;
  if ( re.test(e.oldURL) && !re.test(e.newURL) ) {
    // 假设imgSlider为大图展示组件对象
    imgSlider.hide();
  }
}, false);
复制代码

附:关于chrome下点击浏览器后退按钮不会触发 popstate 事件的情况

后面在查找相关资料时,看到一篇 点击浏览器后退按钮 chrome 不会触发popstate事件分析-[七日打卡5] 的文章。

里面介绍,通过 popstate 事件监听机制。实现 当页面load加载时,js执行pushState操作,在浏览器历史记录中添加一条记录;然后,监听 popstate 事件;在用户点击后退按钮触发 popstate 事件时,提示用户“是否确定退出?”。实现“用户挽留”的操作。

比如下面的简要处理,在页面onload时执行下面的函数:

function watchBackBtn({
    window.addEventListener('load'() => {
        const state = {title'init',url'#'};
        window.history.pushState(state, 'init''#');
    });

    window.addEventListener('popstate'() => {
        alert('back...');
    });
}
复制代码

这个处理流程没有问题。文中提到,在chrome浏览器中,只有手动在页面中进行交互操作(点击元素、滑动页面等)后再点击浏览器的后退按钮才能触发popstate 事件。

文中还引用这篇 Chrome plans to save you from sites that mess with your back button 文章介绍。是为了防止流氓网站禁止用户执行回退操作,把用户困在当前网站,而做的一个改进。【确实遇到过这种流氓界面】

也就是,Chrome中,如果进入页面后没有任何人为交互操作行为的话,是不会触发 popstate 事件的。

但是,目前实际在PC版的Chrome上测试,并没有此问题。

此处仅做个记录。

Location对象

Location 也是浏览器提供的原生对象,可以获取当前页面的URL信息以及进行操作。可以通过window.locationdocument.location属性获取该对象。

location 的属性

  • Location.href:当前页面完整的 URL。等同于 location.toString() 方法的返回值。
  • Location.protocol:当前 URL 的协议,包括冒号(:)。
  • Location.host:主机。如果端口不是协议默认的80433,则还会包括冒号(:)和端口。
  • Location.hostname:主机名,不包括端口。
  • Location.port:端口号。
  • Location.pathname:URL 的路径部分,从根路径/开始。
  • Location.search:查询字符串部分,从问号?开始。
  • Location.hash:片段字符串部分,从#开始。
  • Location.username:域名前面的用户名。
  • Location.password:域名前面的密码。
  • Location.origin:URL 的协议、主机名和端口。
// 当前网址为
// http://user:[email protected]:4097/path/a.html?x=111#part1
document.location.href
// "http://user:[email protected]:4097/path/a.html?x=111#part1"
document.location.protocol
// "http:"
document.location.host
// "www.example.com:4097"
document.location.hostname
// "www.example.com"
document.location.port
// "4097"
document.location.pathname
// "/path/a.html"
document.location.search
// "?x=111"
document.location.hash
// "#part1"
document.location.username
// "user"
document.location.password
// "passwd"
document.location.origin
// "http://user:[email protected]:4097"
复制代码

除了 origin 属性只读,其他属性都可通过赋值修改,实现对页面的操作。

比如,给 location.href 赋值新的URl地址,就可以实现浏览器的跳转。

// 跳转到新网址
window.location.href = 'http://www.example.com';
// 等同于
window.location = 'http://www.example.com';
复制代码

直接改写location 和 修改href属性是一样的。

比如,通过修改 href 或 hash 实现,跳转到指定的hash锚点。

window.location.href = '#top';
// 等同于
window.location.hash = '#top';
复制代码

Location.href属性允许跨域写入

Location.href 是浏览器唯一允许跨域写入的属性,即非同源的窗口可以改写另一个窗口(比如子窗口与父窗口)的Location.href属性实现跳转。

Location的其他属性都不允许跨域写入。

方法

location.assign(url) 跳转到指定的url

location.assign(url) 用于跳转到指定的url,参数必须是有效的URL字符串,否则会报错。

// 跳转到新的网址
document.location.assign('http://www.example.com')
复制代码

location.replace(url) 跳转到新的URL,并在 history 中删除当前网址

location.replace(url) 也用于跳转到新的url,不同的是,它会在浏览器的浏览历史 History 中删除当前的网址。

也就是,使用该方法跳转,后退按钮就无法回到当前页面了。即 在浏览历史中,使用新的url替换旧的url。

此方法最大的一个应用是,判断当前设备的类型,并跳转到指定版本页面。比如,判断是否为移动设备,并立刻跳转到移动版网页。

// 跳转到新的网址
document.location.replace('http://www.example.com')
复制代码

location.reload() 刷新当前页面

location.reload() 用于重新加载当前网址,相当于浏览器的刷新

reload 接受一个布尔值参数,如果参数为true,浏览器将向服务器重新请求这个网页,重新加载后页面将滚动到顶部(即 scrollTop === 0)。

如果参数为 false 或为空,浏览器将从本地缓存重新加载该网页,并且重加载后的网页视口,还是之前的位置。

使用参数 true 和 false 的行为,不同的浏览器会有所差异,并不完全按照此行为

// 向服务器重新请求当前网址
window.location.reload(true);
复制代码

刷新当前页面的方法

location.reload() 标准常用刷新方法

location.reload() 是最常用的刷新当前页面的方法。

直接调用 history.go()

history.go() 不指定参数,或指定参数 0,会刷新当前页面。

使用 document.location.assign(window.location.href)

document.location.assign(window.location.href) 也可以实现刷新当前页面。

附:location.href = window.location.href 并不能实现刷新当前页面

window.location.href=window.location.href 赋值,并不会修改 href 的url地址,因此不会有进入当前页面刷新的效果。

参考

猜你喜欢

转载自juejin.im/post/7036013860255432712
今日推荐