JavaScript DOM 编程艺术

2.4.1 比较操作符

如果想比较两个值是否相等,可以使用等于(==)比较操作符。如果在条件语句的某个条件里使用了单个等号,那么根据相应的赋值操作返回值作为判断条件

if (a = true) {console.log(true)}  // 这里返回值为 true,打印出 true
if (a = false) {console.log(false)}  // 这里返回值为 false,代码块里的语句不会执行

3.2 对象:DOM 中的 “O”

“对象” 是一种自足的数据集合。与某个特定对象相关联的变量被称为这个对象的属性,只能通过某个特定对象去调用的函数被称为这个对象的方法。

JavaScript 语言里的对象可以分为三种类型:

  • 用户定义对象(user-defined object):由程序员自行创建的对象。
  • 内建对象(native object):内建在 JavaScript 语言里的对象,如 Array、Math 和 Date 等。
  • 宿主对象(host object):由浏览器提供的对象。

3.4 节点

文档是由节点构成的集合。在 DOM 里有许多不同类型的节点,也有很多类型的 DOM 节点包含着其他类型的节点。
常用的三种节点:元素节点、文本节点和属性节点

3.4.5 获取元素

有 3 种 DOM 方法可获取元素节点,分别是通过元素 ID、标签名和类名来获取。

  • getElementById:返回一个给定 id 属性值的元素节点对应的对象。
  • getElementsByTagName:返回一个对象数组,每个对象分别对应着文档里有着给定标签的一个元素。
  • getElementsByClassName:返回一个具有相同类名的元素的数组(可指定多个类名,只要在字符串参数中用空格分隔类名即可。类名的实际顺序不重要,甚至还带有更多类名也没有关系)。

3.5 获取和设置属性

  • getAttribute:返回指定属性名的属性值。它不属于 document 对象,只能通过元素节点对象调用。
  • setAttribute:更改属性节点的值。它也只能用于元素节点。

4.4.2 nodeType属性

每一个节点都有 nodeType 属性。nodeType 属性总共有 12 种可取值,但其中仅有 3 种具有实用价值。

  • 元素节点的 nodeType 属性值是 1。
  • 属性节点的 nodeType 属性值是 2。
  • 文本节点的 nodeType 属性值是 3。

5.2 平稳退化

如果正确地使用了 JavaScript 脚本,就可以让访问者在他们的浏览器不支持 JavaScript 的情况下仍能顺利地浏览你的网站。就是说,虽然某些功能无法使用,但最基本的操作仍能顺利完成。

5.2.1 "javascript:" 伪协议

