六、document 文档的 CRUD
6.1 创建元素
创建元素节点:document.createElement(tag)
创建文本节点:document.createTextNode(text)
例子:
javascript1const div = document.createElement('div') //创建div元素节点 2div.className = 'alert' //给div节点赋值上class 3div.innerHTML = '<strong>this is a alert div</strong>' //添加内容
6.2 向页面增加元素
node.append(...nodes or strings)—— 在node里面的末尾 插入节点或字符串,node.prepend(...nodes or strings)—— 在node里面的开头 插入节点或字符串,node.before(...nodes or strings)—— 在node前面 插入节点或字符串,node.after(...nodes or strings)—— 在node后面 插入节点或字符串,node.replaceWith(...nodes or strings)—— 将node替换为给定的节点或字符串。
这些方法的参数可以是一个要插入的任意的 DOM 节点列表,或者文本字符串(会被自动转换成文本节点)。
这些方法可以在单个调用中插入多个节点列表和文本片段。
例如,在这里插入了一个字符串和一个元素:
html1<div id="div"></div> 2<script> 3 div.before('<p>Hello</p>', document.createElement('hr')) 4</script>
请注意:这里的文字都被“作为文本”插入,而不是“作为 HTML 代码”。因此像 <、> 这样的符号都会被作转义处理来保证正确显示。
所以最终插入的其实是这样的:
html1<p>Hello</p> 2<hr /> 3<div id="div"></div>
<p>Hello</p>最终会被浏览器解析成以下字符串内容
html1<p>Hello</p>
换句话说,字符串被以一种安全的方式插入到页面中,就像 elem.textContent 所做的一样。
所以,这些方法只能用来插入 DOM 节点或文本片段。
6.3 insertAdjacentHTML/Text/Element
如果我们希望有一种方法类似于 innerHTML 能够插入 HTML 代码,我们可以使用通用方法:elem.insertAdjacentHTML(where, html)
这个方法的第一个参数是代码字,指定相对于 elem 的插入位置。必须为以下之一:
- beforebegin —— 将 html 插入到 elem 前
- afterbegin —— 将 html 插入到 elem 里面的开头
- beforeend —— 将 html 插入到 elem 里面的末尾
- afterend —— 将 html 插入到 elem 后
第二个参数是 html 字符串,这些字符串会被作为 html 插入。

