## BOM Window对象是客户端JavaScript程序的全局对象。 **11.1 计时器** `setTimeout()`和`setInterval()`可以用来注册在指定的时间之后单次或重复调用的函数。两者都是客户端JavaScript中的全局函数,也就是Window对象的方法。 **(1)setTimeout()** `setTimeout()`方法用来实现一个函数在指定的毫秒数之后运行,返回一个值,这个值可以传递给clearTimeout()用于取消这个函数的执行。 ``` var times = setTimeout(function(){},1000); //1000毫秒后执行 clearTimeout(times); //取消执行 ``` **(2)setInterval()** setInterval()方法和setTimeout()一样,只不过这个函数会在指定毫秒数的间隔里重复调用,也返回一个值,这个值传递给clearInterval(),用于取消后续函数的调用。 ``` var times = setInterval(function(){},1000); //每隔1000毫秒调用一次function clearInterval(times); //取消后续函数执行 ``` 注意:如果以0毫秒的超时时间来调用setTimeout(),那么指定的函数不会立刻执行。相反,会把它放到队列中,等到前面处于等待状态的事件处理程序全部执行完成后,再“立即”调用它。 **11.2 浏览器定位和导航** Window对象的`location`属性引用的是`Location`对象,它表示该窗口中当前显示的文档的URL,并定义了方法来使窗口载入新的文档。 Document对象的location属性也引用到Location对象: ``` window.location === document.location //总是返回true ``` Document对象也有一个URL属性,是文档首次载入后保存该文档的URL的静态字符串。如果定位到文档中的片断标识符(如#top),Location对象会做相应的更新,而document.URL属性却不会改变。 **11.2.1 解析URL** Location对象href属性是一个字符串,href属性包含ULR的完整文本。Location对象的toString()方法返回href属性的值,因此在会隐式调用toString()的情况下,可以用location代替location.href。 ``` // 当前网址为 http://user:passwd@www.example.com:4097/path/a.html?x=111#part1location.href // "http://user:passwd@www.example.com:4097/path/a.html?x=111#part1" location.protocol // "http:" location.host // "www.example.com:4097" location.hostname // "www.example.com" location.port // "4097" location.pathname // "/path/a.html" location.search // "?x=111" location.hash // "#part1" location.user // "user" location.password // "passed" ``` protocol、host、hostname、port、pathname和search属性是“URL分解”属性,是可写的,对它们重新赋值的话,会改变URL的位置,并且浏览器会载入一个新的文档。 `Location`对象的方法: - `location.assign()`:使窗口载入并显示指定的URL中的文档。 - `location.replace()`:和assign()方法类似,但它在载入新文档之前会从浏览历史中把当前文档删除。这样“后退”按钮就不会把浏览器带回到原始文档。 - `location.reload()`:重新载入当前文档,可传入一个布尔值为参数,默认false。如果为true,则优先从服务器重新加载;否则优先从本地缓存中重新加载。 当然,我们还有更直接跳转到新页面的方法: ``` location = 'http://baidu.com'; //或者 location.href = 'http://baidu.com'; ``` 纯粹的片断标识符是相对URL的一种类型,它不会让浏览器载入新文档,而是使浏览器滚动到文档的某个位置。 注意:#top标识符是个特殊值:如果文档中没有元素的ID是“top”,它会让浏览器滚动到文档开始处。 ``` location = '#top'; //跳转到文档的顶部 ``` **11.3 浏览历史** Window对象的history属性引用的是该窗口的History对象。History对象是用来把窗口浏览历史用文档和文档状态列表的形式表示。 History对象的length属性表示浏览历史表中的元素数量。比如你在当前窗口访问了三个不同的网址,那么history.length就等于3。 History对象还提供了一系列的方法,让我们在历史记录中自由前进和后退。 - back():移动到上一个访问页面,等同于浏览器的后退键。 - forward():移动到下一个访问页面,等同于浏览器的前进键。 - go():接受一个整数作为参数,移动到该整数指定的页面,比如go(1)相当于forward(),go(-1)相当于back()。 如果移动的位置超出了访问历史的边界,以上三个方法并不报错,而是默默的失败。 history.go(0)相当于刷新当前页面。 ``` history.go(0) ``` 注意:如果窗口包含多个子窗口(比如<iframe>元素),子窗口的浏览历史会按时间顺序穿插到主窗口的历史中。这就是意味着主窗口调用back()可能会导致其中一个子窗口往回跳转到前一个显示的文档,而主窗口保留当前状态不变。 HTML5为history对象添加了两个新方法,用来添加和修改历史记录条目。 - `history.pushState()`:会改变referrer的值,而在你调用方法后创建的 XMLHttpRequest 对象会在 HTTP 请求头中使用这个值。referrer的值则是创建 XMLHttpRequest 对象时所处的窗口的URL。 - `history.replaceState()`:会修改当前历史记录条目而并非创建新的条目 **(1)history.pushState()** history.pushState方法接受三个参数,依次为: state:一个与指定网址相关的状态对象,popstate事件触发时,该对象会传入回调函数。如果不需要这个对象,此处可以填null。 title:新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填null。 url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。 ``` 假定当前网址是example.com/1.html,我们使用pushState方法在浏览记录(history对象)中添加一个新记录。 var stateObj = { foo: 'bar' }; history.pushState(stateObj, 'page 2', '2.html'); ``` 添加上面这个新记录后,浏览器地址栏立刻显示`example.com/2.html`,但并不会跳转到2.html,甚至也不会检查2.html是否存在,它只是成为浏览历史中的最新记录。假定这时你访问了google.com,然后点击了倒退按钮,页面的url将显示2.html,但是内容还是原来的1.html。你再点击一次倒退按钮,url将显示1.html,内容不变。 总之,pushState方法不会触发页面刷新,只是导致history对象发生变化,地址栏会有反应。 如果pushState的url参数,设置了一个新的锚点值(即hash),并不会触发hashchange事件。如果设置了一个跨域网址,则会报错。 **(2)history.replaceState()** history.replaceState方法的参数与pushState方法一模一样,区别是它修改浏览历史中当前纪录。 假定当前网页是`example.com/example.html`。 ``` history.pushState({page: 1}, 'title 1', '?page=1'); history.pushState({page: 2}, 'title 2', '?page=2'); history.replaceState({page: 3}, 'title 3', '?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 ``` **(3)history.state属性** `history.state`属性返回当前页面的state对象。 ``` history.pushState({page: 1}, 'title 1', '?page=1'); history.state // { page: 1 } ``` **(4)popstate事件** 每当同一个文档的浏览历史(即history对象)出现变化时,就会触发popstate事件。 需要注意的是,仅仅调用pushState方法或replaceState方法 ,并不会触发该事件,只有用户点击浏览器倒退按钮和前进按钮,或者使用JavaScript调用back、forward、go方法时才会触发。另外,该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件也不会触发。 使用的时候,可以为popstate事件指定回调函数。这个回调函数的参数是一个event事件对象,它的state属性指向pushState和replaceState方法为当前URL所提供的状态对象(即这两个方法的第一个参数)。 ``` window.onpopstate = function (event) { console.log('location: ' + document.location); console.log('state: ' + JSON.stringify(event.state)); }; // 或者 window.addEventListener('popstate', function(event) { console.log('location: ' + document.location); console.log('state: ' + JSON.stringify(event.state)); }); ``` 上面代码中的event.state,就是通过pushState和replaceState方法,为当前URL绑定的state对象。 这个state对象也可以直接通过history对象读取。 var currentState = history.state; 注意:页面第一次加载的时候,在load事件发生后,Chrome和Safari浏览器(Webkit核心)会触发popstate事件,而Firefox和IE浏览器不会。 **11.4 浏览器和屏幕信息** Window对象的navigator和screen属性,分别引用的是Navigator和Screen对象。 **11.4.1 Navigator对象** Window对象的navigator属性引用的是包含浏览器产商和版本信息的Navigator对象。 **Navigator对象的属性** **(1)navigator.appName、navigator.appVersion** appName是Web浏览器的全称。在IE中,是“Microsoft Internet Explorer”。在其他浏览器中,是“Netscape”。 appVersion此属性通常以数字开始,并跟着包含浏览器产商和版本信息的详细字符串。字符串前面的数字通常是4.0或5.0,表示它是第4或第5代兼容的浏览器。 **(2)navigator.userAgent** navigator.userAgent属性返回浏览器的User-Agent字符串,标示浏览器的厂商和版本信息。它包含了appVersion中的所有信息。 下面是Chrome浏览器的userAgent。 ``` navigator.userAgent //Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36 ``` 利用这个属性,我们还可以判断手机浏览器的类型。 ``` if (/AppleWebKit.*Mobile/i.test(navigator.userAgent) || (/MIDP|SymbianOS|NOKIA|SAMSUNG|LG|NEC|TCL|Alcatel|BIRD|DBTEL|Dopod|PHILIPS|HAIER|LENOVO|MOT-|Nokia|SonyEricsson|SIE-|Amoi|ZTE/.test(navigator.userAgent))) { try { if (/Android|Windows Phone|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)) { //手机浏览器 } else if (/iPad/i.test(navigator.userAgent)) { //ipad } else {} } catch (e) {} } else { //非手机浏览器 } ``` 如果你只是简单的判断是否手机浏览器。 ``` var ua = navigator.userAgent.toLowerCase(); if(/mobi/i.test(ua)){ //手机浏览器 }else{ //非手机浏览器 } ``` **(3)navigator.platform** 返回用户的操作系统信息 比如我的: ``` navigator.platform //win32 ``` **(4)navigator.onLine** 表示浏览器当前是否连接到网络。返回一个布尔值。 **(5)navigator.geolocation** 返回一个Geolocation对象,包含用户地理位置信息。详情请看《HTML5 API》 **(6)navigator.javaEnabled()** javaEnabled方法返回一个布尔值,表示浏览器是否能运行Java Applet小程序。 **(7)navigator.cookieEnable()** cookieEnabled方法返回一个布尔值,表示浏览器是否能储存Cookie。 **11.4.2 Screen对象** Window对象的screen属性引用的是Screen对象,它提供有关窗口显示的大小和可用的颜色数量的信息。 **(1)screen.width、screen.height** screen.width和screen.height分别返回以像素为单位的窗口大小(设备的宽高)。 **(2)screen.availHeight、screen.availWidth** screen.availHeight和screen.availWidth属性返回屏幕可用的高度和宽度,单位为像素。它们的值为屏幕的实际大小减去操作系统某些功能占据的空间,比如系统的任务栏。 **(3)screen.colorDepth** screen.colorDepth属性返回屏幕的颜色深度,一般为16(表示16-bit)或24(表示24-bit)。 **11.5 对话框** Window对象提供了3个方法来向用户显示简单的对话框。 - `alert()`:向用户显示一条消息并等待用户关闭对话框。 - `confirm()`:同样显示一条消息,要求用户单击“确认”或“取消”按钮,并返回一个布尔值。 - `prompt()`:同样显示一条消息,等待用户输入字符串,并返回这个字符串。 这三个方法都具有堵塞效应,一旦弹出对话框,整个页面就是暂停执行,等待用户做出反应。 **(1)alert()** ``` alert(1); ``` **(2)confirm()** ``` var c = confirm('确认登录'); ``` confirm的一个用途是,当用户离开当前页面时,弹出一个对话框,询问用户是否真的离开。 ``` window.onunload = function() { return confirm('你确定要离开当面页面吗?'); } ``` **(3)prompt()** ``` // 格式 var result = prompt(text[, default]); //实例 var result = prompt('您的年龄?',18); ``` prompt方法的返回值是一个字符串(有可能为空)或者null,具体分成三种情况。 用户输入信息,并点击“确定”,则用户输入的信息就是返回值。 用户没有输入信息,直接点击“确定”,则输入框的默认值就是返回值。 用户点击了“取消”(或者按了Esc按钮),则返回值是null。 注意:这些对话框中显示的文本是纯文本,而不是HTML格式的文本。只能使用空格、换行符或各种标点符号来格式化这些对话框。 **11.6 错误处理** Window对象的onerror属性是一个事件处理程序,当未捕获的异常传播到调用栈上时就会调用它,并把错误信息输出到浏览器的JavaScript控制台上。 ``` window.onerror = function (message, filename, lineno, colno, error) { console.log("出错了!--> %s", error.stack); }; ``` 五个参数的含义: ``` message:出错信息 filename:出错脚本的网址 lineno:行号 colno:列号 error:错误对象 ``` 老式浏览器只支持前三个参数。 并不是所有的错误,都会触发JavaScript的error事件(即让JavaScript报错),只限于以下三类事件。 - JavaScript语言错误 - JavaScript脚本文件不存在 - 图像文件不存在 以下两类事件不会触发JavaScript的error事件。 - CSS文件不存在 - iframe文件不存在 注意:onerror处理程序的返回值也很重要。如果onerror处理程序返回false,它通知浏览器事件处理程序已经处理了错误,不需要其他操作。也就是说,浏览器不应该显示它自己的错误信息。遗憾的是,由于历史原因,Firefox里的错误处理程序必须返回true来表示它已经处理了错误。 **11.7 多窗口和窗体** 由于网页可以使用`<iframe>`嵌套多个网页,因此一个网页之中会形成多个窗口。另一情况是,子网页之中又嵌入别的网页,形成多级窗口。每个窗口的Window对象都是独立的,互不干扰。 浏览器提供了一些特殊变量,用来返回其他窗口。 ``` top:顶层窗口,即最上层的那个窗口 parent:父窗口 self:当前窗口,即自身 下面的代码可以判断当前窗口是否是顶层窗口 window.top === window.self 与这些变量对应,浏览器还提供一些特殊的窗口名,供open方法、`<a>`标签、`<form>`标签等引用。 _top:顶层窗口 _parent:父窗口 _blank:新窗口 ``` **11.7.1 打开和关闭窗口** 使用Window对象的open()方法可以打开一个新的浏览器窗口。Window.open()载入指定的URL到新的或已存在的窗口中,并返回代表那个窗口的Window对象。 open方法一共可以接受四个参数。 第一个参数:字符串,表示新窗口的网址。如果省略,默认网址就是`about:blank`。 第二个参数:字符串,表示新窗口的名字。如果该名字的窗口已经存在,则跳到该窗口,不再新建窗口。如果省略,就默认使用_blank,表示新建一个没有名字的窗口。 第三个参数:字符串,内容为逗号分隔的键值对,表示新窗口的参数,比如有没有提示栏、工具条等等。如果省略,则默认打开一个完整UI的新窗口。 第四个参数:布尔值,表示第一个参数指定的网址,是否应该替换history对象之中的当前网址记录,默认值为false。显然,这个参数只有在第二个参数指向已经存在的窗口时,才有意义。 ``` var modal = window.open( 'example.html', 'modalWindow', 'height=200,width=300' ); modal.window.name //modalWindow ``` window.close方法用于关闭当前窗口,一般用来关闭window.open方法新建的窗口。 modal.close(); 该方法只对顶层窗口有效,iframe框架之中的窗口使用该方法无效。 **11.7.2 窗体** 窗体是通过`<iframe>`元素创建的,我们可以像获取其他元素一样,获取一个表示`<iframe>`的元素对象,同时,`<iframe>`元素有`contentWindow`属性,引用该窗体的Window对象。 ``` <iframe id='f'></iframe> var f = document.getElementById('f'); var win = f.contentWindow; //子窗体的Window对象 var doc = f.contentDocument; //等同于f.contentWindow.document //获取子窗体的变量和属性 f.funciton() f.title ``` iframe嵌入窗口的window对象,有一个frameElement属性,返回它在父窗口中的DOM节点。对于顶级窗口,该属性等于null。 ``` win.frameElement === f //true window.frameElement === null //true ``` 当然,也可以使用Window对象的frames属性,它引用自身包含的窗口或窗体的子窗体。返回一个类数组对象。 var f = window.iframes[0]; 注意:frames[]数组里的元素是Window对象,而非`<iframe>`元素。 利用这个属性,实现窗口之间的互相引用。比如,frames[0]返回第一个子窗口,frames[1].frames[2]返回第二个子窗口内部的第三个子窗口,parent.frames[1]返回父窗口的第二个子窗口。 如果`<iframe>`元素设置了name或id属性,那么属性值会自动成为(Window对象的属性)全局变量,并且可以通过window.frames属性引用,返回子窗口的window对象。 ``` // HTML代码为<iframe id="myFrame"> myFrame // [HTMLIFrameElement] frames.myframe === myFrame // true ``` 另外,name属性的值会自动成为子窗口的名称,可以用在window.open方法的第二个参数,或者`<a>`和`<frame>`标签的target属性。