Dom

文档对象模型(Document Object Model,简称 DOM),是 W3C 组织推荐的处理可扩展标志语言的标准编程接口 (API)。 (DOM 就是用来处理 HTML 机构)。

节点

加载 HTML 页面时,web 浏览器生成一个树型结构,用来表示页面内部结构,称之为 DOM 树,DOM 将这种树型结构理解为由节点组成。

节点属性

nodeType(节点类型) nodeName(节点名称) nodeValue(节点值)
1(元素节点) 大写的标签名 null
3(文本节点) #text 文本内容
8(注释节点) #comment 注释内容
9(文档节点) #document null

获取子节点

Tip:是属性,不是方法。

  • 父亲节点.children; 获取直接元素子节点。注:即 nodeType==1 的元素节点。
  • 父亲节点.childNodes; 获取直接子节点 (和结构相关结构边了这个集合也会改变:比如去掉空格或者换行就相当于去掉了文本节点 text)。
  • 获取子节点,获取到了就是一个类数组集合,获取不到就是空集合。
js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<ul id="ul1">
<li>1</li>
<li>2</li>
<li>
<ul>
<li>3.1</li>
<li>3.2</li>
<li>3.3</li>
</ul>
</li>
<li>4</li>
<li>5</li>
<p></p>
</ul>

var oUl = document.getElementById('ul1');
var olis = oUl.children;
var olis2 = oUl.childNodes;
console.log(olis);
console.log(olis2);

输出

可以通过节点属性获取到想要的节点,比如 P 节点:

js
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
        <ul id="ul1">
<li>1</li>
<li>2</li>
<li>
<ul>
<li>3.1</li>
<li>3.2</li>
<li>3.3</li>
</ul>
</li>
<li>4</li>
<li>5</li>
<p></p>
</ul>
</body>
<script>
var oUl = document.getElementById('ul1');
var olis = Array.from(oUl.children);
var oP="";
olis.forEach(ele=>{
if(ele.nodeName=="P"){
oP=ele;
}
});
console.log(oP); //<p></p>

获取父节点

Tip:是属性,不是方法。

  • parentNode; 获取直接父节点 获取不到是 null。
  • offsetParent; 获取已经定位的父集元素,如果说没有已经定位的父集元素获取到的就是 body。
  • 获取到的还是一个元素节点,所以可以链式操作 =>test2.parentNode.parentNode。
  • 获取到的是一个元素对象(具体的节点),获取不到就是 null。
js
1
2
3
4
5
6
7
8
9
10
11
12
    <div id="test">
<div id="test1">
<div id="test2"></div>
</div>
</div>
var test2 = document.getElementById('test2');
console.log(test2.parentNode);
/*
<div id="test1">
<div id="test2"></div>
</div>
*/

