0、写在前面的话
自己对前端的东西一直不是很熟,现在开始要想办法从前端各个地方去获取想要的属性值的时候,也基本是在网上现炒现卖,几周下来,发现自己还是迷迷糊糊,可以算是一无所获。
所以就抽时间,把这一块的东西收集整理一下,免得每次为了得到一个值要上网查询鼓捣一万年,实在是浪费时间,知其然知其所以然,才能让问题迎刃而解。
这篇博文应该说结构还不够完整,有些知识点还没提到,覆盖的内容其实主要还是为了满足我现在入门知识理解的一个需求,日后再进行完善吧,这里先贴一些相关概念的参考链接,以后方便参考和添加:
1、理解节点
HTML文档中,所有内容都是 “节点”,是一个Element,这意味着:
- 整个文档是一个文档节点
- 每个 HTML 元素是元素节点
- HTML 元素内的文本是文本节点
- 每个 HTML 属性是属性节点
- 注释是注释节点
先看下面的片段:
<html>
<head>
<title>DOM 教程</title>
</head>
<body>
<h1>DOM 第一课</h1>
<p>Hello world!</p>
</body>
</html>
9
1
<html>
2
<head>
3
<title>DOM 教程</title>
4
</head>
5
<body>
6
<h1>DOM 第一课</h1>
7
<p>Hello world!</p>
8
</body>
9
</html>
从上面的 HTML 中:
- <html> 节点没有父节点;它是根节点
- <head> 和 <body> 的父节点是 <html> 节点
- 文本节点 "Hello world!" 的父节点是 <p> 节点
并且:
- <html> 节点拥有两个子节点:<head> 和 <body>
- <head> 节点拥有一个子节点:<title> 节点
- <title> 节点也拥有一个子节点:文本节点 "DOM 教程"
- <h1> 和 <p> 节点是同胞节点,同时也是 <body> 的子节点
并且:
- <head> 元素是 <html> 元素的首个子节点
- <body> 元素是 <html> 元素的最后一个子节点
- <h1> 元素是 <body> 元素的首个子节点
- <p> 元素是 <body> 元素的最后一个子节点
重点:
- 获得的每个节点,都是一个HTML 对象!(点击查看<tr>标签代表的对象)都有属性!
- HTML DOM 允许 JavaScript 改变 HTML 元素的内容
2、JS获取节点
我们知道,HTML文档载入浏览器后,会成为Document对象,这个对象让我们可以从脚本中对HTML页面所有元素进行访问,我们经常获取元素使用的几大方法:getElementById()、 getElementsByClassName、getElementsByName()、getElementsByTagName()就归属其下,也是我们最常使用到的方法。
- getElementById() 根据id属性
- getElementsByName() 根据name属性
- getElementsByClassName 根据class的名称
- getElementsByTagName() 根据标签
2.1 获取单一节点
我们用document.getElementById(id)这个方法举例,因为是根据HTML中唯一的id来获取,所以得到的是单一节点,这个节点实际就是一个对象,也就是说,这个方法返回的是一个HTML对象。
每个标签的出现,都意味着有一个对应的对象被创建,比如 <select> 标签的存在,意味着有一个 Select对象 诞生;<option> 标签,意味着有一个 Option对象 诞生。
我们说过,每个节点,都是一个对象,是对象,就有属性,所以我们想要获取对应的值,你只要找到了这个对象,完全可以到w3school上查看下这个对象有哪些属性,直接调用就可以了。
实际上,很多属性是大部分对象都有的,也是我们所常用的:
- id 该节点的id值
- value 该节点的value值
- text 节点的文本值
- name 节点的name,名称
诸如此类,还有很多,具体的可以直接查询手册。
来看看示例,假如有下面的HTML片段,现在想获取关于 “张三” 的各类信息:
<table class="manage-table space" id="dataTable">
<tr class="center">
<th rowspan="2">学号</th>
<th rowspan="2">姓名</th>
<th rowspan="2">性别</th>
<th colspan="3">班级</th>
<th colspan="2">选课情况</th>
</tr>
<tr class="center">
<th>类别</th>
<th>学届</th>
<th>班号</th>
<th>选课数</th>
</tr>
<tr class="center" idx="89">
<td id="studentId" hidden="true">89</td>
<td>0001</td>
<td>张三</td>
<td><a>男</a></td>
<td id="classId" hidden="true">38</td>
<td>理科</td>
<td>1999</td>
<td>3</td>
<td>3</td>
</tr>
</table>
26
1
<table class="manage-table space" id="dataTable">
2
<tr class="center">
3
<th rowspan="2">学号</th>
4
<th rowspan="2">姓名</th>
5
<th rowspan="2">性别</th>
6
<th colspan="3">班级</th>
7
<th colspan="2">选课情况</th>
8
</tr>
9
<tr class="center">
10
<th>类别</th>
11
<th>学届</th>
12
<th>班号</th>
13
<th>选课数</th>
14
</tr>
15
<tr class="center" idx="89">
16
<td id="studentId" hidden="true">89</td>
17
<td>0001</td>
18
<td>张三</td>
19
<td><a>男</a></td>
20
<td id="classId" hidden="true">38</td>
21
<td>理科</td>
22
<td>1999</td>
23
<td>3</td>
24
<td>3</td>
25
</tr>
26
</table>
先通过之前提到的,可以通过查看网上的文档和浏览器的调试器来找元素的”位置“,最终通过遍历定位到该行 tr :
var dataTable = document.getElementById("dataTable");
var zhangsan;
for(var x in dataTable.rows) {
for(var y in dataTable.rows[x].attributes) {
if(dataTable.rows[x].attributes[y].name === "idx"
&& dataTable.rows[x].attributes[y].value === "89") {
zhangsan = dataTable.rows[x];
}
}
}
10
1
var dataTable = document.getElementById("dataTable");
2
var zhangsan;
3
for(var x in dataTable.rows) {
4
for(var y in dataTable.rows[x].attributes) {
5
if(dataTable.rows[x].attributes[y].name === "idx"
6
&& dataTable.rows[x].attributes[y].value === "89") {
7
zhangsan = dataTable.rows[x];
8
}
9
}
10
}
写到这里,需要注意一点,那就是 for in 这个循环遍历,这个方法是将对象的所有属性遍历,我们知道,数组也是一个对象,索引视为其属性,所以假如数组还有额外的属性,遍历会将该属性输出。
所以,上面的写法实际上存在很大的问题,因为Array表面上看来只有索引,实际上因为原型继承的关系,它还有很多从父类继承下来的属性,在这里也会同时遍历出来,如索引遍历完后紧接着遍历了其原型对象的属性 item,这个属性就没有attributes属性的,但是这里之所以没有报错,是因为JS的灵(缺)活(陷),直接表示出undefined,所以也在我们的if条件判断中溜了过去。
正确的写法怎么写?就老老实实用for循环,后面的例子会再次提到,你只要知道这样写是存在很大风险的,不要这样做,然后继续往下看就好。
另,ES6中引入的iterable类型对象可以使用 for of 方法规避这个问题,而且直接遍历出元素,而不是索引这么麻烦。
回到正题,现在我们得到了这一排,也就是这个 tr,那么什么信息都可以得到了,比如每个单元格的文本内容:
for(var z in zhangsan.cells) {
console.log(zhangsan.cells[z].innerText);
}
3
1
for(var z in zhangsan.cells) {
2
console.log(zhangsan.cells[z].innerText);
3
}
注意 innerHTML 和 innerText 的区别,比如对应zhangsan性别的那栏单元格:
innerHTML --> "<a>男</a>"
innerText --> "男"
2.2 获取多个节点
获取多个节点的方法如 getElementsByClassName、getElementsByName()、getElementsByTagName() 等,其实也很简单,无非是获取到了HTML对象的集合,再根据情况遍历使用就可以了。
比如:
var trs = document.getElementsByTagName("tr");
console.log(trs);
2.3 DOM方式的获取
上面我们提到的获取元素属性等等的方式,实际上很粗暴,就是使用对象直接获取其属性,实际上,还可以使用DOM方法来获取,而且是有一定区别的。
假如我们获取到一个HTML对象,叫做htmlObj,现在我们要获取它的id值:
htmlObj.id; //对象方式,直接获取属性
htmlObj.getAttribute("id"); //DOM方法
3
1
htmlObj.id; //对象方式,直接获取属性
2
3
htmlObj.getAttribute("id"); //DOM方法
如果你在浏览器调试的时候,没有发现获取的HTML对象有类似getAttribute()这种方法,别着急,如果你知道原型的概念,你可以顺着它的 __proto__ 属性延伸一直摸索,看到一个 ElementPrototype 的时候,这些方法就在里面。现在不理解没关系,你只要知道这些方法就是HTML DOM对象从上面继承下来的,并不是你没看见就没有。
用实际点的例子来说明,还是接刚才zhangsan的HTML作为例子,这次不磨叽,先拿到zhangsan看下属性:
var dataTable = document.getElementById("dataTable");
console.log(dataTable);
var zhangsan = dataTable.rows[2];
console.log(zhangsan);
5
1
var dataTable = document.getElementById("dataTable");
2
console.log(dataTable);
3
4
var zhangsan = dataTable.rows[2];
5
console.log(zhangsan);
通过控制台的输出,我们可以看到,对于一些常见的属性,比如id、class等,我们可以确实可以使用 zhangsan.id 或者 zhangsan.className 来获取得到。 可是我们的zhangsan还额外自定义了一个属性idx,那么问题就来了。
问题就出在,这个idx属性并没有直接放在zhangsan这个对象下,而是作为attributes集合中的元素,所以之前我们没有办法直接使用如 zhangsan.idx 的方法获取值,而是很麻烦地再次通过遍历和条件判断来进行定位。
而实际上,DOM对象有一个 getAttribute() 方法,可以通过属性名来获取属性的值,所以之前我们定义zhangsan的方法,实际上可以改成这样:
var dataTable = document.getElementById("dataTable");
console.log(dataTable);
var zhangsan;
for(var x in dataTable.rows) {
var person = dataTable.rows[x];
if(person.getAttribute("idx") === "89") {
zhangsan = person;
}
}
console.log(zhangsan);
13
1
var dataTable = document.getElementById("dataTable");
2
console.log(dataTable);
3
4
var zhangsan;
5
6
for(var x in dataTable.rows) {
7
var person = dataTable.rows[x];
8
if(person.getAttribute("idx") === "89") {
9
zhangsan = person;
10
}
11
}
12
13
console.log(zhangsan);
很不幸,会报出 person.getAttribute() is not a function 的错误,why?之前提到过,for in 会遍历其所有属性,在遍历完索引以后,它继续遍历那些从父类继承下来的属性,某些属性并不是DOM对象,是没有getAttribute方法的,循环到这里的时候,自然就报错了。
所以我们还是老老实实使用for循环:
for(var x = 0; x < dataTable.rows.length; x++ ){
if(dataTable.rows[x].getAttribute("idx") === "89") {
zhangsan = dataTable.rows[x];
}
}
x
1
for(var x = 0; x < dataTable.rows.length; x++ ){
2
if(dataTable.rows[x].getAttribute("idx") === "89") {
3
zhangsan = dataTable.rows[x];
4
}
5
6
}
或者把之前的方法改一下,找到了以后就让循环break,不再遍历后面的元素:
for(var x in dataTable.rows) {
var person = dataTable.rows[x];
if(person.getAttribute("idx") === "89") {
zhangsan = person;
break;
}
}
7
1
for(var x in dataTable.rows) {
2
var person = dataTable.rows[x];
3
if(person.getAttribute("idx") === "89") {
4
zhangsan = person;
5
break;
6
}
7
}
或者把继承的属性筛掉不要:
for(var x in dataTable.rows) {
if(dataTable.rows.hasOwnProperty(x)){
var person = dataTable.rows[x];
if(person.getAttribute("idx") === "89") {
zhangsan = person;
}
}
}
8
1
for(var x in dataTable.rows) {
2
if(dataTable.rows.hasOwnProperty(x)){
3
var person = dataTable.rows[x];
4
if(person.getAttribute("idx") === "89") {
5
zhangsan = person;
6
}
7
}
8
}
这下,就正确了,代码也简单了好多,就是利用DOM方法,所以在特别是使用一些自定义标签的时候,使用DOM的方法可以更加便捷。
3、jQuery获取
3.1 认识jQuery的基本概念
我们都知道jQuery是一个JS函数库,我们也知道jQuery可以更少的代码做更多的事,我们还知道用jQuery获取节点都是通过 $(selector) 的方式。可是,这里的$号什么意思呢?
JS世界中,变量命名的规范里,$是属于合法的标识符的,jQuery的函数有两个名字,一个叫 jQuery,另一个就叫 $。所以实际上 $.ajax(options) 和 jQuery.ajax(options) 是等同的,你可以用浏览器的调试器,到控制台中去输入 $===jQuery,结果会得到 true。
我们都知道JS中函数也是一个对象,所以这里的 $ 表示函数本身,如果是 $(),那么根据函数中return,你就会得到一个对象,我们常说是一个jQuery对象。
现在你不需要去深究太多,你只用知道常见的jQuery语法最终就是得到一个jQuery对象,这个对象看似和你通过document.getElementById之类的方法得到的东西一样,实际上并不是,内容相似但完全是两种不同的对象。所以,jQuery对象没办法使用HTML DOM对象中的方法,比如 getAttribute ,同理HTML DOM对象也无法使用jQuery对象的方法。
不过不用担心,jQuery中封装了大量的方法,可以替代HTML对象中的很多操作。比如 $("#foo").html(); 等同于 document.getElementById("foo").innerHTML;
3.2 jQuery对象和DOM对象的相互转换
上面已经提到了,两者是不同的对象,方法也不能共通,但是他们两者之间是可以进行相互转换的。
jQuery对象 --> DOM对象
var $cr=$("#cr"); //jquery对象
var cr = $cr[0]; //dom对象 jQueryObj[0]也可写成 jQueryObj.get(0);
alert(cr.checked); //检测这个checkbox是否给选中
3
1
var $cr=$("#cr"); //jquery对象
2
var cr = $cr[0]; //dom对象 jQueryObj[0]也可写成 jQueryObj.get(0);
3
alert(cr.checked); //检测这个checkbox是否给选中
DOM对象 --> jQuery对象
//只需要用$()把dom对象包装起来
var cr=document.getElementById("cr"); //dom对象
var $cr = $(cr); //转换成jquery对象
3
1
//只需要用$()把dom对象包装起来
2
var cr=document.getElementById("cr"); //dom对象
3
var $cr = $(cr); //转换成jquery对象
另外,从规范命名来讲,如果获取的对象是 jQuery对象,那么在变量前面加上$,这样方便容易识别出哪些是jQuery对象。
2018.03.22补充如下:
在项目中遇到了这样的问题,有这样一段代码:
function loadLimit(trainingId, departmentId) {
var trs = $("#dataTable tr");
if (trs.length > 1) {
for (var i = 1; i < trs.length; i++) {
trs[i].remove();
}
}
$().invoke("/admin/elite/join/do/loadJoin.q", {trainingId:trainingId, departmentId:departmentId}, function(html) {
$("#dataTable").append($(html).find("tr"));
});
}
11
1
function loadLimit(trainingId, departmentId) {
2
var trs = $("#dataTable tr");
3
if (trs.length > 1) {
4
for (var i = 1; i < trs.length; i++) {
5
trs[i].remove();
6
}
7
}
8
$().invoke("/admin/elite/join/do/loadJoin.q", {trainingId:trainingId, departmentId:departmentId}, function(html) {
9
$("#dataTable").append($(html).find("tr"));
10
});
11
}
这段代码在Chrome中运行正常,后来在IE中出错,这才发现了问题。当时误以为获取的多标签集合jQuery对象,其子元素也是jQuery对象,所以自然而然使用了remove(),实际上trs[i]获得的已经是DOM对象,不支持jQuery方法,故报错。我试着打印了结果,如下图中,上为DOM对象,下为jQuery对象:
可以看到,jQuery对象中index为0的子元素,包含典型的innerHTML等属性,这显然是DOM对象的东西。也印证了之前写过的笔记jQueyr对象转DOM对象只需要类似var cr = $cr[0];即可。所以我自己的错误应该这样修改:
$(trs[i]).remove();
1
1
$(trs[i]).remove();
亦或者有这样的方法eq(),表示将匹配元素集缩减值制定index上的一个:
trs.eq(i).remove();
1
1
trs.eq(i).remove();
3.3 jQuery的选择器和方法
jQuery的选择方式和CSS的选择器方式类似,完整版你可以参考这里:(1)jQuery的选择器 (2)jQuery 参考手册 - 选择器
至于jQuery的方法,还是直接丢链接比较方便:(1)jQuery API 中文文档 (2)w3school的jQuery教程
3.4 jQuery选择示例
还是zhangsan的例子,我们再贴一次代码,免得拉上去再看:
<table class="manage-table space" id="dataTable">
<tr class="center">
<th rowspan="2">学号</th>
<th rowspan="2">姓名</th>
<th rowspan="2">性别</th>
<th colspan="3">班级</th>
<th colspan="2">选课情况</th>
</tr>
<tr class="center">
<th>类别</th>
<th>学届</th>
<th>班号</th>
<th>选课数</th>
</tr>
<tr class="center" idx="89">
<td id="studentId" hidden="true">89</td>
<td>0001</td>
<td>张三</td>
<td><a>男</a></td>
<td id="classId" hidden="true">38</td>
<td>理科</td>
<td>1999</td>
<td>3</td>
<td>3</td>
</tr>
</table>
26
1
<table class="manage-table space" id="dataTable">
2
<tr class="center">
3
<th rowspan="2">学号</th>
4
<th rowspan="2">姓名</th>
5
<th rowspan="2">性别</th>
6
<th colspan="3">班级</th>
7
<th colspan="2">选课情况</th>
8
</tr>
9
<tr class="center">
10
<th>类别</th>
11
<th>学届</th>
12
<th>班号</th>
13
<th>选课数</th>
14
</tr>
15
<tr class="center" idx="89">
16
<td id="studentId" hidden="true">89</td>
17
<td>0001</td>
18
<td>张三</td>
19
<td><a>男</a></td>
20
<td id="classId" hidden="true">38</td>
21
<td>理科</td>
22
<td>1999</td>
23
<td>3</td>
24
<td>3</td>
25
</tr>
26
</table>
我们要获得zhangsan,用jQuery的方式就是:
var zhangsan = $("[idx='89']"); //表示通过属性查找,属性idx为89的
1
var zhangsan = $("[idx='89']"); //表示通过属性查找,属性idx为89的
没错,就是这么简单...
然后你要么使用jQuery的一些方法来获取里面的内容,或者粗暴地将它转换为DOM对象再操作。