فهم التنقل لأسفل في شجرة DOM
بينما ينتقل التنقل لأعلى من الطفل إلى الأب، فإن التنقل لأسفل ينتقل من الأب إلى الأحفاد - الأطفال، الأحفاد، وما بعدهم. هذا ضروري عندما تحتاج إلى العثور على عناصر متداخلة داخل حاوية أو تعديلها أو التفاعل معها.
التنقل لأسفل هو واحد من أكثر العمليات شيوعًا في jQuery، ويُستخدم لكل شيء من العثور على عناصر محددة داخل قسم إلى معالجة جميع العناصر في قائمة.
لماذا التنقل لأسفل؟
- تحديد نطاق العمليات: قصر التغييرات على عناصر داخل حاوية معينة
- العثور على عناصر متداخلة: تحديد موقع عناصر متداخلة بعمق دون التأثير على الآخرين
- التكرار عبر الأطفال: تطبيق عمليات على جميع العناصر الفرعية
- الأداء: البحث داخل نطاق صغير بدلاً من المستند بأكمله
ملاحظة: طرق التنقل لأسفل أكثر كفاءة عندما تبدأ من حاوية محددة بدلاً من البحث في المستند بأكمله. قم دائمًا بتضييق نطاقك عندما يكون ذلك ممكنًا.
طريقة children()
تُرجع طريقة children() الأطفال المباشرين لكل عنصر في المجموعة المطابقة. إنها تنزل بالضبط مستوى واحد، متجاهلة الأحفاد والأعمق.
بنية HTML:
<ul class="menu">
<li class="item">الرئيسية</li>
<li class="item">
من نحن
<ul class="submenu">
<li>الفريق</li>
<li>التاريخ</li>
</ul>
</li>
<li class="item">اتصل بنا</li>
</ul>
jQuery:
// الحصول على الأطفال المباشرين لـ .menu (يُرجع فقط 3 عناصر li من المستوى الأعلى)
$(".menu").children();
// الحصول فقط على أطفال li (نفس النتيجة في هذه الحالة)
$(".menu").children("li");
// إضافة فئة إلى جميع الأطفال المباشرين
$(".menu").children().addClass("top-level");
// ملاحظة: هذا لا يحدد عناصر li المتداخلة في .submenu
نصيحة: children() أسرع بكثير من find() لأنها تبحث فقط على مستوى واحد عميق. استخدمها عندما تحتاج فقط إلى الأطفال المباشرين.
طريقة find()
تبحث طريقة find() عن جميع الأحفاد على أي عمق - الأطفال، الأحفاد، أحفاد الأحفاد، وهكذا. إنها طريقة التنقل لأسفل الأكثر قوة وتتطلب محددًا.
بنية HTML:
<div class="container">
<div class="section">
<p class="text">فقرة 1</p>
<div class="nested">
<p class="text">فقرة 2</p>
</div>
</div>
</div>
jQuery:
// العثور على جميع أحفاد .text (يُرجع كلا الفقرتين)
$(".container").find(".text");
// العثور على جميع divs على أي مستوى
$(".container").find("div");
// التسلسل مع طرق أخرى
$(".container").find("p").css("color", "blue");
// find مطلوب - يجب عليك تقديم محدد
$(".container").find(); // خطأ: المحدد مطلوب
مهم: find() يتطلب وسيطة محدد. على عكس children()، لا يمكنك استدعاؤها بدون معاملات. حدد دائمًا ما تبحث عنه.
مقارنة: children() مقابل find()
HTML:
<div class="box">
<p>طفل مباشر</p>
<div>
<p>حفيد</p>
</div>
</div>
jQuery:
// children() - يُرجع فقط p و div المباشرين
$(".box").children(); // [<p>, <div>]
// children() مع محدد - يُرجع فقط p المباشر
$(".box").children("p"); // [<p>طفل مباشر</p>]
// find() - يُرجع كلا عنصري p على أي عمق
$(".box").find("p"); // [<p>طفل مباشر</p>, <p>حفيد</p>]
طريقة contents()
تُرجع طريقة contents() جميع الأطفال المباشرين بما في ذلك عقد النص والتعليقات. هذا يختلف عن children()، والتي تُرجع فقط عقد العناصر.
HTML:
<div class="wrapper">
بعض النص
<span>عنصر span</span>
المزيد من النص
</div>
jQuery:
// children() يُرجع فقط span
$(".wrapper").children(); // [<span>]
// contents() يُرجع عقد النص وspan
$(".wrapper").contents(); // [text, <span>, text]
// مفيد لـ iframes
$("iframe").contents().find("body"); // الوصول إلى محتويات iframe
نصيحة: contents() مفيدة بشكل خاص للعمل مع iframes، حيث تسمح لك بالوصول إلى مستند iframe ومعالجته.
مثال عملي: أقسام قابلة للطي
HTML:
<div class="faq">
<div class="faq-item">
<h3 class="faq-question">ما هو jQuery؟</h3>
<div class="faq-answer">jQuery هي مكتبة JavaScript...</div>
</div>
<div class="faq-item">
<h3 class="faq-question">كيف أقوم بتثبيت jQuery؟</h3>
<div class="faq-answer">يمكنك تضمينها عبر CDN...</div>
</div>
</div>
jQuery:
$(".faq-question").on("click", function() {
// العثور على الإجابة داخل نفس faq-item
var faqItem = $(this).parent();
// تبديل الإجابة (طفل مباشر لـ faq-item)
faqItem.children(".faq-answer").slideToggle();
// أو استخدم find() للبحث بشكل أعمق إذا تغيرت البنية
// faqItem.find(".faq-answer").slideToggle();
});
مثال عملي: التحقق من صحة النموذج
HTML:
<form class="registration-form">
<div class="form-section">
<h3>المعلومات الشخصية</h3>
<input type="text" name="name" required>
<input type="email" name="email" required>
</div>
<div class="form-section">
<h3>العنوان</h3>
<input type="text" name="street" required>
<input type="text" name="city" required>
</div>
</form>
jQuery:
$(".registration-form").on("submit", function(e) {
var isValid = true;
// العثور على جميع المدخلات المطلوبة في النموذج
$(this).find("input[required]").each(function() {
if ($(this).val() === "") {
$(this).addClass("error");
isValid = false;
} else {
$(this).removeClass("error");
}
});
if (!isValid) {
e.preventDefault();
alert("يرجى ملء جميع الحقول المطلوبة");
}
});
// التحقق من صحة قسم معين
$(".form-section").first().find("input").each(function() {
console.log($(this).attr("name"));
});
مثال عملي: قوائم ديناميكية
HTML:
<div class="todo-list">
<ul class="tasks">
<li class="task">
<span class="task-text">شراء البقالة</span>
<button class="delete-btn">حذف</button>
</li>
<li class="task">
<span class="task-text">المشي مع الكلب</span>
<button class="delete-btn">حذف</button>
</li>
</ul>
<button class="add-task-btn">إضافة مهمة</button>
</div>
jQuery:
// عد جميع المهام
var taskCount = $(".todo-list").find(".task").length;
console.log("إجمالي المهام: " + taskCount);
// الحصول على جميع نصوص المهام
$(".todo-list").find(".task-text").each(function() {
console.log($(this).text());
});
// وضع علامة على جميع المهام كمكتملة
$(".todo-list").find(".task").addClass("completed");
// حذف جميع المهام في القائمة
$(".clear-all-btn").on("click", function() {
$(".tasks").children(".task").remove();
});
مثال عملي: معرض الصور
HTML:
<div class="gallery">
<div class="gallery-row">
<img src="image1.jpg" alt="صورة 1">
<img src="image2.jpg" alt="صورة 2">
</div>
<div class="gallery-row">
<img src="image3.jpg" alt="صورة 3">
<img src="image4.jpg" alt="صورة 4">
</div>
</div>
jQuery:
// إضافة lightbox لجميع الصور
$(".gallery").find("img").on("click", function() {
var src = $(this).attr("src");
var lightbox = $("<div class='lightbox'></div>");
var img = $("<img>").attr("src", src);
lightbox.append(img).appendTo("body");
});
// عد الصور في كل صف
$(".gallery").children(".gallery-row").each(function(index) {
var imageCount = $(this).children("img").length;
console.log("الصف " + (index + 1) + " يحتوي على " + imageCount + " صور");
});
// إضافة تحميل كسول لجميع الصور
$(".gallery").find("img").attr("loading", "lazy");
مقارنة الأداء
السيناريو: العثور على عنصر متداخل 3 مستويات عميقة
// بطيء - يبحث في المستند بأكمله
var element = $(".deeply-nested-element");
// أسرع - يبدأ من حاوية معروفة
var element = $(".container").find(".deeply-nested-element");
// الأسرع - إذا كنت تعرف المسار الدقيق
var element = $(".container").children(".level1")
.children(".level2")
.children(".level3");
نصيحة للأداء: ابدأ دائمًا بحثك من أصغر نطاق ممكن. العثور على عناصر داخل حاوية أسرع بكثير من البحث في المستند بأكمله.
جدول المقارنة
| الطريقة |
عمق البحث |
تُرجع |
الأفضل استخدامها لـ |
children() |
مستوى واحد |
الأطفال المباشرين فقط |
وصول سريع إلى الأطفال المباشرين |
find() |
جميع المستويات |
جميع الأحفاد |
العثور على عناصر متداخلة بعمق |
contents() |
مستوى واحد |
الأطفال + عقد النص |
العمل مع iframes أو عقد النص |
تمرين عملي
السيناريو: إنشاء مرشح منتجات يُظهر/يخفي المنتجات بناءً على الفئة.
بنية HTML:
<div class="shop">
<div class="filters">
<button class="filter-btn" data-category="all">الكل</button>
<button class="filter-btn" data-category="electronics">إلكترونيات</button>
<button class="filter-btn" data-category="clothing">ملابس</button>
</div>
<div class="products">
<div class="product" data-category="electronics">
<img src="laptop.jpg">
<h3>لابتوب</h3>
<p class="price">$999</p>
</div>
<div class="product" data-category="clothing">
<img src="shirt.jpg">
<h3>قميص</h3>
<p class="price">$29</p>
</div>
<div class="product" data-category="electronics">
<img src="phone.jpg">
<h3>هاتف</h3>
<p class="price">$699</p>
</div>
</div>
</div>
مهامك:
- عند النقر على زر التصفية، احصل على قيمة
data-category الخاصة به
- استخدم
find() للحصول على جميع المنتجات داخل .shop
- أظهر جميع المنتجات إذا كانت الفئة "all"
- وإلا، أخفِ المنتجات التي لا تطابق الفئة
- إضافي: عد المنتجات المرئية واعرض العدد
- إضافي: أضف شارة "مميز" للمنتجات التي تقل عن $100
تلميح: استخدم
$(this).closest(".shop").find(".product") لتحديد نطاق بحثك، ثم قم بالتصفية حسب سمة
[data-category].
النقاط الرئيسية
children() سريعة ومحددة - استخدمها للأطفال المباشرين
find() تبحث في جميع المستويات - تتطلب محددًا، أكثر مرونة
contents() تتضمن عقد النص - مفيدة لـ iframes
- قم دائمًا بتضييق نطاق بحثك لأداء أفضل
- ابدأ من حاوية بدلاً من البحث في المستند بأكمله
- التنقل لأسفل ضروري للعمليات المحددة النطاق والتحديثات الجماعية