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>