Javascript-12-BOM-1

window对象

BOM的核心对象是window,他表示浏览器的一个实例。在浏览器中,window对象有双重角色,它既是通过Jacascript访问浏览器窗口的一个接口,又是ECMAScript规定的Global对象。这意味着在网页中定义的任何一个对象、变量和函数,都以window作为其Global对象,因此有权访问parseInt()等方法。

全局作用域

由于window对象同时扮演者ECMAScript中Global对象的角色,因此所有在全局作用域中声明的变量、函数都会变成window对象的属性和方法。抛开全局变量会成为window对象的属性不谈,定义全局变量与在window对象上直接定义属性还是有一点差别:全局变量不能通过delete操作符删除,而直接在window对象上的定义的属性可以。

尝试访问未声明的变量会抛出错误,但是通过查询window对象,可以知道某个可能未声明的变量是否存在。例如:

// 这里会抛出错误,因为oldValue未定义
var newValue = oldValue;

// 这里不会抛出错误,因为这是一次属性查询
// newValue的值是undefined
var newValue = window.oldValue;
窗口关系及框架

如果页面中包含框架,则每个框架都拥有自己饿winodw对象,并且保存在frames集合中。在frames集合中,可以通过数值索引(从0开始,从左至右,从上到下)或者框架名称来访问相应的window对象。每个window对象都有一个name属性,其中包含框架的名称。

top对象始终指向最高(最外)层的框架,也就是浏览器窗口。使用它可以确保在一个框架中正确地访问另一个框架。因为对于在一个框架中编写的任何代码来说,其中的window对象指向的都是那个框架的特定实例,而非最高层的框架。

与top相对的另一个window对象是parent。顾名思义,parent(父)对象始终指向当前框架的直接上层框架。在某些情况下,parent有可能等于top;但在没有框架的情况下,parent一定等于top(此时它们都等于window)。

与框架有关的最后一个对象是self,它始终指向window;实际上,self和window对象可以互换使用。引入self对象的目的只是为了与top和parent对象对应起来,因此它不格外包含其他值。

所有这些对象都是window对象的属性,可以通过window.parent、window.top等形式来访问。同时,这也意味着可以将不同层次的window对象连缀起来,例如window.parent.parent.frames[0].

窗口位置

用来确定和修改window 对象位置的属性和方法有很多。IE、Safari、Opera 和Chrome 都提供了
screenLeft 和screenTop 属性,分别用于表示窗口相对于屏幕左边和上边的位置。Firefox 则在
screenX 和screenY 属性中提供相同的窗口位置信息,Safari 和Chrome 也同时支持这两个属性。Opera
虽然也支持screenX 和screenY 属性,但与screenLeft 和screenTop 属性并不对应,因此建议大
家不要在Opera 中使用它们。使用下列代码可以跨浏览器取得窗口左边和上边的位置。

var leftPos = (typeof window.screenLeft == "number") ?
window.screenLeft : window.screenX;
var topPos = (typeof window.screenTop == "number") ?
window.screenTop : window.screenY;

这个例子运用二元操作符首先确定screenLeft 和screenTop 属性是否存在,如果是(在IE、
Safari、Opera 和Chrome 中),则取得这两个属性的值。如果不存在(在Firefox 中),则取得screenX
和screenY 的值。
在使用这些值的过程中,还必须注意一些小问题。在IE、Opera 中,screenLeft 和screenTop 中保存
的是从屏幕左边和上边到由window 对象表示的页面可见区域的距离。换句话说,如果window 对象是
最外层对象,而且浏览器窗口紧贴屏幕最上端——即y 轴坐标为0,那么screenTop 的值就是位于页面
可见区域上方的浏览器工具栏的像素高度。但是,在Chrome、Firefox 和Safari 中,screenY 或screenTop
中保存的是整个浏览器窗口相对于屏幕的坐标值,即在窗口的y 轴坐标为0 时返回0。
更让人捉摸不透是,Firefox、Safari 和Chrome 始终返回页面中每个框架的top.screenX 和
top.screenY 值。即使在页面由于被设置了外边距而发生偏移的情况下,相对于window 对象使用
screenX 和screenY 每次也都会返回相同的值。而IE 和Opera 则会给出框架相对于屏幕边界的精确坐
标值。
最终结果,就是无法在跨浏览器的条件下取得窗口左边和上边的精确坐标值。然而,使用moveTo()
和moveBy()方法倒是有可能将窗口精确地移动到一个新位置。这两个方法都接收两个参数,其中
moveTo()接收的是新位置的x 和y 坐标值,而moveBy()接收的是在水平和垂直方向上移动的像素数。
下面来看几个例子:


