404 Not Found

404 Not Found


nginx

التلاعب بـ DOM

DOM (نموذج كائن المستند) هو شجرة ينشئها المتصفح من مستند HTML. بالعمل على هذه الشجرة بـ JavaScript، يمكنك تغيير محتوى الصفحة وهياكلها وسماتها بشكل ديناميكي — مثلقطف الثمار من الشجرة، أو تطعيم أغصان جديدة، أو قطع الخشب الميت.


اختيار العناصر

الخطوة الأولى في التلاعب بـ DOM دائماً هي "ابحث عنها أولاً".

الطريقة تُرجع الوصف
getElementById('id') عنصر واحد الأسرع، الـ id فريد
getElementsByClassName('cls') HTMLCollection مجموعة حية، تتزامن مع تغييرات DOM
getElementsByTagName('tag') HTMLCollection مثل أعلاه
querySelector('selector') عنصر واحد محدد CSS، يُرجع أول تطابق
querySelectorAll('selector') NodeList مجموعة ثابتة، لا تتغير مع DOM
💡 عائلة querySelector مقابل getElementBy*: الأولى تستخدم محددات CSS (أكثر مرونة)، الثانية أداء أفضل قليلاً. للتطوير اليومي، فضّل querySelector — أكثر حدسية.

مثال: مقارنة طرق الاختيار الخمس

HTML
<div id="box" class="card active">مرحباً</div>
<div class="card">بالعالم</div>
<p class="card">JS</p>

<script>
const byId = document.getElementById('box');
console.log('byId:', byId.textContent);

const byClass = document.getElementsByClassName('card');
console.log('عدد byClass:', byClass.length);

const byTag = document.getElementsByTagName('p');
console.log('byTag:', byTag[0].textContent);

const first = document.querySelector('.card');
console.log('querySelector:', first.textContent);

const all = document.querySelectorAll('.card');
console.log('عدد querySelectorAll:', all.length);
</script>
▶ جرّب الكود

تعديل المحتوى

بعد العثور على عنصر، الحاجة الأكثر شيوعاً هي تغيير "ما يقوله".

⚠️ لا تستخدم innerHTML أبداً لإدراج محتوى يوفره المستخدم، وإلا يمكن للمهاجمين حقن سكريبتات ضارة. مثل نشر رسالة من غريب مباشرة على لوحة الإعلانات — الرسالة قد تحتوي على "قنبلة".

مثال: textContent مقابل innerHTML

HTML
<div id="safe"></div>
<div id="danger"></div>

<script>
const safe = document.getElementById('safe');
safe.textContent = '<b>هذا لن يكون عريضاً</b>';

const danger = document.getElementById('danger');
danger.innerHTML = '<b>هذا سيكون عريضاً</b>';
</script>
▶ جرّب الكود

تعديل السمات

سمات عناصر HTML (id، class، src، href، إلخ) يمكن أيضاً قراءتها وكتابتها بـ JS.

الطريقة الوصف
getAttribute('name') قراءة قيمة السمة
setAttribute('name', 'value') تعيين السمة
removeAttribute('name') إزالة السمة
💡 بعض السمات يمكن الوصول إليها مباشرة بنقطة: el.id، el.className، el.src. لكن للسمات المخصصة (data-*)، استخدم getAttribute أو el.dataset.

مثال: التلاعب بسمات الروابط

HTML
<a id="link" href="https://example.com" target="_blank">رابط مثال</a>

<script>
const link = document.getElementById('link');

console.log('href:', link.getAttribute('href'));

link.setAttribute('href', 'https://mdn.io');
link.setAttribute('title', 'انقر للذهاب إلى MDN');

link.removeAttribute('target');

console.log('بعد التعديل:', link.getAttribute('href'));
</script>
▶ جرّب الكود

إنشاء وإدراج العناصر

إحدى قدرات التلاعب بـ DOM الأساسية: "إنشاء" عنصر من العدم، ثم إرفاقه بالشجرة.

الطريقة الوصف
createElement('tag') إنشاء عقدة عنصر
createTextNode('text') إنشاء عقدة نص
appendChild(child) إلحاق بالنهاية
insertBefore(new, ref) إدراج قبل المرجع

