كيفية التحقق من صحة البريد الإلكتروني في JavaScript: من التعبير المنتظم البسيط إلى التحليل الكامل تحت الغطاء
\n\nكل مطور واجه مرة واحدة على الأقل في حياته السؤال: «How can I validate an email address in JavaScript?». هذا السؤال على Stack Overflow جمع آلاف المشاهدات ومئات الإجابات، لأن الموضوع عبارة عن كتلة متشابكة من التفاصيل الدقيقة. اليوم لن نقدم فقط كودًا جاهزًا، بل سننظر تحت الغطاء: سنحلل لماذا قد يمرر الفحص البسيط a@b أو يحظر "test@[IPv6:2001:db8::1]". هيا بنا!
لماذا «التعبير المنتظم البسيط» فكرة سيئة؟
\n\nالحل الأكثر شيوعًا على الإنترنت هو نسخ تعبير منتظم. لكن لنكن صادقين: عناوين البريد الإلكتروني هي فوضى. المواصفات RFC 5321 و RFC 5322 تسمح باستخدام الرموز +, -, ., % وحتى التعليقات بين قوسين. وهناك أيضًا نطاقات مثل museum أو @[192.168.1.1]. حاول وضع كل هذا في سطر واحد من التعبير المنتظم - وإما أن تترك عنوانًا صحيحًا أو تحظر مستخدمًا حقيقيًا.
هذا مثال شائع على Stack Overflow:
\n\nconst emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\nهذا الكود يعمل في 90% من الحالات، لكن:
\n- \n
- يترك
test@localhost(صحيح للشبكة المحلية) \n - يحظر
"test@[192.168.1.1]"(صحيح حسب RFC) \n - لا يتحقق من طول النطاق أو وجود النقاط \n
الخلاصة: لا تحاول كتابة تعبير منتظم مثالي. من الأفضل استخدام مزيج من الفحص البسيط + التحقق من الخادم.
\n\nتشريح عنوان البريد الإلكتروني: ما تحتاج معرفته قبل كتابة الكود
\n\nقبل كتابة أداة التحقق، دعنا نحلل هيكل البريد الإلكتروني حسب المواصفات:
\n\n- \n
- الجزء المحلي (قبل @): يمكن أن يحتوي على حروف وأرقام ونقاط وإشارات زائد وشرطات، لكن لا يمكن أن يبدأ أو ينتهي بنقطة. علامات الاقتباس مسموحة للرموز الخاصة. \n
- النطاق (بعد @): يتكون من تسميات مفصولة بنقاط. كل تسمية من 1 إلى 63 حرفًا. يمكن أن يكون النطاق عنوان IP بين قوسين مربعين. \n
- الطول الإجمالي: لا يزيد عن 254 حرفًا. \n
بمعرفة هذا، يمكننا كتابة فحص أكثر معنى.
\n\nالخطوة 1: تحقق بسيط لكن عملي (لـ 99% من الحالات)
\n\nلنبدأ بالكود المستخدم في المشاريع الحقيقية (مثل Angular أو React). إنه ليس مثاليًا، لكنه كافٍ لمعظم النماذج:
\n\nfunction isEmailSimple(email) {\n // إزالة المسافات\n const trimmed = email.trim();\n \n // التحقق من السلسلة الفارغة\n if (!trimmed) return false;\n \n // الهيكل الأساسي: شيء @ شيء . شيء\n const atIndex = trimmed.lastIndexOf('@');\n if (atIndex < 1 || atIndex === trimmed.length - 1) return false;\n \n const localPart = trimmed.slice(0, atIndex);\n const domain = trimmed.slice(atIndex + 1);\n \n // يجب أن يحتوي النطاق على نقطة واحدة على الأقل\n const dotIndex = domain.lastIndexOf('.');\n if (dotIndex < 1 || dotIndex === domain.length - 1) return false;\n \n // التحقق من الطول\n if (trimmed.length > 254) return false;\n if (localPart.length > 64) return false;\n \n return true;\n}\n\n// أمثلة:\nconsole.log(isEmailSimple('user@example.com')); // true\nconsole.log(isEmailSimple('a@b.c')); // true (صحيح رسميًا)\nconsole.log(isEmailSimple('user@localhost')); // false (لا توجد نقطة)\nconsole.log(isEmailSimple('@example.com')); // false\n\n\nهذا الكود أفضل بالفعل من التعبير المنتظم الأعمى. إنه يتحقق من الهيكل والطول ووجود @ مع نقطة. لكنه يترك user@[192.168.1.1] - وهذا ليس حرجًا لمعظم نماذج الويب.
الخطوة 2: تحقق متقدم مع مراعاة RFC (تقريبًا كما في المتصفح)
\n\nالمتصفحات الحديثة تستخدم التحقق المدمج عبر السمة type="e