JavaScript: مقدمة في DOM
DOM هو الجسر الوحيد بين JavaScript وصفحات الويب. بدون DOM، لن تكون JS أكثر من لغة حساب خالصة. مع DOM، يمكن لـ JS "رؤية" الصفحات، وتعديلها، وإحياؤها.
1. شجرة DOM وكائن document
▶ مثال
// مثال على الكود في هذا القسم
const message = "مرحبا بالعالم!";
console.log(message);
هذا أبسط مثال; الأقسام التالية ستشرح بالتفصيل.### (1) ما هو DOM؟
DOM يعني نموذج كائن المستند (Document Object Model). عندما يحمّل المتصفح HTML، لا يخزنه كنص عادي — يترجمه إلى شجرة كائنات. كل وسم HTML يصبح كائن (يسمى عقدة)، والعلاقات المتداخلة بين الأوسام تصبح علاقات أبو-ابن في الشجرة.
فكّر في DOM كشجرة مقلوبة: في الأعلى تماماً الجذر (document)، يتفرع إلى أغصان (html، head، body)، ثم إلى أوراق (div، p، span...).
(2) هيكل شجرة DOM
شجرة DOM لصفحة HTML بسيطة تبدو تقريباً هكذا:
document
└── html
├── head
│ ├── meta
│ └── title
└── body
├── h1
├── p
└── div
└── span
documentهو العقدة الجذر لشجرة DOM ونقطة دخول JS للوصول إلى DOMhtmlهو عنصر الابن الوحيد لـdocument(عنصر المستند)headوbodyهما عنصر الابن لـhtml- من هناك، يستمر التداخل طبقة بطبقة لتشكيل الشجرة الكاملة
(3) أنواع العقد
هناك ثلاثة أنواع أساسية من العقد في DOM:
| نوع العقد | الوصف | قيمة nodeType |
|---|---|---|
| عقدة عنصر | وسم HTML، مثل p، div |
1 |
| عقدة نص | محتوى النص داخل الوسم | 3 |
| عقدة سمة | سمة الوسم، مثل class، id |
2 (مهمل للاستخدام العام) |
<p class="intro">مرحباً بالعالم</p>
في هذا السطر الواحد: <p> هو عقدة عنصر، class="intro" هو عقدة سمة، و مرحباً بالعالم هو عقدة نص. في التطوير اليومي، عقد العناصر هي ما ستعمل معه أكثر.
(4) كائن document
document هو نقطة الدخول إلى DOM — المتصفح يوفر هذا الكائن العام تلقائياً. من خلاله يمكنك:
- الوصول إلى عناصر الصفحة:
document.getElementById()،document.querySelector() - إنشاء عناصر جديدة:
document.createElement() - قراءة وكتابة محتوى الصفحة:
document.title،document.body - الاستماع لأحداث الصفحة:
document.addEventListener()
(5) ماذا يفعل DOM
JS نفسها لا يمكنها "رؤية" صفحة الويب — يمكنها فقط الحساب. DOM يوفر "العيون" و"الأيدي":
- قراءة: الحصول على النصوص والسمات والأنماط من الصفحة
- تعديل: تغيير محتوى النص وقيم السمات وأنماط CSS
- إضافة: إنشاء عناصر جديدة وإدراجها في الصفحة
- إزالة: إزالة العناصر من الصفحة
- استماع: الاستجابة لنقرات المستخدم والإدخال والتفاعلات الأخرى
بدون DOM، ستكون JS و HTML شيئين منفصلين تماماً. DOM يربطهما.
▶ مثال: عرض هيكل DOM
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<title>هيكل DOM</title>
<style>
body { font-family: sans-serif; padding: 20px; }
.tree { margin: 16px 0; padding: 16px; background: #f5f5f5; border-radius: 8px; font-family: monospace; font-size: 14px; line-height: 1.8; direction: ltr; text-align: left; }
.node { color: #4a90d9; font-weight: bold; }
.text-node { color: #5cb85c; font-style: italic; }
.attr-node { color: #f0ad4e; }
.indent { margin-right: 24px; }
.info { background: #e8f4fd; padding: 12px; border-radius: 6px; margin: 16px 0; }
</style>
</head>
<body>
<h1 id="mainTitle">عرض هيكل DOM</h1>
<p class="intro">هذه فقرة</p>
<div id="container">
<span>عنصر سطر</span>
</div>
<div id="output"></div>
<script>
function buildTree(node, depth) {
if (depth > 4) return "";
var indent = "";
for (var i = 0; i < depth; i++) indent += " ";
var result = "";
if (node.nodeType === 1) {
result += indent + '<span class="node"><' + node.nodeName.toLowerCase();
if (node.attributes && node.attributes.length > 0) {
for (var a = 0; a < node.attributes.length && a < 3; a++) {
result += ' <span class="attr-node">' +
node.attributes[a].name + '="' + node.attributes[a].value + '"</span>';
}
}
result += '></span>\n';
} else if (node.nodeType === 3) {
var text = node.textContent.trim();
if (text) {
result += indent + '<span class="text-node">"' + text + '"</span>\n';
}
return result;
}
var children = node.childNodes;
for (var c = 0; c < children.length; c++) {
result += buildTree(children[c], depth + 1);
}
if (node.nodeType === 1) {
result += indent + '<span class="node"></' +
node.nodeName.toLowerCase() + '></span>\n';
}
return result;
}
var treeStr = buildTree(document.documentElement, 0);
document.getElementById("output").innerHTML = `
<h2>شجرة DOM لهذه الصفحة</h2>
<div class="tree"><pre>${treeStr}</pre></div>
<div class="info">
<strong>دليل الألوان:</strong>
<span class="node">أزرق = عقدة عنصر</span> |
<span class="text-node">أخضر = عقدة نص</span> |
<span class="attr-node">برتقالي = سمة</span>
</div>
`;
</script>
</body>
</html>
▶ مثال: استكشاف كائن document
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<title>كائن document</title>
<style>
body { font-family: sans-serif; padding: 20px; }
.card { border: 2px solid #4a90d9; border-radius: 8px; padding: 16px; margin: 12px 0; background: #f0f7ff; }
.card h3 { margin: 0 0 8px; color: #4a90d9; }
.prop { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid #e0e0e0; }
.prop:last-child { border-bottom: none; }
.key { font-weight: bold; color: #333; }
.val { color: #4a90d9; font-family: monospace; word-break: break-all; direction: ltr; }
button { padding: 10px 20px; font-size: 16px; border: none; border-radius: 6px; cursor: pointer; margin: 4px; }
.btn-blue { background: #4a90d9; color: #fff; }
.btn-blue:hover { background: #357abd; }
.btn-green { background: #5cb85c; color: #fff; }
.btn-green:hover { background: #449d44; }
.btn-orange { background: #f0ad4e; color: #fff; }
.btn-orange:hover { background: #ec971f; }
.demo-area { min-height: 60px; border: 2px dashed #ccc; border-radius: 8px; padding: 16px; margin: 12px 0; text-align: center; }
</style>
</head>
<body>
<h2>استكشاف كائن document</h2>
<div class="card">
<h3>خصائص document الشائعة</h3>
<div id="props"></div>
</div>
<div class="card">
<h3>جرّب بنفسك</h3>
<button class="btn-blue" id="btnTitle">غيّر document.title</button>
<button class="btn-green" id="btnBody">غيّر خلفية body</button>
<button class="btn-orange" id="btnCreate">أنشئ عنصراً جديداً</button>
<button class="btn-blue" id="btnReset" style="background:#888;">إعادة تعيين</button>
</div>
<div class="demo-area" id="demoArea">
<p>هذه منطقة العرض</p>
</div>
<script>
function showProps() {
var props = [
["document.title", document.title],
["document.URL", document.URL],
["document.domain", document.domain],
["document.contentType", document.contentType],
["document.documentElement", document.documentElement.nodeName],
["document.body", document.body.nodeName],
["document.head", document.head.nodeName],
["document.body.childElementCount", document.body.childElementCount],
["document.all.length", document.all.length + " عنصر"]
];
document.getElementById("props").innerHTML = props.map(function(p) {
return '<div class="prop"><span class="key">' + p[0] +
'</span><span class="val">' + p[1] + '</span></div>';
}).join("");
}
showProps();
var titleCount = 0;
document.getElementById("btnTitle").addEventListener("click", function() {
titleCount++;
document.title = "تم التغيير " + titleCount + " مرة!";
showProps();
});
var bgColors = ["#fff8e1", "#e8f5e9", "#fce4ec", "#e3f2fd", "#f3e5f5"];
var bgIndex = 0;
document.getElementById("btnBody").addEventListener("click", function() {
document.body.style.background = bgColors[bgIndex % bgColors.length];
bgIndex++;
});
var createCount = 0;
document.getElementById("btnCreate").addEventListener("click", function() {
createCount++;
var p = document.createElement("p");
p.textContent = "فقرة جديدة #" + createCount;
p.style.color = "#4a90d9";
p.style.fontWeight = "bold";
document.getElementById("demoArea").appendChild(p);
showProps();
});
document.getElementById("btnReset").addEventListener("click", function() {
document.title = "كائن document";
document.body.style.background = "";
document.getElementById("demoArea").innerHTML = "<p>هذه منطقة العرض</p>";
titleCount = 0;
bgIndex = 0;
createCount = 0;
showProps();
});
</script>
</body>
</html>
▶ مثال: تحديد أنواع العقد
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<title>أنواع العقد</title>
<style>
body { font-family: sans-serif; padding: 20px; }
table { border-collapse: collapse; margin: 16px 0; width: 100%; max-width: 700px; direction: ltr; }
td, th { border: 1px solid #ddd; padding: 10px 14px; text-align: left; }
th { background: #4a90d9; color: #fff; }
.elem { background: #e3f2fd; }
.text { background: #e8f5e9; }
.note { background: #fff8e1; padding: 12px; border-radius: 6px; border-right: 4px solid #f0ad4e; margin: 16px 0; max-width: 700px; }
</style>
</head>
<body>
<h2>شرح أنواع العقد</h2>
<p id="demo">هذه فقرة <span>تحتوي</span> على عقد متعددة</p>
<div id="output"></div>
<script>
var demo = document.getElementById("demo");
var results = [];
function analyzeNode(node, depth) {
var indent = "";
for (var i = 0; i < depth; i++) indent += "│ ";
var typeMap = { 1: "عقدة عنصر", 3: "عقدة نص" };
var typeName = typeMap[node.nodeType] || "أخرى (" + node.nodeType + ")";
var detail = "";
if (node.nodeType === 1) {
detail = "<" + node.nodeName.toLowerCase() + ">";
} else if (node.nodeType === 3) {
var text = node.textContent.trim();
if (text) detail = '"' + text + '"';
else return;
}
var cls = node.nodeType === 1 ? "elem" : "text";
results.push(
'<tr class="' + cls + '">' +
"<td>" + indent + "</td>" +
"<td><code>" + detail + "</code></td>" +
"<td>" + typeName + "</td>" +
"<td>" + node.nodeType + "</td>" +
"</tr>"
);
var children = node.childNodes;
for (var c = 0; c < children.length; c++) {
analyzeNode(children[c], depth + 1);
}
}
analyzeNode(demo, 0);
document.getElementById("output").innerHTML = `
<p>تحليل <code><p id="demo">هذه فقرة <span>تحتوي</span> على عقد متعددة</p></code></p>
<table>
<tr><th>المستوى</th><th>المحتوى</th><th>نوع العقدة</th><th>nodeType</th></tr>
${results.join("")}
</table>
<div class="note">
💡 ملاحظة: فواصل الأسطر والمسافات البيضاء بين الأوسام هي أيضاً عقد نصية! عملياً، <code>children</code> (عقد العناصر فقط) أكثر فائدة من
<code>childNodes</code> (جميع العقد بما فيها النصية).
</div>
`;
</script>
</body>
</html>
❓ أسئلة شائعة
children بدلاً من childNodes.📖 ملخص
- DOM هو تمثيل شجري لـ HTML؛ كل وسم هو عقدة DOM
documentهو مدخل DOM؛document.getElementById()يجد العناصر بالمعرّفdocument.querySelector()يستخدم محددات CSS لأول تطابق؛querySelectorAllللكل- أنواع العقد: عقد العناصر (وسوم HTML)، عقد النص (المحتوى)، عقد الخصائص (خصائص الوسم)
- عمليات DOM الأساسية: إيجاد العقد، قراءة الخصائص، تعديل المحتوى، إضافة/إزالة العقد الفرعية
📝 تمارين
- افتح أدوات المتصفح (F12)، اكتب
console.dir(document)في Console، واستكشف خصائص وطرق كائن document. اذكر 5 تعرفها أو تجدها مثيرة. - اكتب كوداً يحصل على عنصر
bodyللصفحة الحالية، يطبعchildElementCount(عدد عناصر الأبناء)، ثم يستخدمchildrenللمرور على جميع عناصر الأبناء ويطبع اسم وسم كل منها. - اشرح الفرق بين "عقد العناصر" و"عقد النص". بالنظر إلى
<div>مرحباً <b>بالعالم</b></div>، ارسم هيكل شجرة العقد وحدد نوع كل عقدة.