jQuery DOM 遍歷 (Traversing)

DOM 遍歷是指從一個元素出發,在 DOM 樹中移動尋找其他相關的元素。jQuery 提供了豐富的遍歷方法,讓你能夠輕鬆地找到父元素、子元素、兄弟元素等。

祖先遍歷(向上)

parent() - 直接父元素

取得每個匹配元素的直接父元素:

// 取得 span 的父元素
$('span').parent();

// 加上選擇器過濾
$('span').parent('.container'); // 只返回 class 為 container 的父元素

parents() - 所有祖先元素

取得每個匹配元素的所有祖先元素,一直到 <html>

// 取得 span 的所有祖先元素
$('span').parents();

// 加上選擇器過濾
$('span').parents('div'); // 只返回 div 祖先元素
$('span').parents('.wrapper'); // 只返回 class 為 wrapper 的祖先

parentsUntil() - 到指定祖先為止

取得從當前元素到指定祖先之間的所有祖先元素(不含指定祖先):

// 取得 span 到 #container 之間的所有祖先
$('span').parentsUntil('#container');

// 可以加上第二個參數過濾
$('span').parentsUntil('#container', 'div'); // 只返回其中的 div

closest() - 最近的匹配祖先

從當前元素開始(包含自身),找到第一個匹配的祖先元素:

// 找到最近的 .panel 祖先(可能是自己)
$('button').closest('.panel');

// 在事件處理中很常用
$('button').on('click', function () {
  var $panel = $(this).closest('.panel');
  $panel.addClass('active');
});

closest()parents() 的主要差異:

  • closest() 包含當前元素本身,且只返回第一個匹配的祖先
  • parents() 不包含當前元素,會返回所有匹配的祖先

offsetParent() - 定位父元素

取得最近的定位祖先元素(position 不為 static):

$('.item').offsetParent();

後代遍歷(向下)

children() - 直接子元素

取得每個匹配元素的直接子元素:

// 取得 ul 的所有直接子元素
$('ul').children();

// 加上選擇器過濾
$('ul').children('li'); // 只返回 li 子元素
$('ul').children('.active'); // 只返回 class 為 active 的子元素

find() - 搜尋所有後代

在匹配元素的所有後代中搜尋:

// 在 #content 的所有後代中搜尋 span
$('#content').find('span');

// 搜尋所有後代元素
$('#content').find('*');
children() 只搜尋直接子元素,find() 會搜尋所有後代(包含孫元素、曾孫元素等)。

contents() - 所有子節點

取得每個匹配元素的所有子節點,包含文字節點和註解節點:

// 取得 div 的所有子節點(包含文字節點)
$('div').contents();

// 常用於操作 iframe 內容
$('iframe').contents().find('body');

兄弟遍歷(水平)

siblings() - 所有兄弟元素

取得每個匹配元素的所有兄弟元素:

// 取得 .active 元素的所有兄弟
$('.active').siblings();

// 加上選擇器過濾
$('.active').siblings('li');
$('.active').siblings('.item');

next() - 下一個兄弟

取得每個匹配元素緊鄰的下一個兄弟元素:

// 取得 .current 的下一個兄弟
$('.current').next();

// 加上選擇器過濾
$('.current').next('.item'); // 只有當下一個兄弟符合條件時才返回

nextAll() - 之後所有兄弟

取得每個匹配元素之後的所有兄弟元素:

// 取得 .current 之後的所有兄弟
$('.current').nextAll();

// 加上選擇器過濾
$('.current').nextAll('.item');

nextUntil() - 到指定兄弟為止

取得每個匹配元素之後,到指定兄弟之前的所有兄弟元素:

// 取得 #start 到 #end 之間的所有兄弟
$('#start').nextUntil('#end');

// 可以加上第二個參數過濾
$('#start').nextUntil('#end', 'li');

prev() - 上一個兄弟

取得每個匹配元素緊鄰的上一個兄弟元素:

// 取得 .current 的上一個兄弟
$('.current').prev();

// 加上選擇器過濾
$('.current').prev('.item');

prevAll() - 之前所有兄弟

取得每個匹配元素之前的所有兄弟元素:

// 取得 .current 之前的所有兄弟
$('.current').prevAll();

// 加上選擇器過濾
$('.current').prevAll('.item');

prevUntil() - 到指定兄弟為止

取得每個匹配元素之前,到指定兄弟之後的所有兄弟元素:

// 取得 #end 到 #start 之間的所有兄弟
$('#end').prevUntil('#start');

過濾方法

filter() - 篩選符合條件

從匹配的元素中篩選出符合條件的:

// 篩選出有 .active class 的 li
$('li').filter('.active');

