在日常開發中,使用JavaScript獲取元素的時候,最常用的方法就是document.getElementById(getXXXByXX)方法。但是最近發現有很多地方使用的是querySelector方法而不是使用前面的方法。去官方文檔查看是這么說的:
文檔對象模型Document
引用的querySelector()
方法返回文檔中與指定選擇器或選擇器組匹配的第一個 html元素Element
。 如果找不到匹配項,則返回null
。
這句話看意思和getElementById(getXXXByXX)函數是一樣的。其實大部分的時候這兩種方法是可以互換的,這里介紹一下兩者的不同之處。
其實這兩種方法的區別就是獲取動態集合和靜態集合的關系。
- getElementById()(getXXXByXX)獲取動態集合:通過函數獲取元素之后,元素之后的改變還是會動態添加到已經獲取的這個元素中。換句話說,通過這個方法獲取到元素存儲到變量的時候,以后每一次在Javascript函數中使用這個變量的時候都會再去訪問一下這個變量對應的html元素。
- querySelector()獲取靜態集合:通過函數獲取元素之后,元素之后的改變並不會影響之前獲取后存儲到的變量。也就是獲取到元素之后就和html中的這個元素沒有關系了。
通過一個例子就比較好理解了:
<!DOCTYPE html> <html> <head> <title>querySelector()和getElementById()的區別</title> </head> <body> <ul id="testUl"> <li>1</li> <li>2</li> <li>3</li> </ul> <script type="text/javascript"> var testUl = document.getElementsByTagName('ul')[0]; var liList = testUl.getElementsByTagName('li'); for (var i = 0; i < liList.length; i++) { console.log(liList.length); /** 向ul中追加li標簽 */ testUl.appendChild(document.createElement('li')); } </script> </body> </html>
這段代碼初始的意思是想要向ul中添加目前已經有的等量的li標簽子元素。但通過getElementsByTagName實現這個問題的時候會出現下面的結果:
使用這個方法實現的時候,程序會形成一個死循環。原因是在for循環中每次執行完畢后會判斷 i 和 liList.length 的關系,之前說到,每一次使用 liList 這個變量的時候會再去訪問一下對應的html元素,但通過for循環中的語句,每次執行一遍后liList會增加一個元素,這就導致liList.length一直增加,從而不會到達循環的結束條件,形成死循環。
同樣的代碼將JavaScript中的處理語句替換成下面的語句:
<script type="text/javascript"> var testUl = document.querySelector('ul'); var liList = testUl.querySelectorAll('li'); for (var i = 0; i < liList.length; i++) { console.log(liList.length); /** 向ul中追加li標簽 */ testUl.appendChild(document.createElement('li')); } </script>
- querySelectorAll表示獲取到所有滿足條件的元素,返回的是一個列表。
執行結果如下:
可以看到,在第16行也就是for循環中輸出了三次3,也就是一開始 li 標簽的個數。for循環執行完畢后我在第20行加了console.log(liList.length)語句,輸出的還是3,而重新獲取一下后再輸出一下長度就已經變成了6。這就說明了一個問題:獲取到元素之后,只要我不重新使用語句再次獲取這個元素,之前的變量就一直不會改變,也就是說,通過querySelector(querySelectorAll)獲取到元素之后,不論html元素再怎么改變,這個變量並不會隨之發生改變,這個變量已經和html元素沒有任何關系了。
這就是JavaScript中querySelector()和getElementById()(getXXXByXX)的區別,雖然大部分時間兩者可以互換,但是最好在使用的過程中先斟酌一下是否可以使用其中的某一個,避免出現死循環導致程序都關不了。