前言
這在我們平常操作 DOM 的時(shí)候司空見慣。就比如:我通過獲取一個(gè)元素來改變其樣式,自然而然的反映到 HTML 頁面中。
但是,我們操作 JS 對(duì)象的時(shí)候,本質(zhì)上操作的是 JS 堆內(nèi)存,為什么會(huì)反映到頁面中呢?就是因?yàn)闉g覽器存在這個(gè) DOM 的映射機(jī)制。
1. 什么是 DOM 的映射機(jī)制?
我們使用 JS 從頁面獲取到的元素對(duì)象,或者自己手動(dòng)創(chuàng)建的已經(jīng)插入頁面的元素對(duì)象,與頁面中的 HTML 元素是綁定在一起的。也就是說修改其中一個(gè),另一個(gè)也會(huì)跟著自動(dòng)修改。這就是 DOM 的映射機(jī)制。
2. 形成映射的幾種情形
改變?cè)貙?duì)象的屬性
這是我們最常用到的一種情形。當(dāng)我們需要為元素添加自定義屬性、或者修改屬性等,就可以從頁面中獲取到元素對(duì)象,然后對(duì)其進(jìn)行修改,就能夠自動(dòng)反映到 HTML 頁面元素上。
//=> 修改從頁面中獲取的元素樣式
Div.style.color = 'red';
//=> 修改已經(jīng)插入頁面的元素的屬性
var p = document.createElement('p');
box.appendChild(p);
p.dataset.index = 1;
這兩種方式得到的元素對(duì)象,修改其屬性,都能夠直接反映到頁面中,不需要再次插入頁面中。
在元素內(nèi)部繼續(xù)添加元素
//=> 在其內(nèi)部插入標(biāo)簽或文本
var list = Div.getElementsByTagName('li');
console.log(list); // 空的元素集合
Div.innerHTML('<li></li>');
console.log(list); // 有一個(gè)元素集合
//=> 添加自己創(chuàng)建的元素對(duì)象,同樣原理
Div.appendChild(p);
在容器中的數(shù)據(jù)綁定前,我們獲取容器中元素,得到一個(gè)空的元素集合,容器數(shù)據(jù)綁定后,我們不需要重新獲取,DOM 的映射機(jī)制會(huì)幫我們把新增加的元素映射到之前獲取的空集合中,讓其變?yōu)橛性氐募稀?/p>
在頁面中追加已有元素
list = Div.getElementsByTagName('li')[0];
Div.appendChild(list);
appendChild 在追加元素對(duì)象的時(shí)候,如果這個(gè)元素在容器中已經(jīng)存在,此時(shí)并不是克隆一份新的追加到末尾,而是把原有的元素移動(dòng)到末尾。
其根本原因在于,同一個(gè)元素在頁面中,只能夠有一個(gè)位置。把 JS 元素對(duì)象插入頁面中某個(gè)位置,實(shí)際上就是把其綁定的 HTML 元素移動(dòng)到那個(gè)位置上。
這里的元素已經(jīng)存在有兩種情形:
- 元素是從頁面中獲取到的
- 創(chuàng)建的元素已經(jīng)添加過一次,再次添加時(shí)
因此,就無需手動(dòng)移除原先的元素,再進(jìn)行添加。直接插入即可。
3. 特殊情況
querySelectAll 獲取的集合是靜態(tài)集合(staticNodeList),不存在上述所謂的映射機(jī)制,基于這個(gè)方法,數(shù)據(jù)綁定完成后需要重新獲取一次才可以。
var box = document.querySelectorAll('#box');
var box1 = document.getElementById('box');
console.log(box); //=> 獲取到的是 NodeList 對(duì)象
console.dir(box1); //=> 而這里獲取到的是 HTMLElement 的實(shí)例
NodeList 不存在與 HTML 頁面元素的映射,而且沒有很多 HTMLElement 實(shí)例才擁有的方法。因此,千萬不要使用這個(gè)方法。
另外,jQuery 中獲取的元素同樣不存在這些映射,實(shí)現(xiàn)映射需要使用其內(nèi)部的方法,而且要使用原生 HTMLElement 實(shí)例的方法,需要通過后面加 [0] 的方法轉(zhuǎn)換為原生元素對(duì)象。