思路:首先找到当前current方格在网格中对应的横纵坐标x和y,再根据按键移动方向控制x和y的变化,尤其注意边界条件,即上下和左右的边界调整。其中找到横纵坐标x和y,使用的方法是,首先找到所有tr,并遍历tr是否包含current,如果是则锁定在哪一行即纵坐标,接着再找到该tr内的所有td,并遍历td是否等于current,如果是则锁定在哪一列即横坐标,注意querySelectorAll得到的是伪数组,需要使用Array.from转换为真数组。
<body> <div id="jsContainer"> <table class="game"> <tbody> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td class="current"></td> <td></td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> </tbody> </table> </div> <script type="text/javascript"> function bind() { const con = document.getElementById("jsContainer") const rows=con.querySelectorAll('tr') const cols=rows[0].querySelectorAll('td') document.onkeydown = event => { if (!event) return; var code = event.keyCode || ''; if (!{'37': 1, '38': 1, '39': 1, '40': 1}[code]) return; event.preventDefault && event.preventDefault(); //TODO: 请实现按键控制 const cur=con.querySelector('.current') let x=0 let y=0 // 找到cur对应的横纵坐标 y是哪一行即纵 x是哪一列即横 Array.from(rows).forEach((tr,index)=>{ if(tr.contains(cur)) { y=index Array.from(tr.querySelectorAll('td')).forEach((td,i)=>{ if(td===cur) { x=i } }) } }) //上 if(Number(code)===38) { if(y===0) y=rows.length-1 else y-- } //下 else if(Number(code)===40) { if(y===rows.length-1) y=0 else y++ } //左 else if(Number(code)===37) { if(x===0) x=cols.length-1 else x-- } //右 else if(Number(code)===39) { if(x===cols.length-1) x=0 else x++ } cur.classList.remove('current') rows[y].querySelectorAll('td')[x].classList.add('current') }; } bind() </script> </body>
总结:在javascript中,判断父子节点的包含关系以及寻找父子节点,是一个频繁使用的事情,此处特意总结一下以便后续复习。parentNode属性用于获取当前节点的父节点;childNodes属性用于获取当前节点的所有子节点列表;children属性用于获取当前节点的所有子节点元素列表(不包含文本节点和注释节点);firstChild属性用于获取当前节点的第一个子节点;lastChild属性用于获取当前节点的最后一个子节点;firstElement属性用于获取当前节点的第一个子元素节点;lastElement属性用于获取当前节点的最后一个子元素节点;nextSibling属性用于获取当前节点的下一个兄弟节点;previousSibling属性用于获取当前节点的上一个兄弟节点;contains方法用于判断当前节点是否是另一个节点的后代节点;
<div id="parent"> <p>这是一个段落</p> 文本节点 <span>这是一个span元素</span> <!-- 这是一个注释节点 --> </div> var parent = document.getElementById("parent"); var childNodesList = parent.childNodes; console.log(childNodesList.length); // 输出:4,包括文本节点、元素节点和注释节点 var childrenList = parent.children; console.log(childrenList.length); // 输出:2,只包括元素节点,不包括文本节点和注释节点 console.log(childNodesList[0].nodeType); // 输出:1,元素节点 console.log(childNodesList[1].nodeType); // 输出:3,文本节点 console.log(childNodesList[3].nodeType); // 输出:8,注释节点
注意:childNodesList包含了父节点<div>下的所有子节点,包括文本节点、元素节点和注释节点,而childrenList只包含了元素节点<p>和<span>。通过nodeType属性可对各个节点类型进行判断,其中1表示元素节点,3表示文本节点,8表示注释节点。