الإشارة إلى الطريق نحو الأمام

Sérgio Gomes

كان من السهل الإشارة إلى المحتوى على الويب. كان لديك ماوس، وحركته حوله، وأحيانًا ضغطت على أزرار، وهذا كل شيء. تم محاكاة كل ما لم يكن ماوسًا، وكان المطورون يعرفون بالضبط ما ينبغي الاعتماد عليه.

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

لقد استخدمنا أحداث اللمس لفترة لمساعدتنا في ذلك، لكنها واجهة برمجة تطبيقات منفصلة تمامًا مخصصة للمس، مما يجبرك على ترميز نموذجي أحداث منفصلين إذا كنت تريد التوافق مع الماوس واللمس. يتم شحن Chrome 55 بمعيار أحدث يوحّد كلا النموذجين: أحداث المؤشر.

نموذج حدث فردي

توحِّد أحداث المؤشر نموذج إدخال المؤشر للمتصفّح، مع دمج أدوات اللمس والأقلام وأجهزة الماوس معًا في مجموعة واحدة من الأحداث. مثال:

document.addEventListener('pointermove',
    ev => console.log('The pointer moved.'));
foo.addEventListener('pointerover',
    ev => console.log('The pointer is now over foo.'));

في ما يلي قائمة بجميع الأحداث المتاحة، والتي من المفترض أن تبدو مألوفة جدًا إذا كنت على دراية بأحداث الماوس:

pointerover دخل المؤشر مربع حدود العنصر. ويحدث ذلك فورًا للأجهزة التي تتيح التمرير فوق الحدث، أو قبل حدث pointerdown للأجهزة التي لا تتيح ذلك.
pointerenter تشبه السمة pointerover، ولكنها لا تعرض فقاعات المحادثات وتتعامل مع العناصر التابعة بشكل مختلف. تفاصيل عن المواصفات
pointerdown دخل المؤشر في حالة الزر "نشط"، مع الضغط على زر أو إنشاء اتصال بناءً على دلالات جهاز الإدخال.
pointermove غيَّر موضع المؤشر.
pointerup غادر المؤشر حالة الزر "نشط".
pointercancel حدثت مشكلة، ما يعني أنّه من غير المحتمل أن يُصدر المؤشر أي أحداث أخرى. وهذا يعني أنّه عليك إلغاء أي إجراءات جارية والرجوع إلى حالة الإدخال المحايدة.
pointerout لقد غادر المؤشر مربع الإحاطة للعنصر أو الشاشة. وأيضًا بعد pointerup، إذا كان الجهاز لا يتيح التمرير.
pointerleave تشبه السمة pointerout، ولكنها لا تعرض فقاعات المحادثات وتتعامل مع العناصر التابعة بشكل مختلف. تفاصيل عن المواصفات
gotpointercapture تم تثبيت المؤشر للعنصر.
lostpointercapture تم إطلاق المؤشر الذي جارٍ تسجيله.

أنواع الإدخال المختلفة

بشكل عام، تتيح لك "أحداث المؤشر" كتابة الرموز بطريقة غير مرتبطة بالإدخال، بدون الحاجة إلى تسجيل معالِجات أحداث منفصلة لأجهزة الإدخال المختلفة. بالطبع، ستظل بحاجة إلى مراعاة الاختلافات بين أنواع الإدخالات، مثل ما إذا كان مفهوم التمرير ينطبق أم لا. إذا أردت التفريق بين أنواع أجهزة الإدخال المختلفة، مثلاً لتوفير رموز أو وظائف منفصلة للإدخالات المختلفة، يمكنك إجراء ذلك من داخل معالِجات الأحداث نفسها باستخدام السمة pointerType في واجهة PointerEvent. على سبيل المثال، إذا كنت ترمّز لائحة تنقّل جانبية، قد تتمكّن من اتّباع المنطق التالي في حدث pointermove:

switch(ev.pointerType) {
    case 'mouse':
    // Do nothing.
    break;
    case 'touch':
    // Allow drag gesture.
    break;
    case 'pen':
    // Also allow drag gesture.
    break;
    default:
    // Getting an empty string means the browser doesn't know
    // what device type it is. Let's assume mouse and do nothing.
    break;
}

