Javaبرنامج تعليمي حول DOM النصي مع مثال

⚡ ملخص ذكي

Javaتمنح Script DOM و Events البرامج النصية تحكمًا كاملاً في صفحة الويب، مما يسمح للتعليمات البرمجية بقراءة أو تغيير أو إضافة أو إزالة أي عنصر قام المتصفح بعرضه، ثم الاستجابة على الفور كلما قام الزائر بالنقر أو الكتابة أو التمرير.

  • 🌳 أنواع العقد: تلعب كل من عقد العناصر والنصوص والسمات والتعليقات دورًا مميزًا داخل شجرة DOM.
  • 🔍 تحديد العناصر: تقوم getElementById و querySelector و querySelectorAll بتحديد العقد الدقيقة التي يحتاجها البرنامج النصي للعمل معها.
  • ✏️ تحديث المحتوى: تقوم وظائف innerHTML و textContent و setAttribute بإعادة كتابة ما يراه الزائر فعليًا على الشاشة.
  • 🧩 عقد البناء: تقوم وظائف createElement و appendChild بإدراج عناصر جديدة تمامًا دون إعادة تحميل الصفحة.
  • 🗑️ إزالة العقد: تقوم وظيفة removeChild و remove بإزالة العناصر غير المرغوب فيها من الشجرة الحية.
  • 🖱️ التعامل مع الحدث: تقوم الدالة addEventListener بربط معالجات النقر، وضغط المفاتيح، والإرسال بأي عنصر.
  • 🔄 تفويض الفعاليات: يتولى مستمع واحد للعنصر الأب بالإضافة إلى event.target إدارة النقرات للعديد من العناصر الفرعية في وقت واحد.
  • 🤖 تصحيح الأخطاء بمساعدة الذكاء الاصطناعي: مساعدو البرمجة بالذكاء الاصطناعي يساعدون tracحل مشكلات الفقاعات واقتراح أنماط تفويض لواجهات المستخدم المعقدة.

ما هو DOM في Javaالبرنامج النصي؟

Javaيمكن للبرامج النصية الوصول إلى جميع عناصر صفحة الويب باستخدام نموذج كائن المستند (DOM). في الواقع، يقوم متصفح الويب بإنشاء نموذج DOM لصفحة الويب عند تحميلها. يُنشأ نموذج DOM على شكل شجرة من الكائنات كما يلي:

دوم في Javaسيناريو

يُصبح كل عنصر HTML في الصفحة عقدةً داخل هذه الشجرة. تُصنّف المتصفحات عقد DOM إلى أنواع أساسية قليلة: تمثل عقد العناصر علامات مثل <p> or <div>تحتوي عقد النص على الكلمات المرئية داخل الوسم، بينما تخزن عقد السمات قيمًا مثل id or classوتحافظ عقد التعليق على <!-- --> الترميز. العقدة العلوية هي دائمًا document الكائن، وكل عنصر آخر هو سليل له.

باستخدام DOM، Javaسيناريو يمكنه القيام بمهام متعددة. يمكنه إنشاء عناصر وخصائص جديدة، وتغيير العناصر والخصائص الموجودة، وحتى إزالة العناصر والخصائص الموجودة. Javaيمكن للبرمجة النصية أيضًا التفاعل مع الأحداث الموجودة وإنشاء أحداث جديدة على الصفحة. تشرح الأقسام التالية كيفية تحديد العناصر وتعديلها وربط معالجات الأحداث بأمثلة عملية قابلة للتنفيذ.

من المفيد أن نتذكر أن نموذج كائن المستند (DOM) ليس هو نفسه ملف HTML المصدر. فملف HTML الذي يقوم المتصفح بتنزيله هو نص ثابت، بينما نموذج كائن المستند (DOM) الذي يبنيه من هذا النص هو نموذج كائن حي وديناميكي. أي تغيير Javaيظهر النص البرمجي الذي يتم إنشاؤه في DOM على الشاشة على الفور، دون أن يتم لمس ملف HTML الأصلي على الإطلاق.

كيفية تحديد عناصر DOM في Javaسيناريو

