البحث في المحتوى المخفي

غالبًا ما تخفي واجهات الويب المحتوى المخفي عن العرض لتحسين تجربة المستخدم أو توفير مساحة على الشاشة أو زيادة أداء الصفحة. تُخفي الطرق التقليدية، مثل display: none أو visibility: hidden، المحتوى بصريًا، ولكنّها تجعله أيضًا غير متاح على الإطلاق لبرامج قراءة الشاشة وميزات المتصفّح، مثل "البحث في الصفحة".

لإخفاء المحتوى بصريًا مع السماح للمستخدمين بالبحث عنه وإتاحة ربطه برابط عميق من خلال أجزاء من عنوان URL وروابط "الانتقال إلى جزء النص"، يمكنك استخدام عنصر HTML‏ <details> أو السمة hidden="until-found". يُنصح عمومًا باستخدام العنصر <details> لأنّه أسهل في التنفيذ والصيانة، ولكن هناك بعض الحالات الأكثر تعقيدًا التي لا يكون فيها العنصر <details> كافيًا ويجب استخدام العنصر hidden="until-found".

على سبيل المثال:

  • إذا أردت التحكّم بشكل كامل في تصميم آلية العرض/الإخفاء
  • إذا كانت عناصر التحكّم في واجهة المستخدم لإظهار المحتوى أو إخفائه في جزء آخر من نموذج DOM
  • إذا كنت لا تريد إتاحة إخفاء المحتوى بعد عرضه

كيفية الإعداد

يتضمّن العنصر <details> نصًا قابلاً للبحث والوصول إليه تلقائيًا، ولا يتطلّب أي تنفيذ خاص. يُفضَّل استخدام <details> بدلاً من hidden="until-found" إن أمكن.

إذا كنت بحاجة إلى استخدام hidden="until-found" بدلاً من ذلك، اتّبِع التعليمات التالية:

  1. تطبيق السمة: أضِف سمة HTML‏ hidden="until-found" مباشرةً إلى العناصر التي تتضمّن المحتوى الذي يجب إخفاؤه.
  2. مزامنة حالة واجهة المستخدم: إذا كانت الواجهة تتضمّن حالات ذات صلة تعتمد على ظهور المحتوى (مثل تعديل سمات ARIA أو تبديل فئات CSS المفتوحة/المغلقة أو تدوير رموز القوائم المنسدلة):
    • يجب إضافة متتبِّع أحداث للحدث beforematch.
    • سجِّل متتبِّع الأحداث beforematch مباشرةً على العنصر الذي يحمل السمة hidden="until-found". بما أنّ الحدث يتصاعد، يمكنك بدلاً من ذلك استخدام تفويض الحدث من خلال تسجيل مستمع واحد على عنصر رئيسي (مثل حاوية علامات التبويب) لإدارة أقسام مخفية متعدّدة في آنٍ واحد.
    • داخل متتبِّع الأحداث، نفِّذ المنطق لمزامنة عناصر واجهة المستخدم ذات الصلة (مثل إغلاق علامات التبويب المفتوحة الأخرى أو تغيير حالة زر الإيقاف/التفعيل).

مثال على الرمز

الهدف: عرض حاوية مخفية يظل المحتوى فيها غير مرئي للمستخدم إلى أن يبحث عن كلمة ضمن هذا القسم.

استخدام العنصر <details>

<details>
  <summary>Click to expand</summary>
  <p>This content is visually hidden.</p>
</details>

الإفصاحات المتنافية

عند التعامل مع مناطق محتوى حصرية متبادلة، مثل قائمة منسدلة حصرية، استخدِم عنصر HTML‏ <details> الأصلي مع سمة name مشتركة.

<div class="accordion-group">
  <!-- The name attribute creates an exclusive disclosure group -->
  <details class="disclosure" name="my-accordion">
    <summary>Section 1</summary>
    <p>Section 1 content</p>
  </details>

  <details class="disclosure" name="my-accordion" open>
    <summary>Section 2</summary>
    <p>Section 2 content</p>
  </details>

  <details class="disclosure" name="my-accordion">
    <summary>Section 3</summary>
    <p>Section 3 content</p>
  </details>
</div>

استخدام السمة hidden="until-found"

<!-- The browser automatically removes hidden="until-found" upon a search match -->
<div class="hidden-container" hidden="until-found">
  <p>This content is visually hidden.</p>
</div>

بيانات الإفصاح المخصّصة المتنافية

عند التعامل مع مناطق مخصّصة متنافية تتحكّم فيها أزرار خارجية، تأكَّد من أنّ اللوحة المطابقة فقط تظل مرئية. في معالج الأحداث beforematch، يجب مزامنة حالات ARIA ذات الصلة، مثل ضبط aria-expanded="true" على زر التحكّم.

<div class="custom-accordion">
  <div class="controls">
    <button aria-expanded="true" aria-controls="panel-1" id="btn-1">Section 1</button>
    <button aria-expanded="false" aria-controls="panel-2" id="btn-2">Section 2</button>
  </div>

  <div id="panel-1" class="panel">
    <p>Section 1 content (visible)</p>
  </div>

  <div id="panel-2" class="panel" hidden="until-found">
    <p>Section 2 content (hidden)</p>
  </div>
</div>
const accordion = document.querySelector('.custom-accordion');

accordion.addEventListener('beforematch', (e) => {
  // Hide all panels and synchronize button states before the browser reveals the matched panel
  accordion.querySelectorAll('.panel').forEach((panel) => {
    if (panel !== e.target) {
      panel.hidden = 'until-found';
    }
  });
  accordion.querySelectorAll('button').forEach((btn) => {
    const controls = btn.getAttribute('aria-controls');
    btn.setAttribute('aria-expanded', controls === e.target.id ? 'true' : 'false');
  });
});

أفضل ممارسات hidden="until-found"

  • يجب تطبيق الحدود والحشو والخلفيات على العناصر الثانوية المضمّنة بدلاً من تطبيقها مباشرةً على العنصر الذي يتضمّن السمة hidden="until-found". يمنع ذلك حدوث تغييرات غير مقصودة في التنسيق أو ظهور بقايا مرئية أثناء إخفاء العنصر.
  • لا تطبِّق السمة display: none أو visibility: hidden أو أيّ سمات CSS مرتبطة بها، مثل display أو visibility، مباشرةً على العناصر التي تتضمّن السمة hidden="until-found". يؤدي ذلك إلى تعطيل الوظيفة الأصلية وإخفاء المحتوى نهائيًا من فهرس بحث Google.
  • لا تستخدِم hidden="until-found" للمعلومات الحساسة أو رموز البيانات الداخلية أو البيانات غير ذات الصلة التي يجب عدم عرضها من خلال البحث.
  • لا تستخدِم hidden="until-found" كبديل لنص "قارئ الشاشة فقط" (‎.sr-only).

توافق المتصفّح واستراتيجيات الاحتياط

لا تتجنّب استخدام hidden="until-found" بسبب عدم توفّر دعم المتصفّح، لأنّ مزايا تسهيل الاستخدام تفوق بكثير تكلفة تنفيذ إجراء احتياطي.

الخيار الاحتياطي: hidden="until-found"

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

if (!('onbeforematch' in HTMLElement.prototype)) {
  // Expand all hidden content for unsupported browsers
  document.querySelectorAll('[hidden="until-found"]').forEach((el) => {
    el.removeAttribute('hidden');
    // MANDATORY: also update any aria references to this element.
  });
}

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