//将窗口移动到屏幕左上角
window.moveTo(0,0);
//将窗向下移动100 像素
window.moveBy(0,100);
//将窗口移动到(200,300)
window.moveTo(200,300);
//将窗口向左移动50 像素

需要注意的是,这两个方法可能会被浏览器禁用;而且,在Opera 和IE 7(及更高版本)中默认就
是禁用的。另外,这两个方法都不适用于框架,只能对最外层的window 对象使用。

窗口大小

跨浏览器确定一个窗口的大小不是一件简单的事。IE9+、Firefox、Safari、Opera 和 Chrome 均为此提供了4个属性: innerWidth 、 innerHeight 、 outerWidth 和 outerHeight 。

在 IE9+、Safari 和 Firefox 中, outerWidth 和 outerHeight 返回浏览器窗口本身的尺寸(无论是从最外层的 window 对象还是从某个框架访问)。

在 Opera 中,这两个属性的值表示页面视图容器 的大小。而 innerWidth 和 innerHeight 则表示该容器中页面视图区的大小(减去边框宽度)。

在 Chrome 中, outerWidth 、 outerHeight 与 innerWidth 、 innerHeight 返回相同的值,即视口(viewport)大小而非浏览器窗口大小。

这里所谓的“页面视图容器”指的是 Opera 中单个标签页对应的浏览器窗口。IE8 及更早版本没有提供取得当前浏览器窗口尺寸的属性;不过,它通过 DOM 提供了页面可见区域的相关信息。

在 IE、Firefox、Safari、Opera 和 Chrome 中, document.documentElement.clientWidth 和 document.documentElement.clientHeight 中保存了页面视口的信息。在 IE6 中,这些属性必须在标准模式下才有效;如果是混杂模式,就必须通过

document.body.clientWidth 和 document.body. clientHeight 取得相同信息。而对于混杂模式下的Chrome,则无论通过 document.documentElement 还是 document.body 中的 clientWidth 和 clientHeight 属性,都可以取得视口的大小。

虽然最终无法确定浏览器窗口本身的大小,但却可以取得页面视口的大小,如下所示。

var pageWidth = window.innerWidth,

pageHeight = window.innerHeight;

if (typeof pageWidth != “number”){

    if (document.compatMode == “CSS1Compat”){

    pageWidth = document.documentElement.clientWidth;

    pageHeight = document.documentElement.clientHeight;

} else {

    pageWidth = document.body.clientWidth;

    pageHeight = document.body.clientHeight;

    }

}

在以上代码中,我们首先将 window.innerWidth 和 window.innerHeight 的值分别赋给了 pageWidth 和 pageHeight 。然后检查 pageWidth 中保存的是不是一个数值;如果不是,则通过检查 document.compatMode (这个属性在《 HTML5扩展了HTMLDocument,增加了新的功能 》里面已经说明)来确定页面是否处于标准模式。如果是,则分别使用 document.documentElement.clientWidth 和 document.documentElement.clientHeight 的值。否则,就使用 document.body.clientWidth 和 document.body.clientHeight 的值。

对于移动设备,window.innerWidth 和 window.innerHeight 保存着可见视口,也就是屏幕上可见页面区域的大小。移动IE浏览器不支持这些属性,但通过document.documentElement.clientWidth 和 document.documentElement.clientHeihgt 提供了相同的信息。随着页面的缩放,这些值也会相应变化。

在其他移动浏览器中, document.documentElement 度量的是布局视口,即渲染后页面的实际大小(与可见视口不同,可见视口只是整个页面中的一小部分)。移动IE浏览器把布局视口的信息保存在 document.body.clientWidth和document.body.clientHeight 中。这些值不会随着页面缩放变化。

由于与桌面浏览器间存在这些差异,最好是先检测一下用户是否在使用移动设备,然后再决定使用哪个属性。

导航和打开窗口

使用 window.open() 方法既可以导航到一个特定的URL,也可以打开一个新的浏览器窗口。这个方法可以接收4个参数:要加载的URL、窗口目标、一个特性字符串以及一个表示新页面是否取代浏览器历史记录中当前加载页面的布尔值。通常只须传递第一个参数,最后一个参数只在不打开新窗口的情况下使用。

如果为 window.open() 传递了第二个参数,而且该参数是已有窗口或框架的名称,那么就会在具有该名称的窗口或框架中加载第一个参数指定的URL。

