关系类图
节点操作 (Node 类上的方法)
既然是 Node 类上的方法, 那么根据上面的继承关系图可以得知: HTMLElement 和 Document 及其子类的实例对象都可以使用 这些方法
contains 是否包含某个节点, 返回一个布尔值
html
<div id="app">
<p class="p">p1</p>
</div>
<p class="p">p2</p>
<script>
var app = document.getElementById("app");
var [p1, p2] = document.getElementsByClassName("p");
console.info("p1:", app.contains(p1)); // true
console.info("p2:", app.contains(p2)); // false
</script>
hasChildNodes 是否有子节点, 返回一个布尔值
html
<p class="p"></p>
<p class="p">p2</p>
<p class="p"><span></span></p>
<script>
var [p1, p2, p3] = document.getElementsByClassName("p");
console.info("p1", p1.hasChildNodes()); // false
console.info("p2", p2.hasChildNodes()); // true
console.info("p3", p3.hasChildNodes()); // true
</script>
appendChild 插入一个节点(剪切)
html
<!DOCTYPE html>
<html lang="en">
<head>
<title>js</title>
</head>
<body>
<!-- JavaScripts -->
<p class="test">test</p>
<div id="app"></div>
<script>
var app = document.getElementById("app");
var [oTest] = document.getElementsByClassName("test");
app.appendChild(oTest); // 会将p标签从body标签中剪切掉然后追加到#app这个div中
// 注意: appendChild 无法插入html代码字符串
// 如: appendChild('<div></div>') 这样式不可以的, 报错
</script>
</body>
</html>
insertBefore 插入一个节点, 返回被插入的节点对象
html
<p class="test">test</p>
<div id="app">
<p>other</p>
<a href="" id="link">link</a>
<p>other</p>
</div>
<script>
var app = document.getElementById("app");
var oLink = document.getElementById("link");
var [oTest] = document.getElementsByClassName("test");
var ele = app.insertBefore(oTest, oLink); // 将 oTest 剪切, 插入到 #app下的 #link 节点之前
// var ele = app.insertBefore(oTest, null); // 将 oTest 剪切, 插入到 #app下, 放到最后的位置
console.info(ele === oTest); // true
</script>
removeChild 通过父节点移除一个子节点
返回的结果是: 被移除的节点对象
html
<div id="app">
<p>other</p>
<a href="" id="link">link</a>
<p>other</p>
</div>
<script>
var app = document.getElementById("app");
var oLink = document.getElementById("link");
var obj = app.removeChild(oLink); // 通过父节点移除一个子节点, 不会直接删除, 而是会返回这个值
console.log(obj); // 可以看到并没有直接移除, 而是保存到了内存中
</script>
元素节点操作 Element 类的方法
remove 通过节点自己调用移除节点
html
<!DOCTYPE html>
<html lang="en">
<head>
<title>js</title>
</head>
<body>
<div id="app">app</div>
<!-- javascript -->
<script>
var app = document.getElementById("app");
var obj = app.remove();
console.log(obj); // undefined: 与 removeChild 不同的是: 这个方法不会返回被删除的dom元素, 而是直接删除
</script>
</body>
</html>
innerHTML/innerText 修改 html 的内容(HTMLElement 属性)
html
<!DOCTYPE html>
<html lang="en">
<head>
<title>js</title>
</head>
<body>
<div id="box1"><span>box1</span></div>
<div id="box2"><span>box2</span></div>
<!-- javascript -->
<script>
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
// 取值:
console.info(box1.innerHTML); // 会获取HTML代码: <span>box1</span>
console.info(box1.innerText); // 不会获取HTML代码: box2
// 赋值:
var str = "<h1>hello world</h1>";
box1.innerHTML = str; // 可以解析字符串中的html代码
box2.innerText = str; // 不能解析字符串中的html代码
</script>
</body>
</html>
自定义属性与 dataset
- 自定义属性是
data-xxx="str"
这种格式的属性可以在 dom 元素对象的dataset
属性中直接访问 - 如果修改 dom 元素的
dataset
属性, 对应的也会在标签上添加对应的data-xx="str"
属性
html
<!DOCTYPE html>
<html lang="en">
<head>
<title>js</title>
</head>
<body>
<div id="box" username="tom" data-age="20"></div>
<!-- javascript -->
<script>
var box = document.getElementById("box");
box.dataset.id = 1001;
box.dataset.friends = ["jack", "jerry"]; // 如果是引用类型的值: 会先调用 toString 方法, 然后设置
console.info(box.dataset);
</script>
</body>
</html>
文档碎片 DocumentFragment
DocumentFragment 不是真实的 DOM 树, 它的变化不会让当前 DOM 树重新渲染, 他是存在内存中的, 所以速度非常快不会有性能问题, 非常适合用于大量操作 DOM 的场景
1.直接插入 dom 导致浏览器多次渲染, 性能差
html
<!DOCTYPE html>
<html lang="en">
<head>
<title>js</title>
</head>
<body>
<ul id="list">
<!-- li > a -->
</ul>
<!-- javascript -->
<script>
var ul = document.getElementById("list");
var li;
for (var i = 0; i < 1000; i++) {
li = document.createElement("li");
li.innerText = i + ". 这是第" + i + "个li标签";
ul.append(li); // append 1000 次: 浏览器渲染引擎重新渲染 1000次
}
</script>
</body>
</html>
2.创建一个内存中的 div 容器元素, 只会重新渲染一次, 增加性能, 但是 dom 结构被破坏
html
<!DOCTYPE html>
<html lang="en">
<head>
<title>js</title>
</head>
<body>
<ul id="list">
<!-- li > a -->
</ul>
<!-- javascript -->
<script>
var ul = document.getElementById("list");
var oDiv = document.createElement("div");
var li;
for (var i = 0; i < 1000; i++) {
li = document.createElement("li");
li.innerText = i + ". 这是第" + i + "个li标签";
// oUl.append(li); append 1000 次: 浏览器渲染引擎重新渲染 1000 次
// 因为这个 oDiv 是在内存中的, 所以不会改变当前DOM, 也就不会重新渲染
oDiv.append(li);
}
ul.append(div);
// 只会重新渲染一次, 但是原先的DOM结构被破坏了,我们不希望
// DOM 结构是这样的: ul 中有个div 然后 div 中有 li
// <ul>
// <div>
// <li>xxx</li>
// </div>
// </ul>
// 应该是 ul 中直接是 li 标签, 此时就可以使用 DocumentFragment
</script>
</body>
</html>
3.使用 DocumentFragment 将内容在内存中处理完, 性能好, 插入文档的时候也不会破坏结构
html
<!DOCTYPE html>
<html lang="en">
<head>
<title>js</title>
</head>
<body>
<ul id="list">
<!-- li > a -->
</ul>
<!-- javascript -->
<script>
var ul = document.getElementById("list");
var fragment = document.createDocumentFragment();
var li;
for (var i = 0; i < 1000; i++) {
li = document.createElement("li");
li.innerText = i + ". 这是第" + i + "个li标签";
fragment.appendChild(li); // 因为这个 oDiv 是在内存中的, 所以不会改变当前DOM, 也就不会重新渲染
}
ul.appendChild(fragment); // 因为 fragment 的特殊性, 他不会插入到DOM树中, 也就是说:不会改变当前DOM结构
</script>
</body>
</html>
封装一些实用函数:
$hasChildElements
判断是否有子元素节点, 返回一个布尔值
javascript
/**
* 判断一个元素是否有子元素节点
*
* @return {Boolean} 是否有元素节点
*/
Element.prototype.$hasChildElements = function () {
var children = this.childNodes;
var len = children.length;
if (len === 0) {
return false;
}
for (var i = 0; i < len; i++) {
if (children[i].nodeType === Node.ELEMENT_NODE) {
return true;
}
}
return false;
};
$findSiblings
寻找当前元素的兄弟元素, 传入一个 index 如果是正数则向后找, 如果是负数则向前找, 如果是正数, 则返回自己
javascript
/**
* 根据传入的数字找对应的兄弟元素
* @param {Number} index 参数是正数向后找, 参数是负数, 向前找
* @return {Element|undefined}
*/
Element.prototype.$findSiblings = function () {
var index = arguments[0] >> 0;
var steps = Math.abs(index);
var target = undefined;
// 传入的数字为0
if (steps === 0) {
return this;
}
// 传入的 index 大于父元素的所有子元素数
if (this.parentNode.childElementCount < steps) {
return target;
}
// 找的方向: 大于0向后找, 小于0向前找
var dirKey = index > 0 ? "nextElementSibling" : "previousElementSibling";
var item = this;
target = item;
for (var i = 0; i < steps; i++) {
if (item) {
item = item[dirKey];
target = item;
} else {
target = undefined;
}
}
return target;
};
$insertAfter
有 insertBefore 方法, 现在需要封装一个功能类似的 insertAfter 方法
javascript
/**
* 将传入的元素节点插入到当前元素的后面
*
* @param {Element} target 被插入的元素
* @return {undefined}
*/
Element.prototype.$insertAfter = function (target) {
// 参数必须是一个元素节点
if (!(target && target.nodeType === Node.ELEMENT_NODE)) {
throw new TypeError("The parameter must be a HTMLElement");
}
// 当前节点如果有下一个兄弟节点,就找到下一个兄弟节点然后insertBefore
// 如果没有证明是最后一个子元素, 找到当前元素的父元素 appendChild
var nextEle = this.nextElementSibling;
if (nextEle) {
this.parentNode.insertBefore(target, nextEle);
} else {
this.parentNode.appendChild(target);
}
};
$eachChildElemtns
遍历一个元素节点中的所有子元素节点
javascript
/**
* 遍历某个元素节点下的所有子元素节点
* @param {Function} 处理函数
* @return {undefined}
*/
Element.prototype.$eachChildElemtns = function (callback) {
if (typeof callback !== "function") {
throw new TypeError("The callback must be a function");
}
// 遍历
var current;
for (var i = 0, len = this.childElementCount; i < len; i++) {
current = this.children[i];
callback(current);
if (current.childElementCount) {
current.$eachChildElemtns(callback);
}
}
};
$flipChildNodes
将一个元素节点中的子元素颠倒顺序
javascript
/**
* 将某个元素节点下的所有子元素颠倒顺序
* @return {Element} 返回处理后的元素
*/
Element.prototype.$flipChildNodes = function (el) {
var fragment = document.createDocumentFragment();
var lastNode = this.lastChild;
while (lastNode) {
fragment.appendChild(lastNode);
lastNode = this.lastChild;
}
this.appendChild(fragment);
return this;
};