برای نمایش، تند بکشید

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

برای اشاره تند کشیدن تمام‌عرض که بلافاصله موردی را پس‌از عبور از آستانه اعمال و رد می‌کند، به تند کشیدن برای برداشتن مراجعه کنید. برای آشکار کردن سرصفحه پنهان در بالای محفظه پیمایش عمودی، کشیدن برای آشکار کردن را ببینید.

نحوه پیاده‌سازی

چه دکمه‌های کنش را در یک مورد فهرست نشان دهید یا در یک منو کناری کنار یک قاب پیمایش‌پذیر، سازوکارها یکسان هستند: محتوای پیش‌زمینه و پانل پنهان را درون یک ظرف پیمایش افقی بیرونی که با CSS Scroll Snap (scroll-snap-type: x mandatory) و overscroll-behavior: chain پیکربندی شده است، بپیچید. موتور پیمایش بومی مرورگر، تکانه، چسبیدن، پیمایش تمرکز صفحه‌کلید، و زنجیره‌سازی پیمایش را بدون شنوندگان اشاره جاوا اسکریپت مدیریت می‌کند.

مرحله ۱: نشانه‌گذاری پوشاننده تند کشیدن، محتوا، و پانل آشکارشده

ابتدا محتوای پیش‌زمینه و سپس کنترل‌های آشکارشده (دکمه‌های کنش یا منو کناری) را داخل محفظه پیمایش .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>

مرحله ۲: پیکربندی محفظه چفت کردن افقی و 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 است، محفظه پیمایش در ابتدا روی ستون محتوا قرار می‌گیرد نه پانل پیشرو.

مرحله ۳: برگرداندن قاب‌های محتوای قابل‌تمرکز به نمای تمرکز صفحه‌کلید

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

بازگشت

وقتی محفظه .SwipeReveal، .SwipeReveal-panel را در سمت چپ (inline-start) قبل‌از .SwipeReveal-content قرار می‌دهد، مرورگرهایی که از scroll-initial-target: nearest پشتیبانی نمی‌کنند در ابتدا در جابه‌جایی پیمایش 0 با پانل پیشتاز نمایان‌شده ارائه می‌شوند.

If your Baseline target does not support scroll-initial-target, detect support with CSS.supports('scroll-initial-target', 'nearest') and call scrollTo() with behavior: 'instant' on load so containers with a leading panel jump straight to .SwipeReveal-content without scrolling the page:

ارتقای پیش‌رو

با overscroll-behavior: chain و scrollbar-width: none به‌عنوان ارتقای پیش‌رو بدون بازگشت جاوا اسکریپت رفتار کنید:

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