Erik's Blog

《JavaScript 高级程序设计》笔记(3) —— BOM 及客户端检测

(更新于 )JavaScript#BOM

BOM、客户端检测

第 8 章 BOM

window 对象

window 对象表示浏览器的一个实例。

全局作用域 : window 对象是 ECMAScript 中规定的 Global 对象,是通过 JavaScript 访问浏览器窗口的一个接口。

window 对象和 全局对象差别 : 定义在 window 上的变量可以通过 delete 操作符删除,而定义全局变量不可以;访问不存在的全局变量会报错,查询 window 属性不会。

窗口关系及框架 : 每个框架都有自己的 window 对象,保存在 frames 集合中,使用索引或 name 属性访问对应的 window 对象。top 对象始终指向最外层的框架;parent 始终指向当前框架的直接上层框架;self 始终指向 window。

窗口位置 :

  • IE、Safari、Opera、Chrome 使用 screenLeft 和 screenTop
  • Firefox、Safari、Chrome 使用 screenX 和 screenY
  • 移动使用 moveTo(  ,  ) 和 moveBy(  ,  )

窗口大小 :

  • IE9+、Safari、Opera、Chrome、Firefox 提供 innerWidth、innerHeight【减去边框宽度】,outerWidth、outerHeight。 IE9+、Safari、Firefox 的 outerWidth、outerHeight 返回浏览器本身窗口的尺寸, Opera 返回页面视图容器的大小, Chrome 中 innerWidth、innerHeight,outerWidth、outerHeight 返回相同的值

  • IE、Safari、Opera、Chrome、Firefox 中,document.documentElement.clientWidth 和 document.documentElement.clientHeigth 保存了页面视口的信息, 在IE6中,以上标准模式有效,在混杂模式下,采用 document.body.clientWidth 和 document. body.clientHeigth, 混杂模式下的 Chrome,以上四种都可以。

  • resizeTo(a,b) 调整到a*b ; resizeBy(c,d) 调整到 a+c,b+d

导航和打开窗口 :

  • window.open() 打开一个特定的 url 或者信息浏览器窗口。里面可传四个参数,要加载的 url、窗口目标(可以是一个 target 框架名或者 _self、_blank、_parent、_top)、一个特性字符串(不打开新窗口,忽略这个,打开的话,可设置 fullscreen[yes,no,IE]、heigth、width、left、rigth、scrollbar…)、表示新页面是否取代李兰器历史记录当前加载页面的布尔值 var xxx=window.open(…….); xxx.opener == window window.close(); ======xxx.closed 浏览器内置的屏蔽程序阻止弹出窗口,window.open()可能为null

间歇调用和超时调用

超时调用 : var time = setTimeout(代码字符串或者函数,1000); clearTimeout(time);   间歇调用 : var time = setInterval(代码字符串或者函数,1000); clearInterval(time);

系统对话框

系统对话框:alert()、confirm()、prompt()

location 对象

location 既是 window 的对象的属性,也是 document 的对象的属性。

属性 : hash、host、hostname、href、pathname、port、protocol、search

查询字符串参数

位置操作

使用 assign(URL) 方法。 改变 location.href 或 window.location,也会调用 assign() 方法。 改变 location 其它属性也可以改变当前加载的页面。

replace(URL) 方法不会再历史记录中生成新的记录。

reload() 重新加载当前页面。location.reload() 可能从缓存中加载,location.reload(true) 从服务器加载。

screen 对象

history 对象

  • 后退 : history.go(-1)/back()
  • 前进 : history.go(1)/history.go(2)/history.go(URL)/forward()

第 9 章 客户端检测

能力监测(特性检测)

能力监测用于识别浏览器的能力

// IE5 之前使用 document.all[id], 后来的使用 document.getElementById(id)

if (object.prototypeInQuestion) {
	// 使用 object.prototypeInQuestion
}
  • 两个重要概念 : 先检测达成目的的最常用的特性;必须测试实际要用到的特性。
  • 在可能的情况下,尽量使用 typeof 进行能力检测。
  • 能力检测不是浏览器检测

怪癖检测

怪癖检测用于识别浏览器的特殊行为(bug)

// 检测 IE8- 如果某个实例属性与 [[Enumerable]] 标记为 false 的某个原型属性同名,该实例属性将不会出现在 for-in 循环中
var hasDontEnumQuirk = function () {
	var o = { toString : function () {} };
	for (var prop in o) {
		if (prop == "toString") {
			return false;
		}
	}
	return true;
}();

用户代理检测

通过检测用户代理字符串来确定实际使用的浏览器。优先级排在能力检测和怪癖检测之后。

电子欺骗

navigator.userAgent 属性

完整的用户代理字符串检测脚本 :


