درس تطبيقي حول الترميز: إنشاء مكوِّن Sidenav

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

يمكنك الاطّلاع على مشاركة المدونة إنشاء مكوّن الشريط الجانبي للتعرّف على ميزات منصة الويب CSS التي تم اختيارها لإنشاء هذا المكوّن.

ضبط إعدادات الجهاز

  1. انقر على إنشاء ريمكس للتعديل لجعل المشروع قابلاً للتعديل.
  2. فتح "app/index.html"

HTML

أولاً، احصل على أساسيات إعداد HTML حتى يتوفّر محتوى وبعض المربّعات التي يمكنك العمل عليها.

أضِف رمز HTML التالي إلى العلامة <body>.

<aside></aside>
<main></main>

يحتوي العنصر <aside> على قائمة التنقّل كعنصر مكمّل للعنصر <main> الذي يتضمّن المحتوى الأساسي للصفحة.

بعد ذلك، سنملأ هذه العناصر الدلالية بباقي محتوى الصفحة.

أضِف عنصر تنقّل وبعض روابط التنقّل ورابط إغلاق داخل العنصر <aside>.

<aside>
  <nav>
    <h4>My</h4>
    <a href="#">Dashboard</a>
    <a href="#">Profile</a>
    <a href="#">Preferences</a>
    <a href="#">Archive</a>

    <h4>Settings</h4>
    <a href="#">Accessibility</a>
    <a href="#">Theme</a>
    <a href="#">Admin</a>
  </nav>

  <a href="#"></a>
</aside>

تتلاءم الروابط بشكل رائع مع عناصر <nav>، كما تتلاءم عناصر <nav> بشكل رائع مع الأشرطة الجانبية <aside>. ومع ذلك، يمكننا إجراء المزيد من التحسينات.

في عنصر المحتوى الرئيسي، أضِف عنوانًا رئيسيًا ومقالة لاحتواء محتوى التنسيق بشكل دلالي.

<main>
  <header>
    <a href="#sidenav-open" class="hamburger">
      <svg viewBox="0 0 50 40">
        <line x1="0" x2="100%" y1="10%" y2="10%" />
        <line x1="0" x2="100%" y1="50%" y2="50%" />
        <line x1="0" x2="100%" y1="90%" y2="90%" />
      </svg>
    </a>
    <h1>Site Title</h1>
  </header>

  <article>
    {put some placeholder content here}
  </article>
</main>

يحتوي العنوان على رابط فتح القائمة. يحتوي الشريط الجانبي على زر الإغلاق. سنعرض العناصر ونخفيها استنادًا إلى حجم إطار العرض قريبًا.

في العنصر <article>، لصقنا جملة عنصر نائب. استبدِل `` بمحتواك الخاص، أو الصِق النص التجريبي أدناه:

<h2>Totam Header</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Cum consectetur, necessitatibus velit officia ut impedit veritatis temporibus soluta? Totam odit cupiditate facilis nisi sunt hic necessitatibus voluptatem nihil doloribus! Enim.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>

<h3>Subhead Totam Odit</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>

<h3>Subhead</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>

هذا المحتوى وطوله هما ما سيجعلان الصفحة قابلة للتمرير عندما يتجاوز ارتفاع إطار العرض.

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

أضِف السمتَين title وaria-label إلى عنصر رابط فتح العنوان:

<a href="#sidenav-open" class="hamburger">
<a href="#sidenav-open" title="Open Menu" aria-label="Open Menu" class="hamburger">

يمكن أيضًا تمييز رمز SVG المفتوح بشكل أكثر وضوحًا. أضِف السمات التالية إلى SVG داخل عنصر الرابط المفتوح:

<svg viewBox="0 0 50 40">
<svg viewBox="0 0 50 40" role="presentation" focusable="false" aria-label="trigram for heaven symbol">

يجب أن تكون علامة الإغلاق في الشريط الجانبي أكثر وضوحًا. أضِف السمتَين title وaria-label إلى عنصر رابط إغلاق الشريط الجانبي:

<a href="#"></a>
<a href="#" title="Close Menu" aria-label="Close Menu"></a>

CSS

حان الوقت لتحديد موضع العناصر. المحتوى الرئيسي وقائمة التنقّل الجانبية هما عنصران ثانويان مباشرين للعلامة <body>، لذا هذه نقطة بداية جيدة.

أضِف رمز CSS التالي إلى css/sidenav.css لكي يعرض العنصر <body> العناصر الفرعية.

body {
  display: grid;
  grid: [stack] 1fr / min-content [stack] 1fr;

  @media (max-width: 540px) {
    & > :matches(aside, main) {
      grid-area: stack;
    }
  }
}

يعني هذا التصميم بشكل أساسي: أنشئ صفًا مسمّى stack يتضمّن كل شيء، وعمودَين في هذا الصف، العمود الثاني منهما مسمّى stack أيضًا. يجب أن يتم تحديد حجم العمود الأول حسب الحد الأدنى من احتياجات المحتوى، ويمكن أن يشغل العمود الثاني المساحة المتبقية. بعد ذلك، إذا كان عرض نافذة العرض محدودًا بمقدار 540px أو أقل، ضَع عناصر الشريط الجانبي والمحتوى الرئيسي في الصف والعمود نفسهما، ما يؤدي إلى ظهورهما فوق بعضهما البعض في شبكة 1x1.

وباستخدام وظيفة التراص السريع الاستجابة هذه كأساس، يمكننا الآن الاستفادة من حالة شريط عنوان URL لتبديل مستوى ظهور الشريط الجانبي ونمط انتقاله.

عدِّل العنصر <aside> في app/index.html:

<aside>
<aside id="sidenav-open">

يتيح ذلك لخدمة CSS مطابقة عنصر وتجزئة عنوان URL معًا. وهذا مهم لاستخدام :target. يمكن الآن أن يتطابق رقم تعريف العنصر مع تجزئة عنوان URL التي سنضبطها باستخدام العلامات <a>.

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

<a href="#sidenav-open" class="hamburger" title="Open Menu" aria-label="Open Menu">
<a href="#sidenav-open" id="sidenav-button" class="hamburger" title="Open Menu" aria-label="Open Menu">

بعد ذلك، أضِف معرّفًا إلى رابط إغلاق الشريط الجانبي:

<a href="#" title="Close Menu" aria-label="Close Menu"></a>
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>

هذا يختتم تخطيط التراص السريع الاستجابة <body>، بالإضافة إلى ربطنا بشريط عنوان URL. لنواصل التقدّم!

يتميّز <aside> أيضًا بتصميم مرتّب. وتتضمّن عنصرَين ثانويَين، هما <nav> الذي يمثّل المكوّن الذي يظهر على شكل ورقة وينزلق إلى الخارج، وعنصر الرابط <a> للإغلاق الذي يضبط عنوان URL على #. يظهر الرابط على يسار شريط التنقّل المنزلق الخاص بالورق، وذلك ليتمكّن المستخدمون من "النقر خارج" المكوّن المرئي لإغلاقه.

أضِف رمز CSS التالي إلى css/sidenav.css:

#sidenav-open {
  display: grid;
  grid-template-columns: [nav] 2fr [escape] 1fr;
}

أعتقد أنّ النسبة والأسماء هما لمسة رائعة هنا، حيث يمكن أن يتألق التصميم الشبكي ويمنح المصمم الكثير من التحكّم.

بعد ذلك، يجب أن أضع المحتوى الرئيسي بشكل مشروط وأحافظ على موضع الإعلان أثناء تصفّح أي مستند. هذه وظيفة رائعة لـ position: sticky وبعض overscroll-behavior.

أضِف الأنماط التالية إلى الشريط الجانبي:

#sidenav-open {
  display: grid;
  grid-template-columns: [nav] 2fr [escape] 1fr;

  @media (max-width: 540px) {
    position: sticky;
    top: 0;
    max-height: 100vh;
    overflow: hidden auto;
    overscroll-behavior: contain;

    visibility: hidden; /* not keyboard accessible when closed */
  }
}

تضمن هذه الأنماط أن يكون الشريط الجانبي بارتفاع إطار العرض، وأن يتم التمرير عموديًا وأن يحتوي على شريط التمرير. والأهم من ذلك، أنّه يخفي العنصر. بشكل تلقائي، عندما يكون إطار العرض 540px أو أصغر، يتم إخفاء شريط التنقّل الجانبي. إلا إذا!

أضِف أداة اختيار زائفة :target إلى العنصر #sidenav-open:

#sidenav-open {

  @media (max-width: 540px) {

    &:target {
      visibility: visible;
    }
  }
}

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

أضِف رمز CSS التالي إلى أسفل app/sidenav.css:

#sidenav-button,
#sidenav-close {
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
  user-select: none;
  touch-action: manipulation;

  @media (min-width: 540px) {
    display: none;
  }
}

تستهدف هذه الأنماط زرَي الفتح والإغلاق، وتحدّد أنماط النقر واللمس، كما تخفيهما عندما تكون منافذ العرض 540px أو أكبر.

لإضافة بعض الأناقة، لنضِف تحويلات CSS مع مراعاة تسهيل الاستخدام. أضِف رمز CSS التالي إلى css/sidenav.css:

#sidenav-open {
  --easeOutExpo: cubic-bezier(0.16, 1, 0.3, 1);
  --duration: .6s;

  ...

  @media (max-width: 540px) {
    ...

    transform: translateX(-110vw);
    will-change: transform;
    transition:
      transform var(--duration) var(--easeOutExpo),
      visibility 0s linear var(--duration);

    &:target {
      visibility: visible;
      transform: translateX(0);
      transition: transform var(--duration) var(--easeOutExpo);
    }
  }

  @media (prefers-reduced-motion: reduce) {
    --duration: 1ms;
  }
}
عرض توضيحي للتفاعل مع المدة وبدونها استنادًا إلى طلب البحث عن الوسائط `prefers-reduced-motion`.

إضافة بعض JavaScript

يجب أن يؤدي الضغط على المفتاح Escape إلى إغلاق القائمة. أضِف رمز JavaScript هذا إلى js/index.js:

const sidenav = document.querySelector('#sidenav-open');

sidenav.addEventListener('keyup', e => {
  if (e.code === 'Escape') {
    document.location.hash = '';
  }
});

يستمع هذا الرمز إلى حدث رئيسي في عنصر sidenav. إذا كانت القيمة Escape، سيتم ضبط تجزئة عنوان URL على قيمة فارغة، ما يؤدي إلى إيقاف التنقل الجانبي.

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

أضِف رمز JavaScript التالي إلى js/index.js:

const closenav = document.querySelector('#sidenav-close');
const opennav = document.querySelector('#sidenav-button');

sidenav.addEventListener('transitionend', e => {
  if (e.propertyName !== 'transform') {
    return;
  }

  const isOpen = document.location.hash === '#sidenav-open';

  isOpen
    ? closenav.focus()
    : opennav.focus();
});

جرّبه الآن

  • لمعاينة الموقع الإلكتروني، انقر على عرض التطبيق، ثم انقر على ملء الشاشة ملء الشاشة.

الخاتمة

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

افتح css/brandnav.css للاطّلاع على الأنماط غير المرتبطة بالتصميم التي طبّقتها على هذا المكوّن. لم أكن أرى أنّ ذلك مهمًا لمجموعة الميزات التي كنت أركّز عليها، وكنت آمل أن يؤدي فصل الأنماط عن التنسيق إلى تشجيع النسخ واللصق. قد يكون هناك المزيد من المعلومات التي يمكنك الاستفادة منها.

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