一、节点之间的层级:父子兄
节点主要 有父子兄关系
1.父节点操作
语法:子节点.parentNode,得到的是一个元素不是集合
比如我选了一个类名叫zzy的元素
var zzy = document.querySelector('.zzy');
那么要获取zzy 的父节点,只需要zzy.parentNode
console.log(zzy.parentNode);
注意,得到的时离该元素最近的父节点(亲爹),如果找不到父节点,就返回null
2.子节点操作
(1)父节点.childNodes(目测用的不多)
这个得到的是所有的子节点,除了我们想要的元素节点,还有换行、空格等文本节点,所以要想拿到元素,就要循环遍历,因为元素节点nodeType是1,属性2,文本3。这个真的是麻烦的一塌糊涂
(2)父节点.children(这个才是常用的)
这个只返回子元素节点,其他的都滚蛋,不错
3.获取第一个子元素和最后一个子元素
父节点.firstChild和父节点,lastChild,和父节点.childNodes差不多,返回的是所有类型节点中的第一个和最后一个,所以这两个用的不多,真正带劲的是:
父节点.firstElementChild和父节点.lastElementChild,这俩都是返回的第一个和最后一个元素节点,不错
但是!这个不错的方法,只有IE9以上支持。
这可咋办!
不要怕,在实际开发中,我们都会用父元素.children[i],既没有兼容问题,又能返回首尾元素节点
比如这里有这么几个东西
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
那么首尾元素分别是:
var ul = document.querySelector('ul');
console.log(ul.children[0]);
console.log(ul.children[ul.children.length - 1]);
案例:下拉菜单
下拉菜单的常用布局:头部放个ul,ul里有几个li,每个li里面上边a,下边放ul包几个li
先放html和css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
li {
list-style: none;
}
a {
color: black;
text-decoration: none;
}
.box {
margin: 100px auto;
width: 400px;
height: 50px;
}
.box>li {
float: left;
/* position: relative; */
width: 100px;
height: 50px;
line-height: 50px;
text-align: center;
}
.box>li a {
display: inline-block;
width: 100px;
height: 50px;
}
.box>li a:hover {
background-color: #ccc;
}
.box ul {
display: none;
/* position: absolute; */
left: 0;
top: 50px;
border-left: 1px solid red;
border-right: 1px solid red;
}
.box ul li {
border-bottom: 1px solid red;
}
.box li ul li:hover {
background-color: rgb(247, 149, 149);
}
<ul class="box">
<li>
<a href="#">微博</a>
<ul>
<li>私信</li>
<li>评论</li>
<li>@我</li>
</ul>
</li>
<li>
<a href="#">微博</a>
<ul>
<li>私信</li>
<li>评论</li>
<li>@我</li>
</ul>
</li>
<li>
<a href="#">微博</a>
<ul>
<li>私信</li>
<li>评论</li>
<li>@我</li>
</ul>
</li>
<li>
<a href="#">微博</a>
<ul>
<li>私信</li>
<li>评论</li>
<li>@我</li>
</ul>
</li>
</ul>
然后是JavaScript,其实这部分代码比较好理解,就是我第一步获取元素老写错
var box = document.querySelector('.box'); //这个地方老写错……
var lis = box.children; //得到4个li
for (var i = 0; i < lis.length; i++) {
lis[i].onmouseover = function () {
//鼠标放上去,这个li的第二个孩子(ul)显示
this.children[1].style.display = 'block';
}
lis[i].onmouseout = function () {
this.children[1].style.display = 'none';
}
}
4.获取兄弟节点
和父子一样,有个获取下一个(全部中的下一个)节点和上一个节点的方法,没什么p用
真正有用的是下面这个,但是又有兼容性问题
那咋办呢?实际上兄弟节点用的少,了解下就行了
二、节点的增删
1.创建和添加元素节点
先创建,再添加
比如我要创建一个li的元素节点
var myli = document.createElement('li');
(1)父元素.appendChild(要添加的元素)
先获取它的父元素,然后把我创建的li添加进去,默认是添加到父元素的尾部
var ul = document.querySelector('ul');
//把我创建的li放到ul里面去,默认添加到尾部
ul.appendChild(myli);
(2)父元素.insertBefore(要添加的元素节点,添加到谁前面)
下面这个代码就是在ul的第一个孩子前添加我创建的li
ul.insertBefore(myli, ul.children[0]);
(3)简单添加留言案例
<textarea name="" id="content" cols="30" rows="10"></textarea> <button>发布</button>
<ul class="my">
<li></li>
</ul>
注意获取textarea和input里面的值是.value
//简单留言案例
var content = document.querySelector('textarea');//获取文本域
var my = document.querySelector('.my'); //获取ul
var btn = document.querySelector('button'); //获取button
//点击按钮就生成一个li,添加到ul里
btn.onclick = function () {
if (content.value == '') {
alert('您没有输入任何内容');
} else {
var li = document.createElement('li');
li.innerHTML = content.value;
// my.appendChild(li);
my.insertBefore(li, my.children[0]);
}
}
2.删除元素节点
(1)使用父元素.removeChild(子元素)
<button>删除</button>
<ul>
<li>zzy</li>
<li>aoaoao</li>
<li>3213</li>
</ul>
var ul = document.querySelector('ul');
var btn = document.querySelector('button');
btn.onclick = function () {
if (ul.children.length != 0) {
ul.removeChild(ul.children[0]);
} else {
//如果删完了就禁用按钮
this.disabled = true;
}
}
(2)添加留言和删除留言(上面案例的升级版)
这里遇到个问题就是删除留言的代码要放到btn.onclick里面,要不然会先执行下面这段(注意代码的执行顺序),等点击btn的时候才添加a标签,那样先获取的as就是空了
//简单添加删除留言案例
var content = document.querySelector('textarea');//获取文本域
var my = document.querySelector('.my'); //获取ul
var btn = document.querySelector('button'); //获取button
//1.添加留言:点击按钮就生成一个li,添加到ul里
btn.onclick = function () {
if (content.value == '') {
alert('您没有输入任何内容');
} else {
var li = document.createElement('li');
//把textarea里面的内容取过来,然后再添加一个a标签
li.innerHTML = content.value + "<a href='javascript:;'>删除</a>";//卧槽,还能这么写
// my.appendChild(li);
my.insertBefore(li, my.children[0]);
}
//2.删除留言
//先获取a标签
//这段代码要放到btn.onclick里面,要不然会先执行下面这段,等点击btn的时候才添加a标签,那样as就是空了
var as = document.querySelectorAll('a');
for (var i = 0; i < as.length; i++) {
as[i].onclick = function () {
my.removeChild(this.parentNode);
}
}
}
3.复制元素节点
(1)语法:node.cloneNode()
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
把ul里的第一个孩子复制过来,放到ul的尾部
var ul = document.querySelector('ul');
var li1 = ul.children[0].cloneNode();
ul.appendChild(li1);
(2)浅拷贝和深拷贝
node.cloneNode()括号里默认是false,也就是浅拷贝,只copy元素不copy内容
node.cloneNode(true),如果括号里是true就是深拷贝,copy元素和内容
三、动态生成表格
创建html和css
table {
width: 400px;
margin: 100px auto;
text-align: center;
border-collapse: collapse;
}
th,
td {
border: 1px solid #333;
}
thead tr {
height: 40px;
background-color: #ccc;
}
tbody tr {
height: 30px;
}
<table cellspacing="0">
<thead>
<tr>
<th>姓名</th>
<th>科目</th>
<th>成绩</th>
<th>操作</th>
</tr>
</thead>
<tbody>
</tbody>
1.先准备好学生的数据,以对象形式存储在数组中
var datas = [
{
names: '彭于晏',
subject: 'javascript',
score: 87
},
{
names: '小婷',
subject: 'javascript',
score: 100
}, {
names: '张大仙',
subject: 'javascript',
score: 99
}, {
names: '刘亦菲',
subject: 'javascript',
score: 60
}
]
2.创建行tr
var tbody = document.querySelector('tbody');
for (var i = 0; i < datas.length; i++) {
//先创建,再添加,二者要绑定在一起
var tr = document.createElement('tr');
tbody.appendChild(tr);
}
3.创建行里面的单元格td
var tbody = document.querySelector('tbody');
for (var i = 0; i < datas.length; i++) {
//先创建,再添加,二者要绑定在一起
var tr = document.createElement('tr');
tbody.appendChild(tr);
//3.行里面创建单元格td
for (var k in datas[i]) { //遍历对象,k是属性的个数,有几个属性就循环几次
var td = document.createElement('td');
tr.appendChild(td);
}
4.添加td里面的数据
每个单元格的数据可以用对象里的datas[i][k]来获取
我的写法:
我一开始想获取所有的td元素数组,然后设置一个flag来获取相应的td,但是我发现这样的话因为每次循环td个数都在增加,所以td的数组也在变化,所以intd[flag]并不能添加每一行的数据,每次添加都是添加到第一行,除非flag=3*i。后来想了个办法,可以使用tr.children[flag],这样就可以实现给每行的每个单元格添加相应的数据
var tbody = document.querySelector('tbody');
for (var i = 0; i < datas.length; i++) {
//先创建,再添加,二者要绑定在一起
var tr = document.createElement('tr');
tbody.appendChild(tr);
//3.行里面创建单元格td
var flag = 0;
for (var k in datas[i]) { //遍历对象,k是属性的个数,有几个属性就循环几次
var td = document.createElement('td');
tr.appendChild(td);
// var intd = document.getElementsByTagName('td'); //这是获取所有的td,包括前几行,不对
// intd[flag].innerHTML = datas[i][k];
tr.children[flag].innerHTML = datas[i][k];
flag++;
}
}
看了老师的写法,我才知道我想多了……
其实只需要每次添加完td后,直接往td塞数据,然后再添加到tr里就行
var tbody = document.querySelector('tbody');
for (var i = 0; i < datas.length; i++) {
//先创建,再添加,二者要绑定在一起
var tr = document.createElement('tr');
tbody.appendChild(tr);
//3.行里面创建单元格td
for (var k in datas[i]) { //遍历对象,k是属性的个数,有几个属性就循环几次
var td = document.createElement('td');
//给每个单元格塞数据
td.innerHTML = datas[i][k];
tr.appendChild(td);
}
}
5.添加删除操作的链接
var tbody = document.querySelector('tbody');
for (var i = 0; i < datas.length; i++) {
//先创建,再添加,二者要绑定在一起
var tr = document.createElement('tr');
tbody.appendChild(tr);
//3.行里面创建单元格td
for (var k in datas[i]) { //遍历对象,k是属性的个数,有几个属性就循环几次
var td = document.createElement('td');
//4.给每个单元格先塞数据,再放到tr里
td.innerHTML = datas[i][k];
tr.appendChild(td);
}
//5.添加删除操作的链接
var tdend = document.createElement('td');
tdend.innerHTML = "<a href='javascript:;'>删除</a>";
tr.appendChild(tdend);
}
6.最后一步,给删除操作添加删除事件
//2.先创建行tr,有几个人就创建几行
var tbody = document.querySelector('tbody');
for (var i = 0; i < datas.length; i++) {
//先创建,再添加,二者要绑定在一起
var tr = document.createElement('tr');
tbody.appendChild(tr);
//3.行里面创建单元格td
for (var k in datas[i]) { //遍历对象,k是属性的个数,有几个属性就循环几次
var td = document.createElement('td');
//4.给每个单元格先塞数据,再放到tr里
td.innerHTML = datas[i][k];
tr.appendChild(td);
}
//5.添加删除操作的链接
var tdend = document.createElement('td');
tdend.innerHTML = "<a href='javascript:;'>删除</a>";
tr.appendChild(tdend);
}
//6.给删除按钮添加事件
var as = document.querySelectorAll('a'); //这里一定要是All
for (var i = 0; i < as.length; i++) {
as[i].onclick = function () {
tbody.removeChild(this.parentNode.parentNode);
}
}
四、innerHTML和createElement创建元素谁效率更高?
如果innerHTML是拼接字符串标签的话,拼1000个要用3000ms,但是如果采用数组形式拼接,只用7ms
如果用createElement创建1000个同样的元素,大概18ms