“真”协议用来在因特网上的计算机之间传输数据包,如 HTTP 协议(http://)、FTP 协议(ftp://)等,伪协议则是一种非标准化的协议。"javascript:" 伪协议让我们通过一个链接来调用 JavaScript 函数。

举个栗子:下面通过 "javascript:" 伪协议调用 popUp() 函数:

<a href="javascript:popUp('http://www.example.com/');">Example</a>

这条语句在支持 "javascript:" 伪协议的浏览器中运行正常,较老的浏览器则会去尝试打开那个链接但失败,支持这种伪协议但禁用了 JavaScript 功能的浏览器会什么也不做。
总之,在 HTML 文档里通过 "javascript:" 伪协议调用 JavaScript 代码的做法非常不好。

5.2.2 内嵌的事件处理函数

把事件处理函数作为属性嵌入 HTML 标签

举个栗子:下面通过 onclick 事件处理函数调用 popUp() 函数:

<a href="#" onclick="popUp('http://www.example.com/');return false;">Example</a>

因为在上面的这条 HTML 指令里使用了 return false 语句,这个链接不会真的被打开。把 href 属性的值设置为 “#” 只是为了创建一个空链接,实际工作全部由 onclick 属性负责完成。
很遗憾,这个技巧与用 "javascript:" 伪协议调用 JavaScript 代码的做法同样糟糕,因为它们都不能平稳退化。如果用户已经禁用了浏览器的 JavaScript 功能,这样的链接将毫无用处。

5.2.3 谁关心这个

让那些不支持或禁用了 JavaScript 功能的浏览器也能顺利地访问你的网站真的那么重要吗?
请想象一下,如果那个用户是一个搜索机器人。搜索机器人是一种自动化的程序,它们浏览 Web 的目的是为了把各种网页添加到搜索引擎的数据库里。各大搜索引擎都有类似的程序。目前,只有极少数的搜索机器人能够理解 JavaScript 代码。所以,如果你的 JavaScript 网页不能平稳退化,它们在搜索引擎上的排名就可能大受损害。

举个栗子:具体到 popUp() 函数,为其中的 JavaScript 代码预留出退路很简单:
在链接里把 href 属性设置为真实存在的 URL 地址,让它成为一个有效的链接。

<a href="http://www.example.com/" onclick="popUp(this.href);return false;">Example</a>

所以,在把 href 属性设置为真实存在的 URL 地址后,即使 JavaScript 已被禁用,这个链接也是可用的。这是一个经典的 “平稳退化” 的例子。

5.3.1 结构与样式分离

我们经常会遇到一些几乎每个元素都带有 style 属性的 Web 文档,而这是 CSS 技术最缺乏效率的用法之一。真正能从 CSS 技术获益的方法,是把样式全部转移到外部文件中去。
作为 CSS 技术的突出优点,文档结构与文档样式的分离可以确保网页都能平稳退化。

5.3.2 渐进增强

“标记良好的内容就是一切”
只有正确地使用标记语言才能对内容做出准确的描述。
在给内容加上各种标记后,就可以使用各种 CSS 指令控制内容的显示效果。CSS 指令构成了一个表示层。这个表示层就像是一张透明的彩色薄膜,可以包裹到文档的结构上,使文档的内容呈现出各种色彩。但即使去掉这个表示层,文档的内容也依然可以访问。
所谓 “渐进增强” 就是用一些额外的信息层去包裹原始数据。按照 “渐进增强” 原则创建出来的网页几乎都符合 “平稳退化” 原则。
类似 CSS,JavaScript 和 DOM 提供的所有功能也应该构成一个额外的指令层。CSS 代码负责提供关于 “表示” 的信息,JavaScript 代码负责提供关于 “行为” 的信息。行为层的应用方式与表示层一样。

5.5 向后兼容

正如前面反复强调的那样,你的网站的访问者很可能未启用 JavaScript 功能。此外,不同的浏览器对 JavaScript 的支持程度也不一样。绝大多数浏览器都能或多或少地支持 JavaScript,而绝大多数现代浏览器对 DOM 的支持都非常不错。但比较古老的浏览器却很可能无法理解 DOM 提供的某些方法和属性。因此,即使某位用户在访问你的网站时使用的是支持 JavaScript 的浏览器,某些脚本也不一定能正常工作。
针对这一问题的最简单的解决方案是,检测浏览器对 JavaScript 的支持程度。

5.5.1 对象检测

这个解决方案很容易实现:只要把某个方法打包在一个 if 语句里,就可以根据这条 if 语句的条件表达式的求值结果是 true(这个方法存在) 还是 false(这个方法不存在) 来决定应该采取怎样的行动。这种检测称为对象检测(object detection)。

举个栗子:

window.onload = function () {
  if (!document.getElementsByTagName) {
    return false;
  }
}

虽然只是一条简单的 if 语句,但它可以确保那些 “古老的” 浏览器不会因为我的脚本代码而出问题。这么做是为了让脚本有良好的向后兼容性。因为我在给网页添加各有关行为时始终遵循了 “渐进增强” 的原则,所以可以确切地知道我添加的那些都能平稳退化,我的网页在那些 “古老的” 浏览器里也能正常浏览。那些只支持一部分 JavaScript 功能但不支持 DOM 的浏览器仍可以访问我的网页的内容。

5.6.1 尽量少访问 DOM 和尽量减少标记

访问 DOM 的方式对脚本的性能会产生非常大的影响。不管什么时候,只要是查询 DOM 中的某些元素,浏览器都会搜索整个 DOM 树,从中查找可能匹配的元素。更好的办法是把第一次查询的结果保存在一个变量中,然后重用该结果。

在多个函数都会取得一组类似元素的情况下,可以考虑重构代码,把搜索结果保存在一个全局变量里,或者把一组元素直接以参数形式传递给函数。

另一个需要注意的地方,就是要尽量减少文档中的标记数量。过多不必要的元素只会增加 DOM 树的规模,进而增加遍历 DOM 树以查找特定元素的时间。

5.6.2 合并和放置脚本

包含脚本的最佳方式就是使用外部文件,因为外部文件与标记能清晰地分离开,而且浏览器也能对站点中的多个页面重用缓存过的相同脚本。

把多个脚本文件合并到一个脚本文件中,可以减少加载页面时发送的请求数量。

脚本在标记中的位置对页面的初次加载时间也有很大影响。位于 <head> 块中的脚本会导致浏览器无法并行加载其他文件(如图像或其他脚本)。一般来说,根据 HTTP 规范,浏览器每次从同一个域名中最多只能同时下载两个文件。而在下载脚本期间,浏览器不会下载其他任何文件,即使是来自不同域名的文件也不会下载,所有其他资源都要等脚本加载完毕后才能下载。

把所有 <script> 标签都放在文档的末尾,</body> 标记之前,就可以让页面变得更快。

5.6.3 压缩脚本

所谓压缩脚本,指的是把脚本文件中不必要的字节,如空格和注释,统统删除,从而达到 “压缩” 文件的目的。

多数情况下,你应该有两个版本,一个是工作副本,可以修改代码并添加注释;另一个是精简副本,用于放在站点上。通常,为了与非精简版本区分开,最好在精简副本的文件名中加上 min 字样。

举个栗子:

<script src="scripts/scriptName.min.js"></script>

推荐几个有代表性的代码压缩工具:

  • Douglas Crockford 的 JSMin
  • 雅虎的 YUI Compressor
  • 谷歌的 Closure Compiler

6.8 DOM Core 和 HTML-DOM

列举几个常用的 DOM 方法:

  • getElementById
  • getElementsByTagName
  • getElementsByClassName
  • getAttribute
  • setAttribute

这些方法都是 DOM Core 的组成部分。它们并不专属于 JavaScript,支持 DOM 的任何一种程序设计语言都可以使用它们。它们的用途也并非仅限于处理网页,它们可以用来处理任何一种标记语言(比如 XML)编写出来的文档。

HTML-DOM 提供了许多描述各种 HTML 元素的属性。

举个栗子:

document.getElementsByTagName('form')
可简化为:
document.forms

element.getAttribute('src')
可简化为:
element.src

这些方法和属性可以相互替换。同样的操作既可以使用 DOM Core 来实现,也可以使用 HTML-DOM 来实现。HTML-DOM 代码通常会更短,必须提醒一下,它们只能用来处理 Web 文档。

7.4.1 XMLHttpRequest 对象

Ajax 技术的核心就是 XMLHttpRequest 对象。这个对象充当着浏览器中的脚本(客户端)与服务器之间的中间人的角色。以往的请求都由浏览器发出,而 Javascript 通过这个对象可以自己发出请求,同时也自己处理响应。

XMLHttpRequest 对象有许多的方法。其中最有用的是 open 方法,它用来指定服务器上将要访问的文件,指定请求类型:GET、POST、或 SEND。这个方法的第三个参数用于指定请求是否以异步方式发送和处理。

onreadystatechange 是一个事件处理函数,它会在服务器给 XMLHttpRequest 对象返回响应的时候被触发执行。
服务器在向 XMLHttpRequest 对象返回响应时,该对象有许多属性可用,浏览器会在不同阶段更新 readyState 属性的值,它有 5 个可能的值:

  • 0 表示未初始化
  • 1 表示正在加载
  • 2 表示加载完毕
  • 3 表示正在交互
  • 4 表示完成

只要 readyState 属性的值变成了 4,就可以访问服务器发送回来的数据了。
访问服务器发送回来的数据要通过两个属性完成。一个是 responseText 属性,这个属性用于保存文本字符串形式的数据。另一个属性是 responseXML 属性,用于保存 Content-Type 头部中指定为 “text/xml” 的数据,其实是一个 DocumentFragment 对象。你可以使用各种 DOM 方法来处理这个对象。而这也正是 XMLHttpRequest 这个名称里有 XML 的原因。

注意:在使用 Ajax 时,千万要注意同源策略。使用 XMLHttpRequest 对象发送的请求只能访问与其所在的 HTML 处于同一个域中的数据,不能向其他域发送请求。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容