پیمایش لمسی سریع به صورت پیش‌فرض

دیو تاپوسکا
Dave Tapuska

ما می دانیم که پاسخگویی پیمایش برای تعامل کاربر با وب سایت در تلفن همراه بسیار مهم است، با این حال شنوندگان رویدادهای لمسی اغلب باعث مشکلات جدی عملکرد اسکرول می شوند. Chrome با اجازه دادن غیرفعال بودن شنوندگان رویداد لمسی (گذراندن گزینه {passive: true} به addEventListener() ) و ارسال API رویدادهای اشاره گر به این موضوع پرداخته است. اینها ویژگی‌های عالی برای هدایت محتوای جدید به مدل‌هایی هستند که اسکرول را مسدود نمی‌کنند، اما توسعه‌دهندگان گاهی اوقات درک و پذیرش آنها را سخت می‌بینند.

ما معتقدیم وب باید به طور پیش‌فرض سریع باشد بدون اینکه توسعه‌دهندگان نیازی به درک جزئیات مخفیانه رفتار مرورگر داشته باشند. در Chrome 56 ما شنوندگان لمسی را به طور پیش‌فرض به حالت غیرفعال در مواردی که اغلب با هدف توسعه‌دهنده مطابقت دارد، قرار می‌دهیم. ما معتقدیم که با انجام این کار می‌توانیم تجربه کاربر را تا حد زیادی بهبود بخشیم و در عین حال کمترین تأثیر منفی را روی سایت‌ها داشته باشیم.

در موارد نادر این تغییر می تواند منجر به پیمایش ناخواسته شود. این معمولاً به راحتی با اعمال یک استایل لمسی حل می شود: هیچ استایلی در عنصری که در آن پیمایش نباید رخ دهد. برای جزئیات بیشتر بخوانید، چگونه بدانید تحت تاثیر قرار گرفته اید یا خیر، و چه کاری می توانید در مورد آن انجام دهید.

پس زمینه: رویدادهای قابل لغو سرعت صفحه شما را کاهش می دهند

اگر در رویدادهای touchstart یا first touchmove () preventDefault را فراخوانی کنید، از اسکرول جلوگیری خواهید کرد. مشکل این است که اغلب شنوندگان preventDefault() فراخوانی نمی کنند، اما مرورگر باید منتظر بماند تا رویداد به پایان برسد تا از آن مطمئن شود. «شنوندگان رویداد غیرفعال» تعریف شده توسط توسعه‌دهندگان این مشکل را حل می‌کنند. هنگامی که یک رویداد لمسی را با یک شی {passive: true} به عنوان پارامتر سوم در کنترل کننده رویداد خود اضافه می کنید، به مرورگر می گویید که شنونده touchstart preventDefault() فرا نخواهد خواند و مرورگر می تواند با خیال راحت اسکرول را بدون مسدود کردن روی آن انجام دهد. شنونده مثلا:

window.addEventListener("touchstart", func, {passive: true} );

مداخله

انگیزه اصلی ما کاهش زمان به روز رسانی صفحه نمایش پس از لمس صفحه توسط کاربر است. برای درک استفاده از شروع لمسی و حرکت لمسی، معیارهایی را اضافه کردیم تا مشخص کنیم رفتار مسدود کردن اسکرول به دفعات رخ داده است.

ما درصد رویدادهای لمسی قابل لغو را که به یک هدف اصلی (پنجره، سند یا بدنه) ارسال شده بودند بررسی کردیم و مشخص کردیم که حدود 80 درصد از این شنوندگان از نظر مفهومی غیرفعال هستند اما به این صورت ثبت نشده اند. با توجه به مقیاس این مشکل، ما متوجه فرصتی عالی برای بهبود اسکرول بدون هیچ اقدام توسعه‌دهنده با غیرفعال کردن خودکار این رویدادها شدیم.

این ما را وادار کرد تا مداخله خود را به این صورت تعریف کنیم: اگر هدف شنونده لمسی یا لمسی، window ، document یا body باشد که ما به طور پیش‌فرض passive به true است. این به این معنی است که کدی مانند:

window.addEventListener("touchstart", func);

معادل می شود:

window.addEventListener("touchstart", func, {passive: true} );

اکنون فراخوانی های preventDefault() در داخل شنونده نادیده گرفته می شود.

نمودار زیر زمان صرف شده توسط 1% از اسکرول های بالا را از زمانی که کاربر صفحه را لمس می کند تا زمانی که نمایشگر به روز می شود را نشان می دهد. این داده‌ها برای همه وب‌سایت‌های Chrome برای Android است. قبل از اینکه مداخله فعال شود، 1٪ از اسکرول ها بیش از 400 میلی ثانیه طول می کشد. اکنون در Chrome 56 Beta به بیش از 250 میلی‌ثانیه کاهش یافته است. کاهش حدود 38 درصدی. در آینده امیدواریم که Passive true را به طور پیش‌فرض برای همه شنوندگان touchstart و touchmove تبدیل کنیم و این میزان را به کمتر از 50 میلی‌ثانیه کاهش دهیم.

نمودار 1% زمان اسکرول برتر

شکست و هدایت

در اکثریت قریب به اتفاق موارد شکستگی مشاهده نخواهد شد. اما زمانی که شکستگی رخ می دهد، رایج ترین علامت این است که پیمایش زمانی اتفاق می افتد که شما آن را نمی خواهید. در موارد نادر، توسعه‌دهندگان ممکن است متوجه رویدادهای کلیک غیرمنتظره شوند (زمانی که preventDefault() در شنونده touchend وجود نداشته باشد.

در Chrome 56 و جدیدتر، DevTools هنگام فراخوانی preventDefault() در رویدادی که مداخله فعال است، یک هشدار ثبت می‌کند.

touch-passive.html:19 Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080

برنامه شما می تواند با بررسی اینکه آیا فراخوانی preventDefault اثری از طریق ویژگی defaultPrevented داشته است یا خیر، تعیین کند که آیا ممکن است این مورد را به طور طبیعی انجام دهد یا خیر.

ما دریافته‌ایم که اکثر صفحات تحت تاثیر هر زمان که ممکن است با اعمال ویژگی CSS -action لمسی نسبتاً به راحتی رفع می‌شوند. اگر می‌خواهید از همه پیمایش‌ها و بزرگ‌نمایی مرورگر در یک عنصر جلوگیری کنید touch-action: none برای آن. اگر یک چرخ فلک افقی دارید touch-action: pan-y pinch-zoom روی آن به طوری که کاربر همچنان بتواند به صورت عمودی حرکت کند و به طور معمول بزرگنمایی کند. اعمال عمل لمسی به درستی در حال حاضر در مرورگرهایی مانند Desktop Edge که از رویدادهای اشاره گر و نه رویدادهای لمسی پشتیبانی می کنند، ضروری است. برای Safari تلفن همراه و مرورگرهای تلفن همراه قدیمی‌تر که از عملکرد لمسی پشتیبانی نمی‌کنند، شنوندگان لمسی شما باید به تماس preventDefault ادامه دهند، حتی زمانی که Chrome نادیده می‌گیرد.

در موارد پیچیده تر، ممکن است لازم باشد به یکی از موارد زیر نیز تکیه کنید:

  • اگر شنونده touchstart شما preventDefault() را فراخوانی می‌کند، مطمئن شوید ()preventDefault از شنوندگان لمسی مرتبط نیز فراخوانی می‌شود تا به سرکوب تولید رویدادهای کلیک و سایر رفتارهای پیش‌فرض ضربه زدن ادامه دهد.
  • آخرین (و دلسرد) {passive: false} را به addEventListener() منتقل کنید تا رفتار پیش فرض را لغو کند. توجه داشته باشید که باید مشخص شود که آیا User Agent از EventListenerOptions پشتیبانی می کند یا خیر.

نتیجه

در Chrome 56 پیمایش در بسیاری از وب‌سایت‌ها بسیار سریع‌تر شروع می‌شود. این تنها تاثیری است که اکثر توسعه دهندگان در نتیجه این تغییر متوجه آن خواهند شد. در برخی موارد، توسعه دهندگان ممکن است متوجه پیمایش ناخواسته شوند.

اگرچه انجام این کار برای سافاری موبایل هنوز ضروری است، وب سایت ها نباید به فراخوانی preventDefault() در داخل شنوندگان touchstart و touchmove متکی باشند، زیرا دیگر تضمینی برای رعایت این موارد در Chrome وجود ندارد. توسعه دهندگان باید ویژگی touch-action CSS را روی عناصری که پیمایش و بزرگنمایی باید غیرفعال باشد اعمال کنند تا قبل از وقوع هر گونه رویداد لمسی به مرورگر اطلاع دهند. برای سرکوب رفتار پیش‌فرض یک ضربه (مانند تولید یک رویداد کلیک)، preventDefault() در داخل شنونده touchend فراخوانی کنید.