النقر خارج العنصر: 5 أخطاء قاتلة وكيفية إصلاحها

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

لماذا يعتبر "click outside" كابوسًا لكل مطور واجهات أمامية؟

\n\n

تخيل: أنت تكتب نافذة مشروطة، أو قائمة منسدلة، أو قائمة اختيار. يفتحها المستخدم، ثم ينقر في مكان فارغ. منطقي؟ نعم. سهل التنفيذ؟ هنا تبدأ المشاكل.

\n\n

سؤال "How do I detect a click outside an element?" هو أحد أكثر الأسئلة شيوعًا على Stack Overflow. آلاف المطورين يحاولون فهم لماذا يعمل كودهم أحيانًا ولا يعمل أحيانًا أخرى. اليوم سنستعرض أهم الأخطاء عند اكتشاف النقر خارج العنصر في JavaScript ونتعلم كيفية القيام بذلك بشكل صحيح.

\n\n

المقالة مكتوبة للموقع التعليمي pythonlib.ru، لكن الكود سيكون بلغة JavaScript خالصة - لا أطر عمل، فقط عمل شاق. هيا بنا!

\n\n

الخطأ رقم 1: الاستماع للنقر على العنصر نفسه

\n\n

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

\n\n

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

\n\n

كيف تفعل ذلك بشكل صحيح؟

\n

يجب الاستماع للنقر على المستند (document) (أو على window)، ثم التحقق مما إذا كان النقر داخل العنصر الخاص بك أم خارجه.

\n\n
const myElement = document.querySelector('.my-dropdown');\n\ndocument.addEventListener('click', function(event) {\n  // نتحقق مما إذا تم النقر داخل myElement\n  if (!myElement.contains(event.target)) {\n    // النقر من الخارج - نغلق!\n    myElement.style.display = 'none';\n  }\n});
\n\n

هذا هو الأساس. ولكن هناك أيضًا بعض المزالق هنا. ننتقل إلى الخطأ التالي.

\n\n

الخطأ رقم 2: تجاهل انتشار الأحداث (Event Bubbling)

\n\n

لقد كتبت الكود أعلاه، لكنه لا يزال لا يعمل؟ على الأرجح المشكلة في انتشار الأحداث. عندما تنقر على عنصر فرعي، يتم تشغيل الحدث عليه أولاً، ثم "ينتشر" لأعلى عبر شجرة DOM حتى document.

\n\n

إذا كان هناك event.stopPropagation() في الطريق - سيموت الحدث ولن يصل إلى document. لن يتم تشغيل المعالج الخاص بك.

\n\n

كيف تتجنب ذلك؟

\n

لا تستخدم أبدًا stopPropagation() دون ضرورة قصوى. إذا كنت تكتب مكونًا يعترض النقر (مثل زر داخل قائمة منسدلة)، استخدم event.stopImmediatePropagation() فقط إذا كنت متأكدًا من أنه لن يكسر المعالجات الخارجية.

\n\n

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

\n\n
document.addEventListener('click', function(event) {\n  // الكود الخاص بك\n}, true); // مرحلة capture: الحدث يصل أولاً إلى document، ثم ينزل لأسفل
\n\n

الآن سيتم التقاط الحدث قبل أن يوقفه أي شخص. لكن هذا سلاح ذو حدين - استخدمه بحذر.

\n\n

الخطأ رقم 3: المقارنة غير الصحيحة مع event.target

\n\n

الكلاسيكية: تتحقق من event.target === myElement. هذا يعمل فقط إذا تم النقر بالضبط على العنصر نفسه. ولكن ماذا لو كان داخل العنصر صورة أو زر أو مجرد نص؟

\n\n

النقر على عقدة فرعية سيعطي event.target مساويًا لتلك العقدة الفرعية، وليس للعنصر الخاص بك. لن يتم استيفاء الشرط، ولن يتم إغلاق العنصر.

\n\n

الحل: استخدم Node.contains()

\n

الطريقة contains() تتحقق مما إذا كانت العقدة الممررة هي سليل للعقدة الحالية (أو العقدة نفسها). هذا مثالي لمهمتنا.

\n\n
// صحيح\nif (!myElement.contains(event.target)) {\n  // النقر من الخارج\n}\n\n// غير صحيح\nif (event.target !== myElement) {\n  // سيتجاهل النقر على العناصر الفرعية\n}

المدونات

توصيات الكتب