قبل Javaيمكن للبرامج النصية تغيير أي شيء في الصفحة، ولكنها تحتاج أولاً إلى تحديد العنصر المستهدف. يوفر نموذج كائن المستند (DOM) عدة طرق لتحديد العنصر، كل منها مناسب لحالات مختلفة.

  • getElementById(): تُعيد العنصر الوحيد الذي يُطابق عنصرًا مُحددًا id السمة.
  • getElementsByClassName(): يُعيد هذا الكود مجموعة HTML حية تحتوي على كل عنصر يحمل فئة معينة.
  • getElementsByTagName(): تُعيد هذه الدالة مجموعة HTML حية تحتوي على جميع العناصر التي تحمل اسم وسم معين، مثل: p or li.
  • querySelector(): تُعيد هذه الدالة أول عنصر يطابق أي مُحدِّد CSS.
  • querySelectorAll(): تُرجع قائمة عقد ثابتة تحتوي على كل عنصر يطابق مُحدد CSS.

يقارن الجدول أدناه بين طرق الاختيار الأربع الأكثر شيوعًا حتى تتمكن من اختيار الطريقة المناسبة للوظيفة.

الأسلوب سياسات الإرجاع والاستبدال أفضل استخدام عند
getElementById عنصر واحد أنت تعرف المعرف الدقيق والفريد للهدف.
getElementsByClassName مجموعة HTML مباشرة أنت بحاجة إلى أن تشترك جميع العناصر في اسم فئة واحد.
querySelector عنصر واحد (التطابق الأول) تريد استخدام أي مُحدِّد CSS، وليس فقط id أو class.
الاستعلام قائمة العقد الثابتة تحتاج إلى المرور على كل تطابق باستخدام forEach.

تقبل كل من querySelector و querySelectorAll أي مُحدِّد CSS صالح، مما يجعلهما الخيار الأمثل والأكثر مرونة للبرمجة الحديثة. جرّب المقتطف البرمجي أدناه في صفحة تحتوي على عنصر باستخدام id="title" والعديد من العناصر مع class="item":

const heading = document.getElementById("title");
const itemsByClass = document.getElementsByClassName("item");
const firstItem = document.querySelector(".item");
const allItems = document.querySelectorAll(".item");
console.log(heading.textContent, itemsByClass.length, allItems.length);

يظل getElementById و getElementsByClassName أسرع في عمليات البحث البسيطة، بينما يفضل استخدام querySelector و querySelectorAll عندما تحتاج منطق التحديد إلى قوة بناء جملة CSS الكاملة، مثل محددات السمات أو المحددات الفرعية.

كيفية تعديل وإنشاء وإزالة عناصر DOM

بمجرد تحديد عنصر ما، Javaيمكن للبرنامج النصي قراءة أو إعادة كتابة محتواه وخصائصه، وحتى وجوده في الشجرة. باستخدام DOM، Javaيمكن للبرنامج النصي إنشاء عناصر وسمات جديدة، وتغيير العناصر والسمات الموجودة، وإزالة العناصر والسمات الموجودة بالكامل.

قم بتغيير المحتوى باستخدام getElementById و innerHTML

  1. getElementById: للوصول إلى العناصر والسمات التي تم تعيين معرفها.
  2. InnerHTML: للوصول إلى محتوى العنصر.

جرب هذا المثال بنفسك:

<html>
<head>
<title>DOM!!!</title>
</head>
<body>
<h1 id="one">Welcome</h1>
<p>This is the welcome message.</p>
<h2>Technology</h2>
<p>This is the technology section.</p>
<script type="text/javascript">
var text = document.getElementById("one").innerHTML;
alert("The first heading is " + text);
</script>
</body>
</html>

يؤكد مربع التنبيه أن getElementById قد حدد موقع العنوان وأن innerHTML أعاد النص المخزن بداخله.

اقرأ كل فقرة باستخدام getElementsByTagName

getElementsByTagName: للوصول إلى العناصر والسمات باستخدام اسم العلامة. هذه الطريقة سوف ترجع مجموعة لجميع العناصر التي لها نفس اسم العلامة.

جرب هذا المثال بنفسك:

<html>
<head>
<title>DOM!!!</title>
</head>
<body>
<h1>Welcome</h1>
<p>This is the welcome message.</p>
<h2>Technology</h2>
<p id="second">This is the technology section.</p>
<script type="text/javascript">
var paragraphs = document.getElementsByTagName("p");
alert("Content in the second paragraph is " + paragraphs[1].innerHTML);
document.getElementById("second").innerHTML = "The orginal message is changed.";
</script>
</body>
</html>