节点的其他获取方式

  • 带 Element 的就是元素节点;不带的就是节点 (基本都是文本节点 #text)
  • firstElementChild: 获取第一个元素子节点(不兼容:IE8 及以下没有这个属性 ->undefined)
  • firstChild:获取第一个子节点
  • lastElementChild:获取最后一个元素子节点(不兼容:IE8 及以下没有这个属性 ->undefined)
  • lastChild:获取最后一个子节点
  • previousElementSibling:获取上一个相邻元素节点(不兼容:IE8 及以下没有这个属性 ->undefined)
  • previousSibling:获取上一个相邻节点
  • nextElementSibling:获取下一个相邻元素节点(不兼容:IE8 及以下没有这个属性 ->undefined)
  • nextSibling:获取下一个相邻节点

节点的操作

节点的操作就包含了创建、添加、插入、替换、移除与克隆;

插入元素的方式如下:(可一下传入多个)

  • node.append (…nodes or strings)—— 在 node 下一级末尾插入节点或字符串;

  • node.prepend (…nodes or strings)—— 在 node 下一级开头插入节点或字符串;

  • node.before (…nodes or strings)—— 在 node 同级前面插入节点或字符串;

  • node.after (…nodes or strings)—— 在 node 同级后面插入节点或字符串;

  • node.replaceWith (…nodes or strings)—— 将 node 替换为给定的节点或字符串;

  • node.remove ()—— 将 node 移除;

  • node.cloneNode (boolean)—— 将 node 克隆;

    • boolean 默认为 false,只会 clone 本身;
    • boolean 设置为 true,深度克隆,子元素也会克隆;
  • 添加:

html
1
2
3
4
<div class="box">
<span>hhaa </span>
<p>hhee</p>
</div>
js
1
2
3
4
5
6
7
8
9
const box = document.getElementsByClassName("box")[0];

// 创建一个DOM对象
const h2El = document.createElement("h2");
h2El.className = "title";
h2El.classList.add("active");
h2El.textContent = "我是标题";

box.append(h2El);

执行结果为:

html
1
2
3
4
5
<div class="box">
<span>hhaa </span>
<p>hhee</p>
<h2 class="title active">我是标题</h2>
</div>

旧的节点操作方法

在很多地方我们也会看到一些旧的操作方法:**(不推荐使用)**

  • parentElem.appendChild (node):在 parentElem 的父元素最后位置添加一个子元素;
  • parentElem.insertBefore (node, nextSibling):在 parentElem 的 nextSibling 前面插入一个子元素;
  • parentElem.replaceChild (node, oldChild):在 parentElem 中,新元素替换之前的 oldChild 元素;
  • parentElem.removeChild (node):在 parentElem 中,移除某一个元素;

操作元素结构上的属性

ps:color 属性是 style 里的呀,所以 style 里面的属性用的是 xxx.style.xxx;别的用的 Attribute;

attribute

  • 标准的 attribute:某些 attribute 属性是标准的,比如 id、class、href、type、value 等;
  • 非标准的 attribute:某些 attribute 属性是自定义的,比如 abc、age、height 等;
html
1
<div id="myDiv" class="mydivClass" age="18" height="188"></div>

对于所有的 attribute 访问都支持如下的方法:

  • 元素对象.hasAttribute (attr);检查是否存在 返回 boolean 值;
  • 元素对象.setAttribute (attr,val); 设置
  • 元素对象. getAttribute (attr);获取
  • 元素对象.removeAttribute (attr);移除

attribute 具备以下特征:

  • 它们的名字是大小写不敏感的(id 与 ID 相同);
  • 它们的值总是字符串类型的;

property

对象。属性的方式;

  • 对于标准的 attribute,会在 DOM 对象上创建与其对应的 property 属性:
  • 对于非标准的 attribute,为 undefined
  • 除非特别情况,大多数情况下,设置、获取 attribute,推荐使用 property 的方式:
html
1
<div id="myDiv" class="mydivClass" age="18" height="188"></div>
js
1
2
3
4
5
const myDiv = document.getElementById('myDiv');
console.log(myDiv.hasAttribute('HEIGHT')); // true(对小大写不敏感)
console.log(myDiv.id); // myDiv
console.log(myDiv.className); // mydivClass (class要用className)
console.log(myDiv.age); // undefined

data-* 自定义属性

HTML5 的 data-* 自定义属性,那么它们也是可以在 dataset 属性中获取到的:

html
1
<div id="myDiv" class="mydivClass" data-age="18" data-height="188"></div>
js
1
2
3
const myDiv = document.getElementById('myDiv');
console.log(myDiv.dataset.age); // 18
console.log(myDiv.dataset.height); //188

JavaScript 动态修改样式

其实用到了 property,通过.style 的方式来修改;(即:标签有标准的 attribute:style);

常规写法

JS 动态修改样式,要是修改的样式很多,用 JS 修改,后期不移维护;

html
1
<div id="myDiv" class="mydivClass" age="18">fsllala</div>
js
1
2
3
4
5
6
7
8
9
10
11
/**
* 点击div动态修改样式
*
* 缺陷:要是修改的样式很多,用JS修改,后期不移维护;
*/
const myDiv = document.getElementById('myDiv');
myDiv.onclick = () => {
myDiv.style.color = "red";
myDiv.style.fontSize = "24px";
myDiv.style.background = "orange";
}

className

通过 className 来动态修改样式,它会替换整个 class 中的字符串;

元素的 class attribute,对应的 property 并非叫 class,而是 className;

这是因为 JavaScript 早期是不允许使用 class 这种关键字来作为对象的属性,所以 DOM 规范使用了 className;

html
1
<div id="myDiv" class="mydivClass" age="18">fsllala</div>
css
1
2
3
4
5
.active {
color: red;
background-color: antiquewhite;
font-size: 24px;
}
js
1
2
3
4
const myDiv = document.getElementById('myDiv');
myDiv.onclick = () => {
myDiv.className = "active";
}

执行完后,检查 dom 元素,可以看到原来的 class 被替换掉了;

html
1
<div id="myDiv" class="active" age="18">fsllala</div>

classList

同 className 替换 class 名相比,如果我们需要添加或者移除单个的 class,那么可以使用 classList 属性。

  • elem.classList 是一个特殊的对象:
    • elem.classList.add (class):添加一个类;
    • elem.classList.remove (class):添加 / 移除类;
    • elem.classList.toggle (class):如果类不存在就添加类,存在就移除它;
    • elem.classList.contains (class):检查给定类,返回 true/false;
  • classList 是可迭代对象,可以通过 for of 进行遍历;

将文章上方的 className 替换成 classList.add:

js
1
2
3
4
const myDiv = document.getElementById('myDiv');
myDiv.onclick = () => {
myDiv.classList.add("active");
}

执行完后,检查 dom 元素,可以看到原来的 class 后面添加了新的 class 名为 active ;

html
1
<div id="myDiv" class="mydivClass active" age="18">fsllala</div>

getComputedStyle

如果我们需要读取样式:

  • 对于内联样式,是可以通过 style.* 的方式读取到的;

  • 对于 style、css 文件中的样式,是读取不到的;

这个时候,我们可以通过 getComputedStyle 的全局函数来实现:

html
1
<div id="myDiv" class="active" age="18" style="font-weight: 900;">fsllala</div>
css
1
2
3
4
5
.active {
color: red;
background-color: antiquewhite;
font-size: 24px;
}
js
1
2
3
4
5
6
const myDiv = document.getElementById('myDiv');
myDiv.onclick = () => {
console.log(myDiv.style.color); // 空
console.log(myDiv.style.fontWeight); // 900
console.log(getComputedStyle(myDiv).fontSize);// 24px
}