從JS和jQuery淺談DOM操作,當我們在獲取時,究竟獲取了什么


0、寫在前面的話

自己對前端的東西一直不是很熟,現在開始要想辦法從前端各個地方去獲取想要的屬性值的時候,也基本是在網上現炒現賣,幾周下來,發現自己還是迷迷糊糊,可以算是一無所獲。

所以就抽時間,把這一塊的東西收集整理一下,免得每次為了得到一個值要上網查詢鼓搗一萬年,實在是浪費時間,知其然知其所以然,才能讓問題迎刃而解。

這篇博文應該說結構還不夠完整,有些知識點還沒提到,覆蓋的內容其實主要還是為了滿足我現在入門知識理解的一個需求,日后再進行完善吧,這里先貼一些相關概念的參考鏈接,以后方便參考和添加:

1、理解節點

HTML文檔中,所有內容都是 “節點”,是一個Element,這意味着:
  • 整個文檔是一個文檔節點
  • 每個 HTML 元素是元素節點
  • HTML 元素內的文本是文本節點
  • 每個 HTML 屬性是屬性節點
  • 注釋是注釋節點

先看下面的片段:
<html> 
  <head>
    <title>DOM 教程</title>
  </head>
  <body>
    <h1>DOM 第一課</h1>
    <p>Hello world!</p>
  </body>
</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> 元素的最后一個子節點

重點:

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>

先通過之前提到的,可以通過查看網上的文檔和瀏覽器的調試器來找元素的”位置“,最終通過遍歷定位到該行 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];
        }
    }
}

寫到這里,需要注意一點,那就是 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);
}

注意 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方法

如果你在瀏覽器調試的時候,沒有發現獲取的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);

通過控制台的輸出,我們可以看到,對於一些常見的屬性,比如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);

很不幸,會報出 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];
    }

}

或者把之前的方法改一下,找到了以后就讓循環break,不再遍歷后面的元素:
for(var x in dataTable.rows) {
    var person = dataTable.rows[x];
    if(person.getAttribute("idx") === "89") {
        zhangsan = person;
        break;
    }
}

或者把繼承的屬性篩掉不要:
for(var x in dataTable.rows) {
    if(dataTable.rows.hasOwnProperty(x)){        
        var person = dataTable.rows[x];
        if(person.getAttribute("idx") === "89") {
            zhangsan = person;
        }
    }
}

這下,就正確了,代碼也簡單了好多,就是利用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是否給選中

DOM對象 --> jQuery對象
//只需要用$()把dom對象包裝起來
var cr=document.getElementById("cr"); //dom對象
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"));
    });
}

這段代碼在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();

亦或者有這樣的方法 eq(),表示將匹配元素集縮減值制定index上的一個:
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>

我們要獲得zhangsan,用jQuery的方式就是:
var zhangsan = $("[idx='89']");  //表示通過屬性查找,屬性idx為89的

沒錯,就是這么簡單... 

然后你要么使用jQuery的一些方法來獲取里面的內容,或者粗暴地將它轉換為DOM對象再操作。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM