读完还是能学到很多的基础知识,这里记录下,方便回顾与及时查阅。
内容也有自己的一些补充。
JavaScript DOM 编程艺术(第二版)
1、JavaScript简史
JavaScript由Netscape公司与Sun公司合作开发,在JavaScript之前,web浏览器只是显示文本文档的软件,JavaScript之后,网页内容不再局限于枯燥的文本,交互性显著改善。在JavaScript的第一个版本,即JavaScript 1.0版本,出现在1995年推出的Netscape Navigator 2浏览器中。
在JavaScript 1.0发布时,浏览器市场主要是Netscape Navigator,后来IE开始追赶,微软在IE3时发布了自己的VBScript语言,同时以JScript为名发布了JavaScript的第一个版本,并很快追上了Netscape的步伐。面对微软的竞争,Netscape与Sun公司联合ECMA(欧洲计算机制造商协会)对JavaScript语言进行了标准化,于是出现了ECMAScript语言,这是同一种语言的另一个名字。
到了1996年,JavaScript、ECMAScript、JScript--随便怎么称呼,已经站稳了脚本,Netscape与微软的第三版浏览器都不同程度的支持JavaScript 1.1语言。
在JavaScript早期版本向程序员提供了查询和操控Web文档某些内容的手段,比如:
document.images[2]
dodument.forms['details']
现在人们通常把这种试验性质的初级DOM称为“第0级DOM”(DOM level 0),在还未形成统一标准的初级阶段,“第0级DOM”的常见用途是翻转图片和验证表单数据。
在1997年6月,Netscape Navigator 4发布,10月,IE4发布。这两个早起版本都对他们的浏览器进行了改进,大幅扩展了DOM,同时也接触到一个新名词:DHTML。
不幸的是,NN4和IE4浏览器使用了两种不兼容的DOM。程序员要写两套代码。
NN4中DOM元素为层,层有唯一的ID:
document.layers['myElement']
而微软的DOM需要这样引用:
document.all['myElement']
就在DOM产生分歧,浏览器厂商大战时,W3C推出了一个标准化的DOM,令人欣慰的事,Netscape与微软以及其他浏览器制造商都接受了新的标准,并于1998年10月完成了“第1级DOM”(DOM Level 1)。
二、JavaScript语法
三、DOM
DOM:Document Object Model 文档对象模型
JavaScript中的对象分为三种:
1、用户定义对象:由程序员创建的对象
2、內建对象:内建在JavaScript语言中的对象,如Array、Math和Date等
3、宿主对象:由宿主环境(浏览器)提供的对象,如window、document
window对象对应着浏览器窗口本身,这个对象的属性和方法通常称为BOM(浏览器文档模型)。
3.4 节点
先看三种:元素节点、文本节点、属性节点
<p class="info">my name is Gavin</p>
标签的名字就是元素的名字,这里面有一个元素节点p,一个文本节点“my name is Gavin”,以及一个属性节点class="info"。
属性节点总是被包含在元素节点中,并不是所有的元素节点都有属性节点,在所有的属性都被元素包含。
3.5 获取元素
- getElementById()
- getElementsByTagName()
- getElementsByClassName() -- HTML5 DOM
其中getElementById()返回一个对象,getElementsByTagName()与getElementsByClassName()返回一个对象数组。
每个节点都是一个对象。
3.6 获取和设置属性
- object.getAttribute( attribute )
- object.setAttribute( attribute, value )
四、案例研究:JavaScript图片库
setAttribute方法是“第一级DOM”(DOM level 1)的组成部分,可以设置任意元素的节点的任意属性。在“第一级DOM”出现之前,还可以用另外一个方法设置大部分元素的属性,这个方法到现在仍然有效。
img.src = 'xxx'
// 等价
img.setAttribute( 'src', 'xxx' );
使用“第一级DOM”的优势是:
- 不用特别记忆哪些属性可以用DOM之前的方法设置
- 可移植性好,DOM用于任何一种标记语言,通用API。
4.4.1 childNodes
node.childNodes 获取任何一个元素的所有子元素
4.4.2 nodeType属性
nodeTyoe总共有12种可取值,但其中仅有3个具有实用价值。
- 元素节点的nodeType属性值是1
- 属性节点的nodeType属性值是2
- 文本节点的nodeType属性值是3
4.4.5 nodeValue属性
如果要改变一个文本节点的值,那就实用DOM提供的nodeValue属性,它用来得到(和设置)一个节点的值。
element.nodeValue
注意,文本节点可能是其他节点的子节点,在获取时尤其注意:
<p>my name is Gavin.</p>
<script>
var oP = document.getElementsByTagName('p');
oP[0].nodeValue; // null
op[0].childNodes[0].nodeValue; // my name is Gavin.
</script>
4.4.6 firstChild 和 lastChild
node.childNodes[0] === node.firstChild
node.childNodes[ node.childNodes.length - 1 ] === node.lastChild
补充:nextSibling、previousSibling:兄弟节点
五、最佳实践
- 平稳退化
javascript:
伪协议- 结构与样式分离
- 渐进增强
- 分离JavaScript
- 向后兼容
-
对象检测
if ( !xxx ) return false;
-
浏览器嗅探
-
- 性能考虑
- 尽量少访问DOM和尽量减少标记
- 文档标记少,则DOM树规模少,遍历查找更快速
- 合并和放置脚本:脚本放到body最后,可以让页面更快
- 压缩脚本
- 尽量少访问DOM和尽量减少标记
"真"协议用来在因特网上的计算机之间传输数据包,如HTTP协议(http://)、FTP协议(ftp://)等,伪协议是一个非标准化的协议,javascript:
伪协议可以让我们通过一个链接来调用JavaScript函数。
通过伪协议来调用JavaScript函数的做法非常不好,不是最佳实践。
六、案例研究:图片库改进版
6.6 键盘访问
对于视力残疾的用户往往看不清屏幕上的鼠标指针,他们往往更喜欢使用键盘。众所周知,不使用鼠标也可以浏览web。键盘上的tab
键可以让我们从这个链接移动到另一个链接,然后按下回车键启用当前链接。
在键盘事件中有一个onkeypress
事件,按下键盘的任何一个键都会触发onkeypress事件,但这个键很容易出现问题,因为即使按下tab键也会触发该事件!
onclick
事件更聪明,用tab键移到链接上,按下回车键,链接也能访问。
6.8 DOM Core 和 HTML - DOM
- getElementById
- getElementsByTagName
- getAttribute
- setAttribute
这些都是DOM Core的组成部分,他们并不专属于JavaScript,任何支持DOM的程序设计语言都可以使用它们。
而对于
- element.onclick
- element.forms
- element.src
这些属性属于HTML DOM,在DOM Core出现很久之前就已经为人们所熟悉了。
大多数情况下,同样的操作即可以使用DOM Core实现也可以用HTML DOM实现。HTML DOM通过更加简短,只能用来处理HTML文档。
七、动态创建标记
7.1 一些传统方法
- document.write()
- innderHTML
MIME类型application/xhtml+xml与document.write()不兼容,浏览器呈现这种XHTML文档时不会执行document.write()方法。
另外,document.write()最大的问题是不能进行结构与行为分离。
备注:关于document.write()主要有两种使用方式:
第一种:当页面加载完成后,浏览器输出流自动关闭,此时进行document.write()方法将打开一个新的输出流,它将清除当前页面内容。
第二种:调用document.write()方法在窗口中打开窗口,框架中产生新文档,这种方式必须用document.close()关闭,并且继续使用document.write()写入的内容不会清除文档,而是继续追加。
element.innerHTML 方法可以读写元素的内容。几乎所有的浏览器都支持innerHTML,但这个属性不是W3C DOM的标准属性,现在已经在HTML5规范中,始于IE4浏览器。
和document.write()类似,innerHTML也是HTML专有属性,不能用于其他标记语言文档,在MIME类型为application/xhtml+xml的XHMLT文档中,该属性会被忽略。
7.2 DOM 方法
- createElement( nodeName ) -- 创建元素节点
- createTextNode( text ) -- 创建文本节点
- appendChild( child ) -- 把创建的节点插入到文档节点树中
通过createElement与createTextNode只是创建文档碎片,只有执行appendChild才会插入到文档中。
appendChild除了可以把文档碎片插入到文档树中,还可以连接文档碎片。
比如:
var p = document.createElement('p');
var text = document.createTextNode('my name is Gavin.');
p.appendChild( text );
document.body.appendChild( p );
-
insertBefore -- 在现有元素前插入元素
parentElement.insertBefore( newElement, targetElement );
没有insertAfter方法,可以结合insertBefore来实现
function insertAfter ( newElement, targetElement ) {
var parent = targetElement.parentNode;
if ( parent.lastChild == targetElement ) {
parent.appendChild( newElement );
} else {
parent.insertBefore( newElement, targetElement.nextSiblings );
}
}
7.4 ajax
ajax的技术核心是XMLHttpRequest。在微软较早的IE5中以ActiveX对象的形式实现了一个名叫XMLHTTP的对象,在较早IE中,创建对象的方式为:
var request = new ActiveXObject("Msxml2.XMLHTTP.3.0");
在其他浏览器上的创建方式为:
var request = new XMLHttpRequest();
更烦的是,不同IE版本中使用的XMLHTTP对象不完全相同,为了兼容所有浏览器,需要这样写:
function getHttpObject() {
if ( typeof XMLHttpRequest == 'undefined ) {
XMLHttpRequest = function () {
try {
return new ActiveXObject("Msxml2.XMLHTTP.6.0");
} cache ( e ) {}
try {
return new ActiveXObject("Msxml2.XMLHTTP.3.0");
} cache ( e ) {}
try {
return new ActiveXObject("Msxml2.XMLHTTP");
} cache ( e ) {}
}
}
return new XMLHttpRequest();
}
XMLHttpRequest对象由许多方法,最有用的是open方法,该方法有三个参数,第一个是请求类型:GETPOSTSEND,第二个是请求地址,第三个表示是否异步请求。
request.open( 'GET', 'xxx.json', true );
request.onreadystatechange = function () {}
request.send();
onreadystatechange事件XMLHttpRequest返回响应时触发。
返回响应时,浏览器会在不同阶段更新readyState的值,它有5个可能值:
- 0 表示未初始化
- 1 表示正在加载
- 2 表示加载完毕
- 3 表示正在交互
- 4 表示完成
如果readyState属性值变成了4,则说明服务器已经发回了数据。
访问服务器发送回来的数据需要通过两个属性,一个是responseText,这个用于保存文本字符串形式的数据,另一个是responseXML属性,用于保存Content-Type头部中指定为“text/xml”的数据,这其实是一个DocumentFragment对象。你可以使用各种方法来操作这个DOM对象,这也是XMLHttpRequest中有XML的原因。
八、充实文档的内容
渐进增强
则必然支持平稳退化
。
8.3.1 选用HTML、XHTML还是HTML5
不管使用哪一种标记,必须要与DOCTYPE声明保持一致。
XHMLT比HTML的规则更严格。比如在写属性时,HTML允许使用大写字母,也可以使用小写字母,XHTML却要求所有的标签名和属性名都必须使用小写字母。
备注:关于HTML、XHTML、HTML5的区别,可以看这篇文档:
在HTML早起,W3C成立之前,基本没有标准,标准都在在实现中定义的。一直从HTML2.0到4.0、4.01,这种情况下,HTML标准不是很规范,浏览器也对HTML页面的错误相当宽容。导致HTML作者也出了很多错误的HTML页面。
后来W3C意识到这个问题,开始制定标准,为了规范HTML,W3C结合XML制定了XHTML1.0标准,按照XML的要求规范HTML,并定义了一个新MIME Type:application/xhtml+xml,W3C的初衷是对这个MIME TYPE的浏览器实行强制错误检查,如果页面有错误,就要显示错误。很多开发者拒绝使用这个MIME TYPE,W3C不得已,在XHTML1.0后附加了一个附录C。允许开发者使用XHTML来写页面,同时使用原来的MIME TYPE:application/html。这个MIME TYPE不会触发浏览器的强制错误检查。
W3C随后在XHTML1.1中取消了附录C,即使用XHTML1.1标准的页面必须使用新的MIME TYPE。但并没有多少人使用。
有了XHTML的教训,WHATWG和W3C在制定下一代HTML标准,也就是HTML5的时候,就将向后兼容作为一个很重要的原则。HTML5加入了很多特性,但最重要的特性是,不会break已有的网页。你可以将网页的第一行改为<!DOCTYPE html>
,他就成了一个HTML5页面,可以照常在浏览器中显示。
<!DOCTYPE html>
总共才15个字符,这个声明同时也支持HTML与XHTML标记。
某些浏览器要根据DOCTYPE来决定使用标准模式,还是兼容模式来呈现页面,兼容模式意味着浏览器要模仿早起浏览器的“怪异行为”,并允许不规范的页面也能正常工作。一般来说,我们都应该坚持使用标准模式,避免触发兼容模式,HTML5的DOCTYPE默认就是标准模式。
此外还有XHTML5,即用XML的规则来编写HTML5。
九、CSS-DOM
9.2 Style属性
文档中的每个元素都是一个对象,每个对象都有各种各样的属性,比如:parentNode、childNodes、firstChild、lastChild、nextSibling、previousSibling等,表示节点的关心信息。
还有一些属性:nodeName、nodeValue、nodeType,表示节点的类型信息。
除此之外,还有一个style属性,表示节点的样式信息。
9.2.1 获取样式
element.style.color
对于font-family
,在获取的时候会出错,原因为:JavaScript中连字符-
为减法运算,所以会理解为获取font属性,然后对family做减法运算,而family此时为变量,但我们并没有声明这个变量,继而引发错误。
// 错误写法!!
element.style.font-family;
当引用一个中间带减号的CSS属性时,DOM要求使用驼峰命名法:
// 正确写法
element.style.fontFamily;
不管有多有连字符,一律采用驼峰写法:比如magrin-top-width,写为:marginTopWidth。
style属性只能返回内联样式。
9.2.2 设置样式
element.style.property = value;
9.3 用DOM解耦本设置样式
:first-child,:last-child选择器是CSS2中的
:nth-child()和:nth-of-type()是CSS3中的
在使用CSS时,不要人云亦云的认为表格都是不好的,虽然用表格布局不是好主意,但利用表格来显示数据却是理所当然的。
9.4 className 属性
// 读
element.className
// 写
element.className = value
className在设置的时候是替换(而不是追加)。可以利用字符串拼接,达到追加的效果
element.clssName += ' info';
十、用JavaScript实现动画效果
十一、HTML5
HTML5是HTML语言当前及未来的新标准,HTML规范从HTML4到XHTML,再到Web Apps1.0,最后又回到HTML5,整个过程充满了艰辛与争议。
在结构层,HTML5添加了很多新的标记元素,如<section>
、<article>
、<header>
和<footer>
等。
HTML5还提供了更多的交互即媒体元素,如<canvas>
、<audio>
、<video>
。
表单也进行了增强,新增了颜色选择器
、数据选择器
、滑动条
和进度条
等。
还有很多新的JavaScript API,比如:Geolocation
、Storage
、Drag-and-Drop
、Socket
以及多线程
等。
另外,通过新的MIME TYPE,HTML5保持了向后兼容,就算写代码很不规范,也没问题。
参考链接:
十二、综合示例
附录 JavaScript库
所谓库,就是可重用的代码包,具有如下的一些优点:
- 库代码经过了大量的用户的测试和验证
- 库能够很容易的与已有的开发框架集成
- 可为大多数日常琐碎的DOM变成工作提供了方便,简洁的方案,每个函数都能节省很多行代码
- 库很好的解决了跨浏览器的问题,让你更省心
库也存在问题:
- 库是别人写的,不是自己编写的,不了解内部机制,很难调试bug或由它导致的问题
- 要使用库,就要把它集中到脚本中,增加页面加载负担
- 混合使用多个库可能会造成冲突,同时也会造成功能浪费
A1:选择合适的库
在选择时,建议考虑如下问题:
- 它具有你需要的所有功能吗?
- 它的功能是否比你想要的还多?
- 它是模块化的吗?
- 它的支持情况怎么样?
- 它有文档吗?
- 它的许可合适吗?
A1.2 内容分发网络
一定要尽可能想办法减少网页文档的大小,并让浏览器缓存文件。除此之外,当然还要让用户尽可能的加载到页面。
内容分发网络(CDN,Content Delevery Network)可以解决分布共享库的问题。CDN是一个由服务器构成的网络,这个网络的用户就是分散存储一些公共的内容。CND的每台服务器都包含库的一份副本,这些服务器分布在世界上不同的国家和地区,以便达到和利用带宽和加快下载的目的。
浏览器访问库的时候使用一个公共的URL,而CDN的地层则通过地理位置最近、速度最快的服务器提供相应的文件,从而解决了整个系统中的瓶颈问题。
这些文件是共享的,当用户从一个站点跳到另一个站点,他们就不用再重复的下载相同的文件了。