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

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

يمكنك مراجعة مشاركة مدونتي بعنوان إنشاء مكوّن Sidenav للتعرّف على ميزات منصة 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>، لصقنا جملة نائبة. استبدل `` بعناصرك الخاصة، أو الصق lorem المقدم أدناه:

<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) داخل عنصر الرابط open:

<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 أو أقل، ضع عناصر التنقل الجانبي وعناصر المحتوى الرئيسية في الصف والعمود نفسهما، ليكونهما فوق بعضهما البعض في شبكة 1×1.

باستخدام وظيفة التكديس سريعة الاستجابة هذه كقاعدة، يمكننا الآن الاستفادة من حالة شريط عنوان 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 = '';
  }
});

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

الجزء التالي من JavaScript JS هو إدارة التركيز. أرغب في جعل الفتح والإغلاق سهلاً، لذلك أنتظر حتى ينتهي الانتقال الجانبي من نوع ما، ثم أتحقق منه مقابل تجزئة عنوان 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 للتحقق من الأنماط غير ذات الصلة بالتنسيق التي قمت بتطبيقها على هذا المكون. لم أشعر أنها مهمة لمجموعة الميزات التي كنت أركز عليها، وأردت أن يشجع فصل الأنماط عن التخطيط على النسخ واللصق. يمكن أن يكون هناك المزيد من التعلم هناك!

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