404 Not Found

404 Not Found


nginx

الأحداث في JavaScript

الأحداث هي "إشارات" يرسلها المستخدم أو المتصفح — عندما ينقر المستخدم على زر، يضغط مفتاحاً، يرسل نموذجاً، أو ينتهي المتصفح من تحميل صفحة...这些都是 אירועים. الكود "يسمع" هذه الأحداث ويستجيب في الوقت المناسب. مثل رنين جرس الباب وفتحك الباب — الجرس هو الحدث، فتح الباب هو الاستجابة.


طرق ربط الأحداث

ل JavaScript ثلاث طرق لربط معالجات الأحداث بالعناصر:

1. سمات HTML (غير موصى بها)

HTML
<button onclick="alert('لا تستخدم هذه الطريقة')">انقر هنا</button>

HTML و JS مختلطان معاً، مما يصعّب الصيانة. غير موصى بها.

2. خصائص DOM

<button id="myBtn">انقر هنا</button>
<script>
const btn = document.getElementById('myBtn');
btn.onclick = function() {
  console.log('تم النقر');
};
</script>

HTML و JS منفصلان، لكن كل حدث يمكن أن يكون له معالج واحد فقط — التعيينات اللاحقة تستبدل السابقة.

3. addEventListener (موصى بها)

HTML
<button id="btn1">زر 1</button>
<button id="btn2">زر 2</button>
<script>
const btn1 = document.getElementById('btn1');
const btn2 = document.getElementById('btn2');
btn1.addEventListener('click', function() {
  console.log('المعالج 1');
});
btn1.addEventListener('click', function() {
  console.log('المعالج 2');
});
</script>

نفس الحدث يمكن أن يكون له عدة معالجات، ويمكنك استخدام removeEventListener لإزالتها. هذه هي الطريقة الأكثر مرونة.

💡 الاختيار بين الطرق الثلاث: انسَ الأولتين واستخدم دائماً addEventListener. لا تستبدل المستمعين الحاليين وتتيح التحكم في مرحلة الفقاعات/الالتقاط. هي الخيار الوحيد للتطوير الحديث.


addEventListener و removeEventListener

HTML
<script>
element.addEventListener('eventName', handler, options);
element.removeEventListener('eventName', handler);
</script>
⚠️ removeEventListener يتطلب نفس المرجع للدالة مثل addEventListener. الدوال المجهولة لا يمكن إزالتها! إذا كنت بحاجة لإزالة معالج، حدد الدالة بشكل منفصل.

HTML
<button id="btn">اختبار</button>
<script>
const btn = document.getElementById('btn');

function handler() { console.log('يُفعّل مرة واحدة فقط'); }
btn.addEventListener('click', handler);
btn.removeEventListener('click', handler);  // تمت الإزالة بنجاح

btn.addEventListener('click', function() { console.log('مجهول'); });
btn.removeEventListener('click', function() { console.log('مجهول'); });  // الإزالة تفشل!
</script>

أنواع الأحداث الشائعة

الفئة الحدث الوصف
الماوس click، dblclick نقرة واحدة، نقرة مزدوجة
الماوس mouseover، mouseout الماوس يدخل/يخرج
لوحة المفاتيح keydown، keyup مفتاح ضُغط/أُفرج
النموذج submit تم إرسال النموذج
النموذج change القيمة تغيرت بعد فقدان التركيز
النموذج input القيمة تتغير في الوقت الفعلي (كل حرف مكتوب)
الصفحة load الصفحة وجميع الموارد محملة بالكامل
الصفحة DOMContentLoaded اكتمل تحليل HTML (لا ينتظر الصور)
💡 change مقابل input: input يُطلق أثناء الكتابة، change يُطلق بعد الانتهاء من الكتابة وفقدان التركيز. استخدم input للبحث في الوقت الفعلي، change للتحقق من النماذج.


كائن الحدث

معالجات الأحداث تتلقى معلمة كائن حدث، عادةً تسمى event أو تختصر إلى e.

الخاصية/ال الطريقة الوصف
e.target العنصر الذي فعّل الحدث (الذي نقرت عليه فعلياً)
e.type نوع الحدث، مثل 'click'
e.preventDefault() منع السلوك الافتراضي (مثل منع التنقل عبر الرابط، إرسال النموذج)
e.stopPropagation() إيقاف فقاعات الحدث

مثال: عداد نقرات الزر

<button id="counter">تم النقر 0 مرة</button>

<script>
const btn = document.getElementById('counter');
let count = 0;

btn.addEventListener('click', function(e) {
  count++;
  btn.textContent = 'تم النقر ' + count + ' مرة';
  console.log('نوع الحدث:', e.type);
  console.log('العنصر المستهدف:', e.target.tagName);
});
</script>
▶ جرّب الكود

مثال: اكتشاف لوحة المفاتيح

<p>اضغط أي مفتاح لرؤية المعلومات</p>
<div id="info" style="padding: 10px; border: 1px solid #ccc; min-height: 30px;"></div>

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

document.addEventListener('keydown', function(e) {
  info.innerHTML = 'المفتاح: <b>' + e.key + '</b> | KeyCode: ' + e.keyCode + ' | Ctrl: ' + e.ctrlKey + ' | Shift: ' + e.shiftKey;
});
</script>
▶ جرّب الكود

