Frontend Development

بناء واجهات سحب وإفلات متاحة باستخدام جافا سكريبت الخام ومعايير WAI-ARIA

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

لماذا تهم إمكانية الوصول بلوحة المفاتيح في السحب والإفلات

يعتمد السحب والإفلات التقليدي بشكل كبير على واجهة برمجة تطبيقات أحداث الفأرة (mousedown، mousemove، mouseup). بينما يعمل هذا بشكل جيد مع مستخدمي الفأرة، فإنه يستبعد مستخدمي لوحة المفاتيح تماماً. وفقاً لإرشادات WCAG 2.1، يجب أن تكون جميع الوظائف المتاحة عبر واجهة الفأرة متاحة أيضاً عبر لوحة المفاتيح. هذا يعني أننا نحتاج إلى نهج مزدوج: دعم السحب الأصلي حيثما أمكن، ولكن الأهم من ذلك، توفير "وضع لوحة المفاتيح" بديل يسمح للمستخدمين بنقل العناصر باستخدام مفاتيح الأسهم و Enter/Space.

أدوار وحالات WAI-ARIA الأساسية

لإيصال حالة عناصرنا التفاعلية إلى تقنيات المساعدة، يجب علينا استخدام أدوار WAI-ARIA محددة. النمط الأكثر شيوعاً يتضمن استخدام role="listbox" للحاوية و role="option" للعناصر القابلة للسحب. نحتاج أيضاً إلى إدارة الحالة ديناميكياً باستخدام سمات مثل:

  • aria-grabbed: تشير إلى ما إذا كان العنصر قابلاً للسحب (true) أم لا (false).
  • aria-dropeffect: تشير إلى الإجراء الذي يحدث عند إفلات العنصر (على سبيل المثال، "move"، "copy").
  • aria-activedescendant: تشير إلى الخيار الذي يتم التركيز عليه حالياً أو "النشط" داخل قائمة الخيارات، مما يساعد قارئات الشاشة على تتبع الموضع.

تنفيذ منطق السحب والإفلات المتاح

يُقدم أدناه تنفيذ مبسط لآلية إعادة ترتيب القائمة. يركز هذا المثال على منطق تفاعل لوحة المفاتيح، وهو غالباً الجانب الأكثر إهمالاً في إمكانية الوصول.

const listContainer = document.getElementById('item-list');
const items = Array.from(listContainer.querySelectorAll('[role="option"]'));

// مساعد إدارة التركيز
let currentIndex = 0;

function moveItem(direction) {
  const current = items[currentIndex];
  
  // تحديد الفهرس المستهدف بناءً على الاتجاه ('up' أو 'down')
  const newIndex = direction === 'up' 
    ? currentIndex - 1 
    : currentIndex + 1;

  // التحقق من الحدود
  if (newIndex < 0 || newIndex >= items.length) return;

  const target = items[newIndex];

  // تبديل العناصر في DOM
  if (direction === 'up') {
    listContainer.insertBefore(current, target);
  } else {
    listContainer.insertBefore(target, current);
  }

  // تحديث ترتيب المصفوفة لتعكس التغييرات في DOM
  const tempItems = Array.from(listContainer.querySelectorAll('[role="option"]'));
  const oldIndex = items.indexOf(current);
  items.splice(oldIndex, 1);
  items.splice(newIndex, 0, current);

  // نقل التركيز إلى العنصر الذي تم وضعه حديثاً
  target.focus();
  listContainer.setAttribute('aria-activedescendant', target.id);
  
  // إعلان التغيير لقارئ الشاشة
  current.setAttribute('aria-label', `Moved ${direction}`);
}

// مستمع الأحداث للتنقل بلوحة المفاتيح
listContainer.addEventListener('keydown', (e) => {
  if (e.key === 'ArrowUp') {
    e.preventDefault();
    moveItem('up');
  } else if (e.key === 'ArrowDown') {
    e.preventDefault();
    moveItem('down');
  }
});

تحسين تجربة الفأرة

بينما يتعامل الكود أعلاه مع التنقل بلوحة المفاتيح، يجب أن يدعم الحل الكامل أيضاً سحب الفأرة. لهذا الغرض، يمكنك الاستفادة من واجهة برمجة تطبيقات HTML5 للسحب والإفلات أو مكتبة مثل SortableJS (مُهيأة لإمكانية الوصول). المفتاح هو ضمان تحديث حالة aria-grabbed للعنصر إلى true عند التقاطه بالفأرة، والعودة إلى false عند الإفلات.

// مثال على تحديث حالة ARIA أثناء سحب الفأرة
function onDragStart(event) {
  event.target.setAttribute('aria-grabbed', 'true');
  event.dataTransfer.effectAllowed = 'move';
}

function onDragEnd(event) {
  event.target.setAttribute('aria-grabbed', 'false');
}

الاختبار والتحقق

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

الخاتمة

إن تنفيذ واجهات سحب وإفلات متاحة لا يتعلق فقط بالامتثال؛ بل يتعلق بالتعاطف والشمولية. من خلال الجمع بين سمات WAI-ARIA ومنطق جافا سكريبت الخام، يمكننا إنشاء تجارب تفاعلية غنية تعمل بسلاسة لجميع المستخدمين، بغض النظر عن طريقة إدخالهم. كمطورين للواجهات الأمامية، تمتد مسؤوليتنا إلى ما هو أبعد من الجماليات البصرية لضمان أن تكون تطبيقاتنا مفتوحة وقابلة للاستخدام من قبل الجميع.

Share: