问题:给定一个DOM元素结构如下:
<ul>
<li/>
<li/>
</ul>
如何翻转上述DOM结构?
考点: DOM API,DocumentFragment,ul,li
背景知识
DOM API
节点之间导航
对DOM的所有操作都是从document对象开始的,从这个对象我们可以到达任何节点,如下图所示:
<html> = document.documentElement<body> = document.body<head> = document.head需要注意的是,document.body可能为null,如果body为null,内嵌在
<head>标签中<script>脚本是无法访问document.body元素的,因为浏览器还没有读到其中的内容:
<html>
<head>
<script>
alert( "From HEAD: " + document.body ); // null, there's no <body> yet
</script>
</head>
<body>
<script>
alert( "From BODY: " + document.body ); // HTMLBodyElement, now it exists
</script>
</body>
</html>
子元素:childNodes\firstChild\lastChild
- 子元素,指的是元素下的所有同级元素,举例,都是的子元素
- 子系元素,指的是所有嵌套在一个指定元素中的元素,包括这些元素的子元素,以及所有后代元素
假如有个元素为elem,那么调用elem.childNodes将返回所有elem元素下的子节点和全部文本节点,示例代码:
<html>
<body>
<div>Begin</div>
<ul>
<li>Information</li>
</ul>
<div>End</div>
<script>
for (let i = 0; i < document.body.childNodes.length; i++) {
alert( document.body.childNodes[i] ); // Text, DIV, Text, UL, ..., SCRIPT
}
</script>
...more stuff...
</body>
</html>
上面script标签中访问了body节点下childNodes,得到的结果为Text, DIV, Text, UL, ..., SCRIPT,值得注意的是,由于访问调用是在<scritpt>节点下的,因而<script>标签之后的内容其实是访问不到的,但是确实存在。
- firstChild和lastChild属性是访问第一个和最后一个子元素的快捷方式
如果想判断一个节点是否有子节点,可以调用elem.hasChildNodes()
虽然childNodes看起来像个数组,但其实它并不是数组,而是一个集合,也就是类似数组的可迭代对象,所以常见的用于数组操作的方法,不能够用在childNodes身上,比如childNodes[i] = ...这种操作是不可以的。
- parentNode,previousSibling,nextSibling
通过parentNode访问父节点,通过previousSibling访问上一个节点,通过nextSibling访问下一个节点,这里访问的是节点,也就是说可能会访问到如文本节点、注释节点等等。
元素之间导航
如果只想访问所有HTML标签元素的话,则childNodes相关的属性是不能满足需求的。
上图中和前面的区别只是中间加了Element:
- children,只获取类型为元素节点的子节点
- firstElementChild \ lastElementChild,第一个和最后一个元素
- previouseElementSibling \ nextElementSibling,兄弟元素
- parentElement,父元素
alert( document.documentElement.parentNode ); // document
alert( document.documentElement.parentElement ); // null
parentNode和parentElement的区别是parentNode返回任意类型的父节点,parentElement只返回元素类型父节点。通常来说二者是一样的,但是上面代码已给出唯一的区别。
DocumentFragment
DocumentFragment是一个特殊的DOM节点,用于传递节点列表的包装器,说得简单直白些就是将DocumentFragment挂载到其他节点下时,DocumentFragment会和该节点自动融合,所以看起来好像没有DocumentFragment节点一样
ul, ol, dl
ul,无序列表
<ul id="list">
<li></li>
<li></li>
<li></li>
</ul>
ol,有序列表
<ol id="list2">
<li>1</li>
<li>2</li>
<li>3</li>
</ol>
dl,dt,dd三者通常一起用,用来代表定义性列表,通常是描述一些技术术语,或用于展示key-value这样的键值对
<dl>
<dt>属性名</dt>
<dd>属性值属性值属性值属性值属性值属性值属性值属性值</dd>
</dl>
解决方案
<body>
<ul id="list">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<input type="button" id="reverseBtn" value="翻转"/>
<script>
let list = document.getElementById('list')
function reverseChildNodes(node) {
//创建包装器
let frag = node.ownerDocument.createDocumentFragment();
//每次取出node节点的最后一个子节点
while(node.lastChild) {
//放入包装器中,domApi中取出节点的过程中也会将节点从原父节点中移除
//然后形成倒序结构
frag.appendChild(node.lastChild)
}
//将包装器挂在node节点之下,并于node融合
node.appendChild(frag)
}
btn.onclick = reverseChildNodes.bind(this, list);
</script>
</body>
参考文献
【1】现代Javascript教程 zh.javascript.info