مقدمة في DOM
DOM هو الجسر الوحيد بين JavaScript وصفحات الويب. بدون DOM، لن تكون JS أكثر من لغة حساب خالصة. مع DOM، يمكن لـ JS "رؤية" الصفحات، وتعديلها، وإحياؤها.
📖 ملخص
ما هو DOM؟
DOM يعني نموذج كائن المستند (Document Object Model). عندما يحمّل المتصفح HTML، لا يخزنه كنص عادي — يترجمه إلى شجرة كائنات. كل وسم HTML يصبح كائن (يسمى عقدة)، والعلاقات المتداخلة بين الأوسام تصبح علاقات أبو-ابن في الشجرة.
فكّر في DOM كشجرة مقلوبة: في الأعلى تماماً الجذر (document)، يتفرع إلى أغصان (html، head، body)، ثم إلى أوراق (div، p، span...).
هيكل شجرة DOM
شجرة DOM لصفحة HTML بسيطة تبدو تقريباً هكذا:
document
└── html
├── head
│ ├── meta
│ └── title
└── body
├── h1
├── p
└── div
└── span
documentهو العقدة الجذر لشجرة DOM ونقطة دخول JS للوصول إلى DOMhtmlهو عنصر الابن الوحيد لـdocument(عنصر المستند)headوbodyهما عنصر الابن لـhtml- من هناك، يستمر التداخل طبقة بطبقة لتشكيل الشجرة الكاملة
أنواع العقد
هناك ثلاثة أنواع أساسية من العقد في DOM:
| نوع العقد | الوصف | قيمة nodeType |
|---|---|---|
| عقدة عنصر | وسم HTML، مثل p، div |
1 |
| عقدة نص | محتوى النص داخل الوسم | 3 |
| عقدة سمة | سمة الوسم، مثل class، id |
2 (مهمل للاستخدام العام) |
<p class="intro">مرحباً بالعالم</p>
في هذا السطر الواحد: <p> هو عقدة عنصر، class="intro" هو عقدة سمة، و مرحباً بالعالم هو عقدة نص. في التطوير اليومي، عقد العناصر هي ما ستعمل معه أكثر.
كائن document
document هو نقطة الدخول إلى DOM — المتصفح يوفر هذا الكائن العام تلقائياً. من خلاله يمكنك:
- الوصول إلى عناصر الصفحة:
document.getElementById()،document.querySelector() - إنشاء عناصر جديدة:
document.createElement() - قراءة وكتابة محتوى الصفحة:
document.title،document.body - الاستماع لأحداث الصفحة:
document.addEventListener()
ماذا يفعل 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.📝 تمارين
- افتح أدوات المتصفح (F12)، اكتب
console.dir(document)في Console، واستكشف خصائص وطرق كائن document. اذكر 5 تعرفها أو تجدها مثيرة. - اكتب كوداً يحصل على عنصر
bodyللصفحة الحالية، يطبعchildElementCount(عدد عناصر الأبناء)، ثم يستخدمchildrenللمرور على جميع عناصر الأبناء ويطبع اسم وسم كل منها. - اشرح الفرق بين "عقد العناصر" و"عقد النص". بالنظر إلى
<div>مرحباً <b>بالعالم</b></div>، ارسم هيكل شجرة العقد وحدد نوع كل عقدة.



