JavaScript DOM 5个练习题
课堂练习
1. 遍历元素节点树,要求不能用children属性
2. 封装函数,返回元素e的第n层祖先元素
3. 封装函数,返回元素e的第n个兄弟元素节点,n为正,返回后面的兄弟元素节点,n为负,返回前面的,n为0,返回自己。
4. 编辑函数,封装children功能,解决以前部分浏览器的兼容性问题
5. 自己封装hasChildren()方法,不可用children属性
1、遍历元素节点树(在原型链上编程),要求不能用children属性。
遍历元素节点树分为两种提议:
1. 第一种是简单的,
比如给一个父节点,把它的子节点都给遍历出来,
什么是便利?全部查找出来并且打印出来,这是最简单的方法用 childNodes 挨个打印。
2. 第二种就真正要打印出一个树形结构,
比如给你一个节点树(树型结构)把div里面的层级结构全打印出来
第一层 p、span
第二层 strong、b
<div> <p></p> <span> <strong></strong> <b></b> </span> </div>
先看 div 子元素节点打印出来,然后再挨个判断这些子元素节点还有没有子元素节点,有就一直往下打印,递归...
<div>
<p></p>
<span>
<strong></strong>
<b></b>
</span>
<i>
<strong>
<address></address>
<sub></sub>
</strong>
<b></b>
</i>
</div>
<script>
Element.prototype.retChildren = function(num){
var child = this.childNodes
var len = child.length;
var str = '';
num++;
for(var i =0; num > i; i++){
if(num == 1){
str = '';
}else{
str+='-';
}
}
for(var i = 0; len > i; i++){
if(child[i].nodeType == 1){
document.write(str + num + ": " + child[i].nodeName + '<br/>');
if(child[i].hasChildNodes()){
child[i].retChildren(num);
}
}
}
}
var oDiv = document.getElementsByTagName('div')[0];
console.log(oDiv.retChildren(0));
</script>2. 封装函数,返回元素e的第n层祖先元素。
把 i 标签给我们,返回 i 标签的第二层祖先(父级)元素节点,第一层是 span、第二层是 strong。
<div> <b></b> abc <!-- this is comment --> <strong> <span> <i></i> </span> </strong> <em></em> </div>
封装一个retParent方法把元素传进去,把第几个祖先节点传进去,传进去之后函数运算完要返回这个结果。
/**
* retParent(elem, n)
*
* elem object 元素节点
* n int 第几个父亲级节点
*/
function retParent(elem, n){
}n是多少就有多少个parentElement层,基于n为循环条件,每次循环操作让elem等于它的父级,最后一次把elem返回。
<div>
<b></b>
abc
<!-- this is comment -->
<strong>
<span>
<i></i>
</span>
</strong>
<em></em>
</div>
<script>
function retParent(elem, n){
while(n){
elem = elem.parentElement;
n --;
}
return elem;
}
var i = document.getElementsByTagName('i')[0];
console.log(retParent(i ,2)); // strong
console.log(retParent(i ,3)); // div
console.log(retParent(i ,0)); // 0不进入循环,直接返回它自己
console.log(retParent(i ,10)); // Uncaught TypeError: Cannot read property 'parentElement' of null
</script>如果参数n填10就报错了,因为没有第10个到第6个父级就是null了,null没有parentElement怎么做一个容错?
看报错信息"Cannot read property parentElement of null"也就是说往上求父级,一层层求到最后有一个null了,null调用parent.Elment就报错了,判断一下如果elem等于null了就结束。
<div>
<b></b>
abc
<!-- this is comment -->
<strong>
<span>
<i></i>
</span>
</strong>
<em></em>
</div>
<script>
function retParent(elem, n){
while(elem && n){ // 如果elem等于null就没有意义了,就结束返回null就行了
elem = elem.parentElement;
n --;
}
return elem;
}
var i = document.getElementsByTagName('i')[0];
console.log(retParent(i ,5)); // 到5是html
console.log(retParent(i ,6)); // 到6就是null了,是null"while(elem && n)"并且关系不成立,直接退出了
console.log(retParent(i ,10)); // 6以上返回都是null了
</script>4、编辑函数,封装children功能,解决以前部分浏览器的兼容性问题。
chilren兼容性其实挺好,这个题的意思是用我们自己的方法实现chilren的功能。
chilren的功能是找该元素的"子元素节点"不是"子点节","子节点"五花八门什么都有,现在就要"子元素节点",而且不用children来实现children的功能。
封装一个MyChildren()方法,在原型链上编写。
Element.prototype.MyChilren = function(){
return ...;
}"oDiv.MyChildren()"调用这个方法就能返回"子元素节点"的集合,不能chilren用childNodes代替
<div>
<b></b>
abc
<!-- this is comment -->
<strong>
<span>
<i></i>
</span>
</strong>
<em></em>
</div>
<script>
Element.prototype.MyChilren = function(){
var child = this.childNodes;
var len = child.length;
var arr = [];
for(var i = 0; i < len; i ++){
if(child[i].nodeType == 1){ // 挑出元素节点
arr.push(child[i]);
}
}
return arr;
}
var oDiv = document.getElementsByTagName('div')[0];
console.log(oDiv.MyChilren()); // [b, strong, em]
</script>这题考的是如何区分"元素节点"和"非元素节点"。
5、自己封装hasChildren()方法,不可以用children属性。
有一个Node.hasChildNodes()方法,有childNodes返回true没有返回false。
这个hasChildren()基于上面的MyChildren()简单改一下,先用childNodes然后还是循环看看有没有元素节点,如果有元素节点直接返回true没有返回false。
<div>
<b></b>
abc
<!-- this is comment -->
<strong>
<span>
<i></i>
</span>
</strong>
<em></em>
</div>
<script>
Element.prototype.MyHasChildren = function(){
var child = this.childNodes;
var len = child.length;
var arr = [];
for(var i = 0; i < len; i ++){
if(child[i].nodeType == 1){
return true;
}
}
return false;
}
var b = document.getElementsByTagName('b')[0];
console.log(b.MyHasChildren()); // p标签内没有元素返回false
var strong = document.getElementsByTagName('strong')[0];
console.log(strong.MyHasChildren()); // strong标签有元素返回true
</script>3、封装函数,返回元素e的第n个兄弟元素节点,n为正,返回后面的兄弟元素节点,n为负,返回前面的,n为0,返回自己。
意思是封装一个函数"retSibling(e, n)",参数e是传进去的元素,参数n是元素的第几个"兄弟元素节点",n为正返回后面的,n为负返回前面的,n为0返回自己。
function retSibling(e, n){
}基本思路和"返回元素的第n层祖先元素"差不多,祖先节点是parentElement,兄弟节点是nextSibling、nextElementSibling、previousSibling、previousElementSibling这些东西,然后循环调用。
由于n分正负,分正负就有两种处理方法,为正的是一种循环方法,为负的是一种循环方法写两个循环。
function retSibling(e, n){
if(n > 0){
while(n){
n --
}
}else{
while{
n ++
}
}
}常规结构是这么写的,但是现在不这么写。
用一个简单点的,先写white循环,以n作为循环基数,n为0的时候循环退出,然后在white里面判断。
function retSibling(e, n){
while(n){
if(n > 0){
n --; // n大于0 n--
}else{
n ++; // n小于0 n++
}
}
}这样一个while循环就能解决所有的问,在wilhe里面处理问题就不用写两个循环了,写起来效率更高。
不考虑兼容性的问题
function retSibling(e, n){
while(n){
if(n > 0){
e = e.nextElementSibling;
n --; // n大于0肯定n--不然怎么循环呢
}else{
e = e.previousElementSibling;
n ++;
}
}
return e; // 最后返回这个e
}写到这先不测试,肯定是有一点问题的,比如n特别小没事如果n超大呢,n到边界的时候会有null,"null.nextElementSibling"肯定会报错,这块要加一个兼容?
<div>
<span></span>
<p></p>
<strong></strong>
<!-- this is comment -->
<i></i>
<address></address>
</div>
<script>
function retSibling(e, n){
while(e && n){ // 这加一个 e
if(n > 0){
e = e.nextElementSibling;
n --;
}else{
e = e.previousElementSibling;
n ++;
}
}
return e; // 最后返回这个 e
}
var strong = document.getElementsByTagName('strong')[0];
console.log(retSibling(strong, 1)); // <i></i>
console.log(retSibling(strong, 2)); // <address></address>
console.log(retSibling(strong, 4)); // null
console.log(retSibling(strong, 10000)); // null
console.log(retSibling(strong, -1)); // <p></p>
console.log(retSibling(strong, -2)); // <span></span>
console.log(retSibling(strong, -3)); // null
console.log(retSibling(strong, -300)); // null
console.log(retSibling(strong, 0)); // 0是自己没处理就直接就跳出循环,返回的是自己<strong></strong>
</script>现在在这个基础上加深点难度,nextElementSibling和previousElementSibling这两个兼容性不好,IE9以下不能用,如果要在IE9以下要实现这个功能怎么办?
还有一个方法是"nextSibling",但是nextSibling求的是下一个"兄弟节点",但是我们要求的是"兄弟元素节点"
<div>
<span></span>
<p></p>
<strong></strong>
<!-- this is comment -->
<i></i>
<address></address>
</div>
<script>
function retSibling(e, n){
while(e && n){
if(n > 0){
if(0 && e.nextElementSibling){
e = e.nextElementSibling;
// 判断如果IE9以上还用nextElementSibling
// 加一个控制"if(0 && e.nextElementSibling)",0判断强制程序走else下面的for循环
}else{
// 如果IE9以下写兼容
// 每一圈while循环都要找到下一个兄弟节点,"e.nextSibling"找的不是"元素节点"
// for循环
// 第一步 e = e.nextSibling找的是兄弟节点
// 第二步 判断"e.nodeType != 1",不等于1说明不是元素节点,不是元素节点要继续e = e.nextSibling;
// e = e.nextSibling可以写大括号里
// for(1. e = e.nextSibling; 2.e.nodeType != 1){
// 3. e = e.nextSibling
// }
// e = e.nextSibling写这也行
for(e = e.nextSibling; e.nodeType != 1; e = e.nextSibling);
// 然后for循环的执行体里面什么都不用写,如果for循环执行体里是空,这个执行体可以直接去掉,在后面加一个分号就可以。
// for和if可以不用写执行体,加分号就行。
}
n --;
}else{
e = e.previousElementSibling;
n ++;
}
}
return e;
}
var strong = document.getElementsByTagName('strong')[0];
// 看一下不加负数加正数
console.log(retSibling(strong, 1)); // <i></i>
console.log(retSibling(strong, 2)); // <address></address>
console.log(retSibling(strong, 3)); // 加3出问题了 Uncaught TypeError: Cannot read property 'nodeType' of null at retSibling
</script>为什么到"3"会报错?找到address后"2"就结束了,address后面是文本节点,文本节点后面没有东西了,没东西了是null,往下会判断"null.nodeType != 1",null没有nodeType肯定会报错Cannot read property 'nodeType'。
要加一个容错判断一下e就完事了,如果e是null就停止。
<div>
<span></span>
<p></p>
<strong></strong>
<!-- this is comment -->
<i></i>
<address></address>
</div>
<script>
function retSibling(e, n){
while(e && n){
if(n > 0){
if(0 && e.nextElementSibling){
e = e.nextElementSibling;
}else{
for(e = e.nextSibling; e && e.nodeType != 1; e = e.nextSibling);
// 这里判断一下e如果是null就别看"e.nodeType != 1",直接结束了
}
n --;
}else{
e = e.previousElementSibling;
n ++;
}
}
return e;
}
var strong = document.getElementsByTagName('strong')[0];
console.log(retSibling(strong, 1)); // <i></i>
console.log(retSibling(strong, 2)); // <address></address>
console.log(retSibling(strong, 3)); // null
console.log(retSibling(strong, 4)); // null
</script>完善这个函数
<div>
<span></span>
<p></p>
<strong></strong>
<!-- this is comment -->
<i></i>
<address></address>
</div>
<script>
function retSibling(e, n){
while(e && n){
if(n > 0){
if(e.nextElementSibling){
e = e.nextElementSibling;
}else{
for(e = e.nextSibling; e && e.nodeType != 1; e = e.nextSibling);
}
n --;
}else{
if(e.previousElementSibling){
e = e.previousElementSibling;
}else{
for(e = e.previousSibling; e && e.nodeType != 1; e = e.previousSibling);
}
n ++;
}
}
return e;
}
var strong = document.getElementsByTagName('strong')[0];
console.log(retSibling(strong, 1)); // <i></i>
console.log(retSibling(strong, 2)); // <address></address>
console.log(retSibling(strong, 3)); // null
console.log(retSibling(strong, 4)); // null
console.log(retSibling(strong, -1)); // <p></p>
console.log(retSibling(strong, -2)); // <span></span>
console.log(retSibling(strong, -3)); // null
console.log(retSibling(strong, -4)); // null
</script>