نشان دادن راه رو به جلو

Sérgio Gomes

اشاره کردن به چیزهایی در وب قبلا ساده بود. شما یک ماوس داشتید، آن را جابه‌جا می‌کردید، گاهی اوقات دکمه‌ها را فشار می‌دادید و تمام. هر چیزی که ماوس نبود به عنوان یک موش شبیه سازی شد و توسعه دهندگان دقیقا می دانستند روی چه چیزی باید حساب کنند.

هرچند ساده لزوماً به معنای خوب نیست. با گذشت زمان، اهمیت فزاینده ای پیدا کرد که همه چیز یک موش (یا تظاهر به آن) نیست: شما می توانید قلم های حساس به فشار و کج را برای آزادی خلاق شگفت انگیز داشته باشید. می‌توانستید از انگشتانتان استفاده کنید، بنابراین تنها چیزی که نیاز داشتید دستگاه و دستتان بود. و هی، چرا در حالی که در آن هستید از بیش از یک انگشت استفاده نکنید؟

مدتی است که رویدادهای لمسی برای کمک به ما داشته‌ایم، اما آنها یک API کاملاً مجزا برای لمس هستند و اگر می‌خواهید از ماوس و لمس پشتیبانی کنید، مجبورید دو مدل رویداد جداگانه را کدنویسی کنید. 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 اشاره گر که در حال گرفتن بود آزاد شد.

انواع مختلف ورودی

به طور کلی، Pointer Events به شما امکان می دهد تا کد را به روش ورودی-آگنوستیک بنویسید، بدون نیاز به ثبت کنترل کننده رویداد جداگانه برای دستگاه های ورودی مختلف. البته، همچنان باید به تفاوت‌های بین انواع ورودی توجه داشته باشید، مانند اینکه آیا مفهوم شناور کاربرد دارد یا خیر. اگر می‌خواهید انواع دستگاه‌های ورودی مختلف را از هم جدا کنید – شاید برای ارائه کد/عملکرد جداگانه برای ورودی‌های مختلف – می‌توانید با استفاده از ویژگی 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.'));

سرعت داخلی : این مدل به طور پیش‌فرض عملکرد بهتری را در مقایسه با رویدادهای لمسی امکان‌پذیر می‌کند، جایی که برای دستیابی به همان سطح پاسخ‌دهی باید از شنونده‌های رویداد غیرفعال استفاده کنید.

می‌توانید با ویژگی CSS touch-action کنترل مرورگر را متوقف کنید. با تنظیم آن بر روی 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!'));

پشتیبانی از مرورگر

در زمان نگارش، رویدادهای اشاره گر در اینترنت اکسپلورر 11، مایکروسافت اج، کروم و اپرا پشتیبانی می شوند و تا حدی در فایرفاکس پشتیبانی می شوند. می توانید یک لیست به روز را در caniuse.com پیدا کنید.

می توانید از Pointer Events polyfill برای پر کردن شکاف ها استفاده کنید. از طرف دیگر، بررسی پشتیبانی مرورگر در زمان اجرا ساده است:

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

رویدادهای اشاره گر کاندیدای فوق العاده ای برای بهبود تدریجی هستند: فقط روش های اولیه سازی خود را برای بررسی بالا تغییر دهید، کنترل کننده های رویداد اشاره گر را در بلوک if اضافه کنید و کنترل کننده های رویداد ماوس/لمسی خود را به بلوک else منتقل کنید.

پس ادامه دهید، به آنها نگاهی بیندازید و نظر خود را به ما بگویید!

،

Sérgio Gomes

اشاره کردن به چیزهایی در وب قبلا ساده بود. شما یک ماوس داشتید، آن را جابه‌جا می‌کردید، گاهی اوقات دکمه‌ها را فشار می‌دادید و تمام. هر چیزی که ماوس نبود به عنوان یک موش شبیه سازی شد و توسعه دهندگان دقیقا می دانستند روی چه چیزی باید حساب کنند.

هرچند ساده لزوماً به معنای خوب نیست. با گذشت زمان، اهمیت فزاینده ای پیدا کرد که همه چیز یک موش (یا تظاهر به آن) نیست: شما می توانید قلم های حساس به فشار و کج را برای آزادی خلاق شگفت انگیز داشته باشید. می‌توانستید از انگشتانتان استفاده کنید، بنابراین تنها چیزی که نیاز داشتید دستگاه و دستتان بود. و هی، چرا در حالی که در آن هستید از بیش از یک انگشت استفاده نکنید؟

مدتی است که رویدادهای لمسی برای کمک به ما داشته‌ایم، اما آنها یک API کاملاً مجزا برای لمس هستند و اگر می‌خواهید از ماوس و لمس پشتیبانی کنید، مجبورید دو مدل رویداد جداگانه را کدنویسی کنید. 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 اشاره گر که در حال گرفتن بود آزاد شد.

انواع مختلف ورودی

به طور کلی، Pointer Events به شما امکان می دهد تا کد را به روش ورودی-آگنوستیک بنویسید، بدون نیاز به ثبت کنترل کننده رویداد جداگانه برای دستگاه های ورودی مختلف. البته، همچنان باید به تفاوت‌های بین انواع ورودی توجه داشته باشید، مانند اینکه آیا مفهوم شناور کاربرد دارد یا خیر. اگر می‌خواهید انواع دستگاه‌های ورودی مختلف را از هم جدا کنید – شاید برای ارائه کد/عملکرد جداگانه برای ورودی‌های مختلف – می‌توانید با استفاده از ویژگی 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.'));

سرعت داخلی : این مدل به طور پیش‌فرض عملکرد بهتری را در مقایسه با رویدادهای لمسی امکان‌پذیر می‌کند، جایی که برای دستیابی به همان سطح پاسخ‌دهی باید از شنونده‌های رویداد غیرفعال استفاده کنید.

می‌توانید با ویژگی CSS touch-action کنترل مرورگر را متوقف کنید. با تنظیم آن بر روی 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!'));

پشتیبانی از مرورگر

در زمان نگارش، رویدادهای اشاره گر در اینترنت اکسپلورر 11، مایکروسافت اج، کروم و اپرا پشتیبانی می شوند و تا حدی در فایرفاکس پشتیبانی می شوند. می توانید یک لیست به روز را در caniuse.com پیدا کنید.

می توانید از Pointer Events polyfill برای پر کردن شکاف ها استفاده کنید. از طرف دیگر، بررسی پشتیبانی مرورگر در زمان اجرا ساده است:

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

رویدادهای اشاره گر کاندیدای فوق العاده ای برای بهبود تدریجی هستند: فقط روش های اولیه سازی خود را برای بررسی بالا تغییر دهید، کنترل کننده های رویداد اشاره گر را در بلوک if اضافه کنید و کنترل کننده های رویداد ماوس/لمسی خود را به بلوک else منتقل کنید.

پس ادامه دهید، به آنها نگاهی بیندازید و نظر خود را به ما بگویید!