这个方法还有两个兄弟:
elem.insertAdjacentText(where, text)— 语法一样,但是将text字符串“作为文本”插入而不是作为 HTMLelem.insertAdjacentElement(where, elem)— 语法一样,但是插入的是一个元素
这两兄弟的存在仅仅只是让语法看起来统一,实际上用的最多的是插入 HTML,而这两个兄弟的功能我们可以使用 before、after 等代替。
6.4 删除节点
想要移除一个节点,可以使用elem.remove()
这个方法很简单,有一点细节需要注意,如果我们想要将节点移动到另一个地方,则无需将其从原来的位置删除。
所有插入方法都会自动从旧位置删除节点。
比如
html1<div id="elem">i am elem</div> 2<script> 3 const div = document.createElement('div') 4 div.innerHTML = 'i am div ' 5 elem.before(div) //将div插入到elem的前面 6 setTimeout(() => { 7 elem.after(div) // 1秒后div会移到elem的后面 8 }, 1000) 9</script>
6.5 克隆节点 cloneNode
如果我们想插入一个类似的元素,我们可以创建一个函数来复用代码。还有一种方法是克隆现有的节点,并修改其中的一些内容。
当我们有一个很大的节点,使用克隆的方式更加简单。
Elem.cloneNode(boolean)可以传入参数来控制是否需要深克隆:
- true —— 包含所有 attribute 和子元素
- false —— 不包含子元素
这是一个深克隆的例子
html1<div id="elem"> 2 i am elem 3 <span>i am span in elem</span> 4</div> 5<script> 6 const cloneDiv = elem.cloneNode(true) 7 cloneDiv.querySelector('span').textContent = 'i am span in cloneElem' 8 elem.after(cloneDiv) 9</script>
6.6 DocumentFragment
DocumentFragment是一个特殊的 DOM 节点,用来作为传递节点列表用的包装器。
我们可以向其插入内容,当我们将其插入到某个位置时,则相当于插入它里面的内容。
html1<ul id="ul"></ul> 2<script> 3 function getList() { 4 const fragment = new DocumentFragment() 5 for (let i = 0; i < 3; i++) { 6 const li = document.createElement('li') 7 li.textContent = i 8 fragment.append(li) 9 } 10 return fragment 11 } 12 ul.append(getList()) // (*) 13</script>
第*行我们将 fragment 插入到 ul 内部,现在它里面的内容已经跟 ul 融合到一起了,最终形成的结构是这样的
html1<ul id="ul"> 2 <li>0</li> 3 <li>1</li> 4 <li>2</li> 5</ul>
这个 API 很少被使用,如果可以改为返回一个节点数组,那为什么还要附加到特殊类型的节点上呢?
重写如下:
html1<ul id="ul"></ul> 2<script> 3 function getList() { 4 const result = [] 5 for (let i = 0; i < 3; i++) { 6 const li = document.createElement('li') 7 li.textContent = i 8 result.push(li) 9 } 10 return result 11 } 12 ul.append(...getList()) // append + "..." operator 13</script>
6.7 旧的增删 API
由于历史原因,目前还有一些老式的 DOM 操作方法,列举如下:
- **parentElem.appendChild(node):**将
node附加为parentElem的最后一个子元素。 - **parentElem.insertBefore(node, nextSibling):**在
parentElem的nextSibling前插入node - **parentElem.replaceChild(node, oldChild):**将
parentElem的后代中的oldChild替换为node。 - **parentElem.removeChild(node):**从
parentElem中删除node(假设node为parentElem的后代)。
这些方法都会返回插入/删除的节点。由于这些 API 很怪异,想要做操作都要通过父元素节点才能办到,所以现代 JavaScript 下是不用这些 API 了。
6.8 document.write
document.write方法是非常古老的、来自于没有 DOM、没有标准的 web 上古时期的给页面添加内容的方法。
语法是这样的:
html1<p>Somewhere in the page...</p> 2<script> 3 document.write('<b>Hello from JS</b>') 4</script> 5<p>The end</p>
调用这个方法后,html 会马上写入到页面中。html 字符串是动态生成的,所以这个方法很灵活。
document.write调用只在页面加载时工作。如果我们稍后调用它,那现有文档内容会被擦除。
html1<p>After one second the contents of this page will be replaced...</p> 2<script> 3 // 1 秒后调用 document.write 4 // 这时页面已经加载完成,所以它会擦除现有内容 5 setTimeout(() => document.write('<b>...By this.</b>'), 1000) 6</script>
上面的方法会将 p 标签内的内容全部擦除。
换句话说,它在加载完成阶段是不可用的。
这是它的坏处。
它的好处是运行奇快,它不涉及 DOM 修改,可以直接写入到页面文本中,而此时 DOM 尚未构建。
如果我们需要向 HTML 动态地添加大量文本,并且我们正处于页面加载阶段,并且速度很重要,那么它可能会有帮助。
6.9 小结
创建新节点的方法:
document.createElement(tag)—— 用给定的标签创建一个元素节点document.createTextNode(value)—— 创建一个文本节点elem.cloneNode(deep)—— 克隆元素
插入和移除节点的方法:
node.append(...nodes or strings)—— 在 node 内部的末尾插入node.prepend(...nodes or strings)—— 在 node 内部的开头插入node.before(...nodes or strings)—— 在 node 前面插入node.after(...nodes or strings)—— 在 node 后面插入node.replaceWith(...nodes or strings)——替换 nodenode.remove()——删除 node 节点
文本字符串被作为文本输入
老式方法:
- **parentElem.appendChild(node):**将
node附加为parentElem的最后一个子元素。 - **parentElem.insertBefore(node, nextSibling):**在
parentElem的nextSibling前插入node - **parentElem.replaceChild(node, oldChild):**将
parentElem的后代中的oldChild替换为node。 - **parentElem.removeChild(node):**从
parentElem中删除node(假设node为parentElem的后代)。
在指定位置插入 HTML,elem.insertAdjacentHTML(where, html) 会根据 where 的值来插入它:
"beforebegin"— 将html插入到elem前面,"afterbegin"— 将html插入到elem的开头,"beforeend"— 将html插入到elem的末尾,"afterend"— 将html插入到elem后面。
还有类似的方法,elem.insertAdjacentText 和 elem.insertAdjacentElement,它们会插入文本字符串和元素,但很少使用。
在页面加载完成之前可以使用document.write(html)将 HTML 写到页面上,如果在页面加载完成后使用,会擦除页面内容。