Web/04-JavaScript基础/34-DOM简介和DOM操作.md
2021-05-24 12:43:12 +08:00

653 lines
19 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 常见概念
### JavaScript的组成
JavaScript基础分为三个部分
- ECMAScriptJavaScript的语法标准包括变量表达式运算符函数if语句for语句等
- **DOM**文档对象模型Document object Model操作**网页上的元素**的API比如让盒子移动变色轮播图等
- **BOM**浏览器对象模型Browser Object Model操作**浏览器部分功能**的API比如让浏览器自动滚动
### 节点
**节点**Node构成 HTML 网页的最基本单元网页中的每一个部分都可以称为是一个节点比如html标签属性文本注释整个文档等都是一个节点
虽然都是节点但是实际上他们的具体类型是不同的常见节点分为四类
- 文档节点文档整个 HTML 文档整个 HTML 文档就是一个文档节点
- 元素节点标签HTML标签
- 属性节点属性元素的属性
- 文本节点文本HTML标签中的文本内容包括标签之间的空格换行
节点的类型不同属性和方法也都不尽相同所有的节点都是Object
### 什么是DOM
**DOM**Document Object Model文档对象模型DOM 为文档提供了结构化表示并定义了如何通过脚本来访问文档结构目的其实就是为了能让js操作html元素而制定的一个规范
DOM就是由节点组成的
**解析过程**
HTML加载完毕渲染引擎会在内存中把HTML文档生成一个DOM树getElementById是获取内中DOM上的元素节点然后操作的时候修改的是该元素的**属性**
**DOM树**一切都是节点
DOM的数据结构如下
![](http://img.smyhvae.com/20180126_2105.png)
上图可知**在HTML当中一切都是节点**非常重要节点的分类在上一段中已经讲了
整个html文档就是一个文档节点所有的节点都是Object
### DOM可以做什么
- 找对象元素节点
- 设置元素的属性值
- 设置元素的样式
- 动态创建和删除元素
- 事件的触发响应事件源事件事件的驱动程序
## 元素节点的获取
DOM节点的获取方式其实就是**获取事件源的方式**关于事件上一篇文章中已经讲到了
想要操作元素节点必须首先要找到该节点有三种方式可以获取DOM节点
```javascript
var div1 = document.getElementById("box1"); //方式一:通过 id 获取 一个 元素节点(为什么是一个呢?因为 id 是唯一的)
var arr1 = document.getElementsByTagName("div"); //方式二:通过 标签名 获取 元素节点数组所以有s
var arr2 = document.getElementsByClassName("hehe"); //方式三:通过 类名 获取 元素节点数组所以有s
```
既然方式二方式三获取的是标签数组那么习惯性是**先遍历之后再使用**
特殊情况数组中的值只有1个即便如此这一个值也是包在数组里的这个值的获取方式如下
```javascript
document.getElementsByTagName("div1")[0]; //取数组中的第一个元素
document.getElementsByClassName("hehe")[0]; //取数组中的第一个元素
```
## DOM访问关系的获取
DOM的节点并不是孤立的因此可以通过DOM节点之间的相对关系对它们进行访问如下
![](http://img.smyhvae.com/20180126_2140.png)
节点的访问关系是以**属性**的方式存在的
JS中的**父子兄**访问关系
![](http://img.smyhvae.com/20180126_2145.png)
这里我们要重点知道**parentNode****children**这两个属性的用法下面分别介绍
### 获取父节点
调用者就是节点一个节点只有一个父节点调用方式就是
```javascript
节点.parentNode
```
### 获取兄弟节点
**1下一个节点 | 下一个元素节点**
> Sibling的中文是**兄弟**
1nextSibling
- 火狐谷歌IE9+版本都指的是下一个节点包括标签空文档和换行节点
- IE678版本指下一个元素节点标签
2nextElementSibling
- 火狐谷歌IE9+版本都指的是下一个元素节点标签
**总结**为了获取下一个**元素节点**我们可以这样做在IE678中用nextSibling在火狐谷歌IE9+以后用nextElementSibling于是综合这两个属性可以这样写
```javascript
下一个兄弟节点 = 节点.nextElementSibling || 节点.nextSibling
```
**2前一个节点 | 前一个元素节点**
> previous的中文是前一个
1previousSibling
- 火狐谷歌IE9+版本都指的是前一个节点包括标签空文档和换行节点
- IE678版本指前一个元素节点标签
2previousElementSibling
- 火狐谷歌IE9+版本都指的是前一个元素节点标签
**总结**为了获取前一个**元素节点**我们可以这样做在IE678中用previousSibling在火狐谷歌IE9+以后用previousElementSibling于是综合这两个属性可以这样写
```javascript
前一个兄弟节点 = 节点.previousElementSibling || 节点.previousSibling
```
**3补充**获得任意一个兄弟节点
```javascript
节点自己.parentNode.children[index]; //随意得到兄弟节点
```
### 获取单个的子节点
**1第一个子节点 | 第一个子元素节点**
1firstChild
- 火狐谷歌IE9+版本都指的是第一个子节点包括标签空文档和换行节点
- IE678版本指第一个子元素节点标签
2firstElementChild
- 火狐谷歌IE9+版本都指的是第一个子元素节点标签
**总结**为了获取第一个**子元素节点**我们可以这样做在IE678中用firstChild在火狐谷歌IE9+以后用firstElementChild于是综合这两个属性可以这样写
```javascript
第一个子元素节点 = 节点.firstElementChild || 节点.firstChild
```
**2最后一个子节点 | 最后一个子元素节点**
1lastChild
- 火狐谷歌IE9+版本都指的是最后一个子节点包括标签空文档和换行节点
- IE678版本指最后一个子元素节点标签
2lastElementChild
- 火狐谷歌IE9+版本都指的是最后一个子元素节点标签
**总结**为了获取最后一个**子元素节点**我们可以这样做在IE678中用lastChild在火狐谷歌IE9+以后用lastElementChild于是综合这两个属性可以这样写
```javascript
最后一个子元素节点 = 节点.lastElementChild || 节点.lastChild
```
### 获取所有的子节点
1**childNodes**标准属性返回的是指定元素的**子节点**的集合包括元素节点所有属性文本节点是W3C的亲儿子
- 火狐 谷歌等高本版会把换行也看做是子节点
用法
```javascript
子节点数组 = 父节点.childNodes; //获取所有节点。
```
2**children**非标准属性返回的是指定元素的**子元素节点**的集合重要
- 它只返回HTML节点甚至不返回文本节点
- 在IE6/7/8中包含注释节点在IE678中注释节点不要写在里面
虽然不是标准的DOM属性但它和innerHTML方法一样得到了几乎所有浏览器的支持
用法**用的最多**
```javascript
子节点数组 = 父节点.children; //获取所有节点。用的最多。
```
## DOM节点的操作重要
上一段的内容节点的**访问关系**都是**属性**
本段的内容节点的**操作**都是**函数**方法
### 创建节点
格式如下
```javascript
新的标签(元素节点) = document.createElement("标签名");
```
比如如果我们想创建一个li标签或者是创建一个不存在的adbc标签可以这样做
```html
<script type="text/javascript">
var a1 = document.createElement("li"); //创建一个li标签
var a2 = document.createElement("adbc"); //创建一个不存在的标签
console.log(a1);
console.log(a2);
console.log(typeof a1);
console.log(typeof a2);
</script>
```
打印结果
![](http://img.smyhvae.com/20180127_1135.png)
### 插入节点
插入节点有两种方式它们的含义是不同的
方式1
```javascript
父节点.appendChild(新的子节点);
```
解释父节点的最后插入一个新的子节点
方式2
```javascript
父节点.insertBefore(新的子节点,作为参考的子节点)
```
解释
- 在参考节点前插入一个新的节点
- 如果参考节点为null那么他将在父节点里面的最后插入一个子节点
![](http://img.smyhvae.com/20180127_1257.png)
我们可以看到li标签确实被插入到了box1标签的里面和box2并列了
方式2的举例
![](http://img.smyhvae.com/20180127_1302.png)
我们可以看到b1标签被插入到了box1标签的里面和a1标签并列在a1标签的前面
**特别强调**
关于方式1的appendChild方法这里要强调一下比如现在有下面这样一个div结构
```html
<div class="box11">
<div class="box12">生命壹号</div>
</div>
<div class="box21">
<div class="box22">永不止步</div>
</div>
```
上方结构中子盒子box12是在父亲box11里的子盒子box22是在父亲box21里面的现在如果我调用方法`box11.appendChild(box22)`**最后产生的结果是box22会跑到box11中**也就是说box22不在box21里面了这是一个很神奇的事情
![](http://img.smyhvae.com/20180129_2125.png)
### 删除节点
格式如下
```javascript
父节点.removeChild(子节点);
```
解释**用父节点删除子节点**必须要指定是删除哪个子节点
如果我想删除自己这个节点可以这么做
```javascript
node1.parentNode.removeChild(node1);
```
### 复制节点克隆节点
格式如下
```javascript
要复制的节点.cloneNode(); //括号里不带参数和带参数false效果是一样的。
要复制的节点.cloneNode(true);
```
括号里带不带参数效果是不同的解释如下
- 不带参数/带参数false只复制节点本身不复制子节点
- 带参数true既复制节点本身也复制其所有的子节点
## 设置节点的属性
我们可以获取节点的属性值设置节点的属性值删除节点的属性
我们就统一拿下面这个标签来举例
```html
<img src="images/1.jpg" class="image-box" title="美女图片" alt="地铁一瞥" id="a1">
```
下面分别介绍
### 1获取节点的属性值
**方式1**
```javascript
元素节点.属性名;
元素节点[属性名];
```
举例获取节点的属性值
```html
<body>
<img src="images/1.jpg" class="image-box" title="美女图片" alt="地铁一瞥" id="a1">
<script type="text/javascript">
var myNode = document.getElementsByTagName("img")[0];
console.log(myNode.src);
console.log(myNode.className); //注意是className不是class
console.log(myNode.title);
console.log("------------");
console.log(myNode["src"]);
console.log(myNode["className"]); //注意是className不是class
console.log(myNode["title"]);
</script>
</body>
```
上方代码中的img标签有各种属性我们可以逐一获取打印结果如下
![](http://img.smyhvae.com/20180127_1340.png)
**方式2**
```javascript
元素节点.getAttribute("属性名称");
```
举例
```javascript
console.log(myNode.getAttribute("src"));
console.log(myNode.getAttribute("class")); //注意是class不是className
console.log(myNode.getAttribute("title"));
```
打印结果
![](http://img.smyhvae.com/20180127_1345.png)
方式1和方式2的区别在于前者是直接操作标签后者是把标签作为DOM节点推荐方式2
### 2设置节点的属性值
方式1举例设置节点的属性值
```javascript
myNode.src = "images/2.jpg" //修改src的属性值
myNode.className = "image2-box"; //修改class的name
```
方式2
```javascript
元素节点.setAttribute("属性名", "新的属性值");
```
方式2举例设置节点的属性值
```javascript
myNode.setAttribute("src","images/3.jpg");
myNode.setAttribute("class","image3-box");
myNode.setAttribute("id","你好");
```
### 3删除节点的属性
格式
```javascript
元素节点.removeAttribute(属性名);
```
举例删除节点的属性
```javascript
myNode.removeAttribute("class");
myNode.removeAttribute("id");
```
### 总结
获取节点的属性值和设置节点的属性值都有两种方式
**如果是节点的原始属性**比如 普通标签的`class/className`属性普通标签的`style`属性普通标签的 title属性img 标签的`src`属性超链接的`href`属性等**方式1和方式2是等价的**可以混用怎么理解混用呢比如说 `div.title = '我是标题'`设置属性 `div.getAttribute('title')`获取属性就是混用
但如果是节点的非原始属性比如
```javascript
div.aaa = 'qianguyihao';
div.setAttribute('bbb', 'qianguyihao');
```
上面的这个非原始属性在使用这两种方式时是有区别的区别如下
- 方式1 `元素节点.属性``元素节点[属性]`绑定的属性值不会出现在标签上
- 方式2 `get/set/removeAttribut`绑定的属性值会出现在标签上
- **这两种方式不能交换使用**get值和set值必须使用同一种方法
举例
```html
<body>
<div id="box" title="主体" class="asdfasdfadsfd">我爱你中国</div>
<script>
var div = document.getElementById("box");
//采用方式一进行set
div.aaaa = "1111";
console.log(div.aaaa); //打印结果1111。可以打印出来但是不会出现在标签上
//采用方式二进行set
div.setAttribute("bbbb","2222"); //bbbb作为新增的属性会出现在标签上
console.log(div.getAttribute("aaaa")); //打印结果null。因为方式一的set无法采用方式二进行get。
console.log(div.bbbb); //打印结果undefined。因为方式二的set无法采用方式一进行get。
</script>
</body>
```
## DOM对象的属性-补充
### innerHTML和innerText的区别
- value标签的value属性
- **innerHTML**双闭合标签里面的内容包含标签
- **innerText**双闭合标签里面的内容不包含标签老版本的火狐用textContent
**获取内容举例**
如果我们想获取innerHTML和innerText里的内容看看会如何innerHTML会获取到标签本身而innerText则不会
![](http://img.smyhvae.com/20180127_1652.png)
**修改内容举例**innerHTML会修改标签本身而innerText则不会
![](http://img.smyhvae.com/20180127_1657.png)
### nodeType属性
这里讲一下nodeType属性
- **nodeType == 1 表示的是元素节点**标签 记住在这里元素就是标签
- nodeType == 2 表示是属性节点
- nodeType == 3 是文本节点
### nodeTypenodeNamenodeValue
我们那下面这个标签来举例
```html
<div id="box" value="111">
生命壹号
</div>
```
上面这个标签就包含了三种节点
- 元素节点标签
- 属性节点
- 文本节点
获取这三个节点的方式如下
```javascript
var element = document.getElementById("box1"); //获取元素节点(标签)
var attribute = element.getAttributeNode("id"); //获取box1的属性节点
var txt = element.firstChild; //获取box1的文本节点
var value = element.getAttribute("id"); //获取id的属性值
console.log(element);
console.log("--------------");
console.log(attribute);
console.log("--------------");
console.log(txt);
console.log("--------------");
console.log(value);
```
打印结果如下
![](http://img.smyhvae.com/20180128_1935.png)
既然这三个都是节点如果我想获取它们的nodeTypenodeNamenodeValue代码如下
```javascript
var element = document.getElementById("box1"); //获取元素节点(标签)
var attribute = element.getAttributeNode("id"); //获取box1的属性节点
var txt = element.firstChild; //获取box1的文本节点
//获取nodeType
console.log(element.nodeType); //1
console.log(attribute.nodeType); //2
console.log(txt.nodeType); //3
console.log("--------------");
//获取nodeName
console.log(element.nodeName); //DIV
console.log(attribute.nodeName); //id
console.log(txt.nodeName); //#text
console.log("--------------");
//获取nodeValue
console.log(element.nodeValue); //null
console.log(attribute.nodeValue); //box1
console.log(txt.nodeValue); //生命壹号
```
打印结果如下
![](http://img.smyhvae.com/20180128_1939.png)
## 文档的加载
浏览器在加载一个页面时是按照自上向下的顺序加载的读取到一行就运行一行如果将script标签写到页面的上边在代码执行时页面还没有加载页面没有加载DOM对象也没有加载会导致无法获取到DOM对象
**onload 事件**
onload 事件会在整个页面加载完成之后才触发 window 绑定一个onload事件该事件对应的响应函数将会在页面加载完成之后执行这样可以确保我们的代码执行时所有的DOM对象已经加载完毕了
代码举例
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title></title>
<script type="text/javascript">
// 【方式一:先加载,后执行】这段 js 代码是写在 <head> 标签里的,所以建议放在 window.onload 里面。
window.onload = function() {
// 获取id为btn的按钮
var btn = document.getElementById("btn");
// 为按钮绑定点击事件
btn.onclick = function() {
alert("hello");
};
};
</script>
</head>
<body>
<button id="btn">点我一下</button>
<script type="text/javascript">
// 【方式二:后加载,后执行】这段 js 代码是写在 <body> 标签里的,代码的位置是处在页面的下方。这么做,也可以确保:在页面加载完毕后,再执行 js 代码。
// 获取id为btn的按钮
var btn = document.getElementById("btn");
// 为按钮绑定点击事件
btn.onclick = function() {
alert("hello");
};
</script>
</body>
</html>
```
上方代码中方式一和方式二均可以确保在页面加载完毕后再执行 js 代码
## 我的公众号
想学习**更多技能**不妨关注我的微信公众号**千古壹号**id`qianguyihao`
扫一扫你将发现另一个全新的世界而这将是一场美丽的意外
![](http://img.smyhvae.com/20190101.png)