// 等同于<a href="http://www.domain.com" target="topFrame"></a>
window.open('https://www.domain.com/', 'topFrame')
弹出窗口

如果给window.open()传递的第二个参数并不是一个已经存在的窗口或框架,那么该方法就会根据在第三个参数位置上传入的字符串创建一个新窗口或新标签页。如果没有传入第三个参数,那么就会打开一个带有全部默认设置(工具栏、地址栏和状态栏等)的新浏览器窗口(或者打开一个新标签页——根据浏览器设置)。在不打开新窗口的情况下,会忽略第三个参数。

第三个参数是一个逗号分隔的设置字符串,表示在新窗口中都显示哪些特性。下表列出了可以出现在这个字符串中的设置选项。

表中所列的部分或全部设置选项,都可以通过逗号分隔的名值对列表来指定。其中,名值对以等号表示(注意,整个特性字符串中不允许出现空格),如下面的例子所示。

window.open("http://www.wrox.com/","wroxWindow",
"height=400,width=400,top=10,left=10,resizable=yes");

window.open()方法会返回一个指向新窗口的引用。引用的对象与其他window 对象大致相似,但我们可以对其进行更多控制。例如,有些浏览器在默认情况下可能不允许我们针对主浏览器窗口调整大小或移动位置,但却允许我们针对通过window.open()创建的窗口调整大小或移动位置。通过这个返回的对象,可以像操作其他窗口一样操作新打开的窗口,如下所示。

var wroxWin = window.open("http://www.wrox.com/","wroxWindow",
"height=400,width=400,top=10,left=10,resizable=yes");
//调整大小
wroxWin.resizeTo(500,500);
//移动位置
wroxWin.moveTo(100,100);
调用close()方法还可以关闭新打开的窗口。
wroxWin.close();
// 这个方法仅适用于通过window.open()打开的弹出窗口

新创建的window 对象有一个opener 属性,其中保存着打开它的原始窗口对象。这个属性只在弹出窗口中的最外层window 对象(top)中有定义,而且指向调用window.open()的窗口或框架。例如:


var wroxWin = window.open("http://www.wrox.com/","wroxWindow",
"height=400,width=400,top=10,left=10,resizable=yes");
alert(wroxWin.opener == window); //true

虽然弹出窗口中有一个指针指向打开它的原始窗口,但原始窗口中并没有这样的指针指向弹出窗口。窗口并不跟踪记录它们打开的弹出窗口,因此我们只能在必要的时候自己来手动实现跟踪。有些浏览器(如IE8 和Chrome)会在独立的进程中运行每个标签页。当一个标签页打开另一个标签页时,如果两个window 对象之间需要彼此通信,那么新标签页就不能运行在独立的进程中。在Chrome中,将新创建的标签页的opener 属性设置为null,即表示在单独的进程中运行新标签页,如下所示。


var wroxWin = window.open("http://www.wrox.com/","wroxWindow",
"height=400,width=400,top=10,left=10,resizable=yes");
wroxWin.opener = null

将opener 属性设置为null 就是告诉浏览器新创建的标签页不需要与打开它的标签页通信,因此可以在独立的进程中运行。标签页之间的联系一旦切断,将没有办法恢复。

安全限制

Windows XP SP2 中的IE6 对弹出窗口施加了多方面的安全限制,包括不允许在屏幕之外创建弹出窗口、不允许将弹出窗口移动到屏幕以外、不允许关闭状态栏等。IE7 则增加了更多的安全限制,如不允许关闭地址栏、默认情况下不允许移动弹出窗口或调整其大小。Firefox 1 从一开始就不支持修改状态栏,
因此无论给window.open()传入什么样的特性字符串,弹出窗口中都会无一例外地显示状态栏。后来的Firefox 3 又强制始终在弹出窗口中显示地址栏。Opera 只会在主浏览器窗口中打开弹出窗口,但不允许它们出现在可能与系统对话框混淆的地方。
此外,有的浏览器只根据用户操作来创建弹出窗口。这样一来,在页面尚未加载完成时调用window.open()的语句根本不会执行,而且还可能会将错误消息显示给用户。换句话说,只能通过单击或者击键来打开弹出窗口。对于那些不是用户有意打开的弹出窗口,Chrome 采取了不同的处理方式。它不会像其他浏览器那样简单地屏蔽这些弹出窗口,而是只显示它们的标题栏,并把它们放在浏览器窗口的右下角。

弹出窗口屏蔽程序

