《JavaScript DOM编程艺术》读书笔记

《JavaScript DOM编程艺术》读书笔记

JavaScript简史

DOM可以给文档增加交互能力。

ECMAScript对javascript进行了标准化。

JavaScript为了蹭当时红极一时的Java的流量故而叫做javascript。

DOM是文档对象模型,它是一种API,程序和脚本可以通过这个接口动态地访问和修改文档的内容、结构和样式。

JavaScript语法

使用js的两种方式

  1. 将js代码放入<head>标签中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
// code
</script>
</head>
<body>

</body>
</html>
  1. 将js代码放入一个单独的js文件中,在html中用<script>标签引入。
1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/main.js"></script>
</head>
<body>

</body>
</html>
  1. 将2中的<script>放入</body>之前,以快速加载网页。
1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>


<script src="js/main.js"></script>
</body>
</html>

<script>标签中没必要写type="text/javascript"属性

语法

注释

1
2
3
4
5
// 单行注释

/*
多行注释
*/

变量

赋值:mood = 'happy';

声明:var mood;

声明并赋值:var mood = 'happy';

一次声明并赋值多个变量:var mood = 'happy', age = 33;

变量名命名:字母、数字、美元符号、下划线,第一个字符不允许为数字,可以适当在变量名中间插入下划线一边阅读或者使用驼峰式。但是通常命名函数名、方法名、对象属性名用驼峰式。

js是弱类型语言

数据类型

  1. 字符串
  2. 数值
  3. 布尔值
  4. 数组
  5. 对象

条件语句

if语句

1
2
3
if () {

}
1
2
3
4
5
if () {

} else {

}
1
2
3
4
5
6
7
if () {

} else if () {

} else {

}

比较操作符

1
2
3
4
5
6
7
8
9
>
<
>=
<=

==
===
!=
!==

使用===!==

逻辑操作符

1
2
3
&&
||
!

循环

while

1
2
3
while () {

}

do…while

至少执行一次

1
2
3
do {

} while ();

for

1
2
3
for (var i = 0; i < 10; i++) {

}

函数

1
2
3
function f() {

}

调用

1
f();

例子1:将两个数相乘并返回结果

1
2
3
4
function multiply(num1,num2) {
var total = num1 * num2;
return total;
}

例子2:华氏摄氏度转换

1
2
3
4
5
function convertToCelsius(temp) {
var result = temp -32;
result = result /1.8;
return result;
}

命名变量名用下划线分割单词,命名函数用驼峰式

1
2
3
4
var crew_name = 'wang';
function getCrewsInfo() {

}

变量作用域

全局变量声明在js脚本中,作用域是整个js文件。

局部变量,也就是声明在函数内部,作用域就是这个函数内部。

1
2
3
4
5
6
7
8
function square(num) {
total = num * num;
return total;
}
var total = 50;
var number = square(20);
alert(number);
// 400

需要用var声明局部变量:

1
2
3
4
function square(num) {
var total = num * num;
return total;
}

注意,函数内部变量一定要用var声明以避免二义性。

对象

对象是键值对

1
2
3
{
name: value;
}

比如Person对象的属性和方法的调用:

1
2
Person.age;
Person.eat();

创建一个Person实例:

1
2
3
var mason = new Person;
mason.age;
mason.mood;

内置对象,比如Array对象

1
2
var arr = new Array(5);
arr.length;

还有宿主对象,比如浏览器提供的对象。

DOM

D:文档

O:对象

M:模型

文档中的每一个节点都是一个对象。

DOM就是将网页文档转化为一棵以<html>为根的树。

DOM由节点构成,有些节点可以包含其他的节点,节点有三种:元素节点,文本节点,属性节点。

元素节点可以包含其他节点。

文本节点总是被元素节点包含。

属性节点总是被元素节点包含。

例子:

1
<p title="p_title">标题</p>

DOM:元素节点p里面包含了一个title属性子节点和一个“标题”文本子节点。

获取元素

getElementById

getElementsByTagName

取得文档所有元素节点:

1
var nodes = getElementsByTagName('*');

以上的DOM方法还可以在任意一个节点对象上调用:

1
2
var temp1 = document.getElementById('header');
var temp2 = temp1.getElementsByTagName('div');

getElementsByClassName

获取带有多个类名的元素:

1
document.getElementsByClassName('div p');

通过以上DOM方法取得节点后赋值给变量再进行其他操作。

获取设置属性

获取元素后可以获取其属性和设置其属性。

getAttribute

只能在元素节点上调用

1
2
3
4
5
var temp = getElementById('header');
var t = temp.getAttribute('title');
if (t) {
alert(t);
}

setAttribute

1
2
var temp = getElementById('header');
temp.setAttribute('title','这里是标题');

案例:javascript图片库

图库demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片库</title>
<style>
body {
padding: 0;
margin: 0;
}
</style>
<script>
// 参数是一个a元素
function showPic(whichpic) {
var placeholder = document.getElementById('placeholder');
var source = whichpic.getAttribute('href');
placeholder.setAttribute('src',source);
var text = whichpic.getAttribute('title');
var description = document.getElementById('description');
description.firstChild.nodeValue = text;
}
function countBodyChildren() {
var body_element = document.getElementsByTagName('body')[0];
console.log(body_element.childNodes.length);
}
window.onload = countBodyChildren;
</script>
</head>

