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.