// 使用函式篩選
$('li').filter(function (index) {
  return index % 2 === 0; // 篩選偶數索引的元素
});

// 使用函式檢查內容
$('li').filter(function () {
  return $(this).text().length > 10; // 文字長度大於 10
});

not() - 排除符合條件

從匹配的元素中排除符合條件的:

// 排除有 .disabled class 的 li
$('li').not('.disabled');

// 使用函式排除
$('li').not(function (index) {
  return $(this).hasClass('hidden');
});

first() - 第一個元素

取得匹配元素中的第一個:

$('li').first();

last() - 最後一個元素

取得匹配元素中的最後一個:

$('li').last();

eq() - 指定索引的元素

取得匹配元素中指定索引位置的元素(索引從 0 開始):

$('li').eq(0); // 第一個
$('li').eq(2); // 第三個
$('li').eq(-1); // 最後一個(負數從後面數)
$('li').eq(-2); // 倒數第二個

slice() - 範圍選取

取得匹配元素中指定範圍的元素:

$('li').slice(0, 3); // 前三個(索引 0, 1, 2)
$('li').slice(2); // 從第三個開始到最後
$('li').slice(-3); // 最後三個
$('li').slice(1, -1); // 去掉第一個和最後一個

其他方法

is() - 判斷是否符合

檢查匹配元素是否符合指定條件,返回布林值:

// 檢查是否有 .active class
if ($('#item').is('.active')) {
  console.log('是 active 狀態');
}

// 檢查是否隱藏
if ($('#box').is(':hidden')) {
  console.log('元素是隱藏的');
}

// 在事件中檢查目標
$('a').on('click', function (e) {
  if ($(e.target).is('.external')) {
    // 處理外部連結
  }
});

has() - 是否包含子元素

篩選出包含指定子元素的元素:

// 篩選出包含 span 的 div
$('div').has('span');

// 篩選出包含 .error 的 form
$('form').has('.error');

add() - 加入更多元素

將額外的元素加入到當前匹配的元素集合中:

// 選取所有 p 和 span
$('p').add('span');

// 等同於
$('p, span');

// 加入 DOM 元素
var div = document.getElementById('extra');
$('p').add(div);

// 在特定上下文中加入
$('p').add('span', '#container');

addBack() - 加入前一個選擇狀態

將前一個選擇狀態的元素加入到當前集合:

// 選取 ul 及其所有 li 子元素
$('ul').children('li').addBack();

end() - 回到前一個選擇狀態

在鏈式呼叫中,回到上一個選擇狀態:

// 選取 ul,對 li 做操作,然後回到 ul
$('ul')
  .find('li')
  .addClass('item')
  .end() // 回到 ul
  .addClass('list');

// 多層回退
$('ul')
  .find('li')
  .find('a')
  .addClass('link')
  .end() // 回到 li
  .addClass('item')
  .end() // 回到 ul
  .addClass('list');

each() - 迭代處理

對每個匹配元素執行函式:

$('li').each(function (index, element) {
  console.log(index, $(this).text());

  // 如果返回 false,會中斷迭代
  if (index >= 5) {
    return false;
  }
});

map() - 映射轉換

將每個匹配元素通過函式轉換,返回新的 jQuery 物件:

// 取得所有 li 的文字,組成陣列
var texts = $('li')
  .map(function () {
    return $(this).text();
  })
  .get();

// 取得所有 input 的值
var values = $('input')
  .map(function () {
    return $(this).val();
  })
  .get();

遍歷方法比較表

方法方向說明
parent()向上直接父元素
parents()向上所有祖先元素
parentsUntil()向上到指定祖先為止
closest()向上最近的匹配祖先(含自身)
children()向下直接子元素
find()向下所有後代中搜尋
siblings()水平所有兄弟元素
next() / prev()水平下/上一個兄弟
nextAll() / prevAll()水平之後/之前所有兄弟
nextUntil() / prevUntil()水平到指定兄弟為止

實用範例

找到表格中的特定欄位

// 點擊按鈕時,找到同一列的其他欄位
$('table').on('click', 'button', function () {
  var $row = $(this).closest('tr');
  var name = $row.find('.name').text();
  var email = $row.find('.email').text();
  console.log(name, email);
});

手風琴選單

$('.accordion-header').on('click', function () {
  var $content = $(this).next('.accordion-content');

  // 關閉其他
  $(this).parent().siblings().find('.accordion-content').slideUp();

  // 切換當前
  $content.slideToggle();
});

標籤頁切換

$('.tab').on('click', function () {
  var index = $(this).index();

  // 切換標籤狀態
  $(this).addClass('active').siblings().removeClass('active');

  // 切換內容
  $('.tab-content').eq(index).show().siblings().hide();
});