لأن getElementsByTagName تُرجع مجموعة حية، فإن paragraphs[1] تشير إلى الفقرة الثانية في المستند، وتحديث innerHTML يؤدي على الفور إلى الكتابة فوق نصها على الشاشة.

إنشاء العناصر وإزالتها باستخدام createElement و removeChild

إلى جانب تغيير ما هو موجود بالفعل على الصفحة، يتيح لك نموذج كائن المستند (DOM) إضافة عناصر جديدة تمامًا أو إزالة العناصر الموجودة:

  • إنشاء عنصر(): يقوم بإنشاء عقدة عنصر جديدة في الذاكرة.
  • appendChild(): يقوم بربط عقدة كآخر عنصر فرعي لعنصر أب.
  • setAttribute(): يضيف أو يُحدّث سمة، مثل class or href، على عنصر.
  • removeChild(): يزيل عقدة فرعية محددة من عقدتها الأصلية.

يقوم المثال أدناه بإنشاء عنصر قائمة جديد، وإعطائه نصًا وفئة، وإلحاقه بقائمة موجودة، ثم إزالة العنصر الأول من نفس القائمة:

const list = document.getElementById("myList");
const newItem = document.createElement("li");
newItem.textContent = "Freshly added item";
newItem.setAttribute("class", "highlight");
list.appendChild(newItem);
list.removeChild(list.firstElementChild);

لا يُظهر استدعاء الدالة createElement() وحده أي شيء، لأن العقدة لا تزال موجودة في الذاكرة فقط. ولا تظهر إلا بعد أن تُلحقها الدالة appendChild()، أو دالة مشابهة مثل prepend()، في مكان ما داخل الشجرة المرئية. كما تدعم المتصفحات الحديثة دالة node.remove() المختصرة، التي تحذف العنصر مباشرةً دون الحاجة إلى مرجع إلى العنصر الأب، على عكس الصيغة القديمة parent.removeChild(node).

كيفية التعامل مع الأحداث في Javaسيناريو

الحدث هو أي إجراء يمكن للمتصفح رصده، مثل النقر أو الضغط على مفتاح أو تحميل صفحة. يمكنك إضافة معالج أحداث إلى عنصر معين بطريقتين رئيسيتين.

قم بإرفاق معالج باستخدام onclick أو addEventListener

أما الأسلوب الأول والأقدم فيقوم بتعيين دالة مباشرة لخاصية الحدث الخاص بالعنصر:

document.getElementById(id).onclick=function()
{
lines of code to be executed
}

أو باستخدام طريقة addEventListener() الحديثة والأكثر مرونة:

document.getElementById(id).addEventListener("click", functionname)

يُفضّل عمومًا استخدام الدالة `addEventListener()` لأنها تتيح لك ربط أكثر من معالج حدث واحد بنفس الحدث على نفس العنصر، كما أنها تقبل وسيطًا ثالثًا اختياريًا يتحكم في عملية الالتقاط، والذي سنتناوله لاحقًا في هذا القسم. وتتكامل هذه الدالة أيضًا مع الدالة `removeEventListener()`، التي تفصل معالج الحدث عند انتهاء الحاجة إليه، وهو أمر لا تستطيع الخاصية `onclick` القيام به بمفردها.

يدعم HTML أيضًا نمطًا ثالثًا: معالج مضمن مكتوب مباشرة على الوسم، مثل <button onclick="clicked()">Click Me</button>تخلط المعالجات المضمنة بين الترميز والمنطق، وسرعان ما يصبح صيانتها صعبًا، لذا فإن معظمها Javaيحتفظ مطورو البرامج النصية بها للعروض التوضيحية السريعة ويعتمدون على addEventListener() للتطبيقات الحقيقية.

جربها: مثال كامل لحدث النقر

جرب هذا المثال بنفسك:

<html>
<head>
<title>DOM!!!</title>
</head>
<body>
<input type="button" id="btnClick" value="Click Me!!" />
<script type="text/javascript">
document.getElementById("btnClick").addEventListener("click", clicked);
function clicked()
{
alert("You clicked me!!!");
}
</script>
</body>
</html>

