نظرة عامة أساسية حول كيفية إنشاء عنصر مسار تنقّل سريع الاستجابة وسهل الاستخدام ليتمكّن المستخدمون من التنقّل في موقعك الإلكتروني
في هذه المشاركة، أريد مشاركة أفكار حول طريقة إنشاء مكوّنات مسار التنقّل. تجربة العرض التوضيحي
إذا كنت تفضّل الفيديو، يمكنك الاطّلاع على نسخة من هذه المشاركة على YouTube:
نظرة عامة
يعرض مكوّن شريط التنقل موضع المستخدِم في التدرّج الهرمي للموقع الإلكتروني. ويأتي الاسم من قصة هانسل وغريتل، اللذَين ألقيا شريط التنقل خلفهما في بعض الغابات المظلمة وتمكّنا من العثور على طريقهما إلى المنزل من خلال تتبُّع فتات الخبز إلى الوراء.
إنّ شريط التنقّل في هذا المنشور ليس شريط تنقّل
عاديًا،
بل هو شريط تنقّل مشابه. وتوفّر هذه الإعلانات وظائف إضافية من خلال إضافة الصفحات التابعة إلى شريط التنقّل مباشرةً باستخدام <select>
، ما يتيح إمكانية الوصول المتعدّد المستويات.
تجربة المستخدم في الخلفية
في الفيديو التجريبي للمكوّن أعلاه، تشمل فئات العناصر النائبة أنواع
ألعاب الفيديو. يتم إنشاء هذا الممر من خلال التنقل في المسار التالي: home »
rpg » indie » on sale
، كما هو موضح أدناه.
من المفترض أن يتيح مكوّن "الخيط الساري" هذا للمستخدمين التنقّل في هذا التسلسل الهرمي للمعلومات، والانتقال بين الفروع واختيار الصفحات بسرعة وبدقة.
بنية المعلومات
أجد أنّه من المفيد التفكير في المجموعات والعناصر.
المجموعات
المجموعة هي صفيف من الخيارات للاختيار من بينها. من الصفحة الرئيسية لنموذج شريط التنقّل في هذا المنشور، تضم هذه المجموعات ألعاب FPS وRPG وألعاب القتال وزاحف الزنزانات والرياضة والألغاز.
السلع
لعبة الفيديو هي سلعة، ويمكن أن تكون مجموعة معيّنة أيضًا سلعة إذا كانت represent another collection. على سبيل المثال، RPG هو عنصر ومجموعة صالحة. وعندما تكون سلعة، يكون المستخدم على صفحة المجموعة هذه. على سبيل المثال، تظهر هذه الفئات في صفحة ألعاب تقمّص الأدوار التي تعرض قائمة بألعاب تقمّص الأدوار، بما في ذلك الفئات الفرعية الإضافية "AAA" و"ألعاب مستقلة" و"ألعاب ذاتية النشر".
وفقًا لمصطلحات علوم الكمبيوتر، يمثّل مكوّن "الخبز المحمّص" مصفوفة متعددة الأبعاد:
const rawBreadcrumbData = {
"FPS": {...},
"RPG": {
"AAA": {...},
"indie": {
"new": {...},
"on sale": {...},
"under 5": {...},
},
"self published": {...},
},
"brawler": {...},
"dungeon crawler": {...},
"sports": {...},
"puzzle": {...},
}
سيتضمّن تطبيقك أو موقعك الإلكتروني بنية معلومات مخصّصة (IA) تُنشئ صفيفًا مختلفًا ذي أبعاد متعدّدة، ولكن آمل أن يتمكّن مفهوم الصفحات المقصودة للمجموعات والتنقّل في التسلسل الهرمي من الوصول إلى مسار التنقّل أيضًا.
التنسيقات
Markup
تبدأ المكوّنات الجيدة برمز HTML مناسب. في هذا القسم التالي، سأتناول خيارات الترميز ومدى تأثيرها في المكوّن العام.
المخطط الداكن والفاتح
<meta name="color-scheme" content="dark light">
من خلال العلامة الوصفية color-scheme
في المقتطف أعلاه، يتم إعلام المتصفّح بأنّ هذه الصفحة تريد استخدام نمطَي المتصفّح الفاتح والداكن. لا تشتمل أمثلة أشرطة التنقل على أي CSS لمخططات الألوان هذه، وبالتالي ستستخدم أشرطة التنقل الألوان الافتراضية التي يوفرها المتصفح.
عنصر التنقّل
<nav class="breadcrumbs" role="navigation"></nav>
من المناسب استخدام العنصر
<nav>
لتنقّل في الموقع الإلكتروني، والذي يملك دورًا ضمنيًا في ARIA هو
التنقّل.
أثناء الاختبار، لاحظت أنّ استخدام السمة role
غيّر طريقة تفاعل قارئ الشاشة مع العنصر، حيث تم الإعلان عنه على أنّه عنصر تنقّل، لذلك اخترت إضافته.
الرموز
عند تكرار رمز على صفحة، يعني عنصر SVG
<use>
أنّه يمكنك تحديد path
مرة واحدة واستخدامه لجميع نُسخ الرمز. ويؤدي ذلك إلى منع تكرار معلومات المسار نفسها، ما يؤدي بدوره إلى
زيادة حجم المستندات واحتمال عدم اتساق المسار.
لاستخدام هذه الطريقة، أضِف عنصر SVG مخفيًا إلى الصفحة ولفّ الرموز
في عنصر <symbol>
باستخدام معرّف فريد:
<svg style="display: none;">
<symbol id="icon-home">
<title>A home icon</title>
<path d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
</symbol>
<symbol id="icon-dropdown-arrow">
<title>A down arrow</title>
<path d="M19 9l-7 7-7-7"/>
</symbol>
</svg>
يقرأ المتصفّح ملف SVG HTML ويخزّن معلومات الرمز في الذاكرة، ويتابع قراءة بقية الصفحة التي تشير إلى المعرّف لاستخدامات إضافية للرمز، على النحو التالي:
<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
<use href="#icon-home" />
</svg>
<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
<use href="#icon-dropdown-arrow" />
</svg>
يمكنك تحديده مرة واحدة واستخدامه عدد المرات التي تريدها، مع الحدّ الأدنى من التأثير في أداء الصفحة
واستخدام أسلوب مرن. تمت إضافة aria-hidden="true"
إلى عنصر SVG.
الأيقونات ليست مفيدة لشخص يتصفح ولا يسمع سوى المحتوى، لأن إخفائها
عن هؤلاء المستخدمين يمنعه من إضافة ضوضاء غير ضرورية.
رابط مجزأ .crumb
وهذا هو الفرق بين شريط التنقل التقليدي وشريط التنقل في هذا المكوّن.
في العادة، لا يكون هذا العنصر سوى رابط <a>
، لكنني أضفتُ تجربة مستخدم للاجتياز
مع تحديد مقنع. تتحمّل فئة .crumb
مسؤولية عرض الرابط
والرمز، بينما تتحمّل فئة .crumbicon
مسؤولية تجميع الرمز وأحد عناصرselect
معًا. وقد أطلقنا عليه اسم "رابط مقسَّم" لأنّ وظائفه تشبه إلى حدّ كبير زر التقسيم،
ولكنّه للتنقل في الصفحات.
<span class="crumb">
<a href="#sub-collection-b">Category B</a>
<span class="crumbicon">
<svg>...</svg>
<select class="disguised-select" title="Navigate to another category">
<option>Category A</option>
<option selected>Category B</option>
<option>Category C</option>
</select>
</span>
</span>
لا يُعدّ الرابط وبعض الخيارات أمرًا مميزًا، ولكنّهما يضيفان المزيد من الوظائف إلى مسارbreadcrumb بسيط. إنّ إضافة title
إلى العنصر <select>
مفيد لمستخدمي
قارئ الشاشة، إذ يمنحه معلومات عن إجراء الزر. ومع ذلك، فهو يقدّم نفس المساعدة للجميع أيضًا، ولكنّه ستراه في محور التركيز على iPad. توفّر سمة واحدة سياق الزرّ للعديد من المستخدِمين.
زخارف الفواصل
<span class="crumb-separator" aria-hidden="true">→</span>
الفواصل اختيارية، ويمكنك إضافة فاصل واحد فقط (راجِع المثال الثالث في الفيديو
أعلاه). وأضيف بعد ذلك كل aria-hidden="true"
لأنّها زخرفية وليست
معلومات يحتاج قارئ الشاشة إلى قراءتها.
وتسهّل السمة gap
، التي سنتناولها بعد ذلك، المسافات بين هذه المسافات.
الأنماط
بما أنّ اللون يستخدم ألوان النظام، فهو في الغالب فجوات وحزم للأنماط!
اتجاه التخطيط وتدفقه
يضبط عنصر التنقّل الأساسي nav.breadcrumbs
خاصية مخصّصة على مستوى النطاق
ليتمكّن العناصر الفرعية من استخدامها، وإلا سيُنشئ تنسيقًا أفقيًا ALIGNED عموديًا
. يضمن ذلك محاذاة التنقّل والفاصلات والرموز.
.breadcrumbs {
--nav-gap: 2ch;
display: flex;
align-items: center;
gap: var(--nav-gap);
padding: calc(var(--nav-gap) / 2);
}
ينشئ كل .crumb
أيضًا تنسيقًا أفقيًا مُحاذاً عموديًا مع بعض
الفراغ، ولكنه يستهدف بشكل خاص عناصر الرابط الثانوية ويحدّد النمط
white-space: nowrap
. هذا أمر مهم لشريط التنقّل المكوّن من عدة كلمات لأنّنا لا نريد
أن يظهر على عدة أسطر. في وقت لاحق من هذه المشاركة، سنضيف أنماطًا للتعامل مع
المحتوى الزائد أفقيًا الذي تسبّبت فيه سمة white-space
.
.crumb {
display: inline-flex;
align-items: center;
gap: calc(var(--nav-gap) / 4);
& > a {
white-space: nowrap;
&[aria-current="page"] {
font-weight: bold;
}
}
}
تتم إضافة aria-current="page"
لمساعدة رابط الصفحة الحالية في التميز عن
الباقي. لن يحصل مستخدمو برامج قراءة الشاشة على مؤشر واضح يشير إلى أنّ الرابط يؤدي إلى
الصفحة الحالية فحسب، بل صمّمنا العنصر بشكل مرئي لمساعدة المستخدمين المبصرين في
الحصول على تجربة مستخدم مشابهة.
يستخدم المكوّن .crumbicon
شبكة لتجميع رمز SVG مع عنصر <select>
"يكاد
يكون غير مرئي".
.crumbicon {
--crumbicon-size: 3ch;
display: grid;
grid: [stack] var(--crumbicon-size) / [stack] var(--crumbicon-size);
place-items: center;
& > * {
grid-area: stack;
}
}
العنصر <select>
هو العنصر الأخير في DOM، لذا يكون في أعلى المكدس، وهو تفاعلي. أضِف نمطًا من opacity: .01
حتى يظل العنصر قابلاً للاستخدام،
وتكون النتيجة مربّع اختيار يتلاءم تمامًا مع شكل الرمز.
هذه طريقة رائعة لتخصيص مظهر عنصر <select>
مع
الحفاظ على الوظائف المضمّنة.
.disguised-select {
inline-size: 100%;
block-size: 100%;
opacity: .01;
font-size: min(100%, 16px); /* Defaults to 16px; fixes iOS zoom */
}
القائمة الكاملة
يجب أن تكون خطوات التنقّل قادرة على تمثيل مسار طويل جدًا. أفضّل السماح بخروج العناصر خارج الشاشة أفقيًا، عندما يكون ذلك مناسبًا، وشعرت أنّ مكوّن المخطّط التفصيلي مؤهّل بشكل جيد.
.breadcrumbs {
overflow-x: auto;
overscroll-behavior-x: contain;
scroll-snap-type: x proximity;
scroll-padding-inline: calc(var(--nav-gap) / 2);
& > .crumb:last-of-type {
scroll-snap-align: end;
}
@supports (-webkit-hyphens:none) { & {
scroll-snap-type: none;
}}
}
تُعدّ أنماط المحتوى الزائد ميزة يوفّرها تجرُّبة المستخدم التالية:
- التمرير الأفقي مع احتواء التمرير الفائق
- المساحة المتروكة للتمرير الأفقي
- نقطة التقاط واحدة على آخر فتات وهذا يعني أنّه عند تحميل الصفحة، يتم تحميل أول فتاتة بشكل مفاجئ وتكون مرئية.
- إزالة نقطة التصاق من Safari، الذي يواجه صعوبة في مجموعات التمرير أفقيًا وتأثير التصاق
طلبات الاستعلام عن الوسائط
من التعديلات البسيطة التي يمكن إجراؤها على مساحات العرض الأصغر حجمًا هي إخفاء تصنيف "المنزل"، مع إبقاء الرمز فقط:
@media (width <= 480px) {
.breadcrumbs .home-label {
display: none;
}
}
تسهيل الاستخدام
الحركة
لا يتضمّن هذا العنصر الكثير من الحركة، ولكن يمكننا منع الحركة غير المرغوب فيها من خلال إكمال عملية الانتقال باستخدام علامة prefers-reduced-motion
.
@media (prefers-reduced-motion: no-preference) {
.crumbicon {
transition: box-shadow .2s ease;
}
}
لا حاجة إلى تغيير أي من الأنماط الأخرى، فتأثيرا التمرير فوق العنصر والتركيز عليه هما رائعان
ومفيدان بدون transition
، ولكن إذا كان التأثير المتحرك مناسبًا، سنضيف أثرًا هفيفًا
للانتقال إلى التفاعل.
JavaScript
أولاً، بغض النظر عن نوع جهاز التوجيه الذي تستخدمه في موقعك الإلكتروني أو تطبيقك،
عندما يغيّر المستخدم مسار التنقّل، يجب تعديل عنوان URL ويجب
عرض الصفحة المناسبة للمستخدم. ثانيًا، لتحسين تجربة المستخدم، تأكَّد من عدم حدوث عمليات تنقّل غير متوقّعة عندما يتصفّح المستخدمون <select>
الخيارات.
هناك إجراءان مهمّان لتجربة المستخدم يعالجهما JavaScript: اختيار has
changed ومنع بدء حدث <select>
التغيير المُبكر.
ويجب تفادي الأحداث بشدة بسبب استخدام أحد عناصر
<select>
. في متصفّح Windows Edge، وربما المتصفّحات الأخرى أيضًا، يتمّ تنشيط حدث الاختيار changed
عندما يتصفّح المستخدِم الخيارات باستخدام لوحة المفاتيح. لهذا السبب، سميناه
"مُتطلّعًا"، لأنّ المستخدم اختار الخيار بشكل زائف فقط، مثل التمرير فوقه
أو التركيز عليه، ولكنّه لم يؤكّد الاختيار باستخدام enter
أو click
. ويؤدي الحدث المشغَّل إلى تعذّر الوصول إلى ميزة تغيير فئة المكوِّن هذه، لأن فتح مربّع التحديد وتصفُّح أحد العناصر ببساطة يؤدي إلى تنشيط الحدث وتغيير الصفحة قبل أن يصبح المستخدم جاهزًا.
حدث <select>
تم تغييره بشكل أفضل
const crumbs = document.querySelectorAll('.breadcrumbs select')
const allowedKeys = new Set(['Tab', 'Enter', ' '])
const preventedKeys = new Set(['ArrowUp', 'ArrowDown'])
// watch crumbs for changes,
// ensures it's a full value change, not a user exploring options via keyboard
crumbs.forEach(nav => {
let ignoreChange = false
nav.addEventListener('change', e => {
if (ignoreChange) return
// it's actually changed!
})
nav.addEventListener('keydown', ({ key }) => {
if (preventedKeys.has(key))
ignoreChange = true
else if (allowedKeys.has(key))
ignoreChange = false
})
})
وتتمثّل استراتيجية ذلك في مراقبة أحداث لوحة المفاتيح المتّجهة للأسفل على كل عنصر <select>
وتحديد ما إذا كان المفتاح الذي تم الضغط عليه هو تأكيد التنقّل (Tab
أو
Enter
) أو التنقّل المكاني (ArrowUp
أو ArrowDown
). ومن خلال
هذا التحديد، يمكن للمكوّن تحديد الانتظار أو المغادرة عند تنشيط الحدث
الخاص بالعنصر <select>
.
الخاتمة
الآن بعد أن عرفت كيف فعلت ذلك، كيف يمكنك‽ 🙂
لننوّع أساليبنا ونتعرّف على جميع الطرق لإنشاء تطبيقات على الويب. أنشئ عرضًا توضيحيًا وأرسِل إلينا رابطًا على Twitter، وسنضيفه إلى قسم الريمكسات التي أنشأها المستخدمون أدناه.
الريمكسات التي أنشأها المستخدمون
- Tux Solbakk كمكوّن ويب: العرض التجريبي والرمز