الإجراءات التلقائية

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

عند استخدام أحداث المؤشر، كلما تم تفعيل إجراء تلقائي مثل التمرير أو التكبير أو التصغير، سيظهر لك حدث pointercancel لإعلامك بأنّ المتصفّح قد تحكَّم في المؤشر. مثال:

document.addEventListener('pointercancel',
    ev => console.log('Go home, the browser is in charge now.'));

السرعة المدمجة: يتيح هذا النموذج تحسين الأداء تلقائيًا، مقارنةً بأحداث اللمس، حيث تحتاج إلى استخدام أدوات معالجة الأحداث السلبية لتحقيق مستوى الاستجابة نفسه.

يمكنك منع المتصفّح من التحكّم باستخدام السمة touch-action في CSS. وعند ضبطها على none في عنصر ما، سيؤدّي ذلك إلى إيقاف جميع الإجراءات المحدّدة من خلال المتصفّح والتي بدأت من خلال هذا العنصر. ولكن هناك عدد من القيم الأخرى التي يمكن من خلالها التحكم بشكل أدقّ، مثل pan-x التي تتيح للمتصفح التفاعل مع الحركة على المحور x وليس على المحور y. يدعم Chrome 55 القيم التالية:

auto تلقائي، يمكن للمتصفّح تنفيذ أي إجراء تلقائي.
none لا يُسمح للمتصفّح بتنفيذ أي إجراءات تلقائية.
pan-x يُسمح للمتصفّح فقط بتنفيذ الإجراء التلقائي للتمرير الأفقي.
pan-y يُسمح للمتصفّح بتنفيذ الإجراء التلقائي للانتقال العمودي فقط.
pan-left يُسمح للمتصفّح فقط بتنفيذ الإجراء التلقائي للانتقال الأفقي وتحريك الصفحة إلى اليمين فقط.
pan-right ويُسمح للمتصفّح بتنفيذ الإجراء التلقائي للتمرير الأفقي فقط، وتحريك الصفحة إلى اليسار فقط.
pan-up يُسمح للمتصفّح فقط بتنفيذ الإجراء التلقائي للتمرير العمودي لتحريك الصفحة للأعلى فقط.
pan-down يُسمح للمتصفّح فقط بتنفيذ الإجراء التلقائي للتمرير العمودي لتحريك الصفحة للأسفل فقط.
manipulation لا يُسمح للمتصفح إلا بتنفيذ إجراءات التمرير والتكبير/التصغير.

التقاط المؤشر

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

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

تشكّل ميزة أحداث المؤشر حلاً أفضل بكثير: يمكنك تسجيل المؤشر، وبالتالي، يمكنك التأكد من حصولك على حدث pointerup هذا (أو أي حدث من أصدقائه المراوغين).

const foo = document.querySelector('#foo');
foo.addEventListener('pointerdown', ev => {
    console.log('Button down, capturing!');
    // Every pointer has an ID, which you can read from the event.
    foo.setPointerCapture(ev.pointerId);
});

foo.addEventListener('pointerup', 
    ev => console.log('Button up. Every time!'));

المتصفحات المتوافقة

في وقت كتابة هذا التقرير، تكون ميزة Pointer Events متاحة في Internet Explorer 11 وMicrosoft Edge وChrome وOpera، وتُتاح جزئيًا في Firefox. يمكنك العثور على قائمة محدّثة على caniuse.com.

يمكنك استخدام رمز polyfill أحداث المؤشر لملء الفجوات. يمكنك بدلاً من ذلك التحقّق مباشرةً من إمكانية استخدام المتصفح في وقت التشغيل:

if (window.PointerEvent) {
    // Yay, we can use pointer events!
} else {
    // Back to mouse and touch events, I guess.
}

تُعدّ أحداث المؤشر مرشحًا رائعًا للتحسين التدريجي: ما عليك سوى تعديل طرق الإعداد لإجراء التحقق أعلاه، وإضافة معالِجات أحداث المؤشر في مجموعة if، وتحريك معالِجات أحداث الماوس/اللمس إلى مجموعة else.

لذلك، لا تفوّت فرصة المشاركة وإطلاعنا على رأيك.