حل مشكلة جعل النافذة المشروطة متاحة للأشخاص ذوي الإعاقة

مرحبا!



في هذا المقال ، أود أن أخبركم عن كيفية تنفيذ صيغة يمكن الوصول إليها دون استخدام سمة "aria-modal" .



قليلا من النظرية!



"Aria-modal" هي سمة تُستخدم لإخبار التقنيات المساعدة (مثل برامج قراءة الشاشة) بأن محتوى الويب ضمن مربع الحوار الحالي غير قابل للتشغيل المتبادل (خامل). بمعنى آخر ، يجب ألا يتلقى أي عنصر أسفل النموذج تركيزًا على النقر أو التنقل باستخدام TAB / SHIFT + TAB أو التمرير على أجهزة الاستشعار.



ولكن لماذا لا يمكننا استخدام "aria-modal" للنافذة المشروطة؟



هناك عدة أسباب:



  • فقط لا تدعمه برامج قراءة الشاشة
  • تم تجاهله بواسطة الفئات الزائفة ": قبل /: بعد"


دعنا ننتقل إلى التنفيذ.



التنفيذ



لبدء التطوير ، نحتاج إلى تحديد الخصائص التي يجب أن تحتوي عليها النافذة النموذجية المتاحة :



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


فارغ



سنستخدم نموذجًا حتى لا نضيع الوقت في وصف خطوة بخطوة لإنشاء نافذة مشروطة.



لغة البرمجة:



<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div>
        <button type="button" id="infoBtn" class="btn"> Standart button </button>
        <button type="button" id="openBtn"> Open modal window</button>
        <div role="button" tabindex="0" id="infoBtn" class="btn"> Custom button </button>
    </div>
    <div>
        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Deserunt maxime tenetur sint porro tempore aperiam! Eaque tempore repudiandae culpa omnis placeat, fugit nostrum quisquam in ipsa odit accusamus illum velit?
    </div>


    <div id="modalWindow" class="modal">
        <div>
            <button type="button" id="closeBtn" class="btn-close">Close</button>
            <h2>Modal window</h2>
            <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Expedita, doloribus.</p>
        </div>
    </div>
</body>
</html>


الأنماط:



    .modal {
        position: fixed;
        font-family: Arial, Helvetica, sans-serif;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        background: rgba(0,0,0,0.8);
        z-index: 99999;
        transition: opacity 400ms ease-in;
        display: none;
        pointer-events: none;
    }
    
    .active{
        display: block;
        pointer-events: auto;
    }

    .modal > div {
        width: 400px;
        position: relative;
        margin: 10% auto;
        padding: 5px 20px 13px 20px;
        border-radius: 10px;
        background: #fff;
    }

    .btn-close {
        padding: 5px;
        position: absolute;
        right: 10px;
        border: none;
        background: red;
        color: #fff;
        box-shadow: 0 0 10px rgba(0,0,0,0.5);
    }

    .btn {
        display: inline-block;
        border: 1px solid #222;
        padding: 3px 10px;
        background: #ddd;
        box-sizing: border-box;
    }


شبيبة:



    let modaWindow = document.getElementById('modalWindow');

    document.getElementById('openBtn').addEventListener('click', function() {
        modaWindow.classList.add('active');
    });

    document.getElementById('closeBtn').addEventListener('click', function() {
        modaWindow.classList.remove('active');
    });


إذا قمت بفتح الصفحة وحاولت الانتقال إلى العناصر الموجودة خلف النموذج باستخدام مفاتيح "TAB / SHIFT + TAB" ، فإن هذه العناصر تتلقى التركيز ، كما هو موضح في الصورة المرفقة.



صورة



لحل هذه المشكلة ، نحتاج إلى تخصيص سمة "tabindex" لجميع العناصر التفاعلية بقيمة ناقص واحد.



1. لمزيد من العمل ، قم بإنشاء فئة "modalWindow" بالخصائص والأساليب التالية:



  • وثيقة - صفحة الوثيقة. التي نبني فيها نافذة مشروطة
  • مشروط - الحاوية للنافذة المشروطة
  • قائمة العناصر التفاعلية - مجموعة من العناصر التفاعلية
  • blockElementsList - مجموعة من عناصر كتلة الصفحة
  • منشئ - منشئ الطبقة
  • إنشاء - الطريقة المستخدمة لإنشاء النافذة المشروطة
  • إزالة - الطريقة المستخدمة لإزالة الوسائط


2. لننفذ المنشئ:



constructor(doc, modal) {
    this.doc = doc;
    this.modal = modal;
    this.interactiveElementsList = [];
    this.blockElementsList = [];
}


يلزم استخدام "InteractiveElementsList" و "blockElementsList" لاحتواء عناصر الصفحة التي تم تغييرها عند إنشاء النموذج.



3. أنشئ ثابتًا نخزن فيه قائمة بجميع العناصر التي يمكن التركيز عليها:



