إغلاق مربّع حوار من خلال النقر خارجه

ميزات الويب: <dialog closedby>

غالبًا ما تتيح مربّعات الحوار المشروطة الحديثة ميزة "الإغلاق السريع"، ما يسمح للمستخدمين بإغلاق مربّع حوار من خلال النقر على الخلفية (المنطقة خارج مربّع الحوار). توفّر السمة closedby طريقة تعريفية لتفعيل هذا السلوك بدون استخدام JavaScript مخصّص.

التنفيذ

لتفعيل ميزة "الإغلاق السريع":

  1. أضِف السمة closedby="any" إلى العنصر <dialog>.
  2. افتح مربّع الحوار باستخدام dialog.showModal().

قيم السمات

  • ‫any: تتيح هذه السمة إغلاق النافذة المنبثقة من خلال النقر على الخلفية، أو من خلال "طلبات الإغلاق" (مفتاح Esc)، أو من خلال آليات المطوّرين (مثل dialog.close()).
  • ‫closerequest: تتيح هذه السمة "طلبات الإغلاق" وآليات المطوّرين فقط. هذا هو الخيار التلقائي لمربّعات الحوار المشروطة.
  • ‫none: يمكن فقط لآليات المطوّر إغلاق مربّع الحوار.

تصميم الخلفية

عند فتح مربّع حوار كنموذج باستخدام showModal()، ينشئ المتصفّح عنصرًا زائفًا ::backdrop. تغطي هذه الخلفية إطار العرض بالكامل وتقع خلف مربع الحوار مباشرةً.

/* Style the backdrop to indicate the dialog is modal */
dialog::backdrop {
  background-color: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(2px); /* Optional: add blur for modern browsers */
}

مثال

<!-- MANDATORY: Use closedby="any" to enable light-dismiss behavior -->
<dialog id="myDialog" closedby="any" aria-labelledby="dialogTitle">
  <form method="dialog">
    <h2 id="dialogTitle">Feedback</h2>
    <p>Click outside this box or press Esc to dismiss.</p>
    <button type="submit">Close</button>
  </form>
</dialog>

<button onclick="document.getElementById('myDialog').showModal()">Open Dialog</button>

القيود وتسهيل الاستخدام

  • MANDATORY: Use closedby="any" to enable light-dismiss declaratively.
  • إلزامي: يجب دائمًا فتح مربّعات الحوار المشروطة باستخدام showModal(). يضمن ذلك أن يكون مربّع الحوار في الطبقة العليا، وأن يتم التركيز عليه، وأن يتم التعامل مع مفتاح Esc.
  • ما يجب فعله: استخدِم aria-labelledby أو aria-label لتوفير اسم العنصر الظاهر على واجهة المستخدم لمربّع الحوار.
  • DO NOT: Use closedby for non-modal dialogs (opened with show()), as they do not have a backdrop and won't trigger light-dismiss.
  • لا تستخدِم الحدث click لتنفيذ منطق مهم يجب أن يحدث قبل الإغلاق، بل استمِع إلى الحدثَين close أو cancel بدلاً من ذلك.

استراتيجيات احتياطية

إجراء إلزامي: بالنسبة إلى المتصفّحات التي لا تتيح استخدام السمة closedby بعد، يجب تنفيذ إجراء احتياطي للإغلاق السريع من خلال التحقّق مما إذا حدثت نقرة خارج حدود محتوى مربّع الحوار باستخدام النص البرمجي التالي:

const dialog = document.querySelector('dialog');

// Fallback for browsers without closedby support
if (!('closedBy' in HTMLDialogElement.prototype)) {
  dialog.addEventListener('click', (event) => {
    // 1. When clicking the backdrop, the event target is the dialog element itself.
    // Ignore clicks where the target is a child element inside the dialog.
    if (event.target !== dialog) return;

    // 2. Check if the click coordinates fall within the dialog's content box.
    // This distinguishes between a click on the backdrop vs a click on the dialog's background/padding.
    const rect = dialog.getBoundingClientRect();
    const isDialogContent = (
      rect.top <= event.clientY &&
      event.clientY <= rect.top + rect.height &&
      rect.left <= event.clientX &&
      event.clientX <= rect.left + rect.width
    );

    if (isDialogContent) return;

    // 3. Since the click was outside the content area (on the backdrop), manually close the dialog.
    dialog.close();
  });
}