# 节点操作
网页中所有内容都是节点(标签、属性、文本、注释等),在DOM中,节点使用node来表示。
HTML DOM树中所有节点均可通过JavaScript进行访问,所有HTML元素(节点)均可被修改,也可以创建或删除。
注意:实现开发中,节点操作主要操作的是元素节点。
# - 节点的组成
节点拥有节点类型(nodeType)、节点名称(nodeName)、节点值(nodeValue)这三个基本属性。
# 节点类型
注:红色加粗为常用的节点类型
节点类型 | 值 | 描述 |
---|---|---|
1 | Element | 代表元素 |
2 | Attr | 代表属性 |
3 | Text | 代表元素或属性中的文本内容 |
4 | CDATASection | 代表文档中的 CDATA 部(不会由解析器解析的文本) |
5 | EntityReference | 代表实体引用 |
6 | Entity | 代表实体 |
7 | ProcessingInstruction | 代表处理指令 |
8 | Comment | 代表注释 |
9 | Document | 代表整个文档(DOM 树的根节点) |
10 | DocumentType | 向为文档定义的实体提供接口 |
11 | DocumentFragment | 代表轻量级的 Document 对象(文档的某个部分) |
12 | Notation | 代表 DTD 中声明的符号 |
# - nodeName
元素节点的
nodeName
是标签名称(大写)属性节点的
nodeName
是属性名称文本节点的
nodeName
永远是#text
文档节点的
nodeName
永远是#document
# - nodeValue
对于文本节点,
nodeValue
属性包含文本。对于属性节点,
nodeValue
属性包含属性值。文档节点和元素节点,
nodeValue
属性的值始终为null
。
# - 节点层级关系获得元素
# - parentNode与parentElement
node.parentNode:返回某节点最近的一个父节点,如果没有父节点则返回null。
node.parentElement:返回某节点最近的一个父元素,如果没有父元素则返回null。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="root">
<div id="app"></div>
</div>
</body>
<script>
// parentNode与parentElement唯一的区别:
// parentNode查找的是父节点,parentElement查找的是父元素。当前的节点元素如果是html,那么
// html.parentNode ====> #document
// html.parentElement ====> null
// node.parentNode:返回某节点最近的一个父节点,如果没有父节点则返回null。
var app = document.querySelector("#app");// dom 元素。----->element
// console.log(app.parentNode);// div#root
// 节点拥有节点类型(nodeType)、节点名称(nodeName)、节点值(nodeValue)这三个基本属性。
// console.log(app.parentNode.nodeType);//节点类型 1
// console.log(app.parentNode.nodeName);//节点名称 DIV
// console.log(app.parentNode.nodeValue);//节点值 null
// 通过元素的层级关系获得的。
// console.log(app.parentNode);// div#root
// 通过DOM元素操作获得
// console.log(document.querySelector("#root"));
// 通过元素的层级关系获得的===通过DOM元素操作获得
// console.log(app.parentNode === document.querySelector("#root"));// true
// console.log(app.parentNode);// div#root
// console.log(app.parentNode.parentNode,
// app.parentNode.parentNode===document.body);//body true
// console.log(app.parentNode.parentNode.parentNode,
// app.parentNode.parentNode.parentNode===document.documentElement);//html true
// console.log(app.parentNode.parentNode.parentNode.parentNode);//document
// console.log(app.parentNode.parentNode.parentNode.parentNode.parentNode);//null
// node.parentElement:返回某节点最近的一个父元素,如果没有父元素则返回null。
console.log(app.parentElement);// div#root
console.log(app.parentElement.parentElement);// body
console.log(app.parentElement.parentElement.parentElement);// html
console.log(app.parentElement.parentElement.parentElement.parentElement);// null
</script>
</html>
# - childNodes与children
node.childNodes:返回包含指定节点的所有子节点的集合,包括元素节点,文本节点等。
node.children:返回指定元素的所有子元素集合
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="root">
<p>路</p>
<p>稻香</p>
<p>哪里都是你</p>
<!-- yyds 周杰伦 -->
</div>
</body>
<script>
// node.childNodes:返回包含指定节点的所有子节点的集合,包括元素节点,文本节点等。
// childNodes返回是一个伪数组。
var root = document.querySelector("#root");
// console.log(root.childNodes);
// root.childNodes.forEach(function (item,index){
// console.group("**********"+index+"************")
// console.log("nodeType:",item.nodeType);
// console.log("nodeName:",item.nodeName);
// console.log("nodeValue:",item.nodeValue);
// console.groupEnd();
// })
// 伪数组===>真数组方法1
// Array.from(root.childNodes).map(function (){
//
// });
// 伪数组====》真数组方法2
// 下义一个数组
// var nodeArr = [];
// root.childNodes.forEach(function (item){
// nodeArr.push(item);
// })
// // 通过map映射出来一个新的数组,数组中的元素为节点对应的类型。
// var arrType = nodeArr.map(function (item){
// return item.nodeType;
// })
// console.log(arrType);
// 如果只想得到元素节点,如何去做
// 方法1:通过forEeach
// var arrElement = []
// root.childNodes.forEach(function (item){
// // 当nodeType为1说明是元素节点
// if(item.nodeType===1){
// arrElement.push(item);
// }
// })
// console.log(arrElement);// 元素节点数组
// 方法2:通过filter
// var arrElement = Array.from(root.childNodes).filter(function (item){
// return item.nodeType === 1;// 得到满足条件的元素
// })
// console.log(arrElement);
// node.children:返回指定元素的所有子元素集合。
// console.log(root.children);// 得到的是一个伪数组(HTMLCollection),数组内的元素都是元素节点。
// HTMLCollection集合是没有forEach方法的
// root.children.forEach();
// 遍历1:for
// console.log(root.children.length);// 3
// for(var i=0;i<root.children.length;i++){
// console.log(root.children[i])
// }
// 遍历2:forEach
Array.from(root.children).forEach(function(item){
console.log(item);
})
</script>
</html>
# - firstChild与 firstElementChild
firstChild: IE6、7、8 第一个元素节点; 非IE6、7、8:返回第一个节点
firstElementChild: IE6、7、8不支持;非IE6、7、8,获取第一个元素节点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<p>1</p>
</div>
</body>
<script>
// firstChild:在非IE6、7、8中得到的是第一个子节点。
// 在IE6、7、8中得到的是第一个子元素。
// - firstChild: IE6、7、8 第一个元素节点; 非IE6、7、8:返回第一个元素节点或文本节点
var app = document.getElementById("app");
// 非IE6、7、8:返回第一个元素节点或文本节点
// console.log(1111,app.firstChild);// #text 文本
// IE6、7、8 第一个子元素节点;
// console.log(1111,app.firstChild);// 元素节点
// - firstElementChild:
// IE6、7、8不支持;非IE6、7、8,获取第一个元素节点
// E6、7、8不支持
// console.log(app.firstElementChild);// undefined
// 非IE6、7、8,获取第一个元素节点
console.log(app.firstElementChild);
</script>
</html>
# - lastChild与 lastElementChild
lastChild:非IE678 当中得到的是最后一个子节点。在IE678中得到的是最后一个子元素。
lastElementChild :非IE678中得到的是最后一个子元素,不支持IE678
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="root">
<p>1</p>
</div>
</body>
<script>
// lastChild:非IE678 当中得到的是最后一个子节点。在IE678中得到的是最后一个子元素。
var root = document.getElementById("root");
// console.log(root.lastChild);
// lastElementChild :非IE678中得到的是最后一个子元素,不支持IE678
console.log(root.lastElementChild);
</script>
</html>
# - nextSibling与nextElementSibling
nextSibling:在非IE678中得到的是兄弟级别的下一个节点,在IE678中得到的是下一个元素。
nextElementSibling:在非IE678中得到的是下一个元素节点。在IE678中不支持
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<p id="one">1</p>
<p id="two">2</p>
<p id="three">3</p>
</div>
</body>
<script>
var two = document.getElementById("two");
// * nextSibling:在非IE678中得到的是兄弟级别的下一个节点,在IE678中得到的是下一个元素。
// console.log(two.nextSibling);
// * nextElementSibling:在非IE678中得到的是下一个元素节点。在IE678中不支持
console.log(two.nextElementSibling);
</script>
</html>
# - previousSibling与previousElementSibling
previousSibling:在非IE678中得到的是兄弟级别的上一个节点,在IE678中得到的是下一个元素。
previousElementSibling:在非IE678中得到的是上一个元素节点。在IE678中不支持
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<p id="one">1</p>
<p id="two">2</p>
<p id="three">3</p>
</div>
</body>
<script>
var two = document.getElementById("two");
// * previousSibling:在非IE678中得到的是兄弟级别的上一个节点,在IE678中得到的是下一个元素。
// console.log(two.previousSibling);
// * previousElementSibling:在非IE678中得到的是上一个元素节点。在IE678中不支持
console.log(two.previousElementSibling);
</script>
</html>
# - 操作节点
# - createElement
document.createElement('tagName')方法创建由tagName指定的HTML元素。因为这些元素原先不存在,是根据我们的需求动态生成的,所以我们也称为动态创建元素元点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<ul></ul>
</div>
</body>
<script>
// 需求:在ul中插入<li>我的团长我的团</li>
// 1- innderHTML
// var ul = document.querySelector("#app ul");
// ul.innerHTML = "<li>我的团长我的团</li>";
// 2- createElement
// createELement是document下的方法。该方法接收的字符串即是标签名。
// 2-1:创建元素
var li = document.createElement("li");// 创建了一个名字为li的DOM元素。
li.innerText = "我的团长我的团";
li.style.color = "red";
var ul = document.querySelector("#app ul");
// 2- 将创建的li放入到ul中。
ul.appendChild(li);// 追加到ul的尾部
</script>
</html>
# - appendChild
node.appendChild(child):将一个节点添加到指定父节点的子节点列表末尾。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<p>刘德华</p>
</div>
</body>
<script>
var app = document.querySelector("#app");
// 创建一个元素
var p = document.createElement("p");
// 为元素添加内容
p.innerHTML = "郭富城";
// 将p元素放入到app元素尾部
app.appendChild(p);
</script>
</html>
# - insertBefore
node.insertBefore(child,指定元素):将一个节点添加到父节点的指定子节点前面。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
<li>霸道总裁爱上你</li>
<li>还珠格格</li>
<li>斗罗大陆</li>
</ul>
</body>
<script>
// 1- 创建元素li
var ul = document.querySelector("ul");
var li = document.createElement("li");
li.innerText = "斗破苍穹";
li.style.fontWeight="bold";
// ul.appendChild(li);// 放置在尾部
ul.insertBefore(li,ul.lastElementChild);// 放置在斗罗大陆的前面。
// 将li放在最上面
// 第一个参数是指定插入的元素,第二个参数指定放在哪个元素的前面。
// 要将第一个参数放置在第二个参数的前面
// var lis = ul.querySelectorAll("li");
// ul.insertBefore(li,lis[0])
// ul.insertBefore(li,ul.firstElementChild);
</script>
</html>
# - removeChild
node.removeChild():从DOM中删除一个子节点,返回删除的节点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button>移除最后一个li</button>
<button>移除最上面的li</button>
<button>将最上面的li移动到最下边</button>
<ul>
<li>霸道总裁爱上你</li>
<li>还珠格格</li>
<li>斗罗大陆</li>
</ul>
</body>
<script>
// var ul = document.querySelector("ul");
// // 1- 获得要移除的元素
// var lis = document.querySelectorAll("ul li");
// // 移除下标为0
// ul.removeChild(lis[0]);
// // 移除下标为1
// ul.removeChild(lis[1]);
// // 移除下标为2
// ul.removeChild(lis[2]);
var btns = document.querySelectorAll("button");
var ul = document.querySelector("ul");
btns[0].onclick = function(){
ul.removeChild(ul.lastElementChild);
console.log(ul.children.length);
// 如果ul没有子元素节点,那么让按钮失败
if(ul.children.length===0) this.disabled = true;
}
btns[1].onclick = function (){
// removeChild会将删除的节点进行返回。
var removeNode = ul.removeChild(ul.firstElementChild);
console.log(removeNode);
// 如果ul没有子元素节点,那么让按钮失败
if(ul.children.length===0) this.disabled = true;
}
btns[2].onclick = function(){
// removeNode是删除的元素节点。
var removeNode = ul.removeChild(ul.firstElementChild);
ul.appendChild(removeNode);
}
</script>
</html>
# - cloneNode
node.cloneNode():返回调用该方法的节点的一个副本。也称为克隆节点/拷贝节点。
如果参数为空或者为false,是浅拷贝,即只克隆复制节点本向,不克隆里面的子节点。
如果括号参数为true,则是深度拷贝,会复制节点本身以及里面所有的子节点。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
<li>霸道总裁爱上你</li>
<li>还珠格格</li>
<li>斗罗大陆</li>
</ul>
</body>
<script>
var ul = document.querySelector("ul");
// var lis = document.querySelectorAll("li");
// console.log(lis[0].cloneNode());// 复制节点
// console.log(lis[0].cloneNode(true));// 复制节点以及节点所包裹的内容。
// ul.appendChild(lis[0].cloneNode(true))
// lis[0].parentElement.appendChild(lis[1].cloneNode(true));
ul.appendChild(ul.children[2].cloneNode(true));
</script>
</html>
# - 案例-找徐峥
效果
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> * { padding: 0; margin: 0; } #app { width: 500px; height: 500px; margin: 0 auto; border: 2px solid black; font-size: 0; } </style> </head> <body> <div id="app"> </div> </body> <script> // 获得app元素 var app = document.querySelector("#app"); // 指定列数 init(2); function getRandom(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); } function init(col) { var maxWidth = app.clientWidth / col; var activeIndex = getRandom(0, col * col - 1) for (var i = 0; i < col * col; i++) { var img = new Image(); img.width = img.height = maxWidth; // 判断随机数是否与i相同,相同则为徐峥 img.src = activeIndex === i ? "./img/1.png" : "./img/2.png"; img.dataset.index = i; img.onclick = function () { // console.log(this.dataset.index/1===activeIndex); if (this.dataset.index / 1 === activeIndex) { // 如果选中的是徐峥那么应该加大难度。2-》3 alert("恭喜您 ,选对啦"); app.innerHTML = ""; init(++col); } else { alert("选择错误") } } app.appendChild(img); } } </script> </html>
# - 案例-钟表
效果
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> *{ padding:0; margin:0; } #app{ width:400px; height: 400px; border:2px solid black; margin:0px auto; border-radius: 100%; position: relative; } .kedu{ position: absolute; top:0; /*(400-4)/2*/ left:198px; width: 4px; height:20px; background:black; transform-origin: 2px 200px; } .timeDiv{ position: absolute; /*(400-宽40)/2*/ left:180px; /*刻度的高度*/ top:30px; width: 40px; height:170px; /*background: red;*/ transform-origin: bottom; font-size:25px; } /*时针*/ .hour{ position: absolute; width: 6px; left:197px; top:130px; height: 70px; background: green; transform-origin: bottom; } /*分针*/ .min{ position: absolute; width: 4px; left:198px; top:100px; height: 100px; background: skyblue; transform-origin: bottom; } /*秒针*/ .sec{ position: absolute; width: 2px; left:199px; top:60px; height: 140px; background: red; transform-origin: bottom; } .origin{ position:absolute; width: 20px; height: 20px; background: black; top:190px; left:190px; border-radius: 100%; } </style> </head> <body> <div id="app"> <div class="hour"></div> <div class="min"></div> <div class="sec"></div> <div class="origin"></div> </div> </body> <script> var app = document.querySelector("#app"); // 让时分秒动起来: var hour = document.querySelector(".hour"); var min = document.querySelector(".min"); var sec = document.querySelector(".sec"); init(); function init(){ // 获得当前时间 var date = new Date(); var h = date.getHours();// 小时 var m = date.getMinutes();// 分 var s = date.getSeconds();// 秒 // 1- 每小时占30度 // 2- 每小时共60分钟 // 由以上两点可以得出:每分钟占用 30/60 = 0.5 // 1-每分钟占6度 // 2-每分钟有60秒 // 由以上两点可以得出:每秒钟占用 6/60=0.1 hour.style.transform = "rotateZ("+(h*30+m*0.5)+"deg)";// 360/12 min.style.transform = "rotateZ("+(m*6+s*0.1)+"deg)";// 360/60 sec.style.transform = "rotateZ("+s*6+"deg)";// 360/60 } setInterval(init,1000); // 1- 增加刻度 for(var i=0;i<60;i++){ // 创建div元素 var div = document.createElement("div"); // 为div增加样式 div.className = "kedu"; // 旋转 div.style.transform = "rotateZ("+i*6+"deg)"; // 判断是否被5整除 if(i%5===0){ div.style.height = "30px" } // 将创建的元素div放到app元素的尾部 app.appendChild(div); } // 2- 增加时间 for(var i=1;i<=12;i++){ var timeDiv = document.createElement("div"); timeDiv.className = "timeDiv"; timeDiv.style.transform = "rotateZ("+i*30+"deg)" var numDiv = document.createElement("div"); numDiv.innerHTML = i; numDiv.style.transform = "rotateZ("+(-i*30)+"deg)"; numDiv.style.textAlign="center"; timeDiv.appendChild(numDiv) app.appendChild(timeDiv); } </script> </html>