مثال: إضافة عناصر قائمة ديناميكياً

HTML
<ul id="list">
  <li>عنصر موجود</li>
</ul>

<script>
const list = document.getElementById('list');

const li = document.createElement('li');
li.textContent = 'عنصر مضاف حديثاً';
list.appendChild(li);

const li2 = document.createElement('li');
li2.textContent = 'مدرج في البداية';
list.insertBefore(li2, list.firstChild);
</script>
▶ جرّب الكود

إزالة واستنساخ العناصر

الطريقة الوصف
removeChild(child) الأبو يزيل عنصر الابن
remove() العنصر يزيل نفسه (API أحدث)
cloneNode(true/false) true = استنساخ عميق (بما في ذلك الأبناء)، false = استنساخ سطحي
💡 remove() أبسط من removeChild()، لكن إذا كنت بحاجة للحصول على العقدة المحذوفة لمزيد من المعالجة، استخدم removeChild() — تُرجع العقدة المحذوفة.

مثال: الإزالة والاستنساخ

HTML
<ul id="fruits">
  <li>تفاح</li>
  <li>موز</li>
  <li>برتقال</li>
</ul>

<script>
const fruits = document.getElementById('fruits');

const banana = fruits.children[1];
const removed = fruits.removeChild(banana);
console.log('تمت إزالة:', removed.textContent);

const clone = fruits.cloneNode(true);
document.body.appendChild(clone);
console.log('تم استنساخ القائمة');
</script>
▶ جرّب الكود

📖 ملخص

  1. معسكران لاختيار العناصر: getElementBy* (أداء أفضل) و querySelector* (أكثر مرونة). للاستخدام اليومي، querySelector كافٍ.
  2. استخدم textContent لتغيير النص، استخدم innerHTML فقط عندما تحتاج HTML. الأخيرة تتطلب الوقاية من XSS.
  3. عمليات السمات: getAttribute للقراءة، setAttribute للكتابة، removeAttribute للحذف.
  4. التدفق القياسي لإنشاء العناصر: createElement → تعيين المحتوى/السمات → appendChild للتثبيت.
  5. طريقتان لإزالة العناصر: remove() (العنصر يزيل نفسه)، removeChild() (الأبو يزيله، وتحصل على العقدة مرة أخرى).
  6. cloneNode(true) يستنسخ عميقاً بجميع الأحفاد، cloneNode(false) يستنسخ الغلاف فقط.

❓ أسئلة شائعة

س ماذا يحدث إذا أرجع querySelector قيمة null؟
ج استدعاء أي خاصية/طريقة على null سيطرح TypeError. لذا الأفضل التحقق قبل التشغيل: const el = document.querySelector('.xxx'); if (el) { ... }.
س ما الفرق بين HTMLCollection و NodeList؟
ج HTMLCollection حية — تتحدث تلقائياً عند تغيير DOM؛ NodeList من querySelectorAll هي لقطة ثابتة. عند التكرار وإضافة/إزالة العناصر، المجموعة الثابتة أكثر أماناً، وإلا قد تحصل على حلقة لا نهائية.
س ماذا يحدث إذا appendChild عقدة موجودة بالفعل؟
ج العقدة "تتحرك" من موضعها الأصلي إلى الموضع الجديد. في DOM، نفس العقدة لا يمكن أن تظهر مرة واحدة. هذا نقل، وليس نسخ.

📝 تمارين

  1. أساسي: أنشئ صفحة تستخدم createElement و appendChild لإنشاء قائمة ديناميكياً تحتوي على 5 أسماء مدن.
  2. متوسط: أضف زر حذف للقائمة من التمرين السابق. عند النقر، استخدم removeChild لحذف عنصر القائمة المقابل.
  3. تحدي: نفّذ "مدير ملاحظات بسيط" — حقل إدخال + زر إضافة. في كل مرة تضيف ملاحظة، أنشئ بطاقة بزر حذف. النقر على زر الحذف يزيل تلك البطاقة.
100%