大多数浏览器都内置有弹出窗口屏蔽程序,而没有内置此类程序的浏览器,也可以安装Yahoo!Toolbar 等带有内置屏蔽程序的实用工具。结果就是用户可以将绝大多数不想看到弹出窗口屏蔽掉。于是,在弹出窗口被屏蔽时,就应该考虑两种可能性。如果是浏览器内置的屏蔽程序阻止的弹出窗口,那么window.open()很可能会返回null。此时,只要检测这个返回的值就可以确定弹出窗口是否被屏蔽了,如下面的例子所示。

var wroxWin = window.open("http://www.wrox.com", "_blank");
if (wroxWin == null){
alert("The popup was blocked!");

如果是浏览器扩展或其他程序阻止的弹出窗口,那么window.open()通常会抛出一个错误。因此,要想准确地检测出弹出窗口是否被屏蔽,必须在检测返回值的同时,将对window.open()的调用封装在一个try-catch 块中,如下所示。

var blocked = false;
try {
var wroxWin = window.open("http://www.wrox.com", "_blank");
if (wroxWin == null){
blocked = true;
}
} catch (ex){
blocked = true;
}
if (blocked){
alert("The popup was blocked!");
}

在任何情况下,以上代码都可以检测出调用window.open()打开的弹出窗口是不是被屏蔽了。但要注意的是,检测弹出窗口是否被屏蔽只是一方面,它并不会阻止浏览器显示被屏蔽的弹出窗口有关的消息。

间歇调用和超时调用

Javascript是单线程语言,但它允许通过设置超时值和间歇时间值来调度代码在特定的时刻执行。前者是在指定的时间过后执行代码,而后者则是每隔指定的时间就执行一次代码。

超时调用需要使用window对象的setTimeout()方法,它接受两个参数:要执行的代码和以毫秒表示的时间(即在执行代码前需要等待多少毫秒)。其中,第一个参数可以是一个包含JS代码的字符串(就和在eval()函数中使用的字符串一样),也可以是一个函数。虽然这两种调用方式都没有问题,但由于传递字符串可能导致性能损失,因此,不建议以字符串作为第一个参数。

第二个参数是一个表示等待多长时间的毫秒数,但经过该时间后指定的代码不一定会执行。JS是一个单线程的解释器,因此一定时间内只能执行一段代码。为了控制要执行的代码,就有一个JS任务队列。这些任务会按照将他们添加到队列的顺序执行。setTimeout()的第二个参数告诉JS再过多长时间把当前任务添加到队列中。如果队列是空的,那么添加的代码会立即执行;如果队列不是空的,那么它要等前面的代码执行完了以后再执行。

调用setTimeout()之后,该方法会返回一个数值ID,表示超时调用。该ID是计划执行代码的唯一标识符,可以通过它来取消超时调用,可以调用clearTimeout()方法并将相应的超时调用ID作为参数传递给它。

var timeoutId = setTimeout(function() {
    alert('Hello world');
}, 1000);

clearTimeout(timeoutId);

超时调用的代码都是在全局作用域中执行的,因此函数中this的值在非严格模式下指向window对象,在严格模式下是undefined。

间歇调用和超时调用类似,只不过它会按照指定的时间间隔重复执行代码,直至间歇调用被取消或者页面被卸载。设置间歇调用的方法是setInterval(),它接受的参数与setTimeout()相同;要执行的代码(字符串或函数)和每次执行之前需要等待的毫秒数。

要取消尚未执行的间歇调用,可以使用clearInterval()方法并传入相应的间歇调用ID。
取消间歇调用的重要性远远高于取消超时调用。

var num = 0;
var max = 10;
var intervalId = null

function incrementNumber() {
    num++;

    // 如果执行次数达到max设定的值,则取消后续尚未执行的调用
    if(max === num) {
        clearInterval(intercalId);
        alert("Down");
    }
}

var intercalId = setInterval(incrementNumber, 1000);

也可以使用超时调用来实现:

var num = 0;
var max = 10;

function incrementNumber() {
    num++;

    // 
    if (num < max) {
        setTimeout(incrementNumber, 1000);
    } else {
        alert("Done");
    }
}

setTimeout(incrementNumber, 1000);

可见,在使用超时调用时,没有必要跟踪超时调用ID,因此每次执行代码之后,如果不设置另一个超时调用,调用就会自行停止。一般认为,使用超时调用来模拟间歇调用的是一种最佳模式。在开发环境中,很少使用真正的间歇调用,原因是后一个间歇调用可能会在前一个间歇调用结束之前启动。所以最好不要使用间歇调用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值