HOWTO · Angular
AngularJS 中的 getElementById 和 querySelector
在本教程中,我們將瞭解我們在使用 Angular 元素、document.getElementById 和 querySelector 時遇到的不同問題以及如何解決它們。
本頁內容
已知元素的 ID 時,使用 document.getElementById。若目標是以 CSS 選擇器描述,例如類別、屬性或元素間的關係,則使用 document.querySelector。兩者都會傳回第一個符合的元素,若找不到則傳回 null;兩者都不是 AngularJS 的選擇器函式。先選取 DOM 節點,再將該節點傳給 angular.element,即可取得 AngularJS 的元素包裝物。
為目標選擇 DOM 選擇器
若要選取單一且固定的 ID,document.getElementById('appBusyIndicator') 能直接表達意圖。若要使用 CSS 選擇器,例如 .notice、[data-state="busy"] 或 main .notice,則使用 document.querySelector()。請依目標的識別方式選擇,不要假設兩者在速度或瀏覽器支援度上有所差異。
如果元素可能尚未存在,請先檢查回傳值,再存取其屬性或進行包裝。若結果是 null,通常表示目前文件中沒有符合的 ID 或選擇器,或程式碼執行時元素尚未加入。請等相關標記載入後再查詢 DOM。querySelector() 也要求有效的 CSS 選擇器語法;格式錯誤的選擇器可能會引發 SyntaxError,因此請使用可信任或正確跳脫的值來組成選擇器。
document.getElementById 與 document.querySelector 如何選取元素
getElementById() 會依 ID 查找單一元素。querySelector() 接受 CSS 選擇器語法,並依文件順序傳回第一個符合的元素。如果有多個符合項目,而你需要全部取得,請使用 document.querySelectorAll();它會傳回靜態的 NodeList,而非單一元素。例如,若要對每個 notice 執行相同處理,可以使用 document.querySelectorAll('.notice') 並逐一遍歷結果。
在這段標記中,ID 查詢和類別選擇器各會找到一個節點,而類別查詢則會找到兩個 notice:
本節中的程式碼以及顯示的標籤或輸出仍為英文。
<div id="appBusyIndicator" class="notice">Loading</div>
<p class="notice">Please wait</p>
以下範例使用一個對應上述標記的小型測試資料,因此也能在沒有瀏覽器的 JavaScript 環境中執行。範例會示範各種查詢匹配哪些節點,但不會重現瀏覽器中的集合類型。在瀏覽器中,getElementById 和 querySelector 會傳回一個節點或 null,而 querySelectorAll 會傳回靜態的 NodeList。呼叫這些瀏覽器 API 時,請使用原生的 document。
執行此範例:
const nodes = [
{ id: 'appBusyIndicator', className: 'notice', textContent: 'Loading' },
{ id: '', className: 'notice', textContent: 'Please wait' }
];
const document = {
getElementById(id) {
return nodes.find(node => node.id === id) ?? null;
},
querySelector(selector) {
return nodes.find(node => selector === '.notice' && node.className === 'notice') ?? null;
},
querySelectorAll(selector) {
return selector === '.notice' ? nodes.filter(node => node.className === 'notice') : [];
}
};
const indicator = document.getElementById('appBusyIndicator');
const firstNotice = document.querySelector('.notice');
const notices = document.querySelectorAll('.notice');
console.log(`ID result: ${indicator ? indicator.textContent : 'not found'}`);
console.log(`First notice: ${firstNotice ? firstNotice.textContent : 'not found'}`);
console.log('All notices:');
for (const notice of notices) {
console.log(`- ${notice.textContent}`);
}
在 AngularJS 中搭配 DOM 選擇器使用 angular.element
angular.element() 會包裝 DOM 節點,讓 AngularJS 程式碼使用其元素 API。它通常不會解析選擇器字串並據此尋找相符節點。請先選取節點,再包裝結果:var node = document.querySelector('#appBusyIndicator'); var wrapped = node ? angular.element(node) : null;。
AngularJS 預設提供 jqLite。如果在載入 AngularJS 之前先載入完整的 jQuery,angular.element 就會改用 jQuery;因此,載入順序會影響可用的方法。若程式碼依賴 fadeIn() 等僅屬於 jQuery 的方法,就必須載入 jQuery,且不應假設這些方法也存在於 jqLite。若要建立 AngularJS 介面,建議變更模型狀態並讓 AngularJS 更新畫面,而非直接操作呈現內容。
修正選擇器與元素存取範例
$document 服務可注入 AngularJS;它不是可呼叫的選擇器函式。請將它注入控制器或服務,再透過包裝後的元素存取底層文件。例如,var node = $document[0].getElementById('appBusyIndicator'); 使用原生 DOM 查詢。由於找不到符合項目時會得到 null,請先檢查 node,再讀取或修改它。若要包裝找到的節點,請將它傳給 angular.element(node)。
使用 jqLite 的 find() 時請特別留意。jqLite 的 find() 會依標籤名稱搜尋後代元素;它不是可用於 ID 或類別的一般 CSS 選擇器引擎。因此,angular.element(parent).find('#appBusyIndicator') 和 .find('.notice') 並不是可靠的 jqLite 選擇器。若要使用 CSS 選擇器,請在父節點上呼叫原生的 querySelector() 或 querySelectorAll();若要查找的是後代標籤名稱,則可使用 find('div')。完整 jQuery 的 find() 行為可能較豐富,因此預計以 jqLite 運作的程式碼,不應依賴這項差異。
疑難排解時,請確認三件事:查詢是在目標元素存在後才執行、使用 querySelector() 時選擇器是有效的 CSS,以及程式碼會將找不到結果的情況視為 null。接著確認包裝後的物件是 jqLite 還是 jQuery,再呼叫該程式庫專屬的方法。這樣能分辨問題是 DOM 查詢失敗,還是包裝物件不支援某個方法。