一、DOM修改是创建“实时”页面的关键
二、创建DOM节点的两种方法
1、document.createElement(tag)
用给定的标签创建一个新元素节点(element node)
let div = document.createElement('div');
2、document.createTextNode(text)
用给定的文本创建一个文本节点
let textNode = document.createTextNode('Here I am')
DOM修改
插入方法
插入DOM节点或文本片段
一、插入DOM节点或文本片段的方法:append, prepend, before, after, replaceWith,insertAdjacentText,insertAdjacentElement
二、append方法
1、我们可以在任何元素上调用append方法,以将另外一个元素放入到里面。
【示例1】通过调用div.append(anotherElement),我们便可以在
三、元素插入方法,指明了不同的插入位置
1、node.append(…nodes or strings):在node末尾插入节点或字符串。
2、node.prepend(…nodes or strings):在node开头插入节点或字符串
3、node.before(…nodes or strings):在node前面插入节点或字符串。
4、node.after(…nodes or strings):在node后面插入节点或字符串
5、node.replaceWith(…nodes or strings):将node替换为给定的节点或字符串
注意:这些方法的参数可以是一个要插入的任意的DOM节点列表,或者文本字符串(会被自动转换成文本节点)
【示例1】将列表项添加到列表中,以及将文本添加到列表前面和后面.
<ol id="ol">
<li>0</li>
<li>1</li>
<li>2</li>
</ol>
<script>
ol.before('before'); // 将字符串 "before" 插入到 <ol> 前面
ol.after('after'); // 将字符串 "after" 插入到 <ol> 后面
let liFirst = document.createElement('li');
liFirst.innerHTML = 'prepend';
ol.prepend(liFirst); // 将 liFirst 插入到 <ol> 的最开始
let liLast = document.createElement('li');
liLast.innerHTML = 'append';
ol.append(liLast); // 将 liLast 插入到 <ol> 的最末尾
</script>
before
prepend
0
1
2
append
after
1、这些方法所做的工作
2、最终列表
before
<ol id="ol">
<li>prepend</li>
<li>0</li>
<li>1</li>
<li>2</li>
<li>append</li>
</ol>
after
【示例2】插入一个字符串和一个元素
<div id="div"></div>
<script>
div.before('<p>Hello</p>', document.createElement('hr'));
</script>
1、这里的文字都被“作为文本”插入,而不是“作为HTML代码”。因此像<、>这样的符号都会被作转义处理来保证正确显示。
即字符串被以一种安全的方式插入到页面中,就像elem.textContent所做的一样。
2、最终HTML
<p>Hello</p>
<hr>
<div id="div"></div>
四、insertAdjacentText/Element
1、elem.insertAdjacentText(where, text):将text字符串“作为文本”插入而不是作为HTML
2、elem.insertAdjacentElement(whre, elem):插入的是一个元素
五、大多数时候使用append/prepend/before/after,而不是insertAdjacentText/Element,因为它们都可以用于插入节点/文本片段,但是append/prepend/before/after写起来更简单。
作为HTML代码插入
一、作为HTML代码插入的方法
elem.innerHTML
insertAdjacentHTML
insertAdjacentHTML
一、elem.insertAdjacentHTML(where, html)
1、该方法的第一个参数是代码字(code word),指定相对于elem的插入位置。必须为以下之一
(1)beforebegin:将html插入到elem前插入。
(2)afterbegin:将html插入到elem开头
(3)beforeend:将html插入到elem末尾
(4)afterend:将html插入到elem后
2、第二个参数是HTML字符串,该字符串会被“作为HTML”插入。
【示例1】
<div id="div"></div>
<script>
div.insertAdjacentHTML('beforebegin', '<p>Hello</p>');
div.insertAdjacentHTML('afterend', '<p>Bye</p>');
</script>
1、结果
<p>Hello</p>
<div id="div"></div>
<p>Bye</p>
2、插入变体的示意图
【示例2】
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>
<script>
document.body.insertAdjacentHTML("afterbegin", `<div class="alert">
<strong>Hi there!</strong> You've read an important message.
</div>`);
</script>
节点移除
一、如果想要移除一个节点,可以使用node.remove()
【示例1】消息在1秒后消失
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>
<script>
let div = document.createElement('div');
div.className = "alert";
div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";
document.body.append(div);
setTimeout(() => div.remove(), 1000);
</script>
二、如果要将一个元素移动到另一个地方,则无需将其从原来的位置中删除。
三、所有的插入方法都会自动从旧位置删除该节点
【示例1】元素交换
<div id="first">First</div>
<div id="second">Second</div>
<script>
// 无需调用 remove
second.after(first); // 获取 #second,并在其后面插入 #first
</script>
克隆节点:cloneNode
一、调用elem.cloneNode(true)来创建元素的一个“深”克隆——具有所有特性(attribute)和子元素。如果我们调用elem.cloneNode(false),那克隆就不包括子元素。
【示例1】拷贝消息
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>
<div class="alert" id="div">
<strong>Hi there!</strong> You've read an important message.
</div>
<script>
let div2 = div.cloneNode(true); // 克隆消息
div2.querySelector('strong').innerHTML = 'Bye there!'; // 修改克隆
div.after(div2); // 在已有的 div 后显示克隆
</script>
老式的insert/remove方法
一、parentElem.appendChild(node):将node附加为parentElem的最后一个子元素
二、parentElem.insertBefore(ndoe, nextSibling):在parentElem的nextSibling前插入node
三、parentElem.replaceChild(node, oldChild):将parentElem的后代中的oldChild替换为node
四、parentElem.removeChild(node):从parentElem中删除node(假设node为parentElem的后代)
document.write
一、调用document.write(html)意味着将html“就地马上”写入页面。html字符串可以是动态生成的,所以它很灵活。我们可以使用JavaScript创建一个完整的页面并对其进行写入。
二、document.write调用只在页面加载时工作。如果我们稍后调用它,则现有文档内容将被擦除。
【示例1】
<p>After one second the contents of this page will be replaced...</p>
<script>
// 1 秒后调用 document.write
// 这时页面已经加载完成,所以它会擦除现有内容
setTimeout(() => document.write('<b>...By this.</b>'), 1000);
</script>
三、优缺点
1、缺点:
(1)在某种程度上讲,它在“加载完成”阶段是不可用的,这是它的缺陷。
2、优点
(1)运行速度非常快,而且不涉及DOM修改。它直接写入到页面文本中,而此时DOM尚未构建。从技术上讲,当在浏览器正在读取(“解析”)传入的HTML时调用document.write方法来写入一些东西,浏览器会像它本来就在HTML文本中那样使用它。
DocumentFragment
一、DocumentFragment是一个特殊的DOM节点,用作来传递节点列表的包装器(wrapper)。
二、我们可以向其附加其他节点,但是当我们将其插入某个位置时,则会插入其内容。
【示例1】下面这段代码中的getListContent会生成带有
- 中
<ul id="ul"></ul>
<script>
function getListContent() {
let fragment = new DocumentFragment();
for(let i=1; i<=3; i++) {
let li = document.createElement('li');
li.append(i);
fragment.append(li);
}
return fragment;
}
ul.append(getListContent()); // (*)
</script>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
1、上述示例可以改为返回一个节点数组
【示例2】
<ul id="ul"></ul>
<script>
function getListContent() {
let result = [];
for(let i=1; i<=3; i++) {
let li = document.createElement('li');
li.append(i);
result.push(li);
}
return result;
}
ul.append(...getListContent()); // append + "..." operator = friends!
</script>
【示例3】向当前#list的最后元素之后添加100个新的li节点,合理且安全的操作方式是?
<ul id="list">
<li>1</li>
<li>2</li>
</ul>
A. 通过循环方式创建新的li节点,并依次添加到#list中
B.先将#list节点的display设置为none,通过循环方式创建新的li节点,并依次添加到#list中,最后再将#list节点的display设置为block
C.取出#list中现有的li节点的html,将它与新增的li节点对应的html代码拼接成字符串,一次性插入到#list中
D.创建Fragment,通过循环方式创建新的li节点,添加到Fragment中,最后再将Fragment添加到#list中
1、答案:D
2、解析:合理的方式应当是在保证性能的同时避免安全问题。
A 显然不行,每次插入都会触发重绘和重排;
B 也不行,虽然因为隐藏避免了重绘,但因为没有脱离文档流,每次插入时重排还是会发生;
CD 可能有争议,因为都只会触发一次重绘和重排。按理来说直接操作HTML是性能最好的手段,因为就是一个简单的字符串操作,但是可能存在XSS攻击的风险,就不如 Fragment 安全。