var client = function () {

	// 呈现引擎
	var engine = {
		ie : 0,
		gecko : 0,
		webkit : 0,
		khtml : 0,
		opera : 0,

		// 完整的版本号
		ver : null
	};

	// 浏览器
	var browser = {

		// 主要浏览器
		ie : 0,
		firefox : 0,
		safari : 0,
		konq : 0,
		opera : 0,
		chrome : 0,

		// 具体的版本
		ver : null
	};


	// 平台、设备和操作系统
	var system = {
		win : false,
		mac : false,
		xll : false,

		// 移动设备
		iphone : false,
		ipod : false,
		ipad : false,
		ios : false,
		android : false,
		nokiaN :false,
		winMobile : false,

		// 游戏系统
		wii : false,
		ps : false
	};


	// 检测呈现引擎及浏览器
	var ua = navigator.userAgent;

	if (window.opera) {
		engine.ver = window.opera.version();
		engine.opera = parseFloat(engine.ver);
	} else if (/AppleWebKit\/(\S+)/.test(ua)) {
		engine.ver = RegExp["$1"];
		engine.webkit = parseFloat(engine.ver);

		// 确定是 Chrome 还是 Safari
		if (/Chrome\/(\S+)/.test(ua)) {
			browser.ver = RegExp["$1"];
			browser.chrome = parseFloat(browser.ver);
		} else if (/Version\/(S+)/.test(ua)) {
			browser.ver = RegExp["$1"];
			borwser.safari = parseFloat(browser.ver);
		} else {
			//近似的确定版本号
			var safariVersion = 1;
			if (engine.webkit < 100) {
				safariVersion = 1;
			} else if (engine.webkit < 312) {
				safariVersoin = 1.2;
			} else if (engine.webkit < 412) {
				safariVersion = 1.3;
			} else {
				safariVersion = 2;
			}
			browser.safari = browser.ver = safariVersion;
		}
	} else if (/KHTML\/(\S+)/.test(ua) || /Konqueror\/([^;]+)/.test(ua)) {
		engine.ver = RegExp["$1"];
		engine.khtml = parseFloat(engine.ver);
	} else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)) {
		engine.ver = RegExp["$1"];
		engine.gecko = parseFloat(engine.ver);

		//确定不是Firefox
		if (/Firefox\/(\S+)/.test(ua)) {
			browser.ver = RegExp["$1"];
			browser.firefox = parseFloat(browser.ver);
		}
	} else if (/MSIE ([^;]+)/.test(ua)){
		engine.ver = browser.ver = RegExp["$1"];
		engine.ie = browser.ie = parseFloat(engine.ver);
	}


	// 检测浏览器
	browser.ie = engine.ie;
	browser.opera = engine.opera;

	// 检测平台
	var p = navigator.platform;
	system.win = p.indexOf("Win") == 0;
	system.mac = p.indexOf("Mac") == 0;
	system.xll = (p == "X11") || (p.indexOf("Linux" == 0));

	// 检测 Windows 操作系统
	if (system.win) {
		if (/Win(?:dows )?([^do]{2})\s?(\d+\.\d+)?/.test(ua)) {
			if (RegExp["$1"] == "NT") {
				switch (RegExp["$2"]) {
					case "5.0":
						system.win = "2000";
						break;
					case "5.1":
						system.win = "XP";
						break;
					case "6.0":
						system.win = "Vista";
						break;
					case "6.1":
						system.win = "7";
						break;
					default:
						system.win = "NT";
						break;

				}
			} else if (RegExp["$1"] == "9x") {
				system.win = "ME";
			} else {
				system.win = RegExp["$1"];
			}
		}
	}


	// 移动设备
	system.iphone = ua.indexOf("iPhone") > -1;
	system.ipod = ua.indexOf("iPod") > -1;
	system.ipad = ua.indexOf("iPad") > -1;
	system.nokiaN = ua.indexOf("NokiaN") > -1;

	// windows mobile
	if (system.win == "CE") {
		system.winMobile = system.win;
	} else if (system.win = "Ph") {
		if (/Windows Phone OS (\d+.\d+)/.test(ua)) {
			system.win = "Phone";
			system.winMobile = parseFloat(RegExp["$1"]);
		}
	}

	// 检测 iOS 版本
	if (system.mac && ua.indexOf("Mobile") > -1) {
		if (/CPU (?:iPhone )?OS (\d+_\d+)/.test(ua)) {
			system.ios = parseFloat(RegExp.$1.replace("_", "."));
		} else {
			system.ios = 2; // 不能真正检测出来, 所以只能猜测
		}
	}

	// 检测 Android 版本
	if (/Android (\d+\.\d+)/.test(ua)) {
		system.android = parseFloat(RegExp.$1);
	}

	// 游戏系统
	system.wii = ua.indexOf("Wii") > -1;
	system.ps = /playstation/i.test(ua);


	// 返回这些对象
	return {
		engine : engine,
		browser : browser,
		system : system
	};
}();