يؤدي النقر على الزر إلى تشغيل الدالة clicked()، والتي بدورها تُفعّل Javaمربع تنبيه البرنامج النصي يؤكد أن معالج الأحداث قد تم تشغيله بنجاح.

كائن الحدث

يستقبل كل معالج أحداث تلقائيًا كائن حدث كمعامله الأول. يحمل هذا الكائن تفاصيل مفيدة حول ما حدث، بما في ذلك event.target (العنصر المحدد الذي بدأ منه الإجراء)، وevent.type (اسم الحدث، مثل "click")، وأساليب مثل event.preventDefault()، التي توقف السلوك الافتراضي للمتصفح، مثل النقر على رابط أو إرسال نموذج.

document.getElementById("btnClick").addEventListener("click", function(event) {
console.log("Clicked element: " + event.target.id);
});

هناك خاصيتان إضافيتان تُكملان كائن الحدث: event.currentTarget يشير دائمًا إلى العنصر الذي يرتبط به المستمع، حتى لو أبلغ event.target عن عنصر فرعي متداخل، ويوقف event.stopPropagation() الحدث قبل أن يستمر في الانتشار أو الالتقاط. إلى جانب preventDefault()، تمنح هذه الأدوات المعالج تحكمًا دقيقًا في ما سيحدث لاحقًا.

الحدث/الفعالية Bubblالتسجيل، والتقاط البيانات، والتفويض

عندما يتم إطلاق حدث ما على عنصر متداخل، فإنه لا يتوقف عند هذا الحد. فقاعات في هذه المرحلة، ينتقل الحدث تصاعديًا من العنصر المستهدف عبر كل عنصر سلف، وصولًا إلى المستند. اسر في المرحلة الأولى، والتي يتم تشغيلها اختيارياً، ينتقل الحدث نزولاً من المستند إلى الهدف قبل بدء عملية الانتشار. تجاوز {capture: true} كوسيطة ثالثة للدالة addEventListener() للاستماع أثناء مرحلة الالتقاط.

وفد الحدث يستفيد هذا الأسلوب من خاصية التداخل: فبدلاً من ربط مستمع منفصل بكل عنصر فرعي، يتم ربط مستمع واحد بالعنصر الأب المشترك، ثم يتم فحص خاصية event.target لتحديد العنصر الفرعي الذي أطلق الحدث. هذا الأسلوب يستهلك ذاكرة أقل، ويغطي تلقائيًا العناصر التي تُضاف إلى الصفحة لاحقًا.

const list = document.getElementById("myList");
list.addEventListener("click", function(event) {
  if (event.target.tagName === "LI") {
    alert("You clicked " + event.target.textContent);
  }
});

يمنحك التفويض، إلى جانب خاصية الفقاعات والتقاط البيانات، تحكمًا دقيقًا في كيفية عمل DOM والأحداث في Javaيستجيب البرنامج النصي كلما أصبحت الصفحة أكثر تفاعلية.

الأسئلة الشائعة

لا. HTML هو ترميز ثابت يقوم المتصفح بتحليله، بينما DOM هو شجرة الكائنات الحية الموجودة في الذاكرة والتي Javaيمكن للبرنامج النصي القراءة والتغيير بعد تحميل الصفحة.

يُفضل استخدام addEventListener(). فهو يسمح بمعالجات متعددة لحدث واحد، ويدعم مرحلة الالتقاط، ولا يستبدل معالجًا تم إرفاقه بالفعل من خلال onclick.

Bubblينقل حدث ing الحدث من العنصر المستهدف إلى المستند. أما حدث capturing فيُنفذ أولاً ويتحرك في الاتجاه المعاكس، من المستند إلى العنصر المستهدف.

نعم. المساعدون مثل مساعد الطيار جيثب علبة tracلماذا لا يتم تشغيل المعالج أبدًا، وتحديد استدعاءات event.stopPropagation() المفقودة، واقتراح أنماط التفويض للقوائم الديناميكية.

نعم. أدوات مثل شات جي بي تي ويمكن لـ GitHub Copilot صياغة مقتطفات createElement و querySelector و addEventListener من وصف بلغة بسيطة، على الرغم من أن المخرجات لا تزال بحاجة إلى اختبار.

تلخيص هذه التدوينة بـ: