做前端开发,还是需要通读MDN(一)

407 阅读8分钟

最近在写原生js,遇见了一些问题,然后通过查阅MDN文档,发现了很多知道但又不是很熟悉的api,所以就想着通读一下MDN,总结一些好用且优雅的api用法,来帮助我们快速开发。

下面先来看一些全局对象的继承关系。

全局对象的继承关系

Document

image.png

Element

image.png

dom元素

image.png

Window

image.png

document主要是对整个文档进行操作的。所以会定义一些创建元素的api。

element主要是对元素进行操作,所以会定义一些删除和添加元素的api。

我们发现这些全局对象都间接或直接继承EventTarget,所以我们先来看看EventTarget是个啥东西?

EventTarget

所有继承自他的目标对象都实现了事件监听addEventListener(),事件移除removeEventListener,事件派发dispatchEvent三个方法。

前两种方法大家都很熟悉了。我们来介绍一下事件派发吧。

dispatchEvent会向一个指定的事件目标派发一个 Event,并以合适的顺序(同步地)调用所有受影响的 EventListenerdispatchEvent() 会同步调用事件处理函数。在 dispatchEvent() 返回之前,所有监听该事件的事件处理程序将在代码继续前执行并返回。意思就是说我们在派发事件前,需要定义事件处理程序和事件。

以前写过一篇文章看这里

Node

从上面全局对象继承关系来看,dom元素和document对象都继承自Node。他身上封装了很多和节点操作相关的api。

在DOM中,Node 是一个接口,由文档树中的不同类型的节点(包括元素、文本节点、注释等)实现。

baseURI

返回当前文档的url。即location.href

image.png

childNodes

他主要是获取当前节点元素直接子元素,并且获取的元素集合是实时更新的。如果你单纯想要获取直接子元素,他的效果比通过document.querySelectorAll()获取好,因为通过document.querySelectorAll()获取的元素不会实时更新。比如无限加载时,获取指定元素,childNodes就可以获取实时加载的元素。

注意他们返回的都是类数组的NodeList,该对象提供了一些方法供我们使用

  • entries() 返回迭代器。遍历Nodelist键值对。

  • forEach() 遍历NodeList。

  • item() 根据给定的索引,返回一个 NodeList对象中包含的 Node 对象。索引从0开始。等价于通过下标获取,只是越界返回值不同。前者返回null,后者返回undefined。

  • keys() 遍历NodeList key。即索引。

  • values 该方法返回一个 iterator 迭代器,可以利用迭代器遍历所有 value。即节点对象。

firstChild, lastChild, nextSibling, previousSibling

他们分别可以获取当前节点的第一个孩子节点,最后一个孩子节点,下一个兄弟节点, 上一个兄弟节点。最主要的是他们可以获取任何类型的节点(例如注释,文本节点)。

<body><!-- 22 -->
  <div id="div-1">Here is div-1</div>
  <div id="div-2">Here is div-2</div>
  
<script>
  let el = document.getElementById("div-1");
  console.log("nextSibling", el.nextSibling) // text
  console.log("nextSibling", el.previousSibling, el.previousSibling.nextSibling === el) // text true
  console.log(document.body.firstChild) // comment

image.png

nodeName, nodeType, nodeValue

他们分别返回当前节点名称(全大写),节点类型,节点值。

类型名称如下(正常节点的名称是全大写字母)

image.png

节点类型,返回数字,用于区分不同类型节点,类型映射如下。我们还可以访问提供的全局常量(例如Node.ELEMENT_NODE)

image.png

节点值,如果nodeValue有值,那么我们可以设置其nodeValue。

image.png

image.png

ownerDocument

返回当前节点的顶层的 document 对象。这个主要看在iframe中返回的是根文档对象还是当前文档对象。经过测试发现,iframe中使用该属性返回的是当前文档对象,而非引用文档对象。

image.png

parentElement, parentNode

parentElement 返回当前节点的父元素节点,如果该元素没有父节点,或者父节点不是一个 DOM 元素,则返回 null

parentNode 返回指定的节点在 DOM 树中的父节点。他与parentElement的区别就是它可以返回非Element元素。

textContent

这个api也是最近在写原生时学习到的,返回当前节点中的文本内容。

innerText 与 textContent区别

  • innerText 只会返回可见元素的文本。并且不会获取script和style中的内容。
  • textContent 会返回所有。

