📖 ما هو DOM وكيف يعمل؟
DOM (Document Object Model) هو تمثيل شجري (Tree Structure) لجميع عناصر HTML في الصفحة. يحول المتصفح كود HTML إلى شجرة من الكائنات التي يمكن لـ JavaScript التعامل معها.
💡 كيف يعمل DOM؟
- المتصفح يقرأ ملف HTML
- يُنشئ شجرة DOM في الذاكرة
- JavaScript تتعامل مع هذه الشجرة (تقرأ، تعدل، تضيف، تحذف)
- المتصفح يُعيد رسم الصفحة (Reflow/Repaint) بناءً على التغييرات
مثال من الحياة: تخيل DOM كخريطة مدينة. HTML هو المدينة نفسها، وDOM هو الخريطة المطبوعة. JavaScript هي السيارة التي تتنقل في الخريطة وتغيّر الأماكن (تُغلق متجر، تُفتتح آخر، تغيّر لون مبنى).
🔍 اختيار العناصر: 5 طرق مختلفة
JavaScript توفر عدة طرق للوصول لعناصر HTML. كل طريقة لها استخدامها الخاص:
1. getElementById — الأسرع
يُرجع عنصراً واحداً بناءً على الـ id. هذا هو الأسرع لأن id فريد في الصفحة:
let header = document.getElementById("header");
console.log(header);
2. getElementsByClassName — مجموعة من العناصر
let cards = document.getElementsByClassName("card");
console.log(cards[0]);
console.log(cards.length);
3. getElementsByTagName — حسب نوع الوسم
let paragraphs = document.getElementsByTagName("p");
let allDivs = document.getElementsByTagName("div");
4. querySelector — الأحدث والأكثر مرونة
let firstCard = document.querySelector(".card");
let submitBtn = document.querySelector("#submit");
let navLink = document.querySelector("nav a.active");
5. querySelectorAll — جميع العناصر المطابقة
let allCards = document.querySelectorAll(".card");
let allLinks = document.querySelectorAll("a[href]");
allCards.forEach(card => {
card.style.border = "1px solid #ccc";
});
| الطريقة |
ما تُرجع |
السرعة |
متى تستخدم |
getElementById |
عنصر واحد |
⚡ الأسرع |
عندما تعرف الـ id |
getElementsByClassName |
HTMLCollection (حية) |
⚡ سريع |
مجموعة بنفس الـ class |
getElementsByTagName |
HTMLCollection |
⚡ سريع |
جميع عناصر نوع معين |
querySelector |
عنصر واحد |
⚠️ أبطأ |
selectors معقدة |
querySelectorAll |
NodeList (ثابتة) |
⚠️ أبطأ |
جميع المطابقات + forEach |
✏️ تعديل المحتوى: textContent vs innerHTML
هناك فرق جوهري بين هاتين الخاصيتين. فهم الفرق ضروري لتجنب مشاكل الأمان والأداء:
let element = document.getElementById("demo");
element.textContent = "نص جديد";
element.textContent = "<strong>نص</strong>";
element.innerHTML = "<strong>نص عريض</strong>";
document.getElementById("username").value = "أحمد";
| الخاصية |
يحلل HTML |
السرعة |
الأمان |
متى تستخدم |
textContent |
✗ لا |
⚡ سريع |
✓ آمن |
النصوص العادية |
innerHTML |
✓ نعم |
⚠️ أبطأ |
❌ XSS Risk |
HTML ديناميكي فقط |
🚨 خطر XSS مع innerHTML:
إذا كنت تُدخل بيانات من المستخدم في innerHTML، يمكن للمهاجم إدخال كود JavaScript خبيث! استخدم textContent دائماً للبيانات القادمة من المستخدم.
🎨 تعديل الأنماط والـ CSS
يمكنك تعديل أي خاصية CSS مباشرة من JavaScript. لكن تذكر: inline styles تتجاوز CSS الخارجي:
let box = document.querySelector(".box");
box.style.backgroundColor = "red";
box.style.fontSize = "20px";
box.style.marginTop = "10px";
box.style.display = "none";
box.style.display = "block";
box.style.display = "flex";
Object.assign(box.style, {
backgroundColor: "blue",
color: "white",
padding: "20px",
borderRadius: "10px"
});
⚠️ ملاحظة:
في JavaScript، أسماء CSS التي تحتوي على شرطة (-) تُكتب بـ camelCase: background-color → backgroundColor, font-size → fontSize.
📋 classList — إدارة Classes بذكاء
classList هي الطريقة الحديثة والمثلى لإضافة وإزالة classes. أفضل بكثير من تعديل className يدوياً:
let box = document.querySelector(".box");
box.classList.add("active", "highlight");
box.classList.remove("hidden", "disabled");
box.classList.toggle("visible");
box.classList.toggle("visible");
box.classList.toggle("dark-mode", isNightTime);
if (box.classList.contains("active")) {
console.log("العنصر نشط!");
}
box.classList.replace("old-class", "new-class");
➕ إنشاء وإضافة عناصر جديدة
يمكنك إنشاء عناصر HTML جديدة تماماً وإضافتها للصفحة ديناميكياً:
let newDiv = document.createElement("div");
newDiv.textContent = "أنا عنصر جديد!";
newDiv.className = "new-box";
newDiv.id = "dynamic-box";
newDiv.style.backgroundColor = "lightblue";
document.body.appendChild(newDiv);
let container = document.getElementById("container");
container.append(newDiv, " نص إضافي");
container.prepend(newDiv);
let reference = document.getElementById("reference");
container.insertBefore(newDiv, reference);
reference.insertAdjacentElement("afterend", newDiv);
🗑️ حذف العناصر من الصفحة
let toDelete = document.getElementById("old");
toDelete.remove();
let parent = document.getElementById("container");
let child = document.getElementById("old");
parent.removeChild(child);
let list = document.getElementById("myList");
while (list.firstChild) {
list.removeChild(list.firstChild);
}
list.innerHTML = "";
🌳 التنقل بين العناصر (Traversing)
أحياناً تحتاج للانتقال من عنصر لعنصر قريب (أب، أخ، ابن):
let item = document.querySelector(".list-item");
item.parentElement;
item.closest(".container");
item.children;
item.firstElementChild;
item.lastElementChild;
item.nextElementSibling;
item.previousElementSibling;
🏷️ التعامل مع الخصائص (Attributes)
let link = document.querySelector("a");
link.getAttribute("href");
link.href;
link.setAttribute("target", "_blank");
link.href = "https://new-url.com";
link.removeAttribute("title");
link.hasAttribute("disabled");
let div = document.querySelector("[data-user-id]");
div.dataset.userId;
div.dataset.role;
💼 أمثلة واقعية من التطبيقات
1. عداد النقرات التفاعلي
let count = 0;
const countDisplay = document.getElementById("count");
const incrementBtn = document.getElementById("increment");
const decrementBtn = document.getElementById("decrement");
const resetBtn = document.getElementById("reset");
function updateDisplay() {
countDisplay.textContent = count;
countDisplay.classList.toggle("negative", count < 0);
}
incrementBtn.addEventListener("click", () => {
count++;
updateDisplay();
});
decrementBtn.addEventListener("click", () => {
count--;
updateDisplay();
});
resetBtn.addEventListener("click", () => {
count = 0;
updateDisplay();
});
2. قائمة مهام (To-Do List)
const taskInput = document.getElementById("taskInput");
const addBtn = document.getElementById("addBtn");
const taskList = document.getElementById("taskList");
function addTask() {
const text = taskInput.value.trim();
if (!text) return;
const li = document.createElement("li");
li.className = "task-item";
li.innerHTML = `
<span class="task-text">${text}</span>
<button class="delete-btn">حذف</button>
`;
li.querySelector(".delete-btn").addEventListener("click", () => {
li.remove();
});
taskList.appendChild(li);
taskInput.value = "";
}
addBtn.addEventListener("click", addTask);
taskInput.addEventListener("keypress", (e) => {
if (e.key === "Enter") addTask();
});
3. تبديل الوضع الليلي (Dark Mode)
const themeToggle = document.getElementById("themeToggle");
const body = document.body;
themeToggle.addEventListener("click", () => {
body.classList.toggle("dark-mode");
const isDark = body.classList.contains("dark-mode");
localStorage.setItem("darkMode", isDark);
});
🚀 الخطوة التالية: الأحداث (Events)
في الدرس الثامن، ستتعلم كيف تتعامل مع تفاعلات المستخدم:
- 👆 click, mouseover, mouseout
- ⌨️ keydown, keyup, input
- 📜 scroll, load, resize
- 🎯 Event Delegation — أداء أفضل