Skip to content

关系类图

节点操作 (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>

removeChild.png

元素节点操作 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

  1. 自定义属性是 data-xxx="str" 这种格式的属性可以在 dom 元素对象的 dataset 属性中直接访问
  2. 如果修改 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>

data_set.png

文档碎片 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;
};

操作元素的方法 & 属性

获取元素

Document.getElementsById

Node.contains

Node.getRootNode

Node.hasChildNodes

Element.getElementsByClassName / Document.getElementsByClassName

Element.getElementsByTagName / Document.getElementsByTagName

Element.querySelector / Document.querySelector

Element.querySelectorAll / Document.querySelectorAll

Element.closest

获取元素(属性)

Node.childNodes

Node.firstChild

Node.lastChild

Node.previousSiblings

Node.nextSiblings

Node.parentNode

Node.textContent

Document.documentElement

Document.body

Document.forms

Document.images

Document.scripts

Document.styleSheets

增加元素

Document.createComment

Document.createDocumentFragment

Document.createElement

Document.createTextNode

Node.appendChild

Node.insertBefore

Node.cloneNode

Element.after

Element.before

Element.prepend

Element.append

Element.insertAdjacentElement

Element.insertAdjacentHTML

Element.insertAdjacentText

删除元素

Node.removeChild

Element.remove

替换元素 Ï

Node.replaceChild

Element.replaceChildren

Element.replaceWith

Released under the MIT License.