<body>
<h1>snapshots</h1>
<ol>
<li><a title="title1" href="img/1.jpg" onclick="showPic(this);return false;">img 1</a></li>
<li><a title="title2" href="img/2.jpg" onclick="showPic(this);return false;">img 2</a></li>
<li><a title="title3" href="img/3.jpg" onclick="showPic(this);return false;">img 3</a></li>
<li><a title="title4" href="img/4.jpg" onclick="showPic(this);return false;">img 4</a></li>
</ol>
<img id="placeholder" src="img/placeholder.jpg" alt="" height="666px">
<p id="description">标题</p>
</body>

</html>

nodeType

元素节点的nodeType为1

属性节点的nodeType为2

文本节点的nodeType为3

这里发现了vscode的go live插件不能正确显示改变后的title,垃圾

最佳实践

平稳退化

1
window.open(url,name,features);
1
window.open('https://www.baidu.com','baidu','width: 400,height: 150');

伪协议

1
2
3
function pop(website){
window.open(website,'baidu','width: 400,height: 150');
}
1
<a href="javascript:pop('https://baidu.com');">link</a>

尽量不用伪协议调用js代码。

window.onload = func;

向后兼容

对象检测:

1
2
3
if (document.getElementById) {

}

兼容:

1
2
3
4
window.onload = function() {
if (!document.getElementById) return false;
// ...
}

性能考虑

  1. 尽量少访问DOM,尽量减少标记
  2. 合并脚本,并放在</body>之前
  3. 压缩脚本

案例:图片库改进版

1
2
3
window.onload = function() {

}
1
2
3
4
5
6
7
8
9
10
11
function addloadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
oldonload();
func();
}
}
}

现在再添加函数只需要:addloadEvent(firstFunc)

onkeypress

动态创建标记

传统技术:document.write innerHTML

DOM方法:createElement createTextNode appendChild insertBefore

document.body

parentNode

firstChild

lastChild

insertBefore

nextSibling

编写insertAfter函数:

1
2
3
4
5
6
7
8
function insertAfter(newElement,targetElement) {
var parent = targetElement.parentNode;
if (parent.lastChild == targetElement) {
parent.appendChild(newElement);
} else {
parent.insertBefore(newElement,targetElement.nextSibling);
}
}

Ajax

局部更新页面而不同刷新

XMLHttpRequest对象

创建对象:

ie:

1
var request = new ActiveXObject('Msxml2.XMLHTTP.3.0');

其他浏览器:

1
var request = new XMLHttpRequest();

编写getHTTPObject.js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
function getHTTPObject() {
if (typeof XMLHttpRequest == 'undefined')
XMLHttpRequest = function () {
try {return new ActiveXObject("Msxml2.XMLHTTP.6.0");}
catch (e) {}
try {return new ActiveXObject("Msxml2.XMLHTTP.3.0");}
catch (e) {}
try {return new ActiveXObject("Msxml2.XMLHTTP");}
catch (e) {}
return false;
}
return new XMLHttpRequest();
}

var request = new getHTTPObject();返回一个XMLHttpRequest对象。

XMLHttpRequest对象的方法:

open

GET,POST,SEND

是否异步

编写getNewContent.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function getNewContent() {
var request = getHTTPObject();
if (request) {
request.open('GET','example.txt',true);
request.onreadystatechange = function() {
if (request.readyState == 4) {
var para = document.createElement('p');
var txt = document.createTextNode(request.responseText);
para.appendChild(txt);
document.getElementById('new').appendChild(para);
}
};
request.send(null);
} else {
alert('sorry,your browser dont support XMLHttpRequest');
}
}
addLoadEvent(getNewContent);

充实文档的内容

CSS-DOM

element.style是一个对象

element.style.property引用节点属性,如果属性名是用连字符的则改写为驼峰式。

但是以上只能返回行内样式,不能用来操作外部样式。

通过用DOM去修改元素的className来达到切换样式的目的。

1
element.className = value;

addClass函数:

1
2
3
4
5
6
7
8
function addClass(element,value) {
if (!element.className) {
element.className = value;
} else {
newClassName = element.className + ' ' + value;
element.className = newClassName;
}
}

用js实现动画效果

var variable = setTimeout(func,delay);

clearTimeout(variable);

parseInt()

parseFloat()

html5

canvas

<video>

controls

addEventListener

综合示例

js库

jQuery

prototype

etc

常用代码

addClass

addClass()用于为节点对象添加新类名。

参数:元素节点,新类名。

1
2
3
4
5
6
7
8
9
10
function addClass(element,value) {
if (!element.className) {
element.className = value;
} else {
newClassName = element.className;
newClassName += ' ';
newClassName += value;
element.className = newClassName;
}
}

insertAfter

insertAfter()用于将新节点插到目标节点后面。

参数:新节点,目标节点。

1
2
3
4
5
6
7
8
function insertAfter(newElement,targetElement) {
var parent = targetElement.parentNode;
if (parent.lastChild == targetElement) {
parent.appendChild(newElement);
} else {
parent.insertBefore(newElement,targetElement.nextSibling);
}
}

addLoadEvent

用于添加页面onload后立刻执行的函数。

参数为需要立刻执行的函数。

1
2
3
4
5
6
7
8
9
10
11
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
oldonload();
func();
}
}
}

调用:

1
addLeadEvent(func);

评论