تتيح أنماط التمرير السريع للكشف للمستخدمين تمرير صف في المقدّمة أو لوحة محتوى أفقيًا للكشف عن واجهة مستخدم مخفية، مثل أزرار الإجراءات المستندة إلى السياق (تثبيت أو تجاهل أو أرشفة أو حذف) أو قائمة جانبية ثانوية، بدون تفعيل أي إجراء تلقائيًا. بدلاً من تنفيذ إجراء أثناء التمرير السريع، يتم فتح الحاوية للكشف عن عناصر تحكّم تفاعلية يمكن للمستخدم فحصها وتفعيلها، أو التمرير السريع للخلف لإغلاقها.
للحصول على إيماءة تمرير سريع بعرض كامل تؤدي إلى تنفيذ إجراء على عنصر وتجاهله فورًا بعد تجاوز حدّ معيّن، يُرجى الاطّلاع على التمرير السريع للإزالة. لإظهار عنوان مخفي في أعلى حاوية تمرير عمودية، راجِع السحب للإظهار.
كيفية الإعداد
سواء كنت تكشف عن أزرار الإجراءات في عنصر قائمة أو قائمة جانبية بجانب لوحة قابلة للتمرير، فإنّ الآلية هي نفسها: يمكنك تضمين محتوى المقدّمة واللوحة المخفية داخل حاوية تمرير أفقية خارجية تم ضبطها باستخدام محاذاة مواضع التمرير في CSS (scroll-snap-type: x mandatory) وoverscroll-behavior: chain. يتولّى محرّك التمرير الأصلي في المتصفّح معالجة الزخم والارتداد والتمرير بالتركيز على لوحة المفاتيح وتسلسل التمرير بدون مستمعي إيماءات JavaScript.
الخطوة 1: ترميز حاوية التمرير والمحتوى واللوحة التي تظهر
ضَع محتوى المقدّمة أولاً وعناصر التحكّم التي تم الكشف عنها (أزرار الإجراءات أو قائمة جانبية) ثانيًا داخل حاوية الانتقال .SwipeReveal. بما أنّ المحتوى هو الأول في ترتيب DOM، تبدأ حاوية الانتقال بشكل طبيعي عند إزاحة الانتقال 0 مع إخفاء اللوحة خارج الشاشة على اليمين.
<!-- Example A: List item with revealable action buttons -->
<div class="SwipeReveal">
<div class="SwipeReveal-content">
<strong>Design review notes</strong>
<p>Updated component specs for the Q3 release.</p>
</div>
<div class="SwipeReveal-panel" role="group" aria-label="Item actions">
<button type="button">Pin</button>
<button type="button">Archive</button>
<button type="button">Delete</button>
</div>
</div>
<!-- Example B: Scrollable content pane with a revealable side menu -->
<div class="SwipeReveal" role="region" aria-label="Document preview with swipeable side menu">
<div class="SwipeReveal-content is-scrollable" tabindex="0">
<h2>Article Preview</h2>
<p>Scroll vertically through this pane, or swipe left to reveal the side menu...</p>
</div>
<nav class="SwipeReveal-panel" aria-label="Contextual menu">
<a href="#share">Share</a>
<a href="#bookmark">Bookmark</a>
<a href="#export">Export PDF</a>
</nav>
</div>
الخطوة 2: ضبط حاوية المحاذاة الأفقية وoverscroll-behavior: chain
عندما تضع عنصرًا داخل حاوية تمرير خارجية فقط لإنشاء تأثير التمرير السريع للكشف، تصبح هذه الحاوية الخارجية أداة تمرير وسيطة داخل المستند (أو حاوية تمرير رئيسية أخرى).
تتحكّم الكلمات الرئيسية الأربع لـ overscroll-behavior بشكل مستقل في ما إذا كان التمرير الزائد ينتقل إلى حاوية تمرير سابقة (تسلسل التمرير) وما إذا كانت الحاوية تعرض إمكانيات تجاوز حد التمرير المحلية، مثل تمديد الشريط المطاطي المرن أو توهج الحافة (تأثير الحدود المحلية):
| القيمة | تسلسل الانتقال (ينتشر إلى العنصر الرئيسي) | تأثير الحدود المحلية (شريط مطاطي / توهّج) | دور ميزة "التمرير السريع للكشف" |
|---|---|---|---|
auto |
نعم | نعم | هذا هو الإعداد التلقائي. يمكنك إبقاؤه مفعَّلاً في أشرطة التمرير الداخلية للمحتوى حيث يكون الارتداد العادي مطلوبًا، ولكن تجنَّب تفعيله في حاوية التمرير الخارجية حيث يؤدي إلى ارتداد القائمة. |
chain |
نعم | لا | استخدِم هذه السمة على شريط التمرير الخارجي .SwipeReveal لكي يتم تمرير المحتوى الزائد إلى شريط التمرير الرئيسي (مثل المستند) بدون ارتداد القائمة أو شريط الإجراءات |
contain |
لا | نعم | يتم حصر الانتقال داخل الحاوية مع استمرار الارتداد محليًا |
none |
لا | لا | يحظر كلاً من الارتداد المحلي وتسلسل الانتقال إلى العناصر الرئيسية |
اجعل .SwipeReveal شبكة من عمودَين (grid-template-columns: 100% max-content) حيث يتم محاذاة .SwipeReveal-content مع start ومحاذاة .SwipeReveal-panel مع end:
.SwipeReveal {
/* Column 1: full-width content. Column 2: auto-sized actions or side menu. */
display: grid;
grid-template-columns: 100% max-content;
overflow-x: auto;
scroll-snap-type: x mandatory;
scrollbar-width: none;
/* Prevent the swipe wrapper and revealed panel from bouncing locally while
still chaining excess scroll into the parent scroller (the document). */
overscroll-behavior: chain;
}
@media (prefers-reduced-motion: no-preference) {
.SwipeReveal {
scroll-behavior: smooth;
}
}
.SwipeReveal-content {
scroll-snap-align: start;
scroll-initial-target: nearest;
background: Canvas;
}
.SwipeReveal-content:focus-visible {
outline: auto;
outline-offset: -2px;
}
.SwipeReveal-content.is-scrollable {
block-size: 20rem;
overflow: auto;
/* Keeps default `overscroll-behavior: auto` so the content pane retains
its normal bounce effect while chaining horizontal swipes to `.SwipeReveal`. */
}
.SwipeReveal-panel {
/* Snapping a trailing panel to `end` (or a leading panel to `start`) brings
all controls fully into view while keeping the adjacent edge of the content visible. */
scroll-snap-align: end;
display: flex;
&:first-child {
scroll-snap-align: start;
}
}
إذا أردت أيضًا إظهار عناصر التحكّم على الجانب الأيمن (
inline-start) باستخدام ثلاثة أعمدة (grid-template-columns: max-content 100% max-content) أو عمودَين (max-content 100%)، ضَع السمة.SwipeReveal-panelقبل.SwipeReveal-content. بما أنّ السمة.SwipeReveal-contentتحتوي علىscroll-initial-target: nearest، تستقر حاوية الانتقال في البداية على عمود المحتوى بدلاً من اللوحة الرئيسية.
الخطوة 3: إعادة تثبيت لوحات المحتوى التي يمكن التركيز عليها في العرض عند التركيز على لوحة المفاتيح
بما أنّ الأزرار أو الروابط داخل .SwipeReveal-panel تكون خارج الشاشة في وضع السكون، يؤدي الضغط على مفتاح Tab للانتقال إليها باستخدام لوحة المفاتيح إلى الانتقال تلقائيًا إلى .SwipeReveal للكشف عن عنصر التحكّم الذي تم التركيز عليه. ومع ذلك، بما أنّ .SwipeReveal-content يمتد على 100% من عرض الحاوية الخارجية، يظل معظمها داخل منفذ الانتقال حتى أثناء فتح اللوحة الجانبية، لذا فإنّ الضغط على Shift+Tab لإعادة التركيز إلى .SwipeReveal-content (أو إلى عنصر يمكن التركيز عليه بداخله) لن يؤدي إلى الانتقال تلقائيًا إلى 0.
استمِع إلى focusin على .SwipeReveal-content وتحقَّق من event.target.matches(':focus-visible') ليعود تركيز لوحة المفاتيح على اللوحة أو أي عنصر فرعي إلى .SwipeReveal-content في البداية بدون التداخل مع نقرات المؤشر أو عمليات السحب باللمس:
for (const content of document.querySelectorAll('.SwipeReveal-content')) {
content.addEventListener('focusin', (event) => {
if (event.target.matches(':focus-visible')) {
content.scrollIntoView({
block: 'nearest',
inline: 'start',
});
}
});
}
أفضل الممارسات والصعوبات
- يُرجى استخدام
scroll-snap-type: x mandatoryبدلاً منproximity. باستخدامproximity، يمكن أن يؤدي التمرير السريع إلى ترك أزرار الإجراءات أو القائمة الجانبية مكشوفة جزئيًا في وضع السكون. - يجب ضبط
overscroll-behavior: chainعلى شريط التمرير الأفقي الخارجي الذي يتم التمرير عليه سريعًا للكشف عن المحتوى (.SwipeReveal) لكي يتم ربط التمرير بشريط التمرير الرئيسي بدون ارتداد القائمة أو شريط الإجراءات اللذين تم الكشف عنهما. - يُفضَّل ترك القيمة
overscroll-behavior: auto(القيمة التلقائية) في لوحات المحتوى الداخلية القابلة للتمرير (.SwipeReveal-content.is-scrollable) التي تريد أن تظهر فيها ملاحظات الارتداد العادية مع الاستمرار في ربط عمليات التمرير السريع الأفقية بشريط التمرير السريع الخارجي. - يجب تنفيذ الإجراءات التي تم الكشف عنها وعناصر القائمة الجانبية كعناصر تحكّم حقيقية في نموذج العناصر في المستند (DOM) يمكن التركيز عليها (
<button>و<a>). عندما يكون.SwipeReveal-contentقابلاً للتركيز أو يحتوي على عناصر تحكّم قابلة للتركيز، يجب إقران مستمعfocusinمعevent.target.matches(':focus-visible')لاستدعاءscrollIntoView({ block: 'nearest', inline: 'start' })بحيث يؤدي الضغط على مفتاح التبويب للعودة إلى لوحة المحتوى إلى إغلاق اللوحة الجانبية بدون التأثير في تفاعلات المؤشر. - لا تعترض أحداث
wheelأوtouchmoveأوpointermoveفي JavaScript لترجمة المحتوى يدويًا أو محاكاة تسلسل الانتقال. يؤدي اعتراض الإيماءات في سلسلة التعليمات الرئيسية إلى حظر التمرير المستند إلى المُركِّب، وإيقاف زخم التمرير الأصلي، وتقليل سرعة الاستجابة.
Fallback
عندما تضع حاوية .SwipeReveal عنصر .SwipeReveal-panel على الجانب الأيسر (inline-start) قبل .SwipeReveal-content، فإنّ المتصفّحات التي لا تتيح استخدام scroll-initial-target: nearest ستعرض في البداية عند إزاحة الانتقال 0 مع عرض اللوحة الرئيسية.
إذا كان هدف Baseline لا يتيح استخدام scroll-initial-target، يمكنك رصد مدى توفّر الميزة باستخدام CSS.supports('scroll-initial-target', 'nearest') واستدعاء scrollTo() مع behavior: 'instant' على load لكي تنتقل الحاويات التي تحتوي على لوحة رئيسية مباشرةً إلى .SwipeReveal-content بدون الانتقال إلى أسفل الصفحة:
التحسين التدريجي
يمكنك التعامل مع overscroll-behavior: chain وscrollbar-width: none كتحسينات تدريجية بدون استخدام JavaScript كحل بديل:
- الاحتياطي التلقائي لتسلسل CSS: القيمة الأولية لـ
overscroll-behaviorهيauto. تتجاهل المتصفّحات التي لا تتعرّف بعد على الكلمة الرئيسيةchainالسمةoverscroll-behavior: chainفي وقت التحليل وتحتفظ بالسمةauto. بما أنّautoيسمح حاليًا بتسلسل الانتقال إلى حاويات الانتقال السابقة، فإنّ إيماءة التمرير السريع للكشف وتسلسل الانتقال إلى الحاوية الرئيسية تعملان على جميع المتصفّحات تلقائيًا، وتعمل المتصفّحات المتوافقة ببساطة على منع الارتداد غير المرغوب فيه على غلاف التمرير السريع الخارجي. - إلغاء
containأوnone: إذا كانت ورقة الأنماط الأساسية أو فئة الأداة المساعدة تضبطoverscroll-behavior: containأوnone، يجب تحديدoverscroll-behavior: autoمباشرةً قبلoverscroll-behavior: chainلكي تستمر المتصفحات التي لا تتيح استخدامchainفي ربط التمرير بالعنصر الرئيسي بدلاً من حظره. - إخفاء شريط التمرير (
scrollbar-width: none): إنّ إخفاء مسار شريط التمرير الأفقي هو إجراء تجميلي بحت، فإذا تجاهل محرّك قديم السمةscrollbar-width: none، ستظل حاوية التمرير للكشف عن المحتوى تعمل بشكل كامل مع شريط تمرير عادي.