目前介绍一下比较根级的接口对象EventTarget, Node,下篇文章会介绍Document, Element, Window接口对象,欢迎大家关注。

appendChild(), cloneNode()

方法将一个节点附加到指定父节点的子节点列表的末尾处。如果将被插入的节点已经存在于当前文档的文档树中,那么 appendChild() 只会将它从原先的位置移动到新的位置(不需要事先移除要移动的节点)因为一个相同的节点不可能同时出现在文档的不同位置。如果想要不删除节点,那么我们可以使用cloneNode()克隆一份该节点,然后在进行操作。

  • appendChild 返回追加后的节点。
  • cloneNode(?deep 是否深度克隆) 返回调用该方法的节点的一个副本。
    • 克隆一个元素节点会拷贝它所有的属性以及属性值,当然也就包括了属性上绑定的事件 (比如onclick="alert(1)"),但不会拷贝那些使用addEventListener()方法或者node.onclick = fn这种用 JavaScript 动态绑定的事件。
    • 如果deep参数设为false,则不克隆它的任何子节点。该节点所包含的所有文本也不会被克隆,因为文本本身也是一个或多个的Text节点。
<div>
div标签
</div>
<foot></foot>
<script>
// 将dom树中的标签移动到其他位置
const div = document.querySelector("div")
const foot = document.querySelector("foot")
const r1= foot.appendChild(div)
console.log(r1 === div) // true

// 创建新节点
const p = document.createElement("p")
p.innerHTML = "p标签"
const r2 =  document.body.appendChild(p)
console.log(r2 === p) // true

console.log("克隆", div.cloneNode(false)) // 空div标签,没有内容文本节点,所以一般都传递true,深度克隆。
</script>

image.png

compareDocumentPosition()

可以比较当前节点与任意文档中的另一个节点的位置关系。这个方法大致可以用来比较两个元素在文档中的位置关系,在开发中我们还是可以用来确认位置关系,然后做一些特定的操作。

image.png

image.png

contains()

判断给定节点是否是当前节点的后代,即该节点本身、其直接子节点(childNodes)、子节点的直接子节点等。

这个方法可以用来实现popup类组件的点击mask区域隐藏功能实现,具体可以看这里

<div>
<p>
  p元素
</p>
</div>
<script>
const p = document.querySelector('p')
document.body.addEventListener('click', (event) => {
  console.log("点击的元素", event.target, p.contains(event.target))
})
</script>

getRootNode()

返回上下文中的根节点,如果 shadow DOM 可用,则对 shadow DOM 同样适用。

  • 在标准的网页中调用将会返回一个 HTMLDocument 对象表示整个网页。
  • 在 Shadow DOM 里调用将会返回一个与之相关联的 ShadowRoot

并且他可以传递一个options参数。options.composed = true属性表示在检测shadow Dom时,是否跳过shadowRoot,直接返回顶层文档对象。

测试发现,如果嵌套iframe,那么他返回的依旧是内部文档对象,并不会跨文档返回根节点。

<div class="js-parent">
    <div class="js-child"></div>
  </div>
  <div class="js-shadowHost"></div>
  <script>
    // work on Chrome 54+,Opera41+

    var parent = document.querySelector(".js-parent"),
      child = document.querySelector(".js-child"),
      shadowHost = document.querySelector(".js-shadowHost");

    console.log(parent.getRootNode()); // #document
    console.log(child.getRootNode()); // #document

    // create a ShadowRoot
    var shadowRoot = shadowHost.attachShadow({ mode: "open" });
    shadowRoot.innerHTML =
      "<style>div{background:#2bb8aa;}</style>" +
      '<div class="js-shadowChild">content</div>';
    var shadowChild = shadowRoot.querySelector(".js-shadowChild");

    // The default value of composed is false
    console.log(shadowChild.getRootNode() === shadowRoot); // true
    console.log(shadowChild.getRootNode({ composed: false }) === shadowRoot); // true
    console.log(shadowChild.getRootNode({ composed: true }) === parent.getRootNode()); // true 跳过shadowRoot检测
  </script>

image.png

hasChildNodes()

表示当前节点内部是否有子节点(文本,注释都算)

目前有三种方式可以判断是否有子节点了

  • node.firstChild !== null
  • node.childNodes.length > 0
  • node.hasChildNodes()

参考

往期年度总结

往期文章

专栏文章