const INTERECTIVE_SELECTORS = ['a', 'button', 'input', 'textarea', '[tabindex]'];


4. في طريقة "إنشاء" ، حدد جميع العناصر التي تطابق محدداتنا وقم بتعيين كل العناصر "tabindex = -1" (تجاهل العناصر التي تحتوي بالفعل على هذه القيمة)



 let elements = this.doc.querySelectorAll(INTERECTIVE_SELECTORS.toString());
 let element;
 for (let i = 0; i < elements.length; i++) {
     element = elements[i];
     if (!this.modal.contains(element)) {
         if (element.getAttribute('tabindex') !== '-1') {
               element.setAttribute('tabindex', '-1');
               this.interactiveElementsList.push(element);
         }
     }
 }


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



5. لسنا بحاجة إلى إنشاء مصفوفة للاحتفاظ بالمحددات هنا ، نحن فقط نجمع كل الأطفال من العقدة "body"



let children = this.doc.body.children;


6. الخطوة الرابعة مشابهة للخطوة 2 ، فقط باستخدام "aria-hidden"



for (let i = 0; i < children.length; i++) {
   element = children[i];
   if (!this.modal.contains(element)) {
      if (element.getAttribute('aria-hidden') !== 'true') {
          element.setAttribute('aria-hidden', 'true');
          this.blockElementsList.push(element);
       }
    }
}


طريقة "الإنشاء" المكتملة:



create() {
    let elements = this.doc.querySelectorAll(INTERECTIVE_SELECTORS.toString());
    let element;
    for (let i = 0; i < elements.length; i++) {
        element = elements[i];
        if (!this.modal.contains(element)) {
            if (element.getAttribute('tabindex') !== '-1') {
                element.setAttribute('tabindex', '-1');
                this.interactiveElementsList.push(element);
            }
        }
    }

    let children = this.doc.body.children;
    for (let i = 0; i < children.length; i++) {
        element = children[i];
        if (!this.modal.contains(element)) {
            if (element.getAttribute('aria-hidden') !== 'true') {
                element.setAttribute('aria-hidden', 'true');
                this.blockElementsList.push(element);
            }
        }
    }
}


7. في الخطوة السادسة ، نطبق طريقة "الإنشاء" العكسية:



 remove() {
            let element;
            while(this.interactiveElementsList.length !== 0) {
                element = this.interactiveElementsList.pop();
                element.setAttribute('tabindex', '0');
            }

            while(this.interactiveElementsList.length !== 0) {
                element = this.interactiveElementsList.pop();
                element.setAttribute('aria-gidden', 'false');
            }
}


8. لجعل كل هذا يعمل ، نحتاج إلى إنشاء مثيل لفئة "modalWindow" واستدعاء التابعين "create" و "remove":



    let modaWindow = document.getElementById('modalWindow');
    const modal = new modalWindow(document, modaWindow);

    document.getElementById('openBtn').addEventListener('click', function() {
        modaWindow.classList.add('active');
       // modal.create();
    });

    document.getElementById('closeBtn').addEventListener('click', function() {
        modaWindow.classList.remove('active');
       // modal.remove();
    });


كود الفصل الكامل:



class modalWindow{
    constructor(doc, modal) {
        this.doc = doc;
        this.modal = modal;
        this.interactiveElementsList = [];
        this.blockElementsList = [];
    }

    create() {
        let elements = this.doc.querySelectorAll(INTERECTIVE_SELECTORS.toString());
        let element;
        for (let i = 0; i < elements.length; i++) {
            element = elements[i];
            if (!this.modal.contains(element)) {
                if (element.getAttribute('tabindex') !== '-1') {
                    element.setAttribute('tabindex', '-1');
                    this.interactiveElementsList.push(element);
                }
            }
        }

        let children = this.doc.body.children;
        for (let i = 0; i < children.length; i++) {
            element = children[i];
            if (!this.modal.contains(element)) {
                if (element.getAttribute('aria-hidden') !== 'true') {
                    element.setAttribute('aria-hidden', 'true');
                    this.blockElementsList.push(element);
                }
            }
        }
    }

    remove() {
        let element;
        while(this.interactiveElementsList.length !== 0) {
            element = this.interactiveElementsList.pop();
            element.setAttribute('tabindex', '0');
        }

        while(this.interactiveElementsList.length !== 0) {
            element = this.interactiveElementsList.pop();
            element.setAttribute('aria-gidden', 'false');
        }
    }


ملاحظة



إذا لم يتم حل مشاكل التنقل في عناصر النص على الأجهزة المحمولة ، فيمكن استخدام التحديد التالي:



  const BLOCKS_SELECTORS = ['div', 'header', 'main', 'section', 'footer'];
  let children = this.doc.querySelectorAll(BLOCKS_SELECTORS .toString());


روابط لمصادر مفيدة






All Articles