《JavaScript DOM 编程艺术》读书笔记
本科的时候也有看前端的内容但是没有系统地学习,现在有 WebGIS 相关的项目,并且发现 Android 开发也发生了一些变化: 很多应用不再单单是一个简单的原生 Android 应用,用到了跨平台技术,比如说 React Native 、 Ionic 等等,这都需要 Javascript 基础。所以打算系统地学习下 Javascript 。
其实有这个想法很久了,包括暑假来到也有意学习前端技术,最开始是在慕课网看 前端基础 相关视频,然后也简单地在 菜鸟教程 看了相关文字内容,最后在廖雪峰的网站看 JavaScript全栈教程,这部分对后来 Node.js 后端开发很有帮助。
但是现在发现 Javascript 水品还不行,没有接近实战的水平,所以打算再系统学习下。
按照知乎 如何循序渐进、有效地学习JavaScript? 问题的回答,决定先学习 《JavaScript DOM 编程艺术》(第二版),后面再学习 《JavaScript高级程序设计》,最后再刷 《ECMAScript 6 入门》 学习 ES6 。
第1章 JavaScript 简史
本章主要介绍了 JavaScript 的起源、浏览器之间的战争、DOM 的演变史。
DOM (Document Object Model,文档对象模型) 是一套对文档的内容进行抽象和概念化的方法。
感觉和类的说法很类似。
第2章 JavaScript 语法
程序设计语言分为解释型和编译型两大类。Java或者C++等语言需要一个编译器,把用Java等高级语言编写出来的源代码翻译为计算机能直接执行的文件。
解释型语言不需要编译器--它们仅需要解释器,对于JavaScript而言,Web浏览器负责完成有关解释和执行工作。
浏览器的JavaScript解释器将直接读取源代码并执行,相关错误也只能在此时才能发现。
语法
JavaScript 语法基本上和 Java 或者 C++ 类似,下面主要介绍不同点。
JavaScript 是一种弱类型语言,变量使用前不需要进行类型声明,但不建议这样做。
数据类型
JavaScript 中的数据类型主要包括 字符串、数值、布尔值 三种。
- 字符串:单引号双引号都可以,最好根据字符内容选择。
- 数值:不单单是整数,允许任意位小数。
- 布尔值:true 或 false 。
数组
JavaScript 数组声明不必指出数组长度:
var car = Array(5);
var car = Array();
var car = [];
声明数组的同时也可以进行填充(向数组添加元素):
var cars = new Array("Saab","Volvo","BMW");
var cars = ["Saab","Volvo","BMW"];
数组元素类型不必相同,甚至可以是数组或者对象:
var cars = new Array("Saab",2017,true);
var beatles = [];
beatles[0] = cars;
// beatles[0][1] 的值为 2017 。
对象简介
对象的声明使用 Object 关键字:
var car = new Object();
var car = {};
var person = {firstname:"John", lastname:"Doe", id:5566};
对象属性取值赋值方法:
var person = {firstname:"John", lastname:"Doe", id:5566};
person.lastname = "Wang";
var name = person.lastname;
var name = person["lastname"];
算数操作符、条件语句、循环语句
和 Java、C++ 基本一致,不再赘述。
比较操作符不太一样:
== : 表示类型转换后值是否相等;
=== :严格相等,类型必须相同。
函数
函数声明及调用方法:
function myFunction(a, b) {
return a * b;
}
// 调用函数
myFunction(2,5);
变量的作用域
全局变量:可以在脚本的任意位置引用,包括函数内部。
局部变量:在函数内部声明,只在函数内部有效。
对象
对象是自包含的数据集合,包含在对象里的数据可通过 属性(property) 和 方法(method) 访问。
- 属性是隶属于某个特定对象的变量
- 方法是只有某个特定对象才能调用的函数
对象分类:
- 自定义对象:利用 JavaScript 创建的自己的对象;
- 内建对象:JavaScript提供的一系列预先定义好的对象。数组也可以看做是 JavaScript 的内建对象的一种。常见的还有Data对象;
- 宿主对象:由浏览器提供的预定义对象。常见的有 windows,document 等。
第3章 DOM
DOM 三个字母的具体含义:
D 是基础,没有文档(Document)DOM 也就无从谈起;
O 是对象(Object),JavaScript 本身就可以看做是由对象构成的语言,其重要性不言而喻;
M 是模型(Model),其含义是某种事物的表现形式。
具体的说 DOM 把文档表示成了一颗家谱树(DOM 使用 parent、child,sibling 等记号来表明家庭成员之间的关系。
节点
DOM 由许多不同的节点(node)组成,节点可分为三类:
元素节点:DOM 的原子是元素节点,可以包含其他元素。
文本节点:元素节点的内容。
属性节点:元素节点的描述。
<p title="Paragraph">This is a paragraph.</p>
/**
* 元素节点:p
* 文本节点:This is a paragraph.
* 属性节点:title="Paragraph"
*/
获取元素
有三种方法可以获取元素节点,分别通过 id、标签名、class 。
// document 特有函数,返回一个元素
var x = document.getElementById("intro");
// getElementsByTagName、getElementsByClassName 返回元素数组
var y = document.getElementsByTagName("p");
var y = x.getElementsByTagName("p");
// 允许使用通配符
var y = x.getElementsByTagName("*");
var z = document.getElementsByClassName("intro");
var z = x.getElementsByClassName("intro");
// 允许查找带有多个类名的元素,并且类名顺序不重要
var z = x.getElementsByClassName("import intro");
获取和设置属性
获取和设置属性的方法如下:
getAttribute():该方法只能通过元素节点对象调用;
setAttribute():该方法允许我们对属性节点的值做出修改。
var img = document.getElementById("image");
img.getAttribute("src");
img.src;
img.setAttribute("src","landscape.jpg");
img.src = "landscape.jpg";
document.getElementById("image").src="landscape.jpg";
通过浏览器查看源代码,其属性并不会改变,也就是说 setAttribute 做出的修改不会反映到文档本身的源码里。这种“表里不一”的的现象源自 DOM 的工作模式:
先加载文档的静态内容,再动态刷新,动态刷新不影响文档的静态内容。
第4章 案例研究:JavaScript图片库
事件处理函数
事件处理函数的作用是,在特定事件发生时调用特定的代码。
<a href="images/fireworks.jpg" title="A fireworks display" onclick="showPic(this); return false;">Fireworks</a>
事件处理函数的工作机制:在给某元素添加事件处理函数后,一旦事件发生相应的 JavaScript 代码就会执行。
被调用的 JavaScript 代码可以返回一个值,这个值将被传递至事件处理函数。
例如:我们给某链接添加一个 onclick 事件处理函数,并把这个事件处理函数所触发的 JavaScript 代码返回 true 或 false 。
这样一来,当这个链接被点击时,JavaScript 代码返回值是 true ,onclick 事件处理函数就认为 这个链接被点击了;反之,认为没有被点击。
所以在 onclick 事件处理函数所触发的 JavaScript 代码里增加一条 return false;
语句,屏蔽掉链接的默认行为。
对函数进行拓展
childNodes 属性
childNodes 属性可以获取任一元素的所有子元素。
var body_element = document.getElementById("body")[0];
// 得到 body 的所有子元素、数组
body_element.childNodes;
nodeType 属性
childNodes 属性返回的数组包括所有类型的节点,而不仅仅是元素节点。
事实上,文档里的每样东西都是一个节点,设置连空格和换行符都被解释为节点。
可通过节点的 nodeType 属性区分不同的节点。
node.nodeType
返回值是一个数字。
nodeType 共有 12 种取值,其中仅 3 种具有实用价值。
元素节点:1.
属性节点:2.
文本节点:2.
nodeValue 属性
若改变 文本节点 的值,可通过 DOM 提供的 nodeValue 属性。
node.nodeValue
注意:对于元素节点而言,element.nodValue
得到的值并不是元素的文本值。
正确的做法的先得到元素节点的文本节点,再取 nodeValue 属性:
element.childNodes[0].nodeValue
firstChild 和 lastChild 属性
firstChild:元素的第一个子元素。
lastChild:元素的最后一个元素。
function showPic(whichPic) {
var source = whichPic.getAttribute("href");//获取资源路径
var placeholder = document.getElementById("placeholder");
placeholder.setAttribute("src",source);
var text = whichPic.getAttribute("title");//获取内容
var description = document.getElementById("description");
description.firstChild.nodeValue = text;
}
第5章 最佳实践
1、在使用任何一句JavaScript代码时,都应该想想,对这个网页是否有用;
2、平稳退化(graceful degradation):如果正确使用了 JavaScript 脚本,可以让访问者在他们的浏览器不支持 JavaScript 的情况下仍能顺利地浏览你网站。
虽然某些功能无法使用,但是最基本的操作仍能顺利完成;
3、渐进加强:用额外的信息层去包裹原始数据;使 CSS 代码负责提供关于“表示”的信息,JavaScript 代码负责提供关于“行为”的信息。
4、分离 JavaScript:在 HTML 文档中使用诸如 onclick 之类的属性也是一种没有效率又容易引发问题的做法。
如果利用像 CSS 中的 class 和 id 属性那样,把JavaScript 代码调用行为与 HTML 文档内容和结构分离,网页就会健壮不少。
5、向后兼容:对象检测:检测浏览器对 JavaScript 的支持程度。
用一个 if 语句的条件表达式看求值结果是 true 还是 false 来采取不同的行动。
如在代码前加上 if(!getElementById) return false
;
6、性能考虑:
尽量少访问DOM和尽量减少标记,不管什么时候只要是查询DOM中的某些元素,浏览器就会搜索整个DOM树,从中查找可能匹配的元素。
在多个函数都会取得一组类似元素的情况下,可以考虑重新构建代码,把搜索结果保存在一个全局变量里,或者把一组元素以参数形式传递给函数。
减少标记数量的目的在于,过多的不必要的元素只会增加DOM树的规模。
7、合并和放置脚本:减少请求数量是在性能优化时首先要考虑的;
把所以的<script>
标签都放在文档的末尾,</body>
标记之前,就可以让页面变得更快。
8、压缩脚本:指的是把脚本文件中的不必要的字节,比如空格和注释,统统删除,从而达到“压缩”文件的目的;
多数情况下应该有两个版本,一个是工作副本,可以修改代码并添加注释,另一个是精简副本,用于放在站点上,通常在精简副本的文件名上加上 min 字样。
第6章 案例研究:图片库改进版
本章主要是第5章内容在 图片库 上的实践。
共享onload事件:
假如有两个函数 firstFunction 和 secondFunction 需要在页面加载时执行:
window.onload = firstFunction;
window.onload = secondFunction;
这样做的话实际上只有后一个可以执行。
window.onload = function()
{
firstFunction();
secondFunction();
}
这样做创建了一个匿名函数,在需要绑定的函数不是很多的场合的确很实用.
最佳解决方案,使用 addLoadEvent 函数。
这个方案需要额外添加一些代码,但一旦有了这些代码,绑定函数到 onload 就很方便。
function addLoadEvent(func){
//将现有的 window.onload 处理函数保存到 oldonload
var oldonload = window.onload;
//如果现有的 window.onload 上没有处理函数,将 func 添加给它
if(typeof oldonload != 'function')
{
window.onload = func;
}
else{
window.onload function()
{
oldload();
func();
}
}
}
第7章 动态创建标记
JavaScript 可以通过创建新元素和修改现有元素改变网页的结构。
传统方法
document.write
document 的 write() 方法可以方便快速地将字符串插入到文档内。
<body>
<script type="text/javascript">
document.write("<p>This is inserted.</p>");
</script>
</body>
缺点就是违背了"行为与表现分离的原则",
即使把这句语句挪到外部,还是需要在<body>
里边添加<script>
标签才可以调用。
innerHTML 属性
innerHTML 属性可以用来读写某给定元素里地 HTML 元素。
<body>
<div id="testdiv"></div>
</body>
window.onload = function(){
var testdiv = document.getElementById("testdiv");
testdiv.innerHTML="<p>I inserted <em>this</em> content.</p>";
}
利用此技术无法区分“插入一段内容”还是“替换一段内容”。
DOM 方法
createElement 方法
var para = document.createElement("p");
创建元素节点,只创建会出现一个文档碎片(document fragment)。
本身并不影响页面表现,它是游荡在JavaScript世界里的一个孤儿。
但是它已经有 nodeType 和 nodeName 属性。
appendChild 方法
把新创建的节点插入文档的节点树最简单方法是:让其成为某个现有节点的一个子节点。
var para = document.createElement("p");
var testdiv = document.getElementById("testdiv");
testdiv.appendChild(para);
createTextNode 方法
创建文本节点填充元素节点的内容。
把文本节点插入为元素节点的子节点。
var para = document.createElement("p");
var testdiv = document.getElementById("testdiv");
testdiv.appendChild(para);
var txt = document.createTextNode("Hello World");
para.appendChild(txt);
改变顺序,二者结果相同。
var para = document.createElement("p");
var txt = document.createTextNode("Hello World");
para.appendChild(txt);
var testdiv = document.getElementById("testdiv");
testdiv.appendChild(para);
重回图片库
在已有元素前插入元素
DOM 提供了 insertBefore() 方法,把一个元素插入到现有元素之前。
parentElement.insertBefore(newElement,targetElement);
其中:
parentElement:目标元素的父元素,
newElement:想插入的元素,
targetElement:想插入哪个元素之前。
var gallery = document.getElementById("imagegallery");
gallery.parentNode.insertBefore(placeholder,gallery);
在已有元素后插入元素
DOM 并没有提供了 insertAfter() 方法,下面编写:
function insertAfter (newElement,targetElement){
var parent = targetElement.parentNode;
if(parent.lastChild == targetElement){
parent.appendChild(newElement);
}else{
parent.insertBefore(newElement,targetElement.nextSibling);
}
}
var gallery = document.getElementById("imagegallery");
insertAfter(placeholder,gallery);
Ajax
Ajax 可以做到只更新页面中的一小部分,其它内容不用重新加载。
Ajax 的主要优势是对页面的请求以异步方式发送到服务器。
XMLHttpRequest 对象
Ajax 的核心是 XMLHttpRequest 对象,XMLHttpRequest 充当浏览器脚本与服务器之间的中间人的角色。
JavaScript 可以通过这个对象自己发送请求,同时自己处理响应。
function getnewContent (){
var request = new XMLHttpRequest();
if(request){
request.open("GET","ajax_info.txt",true);
request.onreadystatechange = function(){
if(request.readyState == 4){
var para = document.createElement("p");
var txt = document.createTextNode(request.responseText);
para.appendChild(txt);
var testdiv = document.getElementById("testdiv");
testdiv.appendChild(para);
}
};
request.send(null);
}
}
其中 readyState 属性的值,有5个可能值:
0 表示未初始化
1 表示正在加载
2 表示加载完毕
3 表示正在交互
4 表示完成
访问服务器返回的数据要通过两个属性完成。
responseText:保存文本字符串形式的数据。
responseXML:保存 Content-Type 头部指定为 "text/xml" 的数据。
注意 异步请求,脚本在发送 XMLHttpRequest 请求之后仍然会继续执行,不会等待响应返回。
HIjax
HIjax 意思是渐进增强地使用 Ajax 。
第8章 充实文档内容
JavaScript 脚本只应该用来充实文档内容,要避免使用 DOM 技术来创建核心内容。
function displayAbbreviations() {
if (!document.getElementsByTagName || !document.createElement || !document.createTextNode) return false;
// 得到所有链接
var abbreviations = document.getElementsByTagName("abbr");
if (abbreviations.length < 1) return false;
var defs = new Array();
// 遍历链接
for (var i=0; i<abbreviations.length; i++) {
var current_abbr = abbreviations[i];
if (current_abbr.childNodes.length < 1) continue;
var definition = current_abbr.getAttribute("title");
var key = current_abbr.lastChild.nodeValue;
defs[key] = definition;
}
// 创建列表
var dlist = document.createElement("dl");
// 遍历访问键
for (key in defs) {
var definition = defs[key];
var dtitle = document.createElement("dt");
var dtitle_text = document.createTextNode(key);
dtitle.appendChild(dtitle_text);
var ddesc = document.createElement("dd");
var ddesc_text = document.createTextNode(definition);
ddesc.appendChild(ddesc_text);
// 添加列表项到列表中
dlist.appendChild(dtitle);
dlist.appendChild(ddesc);
}
if (dlist.childNodes.length < 1) return false;
// 创建标题
var header = document.createElement("h2");
var header_text = document.createTextNode("Abbreviations");
header.appendChild(header_text);
// 把标题添加到页面主体
document.body.appendChild(header);
// 把列表添加到页面主体
document.body.appendChild(dlist);
}
addLoadEvent(displayAbbreviations);
我的理解就是把原本 HTML 的内容通过 JavaScript 操作提取出来摘要,再加到 HTML 中充实文档内容。
第9章 CSS-DOM
我们在浏览器看到的网页其实有三部分构成:
- 结构层(structural layer) 由 XHTML 或者 HTML 等标记语言创建。
- 表示层(presentation layer) 由 CSS 负责创建。
- 行为层(behavior layer) 负责内容应该如何响应事件这一动作,这主要是由 javaScript 和 DOM 负责。
style 属性
在文档中每个人元素都是一个对象,每个元素都有一个 style 属性,他们也是一个对象。
获取样式
element.style.color;
// 中间带连字符的 CSS 属性要使用驼峰写法
element.style.fontFamily;
获取样式属性的返回值与设置值采用同样的单位。
如我们在 CSS font-size 属性时以 em 为单位,相应的 DOM fontSize 属性也以 em 为单位。
注意 通过 style 获取属性的局限性,即只能返回 内联样式。
<p style="color:sienna;margin-left:20px">这是一个段落。</p>
通过 link 元素引入的 CSS 文件样式不能用 DOM style 属性检索出来。
<link rel="stylesheet" type="text/css" href="mystyle.css">
通过 <head>
部分引入的 <style>
标签里也不能用 DOM style 属性检索出来。
<head>
<style>
hr {color:sienna;}
p {margin-left:20px;}
</style>
</head>
设置样式
style 对象的各个属性都是可读写的,可以通过元素的 style 属性获取样式,也可以通过它更新样式。
element.style.property = value;
// 例如:
para.style.color = "black";
何时该用 DOM 脚本设置样式
在绝大多数场合还是应该使用 CSS 声明样式。
在使用 CSS 不方便的场合,可以利用 DOM 对文档的样式做一些小的增强。
通过 CSS 设置样式方式:
// 1.通过标签元素
p {
font-size: 1em;
}
// 2.通过class属性
.fineorint {
font-size: 1em;
}
// 3.通过id属性
#fineorint {
font-size: 1em;
}
通过 DOM 脚本设置样式的情况:
- 根据元素在节点数的位置来设置样式
- 根据某种条件反复设置某种样式
- 响应事件,即事件发生时设置有关元素的样式
className 属性
前面一直在使用 DOM 直接设置或者修改元素的样式,这种让行为层干表示层的活,并不是理想的工作方式。
与其使用 DOM 直接改变某个元素的样式,倒不如通过 JavaScript 代码修改元素的 class 属性。
通过修改 DOM 直接改变元素样式:
function styleHeaderSiblings() {
if (!document.getElementsByTagName) return false;
var headers = document.getElementsByTagName("h1");
for (var i=0; i<headers.length; i++) {
var elem = getNextElement(headers[i].nextSibling);
elem.style.fontWeight = "bold";
elem.style.fontSize = "1.2em";
}
}
通过修改 class 属性:
function styleHeaderSiblings() {
if (!document.getElementsByTagName) return false;
var headers = document.getElementsByTagName("h1");
for (var i=0; i<headers.length; i++) {
var elem = getNextElement(headers[i].nextSibling);
elem.setAttribute("class","intro");
}
}
当然需要提前引入 CSS 样式:
.intro {
font-weight: bold;
font-size: 1.2em;
}
更简单的是通过 className 属性。
className 属性是一个可读/可写的属性,凡是元素节点都有这个属性。
获得元素的 class 属性:
element.className
修改元素的 class 属性:
element.className = value
通过 className 属性修改样式:
function styleHeaderSiblings() {
if (!document.getElementsByTagName) return false;
var headers = document.getElementsByTagName("h1");
for (var i=0; i<headers.length; i++) {
var elem = getNextElement(headers[i].nextSibling);
elem.className = "intro";
}
}
缺点:通过 className 属性设置元素的 class 属性将替换原有 class 设置。
可以通过 字符串拼接 解决(注意 intro 前的空格):
elem.className += " intro";
第10章 用JavaScript实现动画效果
JavaScript 能够按照预定的时间间隔重复调用一个函数,而这意味着我们可以随着时间的推移而不断地改变某个元素地样式。
位置
位置通常是由 CSS 负责设置的:
element {
position: absolute;
top: 50px;
left: 100px;
}
- static: 默认值。没有定位,元素出现在正常的流中(忽略 top, bottom, left, right 或者 z-index 声明)。
- relative: 生成相对定位的元素,相对于其正常位置进行定位。因此,"left:20" 会向元素的 LEFT 位置添加 20 像素。
- absolute: 生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位。元素的位置通过 "left", "top", "right" 以及 "bottom" 属性进行规定。
- fixed: 生成绝对定位的元素,相对于浏览器窗口进行定位。元素的位置通过 "left", "top", "right" 以及 "bottom" 属性进行规定。
- inherit: 规定应该从父元素继承 position 属性的值。
时间
JavaScript 函数 setTimeout 能让某个函数在经过一段预定的时间后才开始执行。
variable = setTimeout("function",interval);
第一个参数为函数名字,第二个参数为间隔时间。若想取消执行:
clearTimeout(variable)
本章主要是定义的一个位置随时间运动的函数:
function moveElement(elementID,final_x,final_y,interval) {
if (!document.getElementById) return false;
if (!document.getElementById(elementID)) return false;
var elem = document.getElementById(elementID);
var xpos = parseInt(elem.style.left);
var ypos = parseInt(elem.style.top);
if (xpos == final_x && ypos == final_y) {
return true;
}
if (xpos < final_x) {
xpos++;
}
if (xpos > final_x) {
xpos--;
}
if (ypos < final_y) {
ypos++;
}
if (ypos > final_y) {
ypos--;
}
elem.style.left = xpos + "px";
elem.style.top = ypos + "px";
var repeat = "moveElement('"+elementID+"',"+final_x+","+final_y+","+interval+")";
movement = setTimeout(repeat,interval);
}
第11章 HTML5
本章介绍了 HTML5 的新特性,包括 <canvas>
、 <audio>
、 <video>
元素及新的表单控件。
第12章 综合示例
结合前面的知识实现了一个综合示例,对前面的知识进行了很好的回顾。
这本书看下来,感受最大的是 HTML 和 CSS 基础太差,JavaScript 部分还好。
原来计划上个周末看完呢,中间又催文档啥的,这个周末(2017.10.29)才看完。
评论 (0)