مثال: اعتراض النموذج

HTML
<form id="myForm">
  <input type="text" id="name" placeholder="أدخل الاسم" required>
  <button type="submit">إرسال</button>
</form>
<p id="result"></p>

<script>
const form = document.getElementById('myForm');
const result = document.getElementById('result');

form.addEventListener('submit', function(e) {
  e.preventDefault();
  const name = document.getElementById('name').value;
  result.textContent = 'مرحباً، ' + name + '! تم اعتراض النموذج، لم يُرسل فعلياً.';
});
</script>
▶ جرّب الكود

مثال: تفويض الأحداث

<ul id="menu">
  <li>الرئيسية</li>
  <li>حول</li>
  <li>اتصل بنا</li>
  <li>مساعدة</li>
</ul>
<p id="selected"></p>

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

menu.addEventListener('click', function(e) {
  if (e.target.tagName === 'LI') {
    selected.textContent = 'اخترت: ' + e.target.textContent;
  }
});
</script>
▶ جرّب الكود
💡 التقنية أعلاه تسمى "تفويض الأحداث" — ربط المستمع بالعنصر الأب واستخدام e.target لتحديد أي ابن تم النقر عليه فعلياً. لا حاجة لربط الأحداث بكل li على حدة، وعناصر الأبناء الجديدة تعمل تلقائياً. هذه هي قوة آلية الفقاعات.


فقاعات الأحداث والالتقاط

انتشار أحداث DOM له ثلاث مراحل:

  1. مرحلة الالتقاط: الحدث يسافر من window نزولاً إلى العنصر المستهدف
  2. مرحلة الهدف: الحدث يصل إلى العنصر المفعّل فعلياً
  3. مرحلة الفقاعات: الحدث يتصاعد من العنصر المستهدف عائداً إلى window

بشكل افتراضي، addEventListener يُفعّل أثناء مرحلة الفقاعات. اضبط المعلمة الثالثة على true للتفعيل أثناء مرحلة الالتقاط.

     window
       ↓ الالتقاط
    document
       ↓
      body
       ↓
   عنصر الهدف ← مرحلة الهدف
       ↑ الفقاعات
      body
       ↑
    document
       ↑
     window
💡 في معظم الحالات، لا حاجة للقلق بشأن الالتقاط — الفقاعات الافتراضية كافية. e.stopPropagation() يمكن أن يوقف انتشار الحدث، لكن لا تفرط في استخدامه — قد يكسر تفويض الأحداث.


📖 ملخص

  1. بين الطرق الثلاث للربط، addEventListener هي الوحيدة الموصى بها — تدعم مستمعين متعددين، قابلة للإزالة، وتحكم في مرحلة الانتشار.
  2. removeEventListener يتطلب نفس المرجع للدالة؛ الدوال المجهولة لا يمكن إزالتها.
  3. خصائص كائن الحدث الأساسية e: target (من فعّله)، type (ما الحدث)، preventDefault() (منع السلوك الافتراضي)، stopPropagation() (إيقاف الانتشار).
  4. change مقابل input:前者 يُطلق عند الضياع،后者 في الوقت الفعلي.
  5. فقاعات الأحداث تجعل "تفويض الأحداث" ممكناً — استمع على الأب، تعامل مع الأبناء بـ e.target.
  6. DOMContentLoaded يُطلق قبل load، مناسب لتهيئة DOM.

❓ أسئلة شائعة

س ما الفرق بين e.target و e.currentTarget؟
ج e.target هو العنصر الذي فعّل الحدث فعلياً (الذي نقرت عليه)، e.currentTarget هو العنصر الذي ينفذ المعالج حالياً (الذي عليه المستمع). في تفويض الأحداث هما مختلفان؛ في الربط المباشر هما متماثلان.
س لماذا الصفحة تتحدث بعد إرسال النموذج؟
ج السلوك الافتراضي لحدث submit في <form> هو الإرسال وتحديث الصفحة. استدعاء e.preventDefault() يمنع ذلك، ثم تعامل مع البيانات بـ JS.
س هل يمكن استخدام onclick و addEventListener('click') معاً؟
ج نعم، لكن تعيين onclick يستبدل الدوال المربوطة سابقاً عبر onclick، بينما addEventListener لا يفعل. خلطهما قد يكون مربكاً — التزم بـ addEventListener لراحة البال.

📝 تمارين

  1. أساسي: أنشئ زراً واستخدم addEventListener لربط حدث click. كل نقرة تزيد عرض الزر بـ 10px.
  2. متوسط: نفّذ آلة مفاتيح — استمع لأحداث keydown. عند ضغط A/S/D/F/G، اعرض أسماء ملاحظات مختلفة (Do/Re/Mi/Fa/Sol) على الصفحة.
  3. تحدي: استخدم تفويض الأحداث لتنفيذ قائمة ديناميكية — قائمة ul بزر "إضافة عنصر". عناصر li المضافة حديثاً يجب أن تتميّز أيضاً عند النقر (بدون حاجة لربط الأحداث بعناصر li الجديدة على حدة).
  4. مكافأة: نفّذ بحثاً فورياً بحدث input — أثناء الكتابة في حقل الإدخال، القائمة أدناه تعرض فقط العناصر التي تحتوي على النص المكتوب.
100%