المحتوى عن 'قائمة'.



مزيد من الخيارات

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المُحتوى


التصنيفات

  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • نصائح وإرشادات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • مقالات عامة
  • التجارة الإلكترونية

التصنيفات

  • PHP
    • Laravel
    • ووردبريس
  • جافاسكريبت
    • Node.js
    • jQuery
    • AngularJS
    • Cordova
  • HTML5
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • سي شارب #C
    • منصة Xamarin
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • برمجة أندرويد
  • لغة R
  • سير العمل
    • Git
  • صناعة الألعاب
    • Unity3D
  • مقالات عامّة

التصنيفات

  • تجربة المستخدم
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
    • كوريل درو
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • مقالات عامّة

التصنيفات

  • خواديم
    • الويب HTTP
    • قواعد البيانات
    • البريد الإلكتروني
    • DNS
    • Samba
  • الحوسبة السّحابية
    • Docker
  • إدارة الإعدادات والنّشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • مقالات عامة

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات

التصنيفات

  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • مقالات عامة

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
  • أندرويد
  • iOS
  • macOS
  • ويندوز

التصنيفات

  • شهادات سيسكو
    • CCNA
  • شهادات مايكروسوفت
  • شهادات Amazon Web Services
  • شهادات ريدهات
    • RHCSA
  • شهادات CompTIA
  • مقالات عامة

أسئلة وأجوبة

  • الأقسام
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة البرمجة
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات
    • أسئلة الشهادات المتخصصة

التصنيفات

  • ريادة الأعمال
  • العمل الحر
  • التسويق والمبيعات
  • البرمجة
  • التصميم
  • DevOps

تمّ العثور على 9 نتائج

  1. يوفر بوتستراب بيئة عمل جيدة لبناء واجهات صفحات الانترنت بشكل احترافي وسهل الاستخدام من قبل زوار الموقع، بالإضافة إلى التصاميم الجذابة المريحة للعين في التصفح والانتقال ضمن الموقع. سنقوم اليوم ببناء قائمة شجرية قابلة للتوسع أو الطي collapse tree grid ضمن القائمة الجانبية للموقع، والتي تعد أحد الأعمدة الأساسية عند بناء المواقع القائمة في ترتيب صفحاتها على التصنيف ضمن بنية شجرية. يمكن الاستخدام في بناء القائمة الشجرية إضافة بنيت من قبل Pomazan Max ونشرها على GitHub وتخضع هذه الإضافة لشروط اتفاقية MIT من خلال استخدام ملفي jquery.treegrid.js و jquery.treegrid.css سنقوم الآن بتخصيص بناء هذه الشجرة وإكمالها. أولًا من المهم إضافة ملفات css التي نستخدمها لبناء الشجرة وهي الملفات الخاصة بنا Main ملف الخاص بالقائمة الشجرية jquery.treegrid.css وملفات البوتستراب <link rel="stylesheet" href="css/bootstrap.min.css"> <link rel="stylesheet" href="css/bootstrap-theme.min.css"> <link rel="stylesheet" href="css/main.css"> <link rel="stylesheet" href="css/jquery.treegrid.css"> نقوم ببناء panel-group لنضمن داخله العقد الثلاث. المعرف الخاص id والذي يملك القيمة accordion هو أحد معرفات bootstrap ويفيد من أجل التبديل بين العقد المفتوحة أو المطويّة <div class="panel-group" id="accordion"> </div> نضيف عنوان للقائمة الجانبية <h3>Try Tree Gird Sidebar</h3> لبناء العقدة الأولى node1 نضيف التعليمات حيث أضفنا node1 كعنوان للقائمة التي تحته node1-1... ولذلك ضمناه تحت اسم الصف panel-heading أما بالنسبة للقائمة التي تحته فقد تم بناؤها كقائمة حيث يضاف إلى كل عنص من القائمة صف list-group-item وبنفس الطريقة قمنا بإضافة قائمة فرعية ضمن عنصر القائمة node1-3 من أجل تغيير شكل العنوان لكل عقدة رئيسية node1، node2، node3 من خلال إضافة أحد الصفوف panel-info والتي تعطي اللون الأزرق panel-danger وتعطي اللون الأحمر و panel-success وتعطي اللون الأخضر <div class="panel panel-info"> <div class="panel-heading"> <div class="panel-title"> <a data-toggle="collapse" data-parent="#accordion" href="#node1">Node1</a> </div> </div> <div id="node1" class="panel-collapse collapse in"> <div class="panel-body"> <div class="list-group-item">Node1-1</div> <div class="list-group-item">Node1-2</div> <div class="list-group-item"> <div class="panel-title"> <a data-toggle="collapse" href="#node1-3"> Node1-3 <span class="glyphicon glyphicon-chevron-down"></span> </a> </div> <div id="node1-3" class="panel-collapse collapse"> <ul class="list-group"> <li class="list-group-item">node1-3-1</li> <li class="list-group-item">node1-3-2</li> <li class="list-group-item">node1-3-3</li> </ul> </div> </div> </div> </div> </div> وبنفس الطريقة السابقة نبني العقد node2 و node3 في حال أردت أي تغيير للقائمة تستطيع القيام بذلك: لإضافة عقدة جديدة وعنوان جديد يتم من خلال إضافة panel جديد ضمن panel-group لإضافة عنصر تابع لـ panel يتم إضافته كـ list-group-item يمكن إضافة لكل من العقد الداخلية رابط للتتمكن من الولوج إلى صفحة مختلفة من خلال وضع اسم العقدة ضمن رمز الرابط <a>node1-1</a> وفي نهاية ملف html نضيف ملفات الجافات سكربت التي نستخدمها في البوتستراب والخاص ببناء القائمة الشجرية jquery.treegrid.js <script src="js/vendor/jquery-1.11.2.min.js"></script> <script src="js/vendor/bootstrap.min.js"></script> <script src="js/vendor/jquery.treegrid.js"></script> <script src="js/main.js"></script> ضمن ملف الجافا سكربت Main.js نبني التابع الذي يقوم بفتح وإغلاق كل عقدة عند النقر عليها وذلك من خلال إضافة الصف active عند فتح العقدة إليها function openNode(evt, nodeName) { // Declare all variables var i, tablinks; // Get all elements with class="tablinks" and remove the class "active" tablinks = document.getElementsByClassName("tablinks"); for (i = 0; i < tablinks.length; i++) { tablinks[i].className = tablinks[i].className.replace(" active", ""); } // Show the current tab, and add an "active" class to the link that opened the tab document.getElementById(nodeName).style.display = "block"; evt.currentTarget.className += " active"; } ونضيف التابع الذي يقوم ببداية تحميل الصفحة إغلاق جميع العقد التي كانت مفتوحة أي إعادة كل المتغيرات إلى الحالة الافتراضية $(document).ready(function () { $('#accordion').find('.collapse').collapse('hide'); var $myGroup = $('#accordion'); $myGroup.on('show.bs.collapse', '.collapse', function () { $myGroup.not($(this).parents()).find('.collapse.in').collapse('hide'); }); } ); وبذلك نكون قد أنشأنا قائمة شجرية قابلة للطي والتوسع ديناميكية تمكّن المستخدم من الاطلاع على محتويات الموقع والانتقال بين صفحاته بسهولة
  2. من المؤكد أنك قمت بتصفح الإنترنت من هاتف محمول، ستلاحظ في كثير من المواقع زر فتح قائمة التنقل الموجود في أعلى الموقع الذي بمجرد أن تضغط عليه يقوم بفتح قائمة تنقل الموقع. إضافة هذا الزر إلى موقع ووردبريس الخاص بك سوف تحسن تجربة المستخدم بشكل كبير للمستخدمين الذين يزورون الموقع من أجهزة الهاتف المحمول أو الأجهزة اللوحية. صحيح بأنه يمكنك استعمال إضافة ووردبريس جاهزة للقيام بهذه المهمة أو تنصيب قالب يحوي هذه الميزة ولكن ماذا لو كنت تستعمل قالب خاص بك وتريد إضافة هذه الميزة بنفسك؟ في هذه المقالة سوف أريك كيف تقوم بعمل هذا بمساعدة ووردبريس وبعض تعليمات css وjavascript وتحويل القائمة إلى الشكل المطلوب. ماذا ستحتاج لتتابع مع هذا المقال، ستحتاج إلى: موقع ووردبريس على حاسوبك الذي يحتوي قائمة. قالب ابن للقالب الذي تعمل عليه من أجل أن تبقى تعديلاتك في مكانها عندما يتم تحديث هذا القالب من الإنترنت. سأقوم بتنفيذ هذه التعليمات على موقعي الخاص، الذي يستهدف قائمة التنقل الرئيسية والتي في موقعي الخاص تملك اسم صنف css وهو menu.main ، إذا كان اسم هذا الصنف مختلفًا في موقعك فسوف تقوم بتطبيق التعليمات على اسم الصنف الخاص بقائمتك. القائمة الحالية قائمتي تبدو على أجهزة الحاسب بهذا الشكل: لكن على شاشة الهاتف ليست جميلة: يمكنني تحسين ذلك بجعل محاذاة القائمة إلى المنتصف لكن ذلك سيأخذ الكثير من مساحة الشاشة لذلك سنقوم بعمل زر القائمة الذي بمجرد الضغط عليه سوف تظهر القائمة التي سنقوم بإخفائها في أجهزة الهاتف المحمول. إضافة زر القائمة الخطوة الأولى هي إضافة أيقونة زر القائمة. سوف نقوم بذلك في ملف header.php في قالبك أضف رابط تحت قائمة التنقل الرئيسية ها هو الخاص بي: <?php wp_nav_menu( array( 'container_class' => 'main-nav', 'theme_location' => 'primary' ) ); ?> <a class="toggle-nav" href="#">☰</a> هذا الرابط سيقوم بعمل زر الذي يقوم بفتح وإغلاق زر القائمة وله اسم الصنف togglenav وبداخله الأيقونة وهي في هذه الحالة رمز html. هذا كل ما يتطلب إضافته إلى ملف header.php إذا قمت بإعادة تحميل صفحتك سترى بأن زر القائمة قد ظهر على الشاشة: إخفاء زر القائمة على الشاشات الكبيرة لنبدأ مع شاشات الحاسب ونضيف هذه التعليمات، لكن لا تنسى وضع هذه التعليمات ضمن media query المناسبة لعرض الشاشة: .toggle-nav { display: none !important; } هذا يجعل الرابط الذي يحوي الأيقونة بداخله يختفي. لقد أضفت كلمة important! حتى تبقى هذه التعليمة هي الأقوى و لا يتم الكتابة عليها من قبل التعليمات الآخرى. لقد اختفى. سوف نقوم بإخفائه أيضًا في الشاشات الأصغر من ذلك و لكن سنأتي لهذا لاحقًا. إضافة تنسيقات css لزر القائمة الأن نحن نحتاج لإضافة بعض التنسيقات إلى القائمة في شاشات الهاتف والتي تظهر عندما يضغط المستخدم زر القائمة. أولًا، في ملف css الخاص بك أضف media query المناسبة: @media screen and ( max-width: 480px) { } لقد استهدفت الشاشات التي يبلغ أكبر عرض لها 480px ولكن يمكنك اختيار الأبعاد المناسبة لقائمتك. الآن لنقم بإضافة بعض تعليمات css في media query أولًا سنقوم بإظهار الأيقونة مجددًا و نقوم بوضع تنسيقات لها لتبدو أفضل: .toggle-nav { padding: 15px; margin: 15px; display: inline-block !important; color: #8D7F68; color: #fff; transition: color linear 0.15s; } .toggle-nav:hover, .toggle-nav.active { text-decoration: none; color: #8D7F68; } الأن لنقم بتنسيق القائمة نفسها و إضافة هذه التعليمات في نفس media query : .menu.main { display: inline-block; position: relative; background: #fff; } .menu.main ul { display: none; position: absolute; top: 80%; left: 0px; padding-left: 15px; background: #fff; } .menu.main li { display: block; float: none; } والآن الخطوة الأخيرة هي إضافة javascript المسؤولة عن إظهار القائمة عندما يضغط المستخدم على زر الأيقونة. إضافة سكربت javascript هذه الخطوة تتألف من خطوتين استدعاء السكربت وإضافة التعليمات له. لنقم أولًا باستدعائه. في قالبك أضف مجلدًا جديدًا يسمى scirpts وفي داخله ملف فارغ باسم burger-menu-script.js. الآن افتح ملف functions.php في قالبك وأضف التالي إليه: function wpmu_burger_menu_scripts() { wp_enqueue_script( 'burger-menu-script', get_stylesheet_directory_uri() . '/scripts/burger-menu.js', array( 'jquery' ) ); } add_action( 'wp_enqueue_scripts', 'wpmu_burger_menu_scripts' ); /* الآن قمنا باستدعاء الملف بشكل صحيح وسنضيف التعليمات البرمجية له، افتح ملف وأضف التالي */ jQuery(document).ready(function() { jQuery('.toggle-nav').click(function(e) { jQuery('.menu.main ul').slideToggle(500); e.preventDefault(); }); }); هذه التعليمات استهدفت العنصر الذي له اسم الصنف toggle-nav. و هو في حالتنا الزر و الذي سوف يتم تنفيذ التعليمات عليه عند الضغط عليه من قبل المستخدم و ستعمل دالة slideToggle الخاصة بمكتبة jquery لإخفاء زر القائمة و إظهاره عند الضغط عليه من قبل المستخدم. والآن لنرى النتيجة: وعندما أضغط على الأيقونة تظهر القائمة: وهذا فيديو يوضح كيف تجري العملية: استخدام زر القائمة سيحسن تجربة المستخدم على جهاز الهاتف المحمول إذا اتبعت الخطوات السابقة، سيصبح لديك زر قائمة بسيط والذي سيحسن تجربة المستخدم بشكل كبير عندما يزور المستخدمون موقعك من شاشات صغيرة. ويمكنك فعل ما تشاء لتحسن مظهر هذا الزر وجعله مناسبًا لموقعك. ترجمة -وبتصرّف- للمقال How to Create a Custom Animated Burger Menu for WordPress لصاحبته Rachel McCollin حقوق الصورة البارزة محفوظة لـ Freepik
  3. تعلمنا في الدرس السابق (إنشاء جدول لعلامات الطلاب) كيفيَّة إنشاء جدول وتنسيقه كاملًا بدءًا من رأس الجدول وجسمه وحتى تذييله، وإدراج الحدود وتنسيقها، وتطرَّقنا إلى موضوع التوابع حيث أدرجنا وشرحنا بعضًا منها. سنكمل في هذا الدرس موضوع تنسيق الخلايا ولكن من حيث المحتوى وسنتطرَّق إلى إدراج الروابط وكيفيَّة الاستفادة منها بالتنقل بين أوراق العمل ثمَّ سننشئ قائمة منسدلة بتحديد عدد معين من البيانات لإدخالها في الخليَّة وذلك بالتطبيق العملي عبر إنشاء تقرير مبيعات يومي. إنشاء جدول المبيعات ننشئ جدولًا جديدًا ونسميه "تقرير المبيعات اليومية" وننشئ ورقتي عمل الأولى هي بيانات المبيعات لإدخال بيانات المواد التي تُباع والثانية هي المخزون لإدخال بيانات المواد المتوافرة؛ نضيف أسماء الحقول اللازمة وهي البيانات التي نحتاج إدخالها أو استنتاجها عند كل عملية بيع نقدية، ونُنسِّق للخلايا ونخفي خطوط الشبكة من عرض ← خطوط الشبكة لإعطاء مظهر جمالي للورقة. يظهر جدول بيانات المبيعات كما موضح في الصورة. بعد تنسيق جدول بيانات المبيعات نضغط على خصائص ورقة بيانات المبيعات ثمَّ نختار "تكرار" لتكرار الورقة وذلك لتوفير عناء إعادة تنسيق الخلايا من جديد ويبقى لدينا تعديل حقول الجدول فقط ليوافق جدول المخزون. سيظهر جدول المخزون النهائي كما هو موضح في الصورة. يمكن إضافة حقول أخرى في هذا الجدول متعلِّقة بالمُنتج مثل السعر الإفرادي والكميَّة المتوافرة وغيرها من المعلومات. إدراج رابط قد تتساءل عن الهدف من كتابة عبارة "بيانات المبيعات" وعبارة "المخزون" أعلى الجدولين السابقين إذ سنجعلها زرين ننتقَّل بالضغط على أحدهما إلى ورقة العمل المقابلة وذلك لتسهيل التنقُّل بين أوراق العمل. نحدِّد خليَّة "بيانات المبيعات" ثمَّ نختار من قائمة إدراج ← رابط أو من شريط الأدوات نختار "رابط" أو باستعمال الاختصار Ctrl +K ونختار من قائمة "الأوراق" ورقة بيانات المبيعات؛ نفعل الأمر ذاته مع خليَّة "المخزون". نستطيع بالضغط على الخليَّة أو الرابط الأزرق الذي يظهر فوقها التنقُّل بسهولة بين أوراق العمل في الجدول. يمكن باستعمال إدراج رابط الإشارة إلى خلايا ذات نطاق محدَّد أيضًا أو إدراج رابط خارجي. تنسيق محتوى الخلايا يتألف الجدول من صفوف (أسطر) وحقول (أعمدة) وتكون الحقول ذات نوع واحد من البيانات أي يحتوي الحقل على تواريخ ميلاد أو أسماء طلاب أو منتجات أي نصوص أو نسب مئوية ...إلخ. بينما يضم السطر حقولًا عدَّة وبذلك يجمع بيانات ذات أنواع مختلفة وتعطي بمجملها معلومة مفيدة عن شخص أو مُنتج ...إلخ. قد نحتاج إلى تنسيق محتوى الحقول بما أنَّها تضم نوع واحد من البيانات بطريقة تناسب الجدول مثل تحديد عدد الفواصل العشرية إن كانت بيانات الحقل هي أرقام أو تحديد صيغة معيَّنة لإدخال التاريخ ضمن الحقل الذي يحتوي على تواريخ ميلاد أو يمكن اختيار تنسيق النسبة المئوية أو تنسيق العملة للحقل الذي يحتوي على نسبة مئوية أو عملة معيَّنة إلى ما هنالك من تنسيقات سنستعرضها كاملًا. نبدأ بتنسيق محتوى العمود الأول من جدول المبيعات وهو حقل "التاريخ" حيث نحدِّد العمود بأكمله بالضغط على الخليَّة A أو نحدِّد نطاق الخلايا بدءًا من الخليَّة أسفل التاريخ وحتى آخر الجدول ثمَّ نختار من قائمة التنسيق ← رقم ← التاريخ أو نضغط على "مزيد من التنسيقات" من شريط الأدوات في قسم التنسيقات ونختار "التاريخ" وبذلك تصبح الخلايا مخصَّصة لإدخال التاريخ مع عدم الاعتراض على إدخال بيانات مخالفة في الخليَّة. نغيِّر تنسيق محتوى حقل الوقت بالطريقة ذاتها إلى نوع "الوقت"؛ يوجد الكثير من تنسيقات التاريخ والوقت بالإضافة إلى إمكانيَّة تصميم التنسيق الذي نريده؛ نجد ذلك في قائمة تنسيق ← رقم ← المزيد من التنسيقات ← المزيد من تنسيقات التاريخ والوقت. يمكن اختيار أحد التنسيقات الموجودة والتعديل عليها بالضغط على "اليوم" واختيار "إضافة صفر" مثلًا أو وضع اختصار للشهر بدلًا من الاسم الكامل أو وضع رقمين للدلالة على العام، ويمكن تصميم التنسيق الذي ترغب به بالضغط على السهم الصغير الموجود على يسار مربع الصيغة واختيار الصيغة المناسبة والتعديل عليها أيضًا. بعد اختيار التنسيق المناسب جرِّب إدخال تاريخ أو وقت ما وسوف يتغير تنسيقه مباشرةً إلى التنسيق الذي اخترناه للخليَّة وسيظهر بالضغط على الخليَّة مرتين رزنامة تسهِّل علينا تحديد التاريخ. ماذا لو أردنا من البرنامج الاعتراض وعدم قبول تاريخ غير صحيح أو عدم قبول أي قيمة سوى التاريخ؟ يوفِّر تطبيق جداول بيانات جوجل ذلك حيث نحدِّد خلايا حقل التاريخ ونضغط زر الفأرة الأيمن ونختار من أسفل القائمة خيار "التحقق من صحة البيانات" فتظهر نافذة شبيهة بالنافذة الموضحة بالصورة التالية. حدَّدنا مسبقًا نطاق البيانات؛ نختار من المعايير "التاريخ" ونحدِّد الخيار المناسب من القائمة اللاحقة. اخترنا في حالتنا "يعد تاريخًا صالحًا" كما توجد خيارات كثيرة يمكنك استكشافها لاحقًا. يوجد خياران يلجأ إليهما التطبيق إن كانت البيانات المدخلة غير صالحة أولهما هو عرض تحذير للمستخدم ينبهه أنَّ البيانات المدخلة غير صالحة مع إمكانيَّة إدخال تلك البيانات في الخليَّة، وثانيهما هو رفض إدخال القيمة غير الصالحة في الخليَّة إذ لا يمكن للمستخدم الانتقال إلى الخليَّة التالية حتى يُدخل قيمة صالحة في الخليَّة الحالية. يمكن إظهار رسالة مساعدة للمستخدم ترشده حول كيفيَّة إدخال قيمة صالحة في الخليَّة. نعرِّف بقيَّة حقول الجدول بالطريقة ذاتها بحث يكون نوع محتوى الخلايا "رقم" لحقل رقم العمليَّة وحقل "رقم المنتج" مع إزالة الفاصلة من التنسيق بالضغط على زر "تقليل المنازل العشرية" في قسم تنسيق محتوى الخليَّة في شريط الأدوات، ونوع "نسبة مئوية" لحقل نسبة الضريبة، ونوع "عملة" لحقول مقدار المبيعات ومقدار الضريبة والإجمالي؛ يظهر تنسيق الحقول كما هو موضح بالصورة التالية. إنشاء قائمة منسدلة سننشئ قائمة منسدلة لحقل الوصف ورقم المنتج لنتمكن من إضافة المواد بسهولة وسرعة؛ ننتقل أولًا إلى ورقة المخزون ونملأ جدول المخزون بالمواد المتوافرة للبيع أي نضيف رقم المادة واسمها أو وصف عنها لنتمكن من معرفة المواد المتوافرة لدينا عند البيع. نعود إلى ورقة بيانات المبيعات ونحدِّد حقل الوصف ثمَّ نضغط بزر الفأرة الأيمن ونختار "التحقق من صحة البيانات"؛ نختار من قائمة المعايير خيار "قائمة من النطاق" لتحديد البيانات من خلايا موجودة مسبقًا؛ نضغط على مربع صغير يشبه الجدول الموجود في مربع الإدخال لتحديد نطاق البيانات ثمَّ نذهب إلى ورقة المخزون ونحدِّد نطاق البيانات الموجودة في حقل الوصف ونضغط "موافق". نحدِّد بعد ذلك على خيار "إظهار القائمة المنسدلة في الخليَّة" ونحدِّد على خيار "رفض الإدخال" لمنع إدخال المواد غير المتوافرة في الخليَّة. نضغط على "حفظ" فيظهر سهمٌ صغيرٌ بجانب الخلايا؛ نضغط على السهم فتظهر قائمة بالمواد المتوافرة وتُحدَّث تلقائيًّا كلما حُدِّثت البيانات في ورقة المخزون. يمكن إنشاء قائمة منسدلة عبر خيار "قائمة العناصر" في معايير التحقق من صحة البيانات ولكن هذه العناصر ستكون دائمة ويمكن تعديلها يدويًا بينما ترتبط القائمة السابقة بنطاق من البيانات وتُحدَّث تلقائيًّا. بقيَّة المعايير هي مهمتك للتعرُّف عليها وتجريبها واحدة تلو الأخرى. الخاتمة أنشأنا جدولا مميزًا خاصًّا بنا لإدارة عملية البيع وتعلمنا بوساطته كيفيَّة تنسيق محتوى الخلايا وإدراج الروابط وكيفيَّة التحقق من صحة إدخال البيانات وإنشاء قائمة منسدلة. لم ننتهِ بعد من إنشاء هذا الجدول إذ سنتعلم في الدرس القادم الكثير من الأمور المفيدة والجديدة حول هذا التطبيق منها كيفيَّة حماية أوراق العمل والنطاقات عند مشاركة الملف وإضافة توابع جديدة.
  4. بعد أن تطرّقنا في هذه السّلسلة حول أساسيات تطوير قوالب ووردبريس إلى تحويل صفحة HTML إلى قالب ووردبريس ثم إلى كيفية إضافة Pagination (أو ما يُعرف بالتّصفيح) إليها، سنتطرّق اليوم إلى خاصّيّة أخرى لا تقل أهمّية. قوائم التنقّل (Navigation Menu) هي إحدى ميزات القوالب، توفّر ووردبريس طريقة سهلة للتحكم بالقوائم المخصصة للقالب من داخل لوحة تحكم ووردبريس، وكل ما تحتاجه هو إضافة بضعة أسطر برمجية لتضيف دعم القوائم في قالبك. فهرس السلسلة: مقدمة إلى تطوير قوالب ووردبريس: تحويل صفحة HTML إلى قالب ووردبريس التصفيح (Pagination) في قوالب ووردبريس إضافة قوائم التنقل (Navigation Menu) إلى قالب ووردبريس (هذا الدرس) صف وتسجيل ملفات Javascript و CSS في قوالب ووردبريس تسجيل القوائم بدايةً في ملف functions.php ضمن ملفات القالب نحتاج لإضافة دالّة تقوم بتسجيل أسماء القائمة (أو القوائم) التي تريد إضافتها. كالتالي: add_action('init', function() { register_nav_menu('our-custom-menu', 'القائمة الرئيسية'); }); بعد ذلك يمكن التأكد من صحة إضافة القائمة عن طريق الذهاب من لوحة التحكم إلى المظهر (Appearance) ثم القوائم (Menus)، ستظهر لدينا قائمة باسم القائمة الرئيسية في تبويب إدارة موضع القوائم. تأخذ دالّة register_nav_menu محدّدين هما: المكان (Location) والوصف (Description). محدّد المكان يستخدم كمعرّف للقائمة، حيث يتم طلب محتوى القائمة ضمن ملفات القالب عن طريق محدّد المكان (location) الذي قمنا بتعيينه أثناء تسجيل القائمة. في حالتنا قمنا بوضع قيمة المحدد هي: our-custom-menu. والوصف يتم استخدامه عند عرض القائمة في لوحة التحكم ليكون أنسب وأسهل للقراءة من محدد المكان، في حالتنا قمنا بوضع قيمة الوصف هي: القائمة الرئيسية. يمكننا تسجيل أكثر من قائمة في آن واحد عن طريق استخدام دالّة register_nav_menus التي تستخدم بشكل مشابه لدالّة register_nav_menu لكن المحدّدات تكون على شكل مصفوفة اسميّة (Associative Array)، كل عنصر في المصفوفة يمثّل قائمة واحدة بحيث يكون مفتاح العنصر هو محدّد المكان وقيمة العنصر هي محدّد الوصف. في قالبنا لا نحتاج سوى لقائمة واحدة، في ما يلي كيفية تسجيل أكثر من قائمة معًا: add_action('init', function() { register_nav_menus([ 'our-custom-menu' => 'القائمة الرئيسية', 'our-second-menu' => 'القائمة الفرعية', ]); }); كما ننوه إلى وجود دالّة معاكسة لدالّة تسجيل القوائم هي unregister_nav_menu لكننا لن نتطرّق إليها الآن. إظهار القائمة في القالب الخطوة الأخيرة ضمن القالب هي عرضه في المكان المناسب. في القالب الذي نستعمله نجد أن موقع القوائم أصبح في ملف header.php، في السطر 30 من الملف نجد وسم <section class="top-bar-section">، نريد أن نضع القائمة بدلاً من وسم <ul> الموجود بداخله. نقوم بحذف وسم <ul> مع محتواه، ثم نستخدم السطر البرمجيّ التالي لعرض القائمة: <?php wp_nav_menu(['theme_location' => 'our-custom-menu']); ?> تقبل دالّة wp_nav_menu مُحدداً واحداً هو مصفوفة تحوي عددًا من الإعدادات، الإعداد الوحيد الضروري هو theme_location ويتم استخدامه كما في السطر البرمجي السابق. يمثل هذا المحدّد قيمة محدّد المكان السابقة التي استخدمناها أثناء تسجيل قائمة جديدة والتي كانت: our-custom-menu. في حال لم يتم إدخال هذا المحدّد تقوم ووردبريس باستخدام قيمة إعداد menu (سنأتي إليه في الفقرة التالية)، وإن لم تجد قيمة فسيتم عرض أول قائمة غير فارغة تجدها ووردبريس، وفي حال عدم وجود أي قوائم غير فارغة وعدم تمرير محدّد المكان فلا يتم عرض شيء. إن ألقينا نظرة على القالب من المتصفح سنجد أن شكل عناصر القائمة أصبح مختلف قليلاً عن الشكل الذي كان عليه، وذلك ﻷن الدالّة تقوم بإضافة وسم <div> محيط بوسوم القائمة. في الفقرة التالية سنتعرف على بقية الإعدادات التي يمكن أن نستخدمها مع دالّة wp_nav_menu والتي ستمكننا من إظهار القائمة على الشكل الأنسب. إعدادات دالة wp_nav_menu كما قلنا من قبل فإن دالّة wp_nav_menu تأخذ محدّداً واحداً هو مصفوفة تحوي مجموعة إعدادات، المبرمج ليس مضطراً لإدخال جميع الإعدادات، يمكنه إدخال بعضها والباقي ستقوم ووردبريس بمعالجته وإسناد قيمته الافتراضية. الإعدادات بشكل كامل هي: <?php $defaults = array( 'theme_location' => '', 'menu' => '', 'container' => 'div', 'container_class' => '', 'container_id' => '', 'menu_class' => 'menu', 'menu_id' => '', 'echo' => true, 'fallback_cb' => 'wp_page_menu', 'before' => '', 'after' => '', 'link_before' => '', 'link_after' => '', 'items_wrap' => '<ul id="%1$s" class="%2$s">%3$s</ul>', 'depth' => 0, 'walker' => '' ); wp_nav_menu( $defaults ); ?> theme_location مكان القائمة ضمن القالب، كما تم تحديده أثناء تسجيل القائمة باستخدام دالّة register_nav_menu، ليكون بإمكان المستخدم تحديد القائمة المطلوبة للمكان الذي تم تسجيله (دون تقييد المستخدم بقائمة واحدة). القيمة الافتراضية: '' (سلسلة نصية فارغة) menu القائمة المطلوب عرضها، تقبل قيمة المعرّف الرقمي (ID)، أو الاسم اللطيف (slug)، أو الاسم الخاص بالقائمة (وليس مكان القائمة ضمن القالب). القيمة الافتراضية: '' (سلسلة نصية فارغة) container لتحديد إن كان مطلوبًا من ووردبريس إحاطة وسم <ul> بوسم آخر أم لا، القيمة المسموحة هي div أو nav، وفي حال عدم الرغبة بإحاطة القائمة بوسم نجعل القيمة false. القيمة الافتراضية: 'div' container_class الصنف الخاص بوسم html المحيط بالقائمة، بشكل افتراضي يأخذ الصنف الشكل: menu-{menu slug}-container حيث يكون {menu slug} هو الاسم اللطيف للقائمة. القيمة الافتراضية: '' (سلسلة نصية فارغة) container_id معرّف CSS (ID) الذي يتم تطبيقه على الوسم المحيط (container). القيمة الافتراضية: '' (سلسلة نصية فارغة) menu_class الصنف الذي يتم تطبيقه على وسم القائمة <ul>، يمكن أن يتم وضع أكثر من صنف يتم الفصل بينهم بفراغات (space). القيمة الافتراضية: 'menu' menu_id معرّف (ID) CSS الذي يتم تطبيقه على وسم القائمة <ul>. بشكل افتراضي تكون قيمته: menu-{menu slug} حيث {menu slug} هو الاسم اللطيف للقائمة؛ في حال حدوث تكرار بالاسم يتم إضافة إشارة - مع رقم مميز يبدأ من 1 ويزيد عند كل تكرار، مثلًا: menu-{menu slug}-1، menu-{menu slug}-2، إلخ. القيمة الافتراضية: '' (سلسلة نصية فارغة) echo فيما إن كنا نريد إظهار القائمة مباشرة في مكان استخدام دالّة wp_nav_menu أو نريد إرجاعها (return). القيمة الافتراضية: true fallback_cb دالّة ليتم استخدامها في حال لم تكن القائمة موجودة، نضع قيمتها false في حال لم نكن نريد استخدام دالّة. ويتم تمرير محدّد $args للدالّة التي يتم استخدامها. القيمة الافتراضية: 'wp_page_menu' before إظهار نصّ قبل وسم <a>. القيمة الافتراضية: '' (سلسلة نصية فارغة) after إظهار نصّ بعد وسم </a>. القيمة الافتراضية: '' (سلسلة نصية فارغة) link_before إظهار نصّ قبل نصّ الرابط. القيمة الافتراضية: '' (سلسلة نصية فارغة) link_after إظهار نصّ بعد نصّ الرابط. القيمة الافتراضية: '' (سلسلة نصية فارغة) items_wrap يتم تفسيرها بنفس الطريقة التي يتم تفسير محدّد الهيئة (format) لدالّة sprintf. يحدث تعاون بين المحددات الأخرى عن طريق رموز رقمية. %1$s تُستبدل بقيمة محدد menu_id، %2$s تُستبدل بقيمة محدد menu_class، و %3$s تُستبدل بقيمة عناصر القائمة (وسوم <li>). إن تم استبعاد أي رمز رقمي من هذا المحدّد، سيتم استبعاد المحدّد المرتبط به من وسوم القائمة. القيمة الافتراضية: '<ul id="%1$s" class="%2$s">%3$s</ul>' depth يمثل عدد المستويات الهرمية التي سيتم استخدامها، حيث رقم 0 يعني جميع المستويات. ويتم استخدام قيمة -1 لتحويل جميع المستويات إلى مستوى واحد فقط. القيمة الافتراضية: 0 walker يتم تمرير عنصر هو نسخة من صنف Walker_Nav_Menu أو من صنف يرث من ذلك الصنف. الهدف من هذا المحدد هو التحكم بشكل كامل بالأصناف (classes) والمحدّدات (IDs) ووسوم HTML للقائمة. يمكن العودة لتوثيق WordPress للاطلاع على المثال المقدم القيمة الافتراضية: '' (سلسلة نصية فارغة) تحسين ظهور القائمة في القالب بعد أن تعرّفنا على إعدادات إظهار القائمة، دعونا نقوم بتعديلها في قالبنا لتصبح مناسبة أكثر. إن شاهدنا مصدر HTML للصفحة الرئيسية من المتصفح، نجد أننا نحتاج لإزالة الوسم المحيط بالقائمة (container)، ونحتاج لإضافة صنف right لوسم <ul> المحيط بعناصر القائمة. يمكننا تعديل السطر البرمجي في ملف header.php ليصبح: <?php wp_nav_menu([ 'theme_location' => 'our-custom-menu', 'container' => false, 'menu_class' => 'right', ]); ?> قمنا بجعل قيمة الوسم المحيط (container) تساوي false ﻷننا لا نريد إحاطة القائمة بأي وسم، فنحن من البداية نقوم بإحاطة القائمة بوسم <section> في قالب HTML الذي نستخدمه. وقمنا أيضًا بإضافة قيمة محدد menu_class لتساوي: right، ليتم إضافة صنف right لوسم القائمة <ul> ليظهر بشكل جيّد. بهذا نكون قد جعلنا القائمة تظهر بشكل مرن، ونعطي المستخدم إمكانية أكبر لاختيار ما يناسبه من عناصر للقائمة من خلال لوحة التحكم، وبنفس الوقت جعلنا القائمة التي يختارها المستخدم تظهر بأفضل شكل ضمن القالب الذي نعمل عليه. الشريط الجانبي الأشرطة الجانبية (Sidebars) هي إحدى ميزات القوالب، هو بشكل بسيط عمود شاقولي يقوم القالب بتزويده لعرض معلومات مختلفة عن المحتوى الأساسي للموقع، تقوم الأشرطة الجانبية بعرض ودجات/مربعات (widgets) يقوم مدير المدونة بالتحكم بها. التعامل مع الشريط الجانبي يشبه إلى حدّ كبير التعامل مع القوائم. تسجيل شريط جانبي لنقم معاً بإضافة ما يلي إلى ملف functions.php لتعريف شريط جانبيّ جديد: add_action('widgets_init', function() { register_sidebar(); }); إن قمنا بزيارة لوحة التحكم، نجد أن هناك عنصراً جديداً في قائمة المظهر (Appearance) هو الودجات (Widgets)، بداخله يظهر لنا الشريط الجانبي الجديد بعنوان الشريط الجانبي 1، إن قمنا بإضافة شريط جانبي آخر سيأخذ نفس الاسم لكن بزيادة الرقم بمقدار واحد. والسبب أننا عندما قمنا بتسجيل الشريط الجانبي لم نحدد له اسماً أو معرّفاً. سنقوم بهذا الآن بعد أن نتعرف على إعدادات هذه الدالّة. إعدادات دالّة register_sidebar الإعدادات الافتراضية: $args = array( 'name' => sprintf( __( 'Sidebar %d' ), $i ), 'id' => "sidebar-$i", 'description' => '', 'class' => '', 'before_widget' => '<li id="%1$s" class="widget %2$s">', 'after_widget' => "</li>\n", 'before_title' => '<h2 class="widgettitle">', 'after_title' => "</h2>\n", ); شرح الإعدادات: name اسم الشريط الجانبي (القيمة الافتراضية هي ترجمة كلمة ‘Sidebar’ مع معرّف رقميّ). id معرّف الشريط الجانبي - يجب أن يكون بأحرف صغيرة (lowercase), دون فراغات (القيمة الافتراضية هي معرّف رقمية يتم زيادته تلقائياً ). description وصف نصّي لماهيّة/مكان الشريط الجانبي. يظهر في واجهة إدارجة الودجات في لوحة التحكم. (القيمة الافتراضية: فارغة) class صنف CSS ليتم إسناده للشريط الجانبي في صفحة إدارة الودجات، ولن يتم استخدام هذا الصنف في القالب. ملاحظة: سيتم إضافة كلمة “sidebar” إلى قيمة الصنف. مثلاً: إن وضعنا اسم الصنف: tal ستكون قيمة الصنف هي: sidebar-tal. (القيمة الافتراضية: فارغة). before_widget وسم/وسوم HTML ليتم وضعها قبل كل واحد من الودجات (widget) (القيمة الافتراضية: ‘<li id="%1$s" class="widget %2$s">‘). ملاحظة: يتم استخدام دالّة sprintf لاستبدال المتحولات. after_widget وسم/وسوم HTML ليتم وضعها بعد كل واحد من الودجات (widget) (القيمة الافتراضية: '\n'). before_title وسم/وسوم HTML ليتم وضعها قبل كل عنوان (القيمة الافتراضية: <h2 class="widgettitle">). after_title وسم/وسوم HTML ليتم وضعها بعد كل عنوان (القيمة الافتراضية: “</h2>\n“). عرض الشريط الجانبي في القالب نتوجه إلى ملف sidebar.php، ونقوم بتعديله ليصبح كالتالي: <div class="large-4 columns sidebar"> <?php dynamic_sidebar(); ?> </div> قمنا باستبدال النصّ الموجود مسبقاً “Sidebar” بدالّة ()dynamic_sidebar التي وظيفتها عرض محتويات الشريط الجانبي في المكان المحدد. يمكن أن نمرّر قيمة واحدة لتلك الدالّة إما أن تكون اسم (name) أو معرّف (ID) الشريط الجانبي. وفي حال عدم تمرير أي قيمة يتم عرض الشريط الجانبي الأول. تخصيص الشريط الجانبي كما لاحظنا فشكل ودجات الشريط الجانبي غير مقبولة، لنقم معًا بتحسين مظهرها عن طريق إحاطة كل واحدة من الودجات (widgets) بوسم <div class="card"> الذي كان موجودًا في ملف sidebar.php قبل استخدام دالّة عرض الشريط الجانبي. لنقم باستخدام إعدادات دالّة ()register_sidebar التي مرّت معنا في الفقرات السابقة، في ملف functions.php نقوم بتعديل الدالّة لتصبح: register_sidebar(['before_widget' => '<div class="card">', 'after_widget' => '</div>']); قمنا بتمرير إعدادَين فقط لإضافة وسم div قبل واحدة من الودجات وإضافة إغلاق الوسم بعد كل واحدة منها. بهذا نكون قد انتهينا من إضافة قائمة رئيسية وشريط جانبي إلى القالب، وتعلمنا معاً كيف يمكن تخصيص شكل القائمة والشريط الجانبي ليتناسب مع القالب بأفضل شكل ممكن.
  5. css 101

    في هذا الدرس من سلسلة تعلّم CSS، سنشرح كيف يمكن استخدام CSS لتحديد مظهر القوائم؛ وسنتدرّب على ذلك بإنشاء مستند جديد يحوي قوائم، ونُرفقه بورقة أنماط جديدة تُنسّق القوائم الّتي أنشأناها. فهرس السلسلة: مدخل إلى أوراق الأنماط المتتالية (CSS). آلية عمل تعليمات CSS داخل المتصفحات. المحددات (Selectors) في CSS. كيفية كتابة تعليمات CSS يسهل قراءتها. تنسيق نصوص صفحات الويب باستخدام CSS. التعامل مع الألوان في CSS. إضافة محتوى إلى صفحة ويب باستخدام CSS. تنسيق القوائم (Lists) في CSS. (هذا الدرس) تعرف على الصناديق (Boxes) في CSS. رصف العناصر (Layout) في CSS. الجداول (Tables) في CSS. التعامل مع أجهزة العرض المختلفة والمطبوعات في CSS. القوائم إن كنت قد أتممت التّمرين في الدرس السابق "إضافة محتوى إلى صفحة ويب باستخدام CSS" فلعلّك لاحظت كيف يمكن إضافة محتوىً قبل أي عنصر بحيث يظهر وكأنّه عنصرٌ في قائمة. تقدّم CSS بعض الخواصّ المُصمّمة خصّوصًا للقوائم، ومن الأفضل استخدام هذه الخواصّ في معظم الحالات. لتعيين نمط القائمة، استخدام الخاصّة list-style لتحديد نوع العلامة الّتي تظهر قبل كلّ عنصر في القائمة. يمكن استهداف القائمة ذاتها (<ul> مثلًا) بحيث ترث العناصر منها، أو يمكن استهداف العناصر ضمن القائمة (<li> مثلًا). القوائم غير المرتبة في القوائم غير المرتّبة، تكون لكلّ العناصر العلامة ذاتها. تتضمّن CSS ثلاثة أنواع للعلامات: disc (قرص) circle (دائرة) square (مربّع) يمكن أيضًا تحديد رابط صورة لاستخدامها كعلامة للعناصر كخيار بديلٍ. مثال القاعدتان التاليتان تُحدّدان علامات مختلفة لأصناف مختلفة من عناصر القائمة: li.open {list-style: circle;} li.closed {list-style: disc;} نستخدم الأصناف للتمييز بين العناصر المفتوحة والمغلقة (مثلًا: في قائمة مهامّ): <ul> <li class="open">Lorem ipsum</li> <li class="closed">Dolor sit</li> <li class="closed">Amet consectetuer</li> <li class="open">Magna aliquam</li> <li class="closed">Autem veleum</li> </ul> قد تبدو النّتيجة هكذا: القوائم المرتبة في القوائم المُرتّبة، يكون لكل عنصر علامة مختلفة تُميّز موضعه في السلسلة. لتعيين نمط القائمة، استخدام الخاصّة list-style لتحديد نوع العلامة الّتي تظهر قبل كلّ عنصر في القائمة: decimal (أرقام بنظام العدّ العشريّ) lower-roman upper-roman lower-latin upper-latin مثال القاعدة التّالية تجعل العناصر في القائمة المرتّبة <ol> من الصّنف info مرتّبة بحروف لاتينيّة كبيرة: <ol class="info"> <li>Lorem ipsum</li> <li>Dolor sit</li> <li>Amet consectetuer</li> <li>Magna aliquam</li> <li>Autem veleum</li> </ol> ol.info {list-style: upper-latin;} ترث العناصر <li> هذا التنسيق عن القائمة: تفاصيل أكثر الخاصّة list-style هي خاصّة مختصرة، وقد ترغب في التنسيقات المعقّدة باستخدام الخصائص المنفردة لتعيين قيم مستقلّة. للاطّلاع على الخصائص المنفردة، وعلى تفاصيل أكثر عن قوائم CSS، راجع صفحة list-style. إن كنت تستخدم لغة رماز مثل HTML توفّر وسومًا مختلفة للقوائم المرتّبة (<li>) وتلك غير المرتّبة (<ol>)، فيفضّل استخدام هذه القوائم بحسب دلالتها، على أنّه يمكن عرض القوائم المرتّبة لتبدو وكأنها غير مرتّبة من خلال CSS والعكس بالعكس. قد تختلف المتصفّحات في طرق عرضها لتنسيق القوائم، لا تتوقّع الحصول على نتائج متطابقة تمامًا في كلّ المتصفّحات. العدادات ملاحظة هامّة: بعض المتصفحات لا تدعم العدّادات، تقدّم الصّفحة CSS contents and browser compatibility على موقع َQuirks Mode مخطّطًا تفصيليًّا لتوافق المتصفحات مع هذه الميزة وميزات أخرى، كما توفّر الصّفحات الفرديّة في مرجع CSS معلومات عن دعم المتصفّحات. يمكن استخدام العدّادات لعدّ أيّة عناصر، وليس فقط عناصر القوائم، فمثلًا يمكن عدّ العناوين والفقرات في المستند. لمتابعة العدّ، تحتاج إلى إنشاء عدّاد (counter) ذي اسم خاصّ تحدّده بنفسك. يمكن تصفير العدّاد ضمن عنصر ما قبل البدء بالعدّ باستخدام الخاصّة counter-reset مع اسم العدّاد الذي اخترته. الأب المشترك للعناصر الّتي ترغب بعدّها مكانٌ مناسب لتصفير العدّاد، ولكن يمكن استخدام أي عنصر يرد قبل العناصر المطلوب عدّها. في كلّ عنصر ترغب بعدّه، استخدم الخاصّة counter-increment مع اسم العدّاد الّذي اخترته. لعرض العدّاد، استخدام ‎::before‎ أو ‎::after‎ مع المُحدّد واستخدم الخاصّة content (كما شاهدنا في الدّرس السابق عن إضافة المحتوى). استخدم ‎counter()‎ مع اسم العدّاد كقيمة للخاصّة content، ويمكن كذلك استخدام نوع للعلامة (غير إلزاميّ). الأنواع المُتاحة هي ذاتها الّتي عرضناها في فقرة القوائم المرتّبة. يزيد العنصر الّذي يعرض العدّاد قيمته عادةً. مثال هذه القاعدة تُنشئ عدّادًا لكلّ عنصر <h3> من الصّنف numbered: h3.numbered {counter-reset: mynum;} هذه القاعدة تعرض وتزيد العدّاد لكلّ عنصر <p> من الصّنف numbered: <p class="numbered">Lorem ipsum</p> <p class="numbered">Dolor sit</p> <p class="numbered">Amet consectetuer</p> <p class="numbered">Magna aliquam</p> <p class="numbered">Autem veleum</p> body { counter-reset: mynum; } p.numbered:before { content: counter(mynum) ": "; counter-increment: mynum; font-weight: bold; } هكذا تبدو النّتيجة: تفاصيل أكثر لا يمكنك استخدام العدّادات إلّا إن كنت متأكّدًا من أن كلّ جمهورك يستخدم مُتصفّحًا يوفّر العدّادات. إحدى مزايا العدّادات أنّها تُوفّر إمكانيّة تنسيق العدد بصورة مستقلّة عن عنصر القائمة المرافق لها، لاحظ كيف جعلنا العدد ذا خطّ عريض دون عناصر القائمة في المثال السّابق. يمكن أيضًا استخدام العدّادات بطرق أكثر تعقيدًا؛ مثلًا: لعدّ الفقرات والعناوين والعناوين الفرعيّة والفقرات في المستندات الرّسميّة. تمرين: قوائم منسقة أنشئ مستند HTML جديد في ملف doc2.html، انسخ والصق المحتوى التالي: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Sample document 2</title> <link rel="stylesheet" href="style2.css"> </head> <body> <h3 id="oceans">The oceans</h3> <ul> <li>Arctic</li> <li>Atlantic</li> <li>Pacific</li> <li>Indian</li> <li>Southern</li> </ul> <h3 class="numbered">Numbered paragraphs</h3> <p class="numbered">Lorem ipsum</p> <p class="numbered">Dolor sit</p> <p class="numbered">Amet consectetuer</p> <p class="numbered">Magna aliquam</p> <p class="numbered">Autem veleum</p> </body> </html> أنشئ ورقة أنماط جديدة style2.css وضع فيها المحتوى التّالي: /* numbered paragraphs */ h3.numbered {counter-reset: mynum;} p.numbered:before { content: counter(mynum) ": "; counter-increment: mynum; font-weight: bold; } غيّر أسلوب تنسيق الشّيفرة والتعليقات كما تحبّ إن لم يُعجباك. افتح المستند في المتصفّح، إن كان متصفّحك يدعم العدّادات، سترى ما يشبه الصّورة أدناه، وإلّا فلن ترى الأرقام (ولا النّقطتين (:) حتّى في بعض المتصفّحات): تمرين أضف قاعدة إلى ورقة الأنماط السّابقة بحيث تعدّ الأرقام باستخدام الحروف الرّومانيّة من i إلى v: وعدّل ورقة الأنماط بحيث تستخدم العناوين حروفًا لاتينيّة كبيرة: شاهد الحل الحروف الرّومانيّة الصّغيرة عرّف قاعدة لعناصر القائمة لتستخدم lower-roman كقيمة للخاصّة list-style: li { list-style: lower-roman; } الحروف اللاتينيّة الكبيرة أضف قاعدة لمتن المستند (كونه أب العناوين) لتصفير عدّاد جديد، ثمّ زد قيمته عند كلّ عنوان: /* numbered headings */ body {counter-reset: headnum;} h3:before { content: "(" counter(headnum, upper-latin) ") "; counter-increment: headnum; } ما التّالي؟ عندما يعرض المتصفّح مستندك، فإنّه يُنشئ مساحات حول العناصر عندما يضعها في مواضعها في الصّفحة، سنشرح في الدّرس القادم كيف يمكن استخدام CSS للتّعامل مع الأشكال الضّمنيّة للعناصر (المستطيلات) من خلال استخدام الصناديق Boxes في CSS. ترجمة -وبتصرف- للمقال Lists من سلسلة Getting started with CSS على شبكة مطوّري Mozilla.
  6. عند استخدام التحريكات animations والانتقالات transitions ضمن واجهة المستخدم، فإنّه من الضروري أن يكون لها هدف واضح ومحدّد، ألا وهو تحسين تجربة المستخدم. تؤمّن لنا الانتقالات transitions الوسيلة المناسبة والمثالية لجعل الحركة سلسة وانسيابية أمام المستخدم. بدون تأثيرات الانتقال من الممكن أن يصبح المستخدم في حيرة من أمره حول الذي حدث بالضبط عند تنفيذه لأمر معين. في هذا المقال، سننشئ بعض التحريكات والانتقالات الإبداعية لإضافة وإزالة عناصر من قائمة، لقد أعجبتني الفكرة الواردة في مقال باسكويل دي سيلفيا. أمّا بالنسبة للشيفرة المسؤولة عن الإنتقالات في مقال باسكويل، فقد كتبه كريس كوير. سأعمل على تطوير المثال الوارد في مقال باسكويل، وذلك بإضافة المزيد من تأثيرات الانتقالات والتحريكات، وسأستخدم أيضاً شيفرة صغيرة من مقال كريس لإضافة خطوة إضافية في كل تحريكة، تتمثل بحجز مكان للعناصر المراد إضافتها إلى القائمة قبل إضافتها فعلياً. سأستخدم خصائص CSS بدون أي بادئة prefix وذلك بغرض الاختصار، لكنك ستجد الخصائص كاملة ضمن النص المصدري للمشروع على Github. ستعمل مقاطع الشيفرة التي ستجدها ضمن هذا الدرس على متصفحات تدعم خصائص CSS المستخدمة. لنبدأ العمل! الرماز The Markupلتوضيح فكرة الدرس بشكل جيد، أنشأت تطبيقاً بسيطاً خاصاً بالملاحظات البسيطة. يستخدم هذا التطبيق تقنية التخزين المحلي LocalStorage التي توفرها HTML5 وذلك لحفظ الملاحظات ضمن التخزين المحلي لمتصفح ويب لديك. يسمح لك التطبيق بأخذ ملاحظات وحفظها ضمن المتصفح إن أردت ذلك، في الحقيقة هو السبب الذي من أجله بنيت هذا التطبيق، وذلك من أجل ملاحظاتي الخاصة. لن أخوض في تفاصيل كيفية بناء هذا التطبيق لأنّ ذلك ليس هدف هذا الدرس. الرُماز markup المستخدم في هذا التطبيق هو مجرد نموذج form بسيط يحتوي على حقل نصي text field وزر إرسال submit button، بالإضافة إلى قائمة غير مرتّبة unordered list. ستُضاف الملاحظات إلى هذه القائمة بشكل تلقائي. كما يوجد أيضاً عنصري div لعرض التنبيهات، التي ستظهر عند حفظ أو إزالة أي عنصر، بالإضافة إلى عدّاد وزر لحذف جميع العناصر بنقرة واحدة. فيما يلي جميع رُماز HTML الذي سنحتاجه: <div class="notification undo-button"> Item Deleted. Undo? </div> <div class="notification save-notification"> Item Saved </div> <div class="reminder-container"> <header> <h1>mini reminders list</h1> </header> <form id="input-form"> <input type="text" id="text" placeholder="Remind me to.."/> <input type="submit" value="Add" /> </form> <ul class="reminders"> </ul> <footer> <span class="count"></span> <button class="clear-all"> Delete All </button> </footer> </div>يمكنك إضافة وتحرير وإزالة العناصر (الملاحظات) بالإضافة إلى إمكانية إستعادة العنصر المحذوف. في الواقع، تأتي أغلب التحريكات مرافقة لعملية إزالة وإستعادة العناصر. تُعتبر عملية إضافة العناصر بسيطة ولا تترافق مع الكثير من التحريكات، باستثناء التحريك الخاص بالظهور التدريجي fade in والسقوط إلى أسفل falling down واللذان سنتحدث عنهما عندما نبدأ العمل مع CSS. تنسيقات CSSستحصل العناصر المُضافة توّاً عن طريق JavaScript على الصنف new-item. (صنف CSS). أمّا العناصر المُزالة فستحصل على الصنف removed-item. كما ستحصل العناصر المُستعادة على الصنف restored-item. وكل صنف من الأصناف السابقة سيُفعّل التحريكات الخاصة به. ستبقى أسماء الأصناف السابقة ثابتةً لجميع الأمثلة التوضيحية، في حين ستختلف فيما بينها بالتوجيه المسؤول عن مظهر التحريك keyframes@. لنبدأ الآن بالمثال التوضيحي الأول. المثال الأول المثال الأول: العناصر المُزالة "تسقط إلى أسفل"، والعناصر المُستعادة ستعود بحركة معاكسة. ستسقط العناصر المضافة حديثاً من الأعلى، وهذا تأثير بسيط لكنه جميل. سيبدأ كل عنصر مُضاف حديثاً بالسقوط إلى أسفل وذلك من موقع يعلو 400 بيكسل عن الموقع النهائي الذي سيستقر فيه (أي الموقع النهائي ناقص 400 بيكسل) لا تنس أنّه يجب على الخاصية الفرعية animation-fill-mode أن تحمل القيمة forwards وذلك للتأكّد من أنّ العناصر ستبقى في موقعها النهائي ضمن القائمة، وإلّا فإنّها ستختفي بمجرّد انتهاء عملية التحريك. li.new-item { opacity: 0; animation: new-item-animation .3s linear forwards; } @keyframes new-item-animation { from { opacity: 0; transform: translateY(-400px); } to { opacity: 1; transform : translateY(0); } }ستسقط العناصر المزالة وتتلاشى fade out. بالنسبة لتحريكة السقوط إلى أسفل فهي بسيطة جداً، حيث ينتقل العنصر إلى أسفل وفق محور التراتيب (محور y) ليحاكي تحريكة السقوط، ويدور بينما يسقط ويتلاشى بالتدريج حتى يختفي تماماً (ستتحقّق شيفرة JavaScript من أنّ العنصر قد أُزيل كليّاً من DOM في نهاية هذه التحريكة). li.removed-item { animation: removed-item-animation 1s cubic-bezier(0.55, -0.04, 0.91, 0.94) forwards; transform-origin: 0% 100%; } @keyframes removed-item-animation { 0% { opacity: 1; transform: rotateZ(0); } 100% { opacity: 0; transform: translateY(600px) rotateZ(90deg); } }أمّا عندما نستعيد عنصرًا فستعمل تحريكة الاستعادة على عكس المنطق الموجود في تحريكة إزالة عنصر، لذلك فستكون الأُطر الأساسية keyframes مُعرّفة بشكل معاكس تماماً لما هو عليه في تحريكة إزالة عنصر: li.restored-item { animation: openspace 0.3s ease forwards, restored-item-animation 0.3s 0.3s cubic-bezier(0, 0.8, 0.32, 1.07) forwards; } @keyframes openspace { to { height: auto; } } @keyframes restored-item-animation { 0% { opacity: 0; transform: translateY(600px) rotateZ(90deg); } 10% { opacity: 1; transform: translateY(600px) rotateZ(90deg); } 100% { opacity: 1; transform: rotateZ(0); } }يمكنك أن ترى أننا نستخدم في الشيفرة السابقة مظهر تحريك اسمه openspace استعرته من مقال كريس كوير. يتأكّد مظهر التحريك هذا من أنّ العناصر التي تقع أسفل العنصر المُسترجع (إن وجدت)، ستنزلق إلى الأسفل وتفسح مجالاً للعنصر المُسترجَع ليعود إلى مكانه. إذاً عندما تنزلق العناصر إلى الأسفل لتفسح مجالاً open space للعنصر المُسترجَع، فإنّها فعلياً يجب أن تنتقل إلى الأسفل بشكل سلس، ولكن بما أنّ العناصر في هذا التطبيق لا تملك ارتفاعاً height ثابتاً، لذلك فإنّ إطار التحريك الأساسي to (انظر الشيفرة في الأعلى) سيجعل قيمة الارتفاع height لها لتصبح auto في نهاية عملية التحريك، سيؤدي ذلك لسوء الحظ إلى أنّ العناصر لن تنزلق إلى الأسفل، بل ستبدو كما لو أنّها تقفز إلى الأسفل. على أية حال توجد طريقة تجعل العناصر تغير مواقعها بشكل سلس، وهي تقنية كتب عنها ستيف ساندرسون Steve Sanderson هنا. لكنه يستخدم لهذا الغرض التموضع المطلق absolute positioning، وكمية لا بأس بها من شيفرة JavaScript. يمكنك تفقّد مقالته إذا كنت مهتماً بمعرفة المزيد عن التقنيّة التي يستخدمها، والتي تعطي في الحقيقة نتائج رائعة! المثال الثاني المثال الثاني: العناصر تكبُر وتتلاشى أمام المستخدم، وتُستَعاد بطريقة معكوسة. يعود الفضل في هذه الفكرة إلى تيم بيتروسكي Tim Pietrusky، حيث جاء بها عندما أخبرته أنّ الأفكار قد نفذت منّي بعد أن وضعت خمسة أمثلة توضيحية! في هذه الفكرة، تظهر العناصر المُضافة حديثاً (أي تلك العناصر التي لم تُزال من قبل ثم استُعيدت) بشكل تدريجي fade in ضمن موقعها. li.new-item { opacity: 0; animation: fadeIn .3s linear forwards; } @keyframes fadeIn { to { opacity: 1; } }عندما تُزال العناصر، فإنّها تكبُر وتتلاشى أمام المستخدم، أمّا العناصر المُستعادة فتسلك الأسلوب المعاكس، فعملية التحريك بالنسبة للعناصر المستعادة تماثل تماماً عملية التحريك بالنسبة للعناصر المزالة ولكن بالمقلوب. li.removed-item { animation: removed-item-animation .6s ease-out forwards; transform-origin: 50% 50%; } @keyframes removed-item-animation { 0% { opacity: 1; transform: scale(1); } 100% { opacity: 0; transform: scale(4); } } li.restored-item { animation: openspace .3s ease forwards, restored-item-animation .3s .3s ease-out forwards; } @keyframes openspace { to { height: auto; } } @keyframes restored-item-animation { 0% { opacity: 0; transform: scale(4); } 100% { opacity: 1; transform: scale(1); } }المثال الثالث المثال الثالث: ستنزلق العناصر المستعادة لتدخل من اليمين، أما العناصر المزالة فستنزلق يساراً إلى الخارج. يُعتبر المثال الثالث أبسط من سابقيه من الناحية الشكلية. فالعناصر المُضافة حديثاً سيكون لها نفس تأثير الظهور التدريجي كما في المثالين السابقين، لذلك سنتجاوز عملية التحريك الخاصة بإضافة عنصر جديد. بالنسبة للعناصر المُزالة فإنها ستنزلق يساراً إلى الخارج، مع ملاحظة تأثير جميل يحدث عند بدء عملية الإزالة باستخدام دالة توقيت من النوع Cubic Bezier، انظر إلى المثال التطبيقي لترى كيف تعمل هذه التحريكة. li.removed-item { animation: removed-item-animation .8s cubic-bezier(.65,-0.02,.72,.29); } @keyframes removed-item-animation { 0% { opacity: 1; transform: translateX(0); } 30% { opacity: 1; transform: translateX(50px); } 80% { opacity: 1; transform: translateX(-800px); } 100% { opacity: 0; transform: translateX(-800px); } }أمّا العناصر المستعادة فستنزلق إلى الداخل من اليمين، وذلك باستخدام نفس دالة التوقيت السابقة، ولكنها ليست الحالة المعاكسة تماماً لها (تفقّد المثال التطبيقي لترى النتيجة النهائية). li.restored-item { animation: openspace .3s ease forwards, restored-item-animation .5s .3s cubic-bezier(.14,.25,.52,1.56) forwards; } @keyframes openspace { to { height: auto; } } @keyframes restored-item-animation { 0% { opacity: 0; transform: translateX(300px); } 70% { opacity: 1; transform: translateX(-50px); } 100% { opacity: 1; transform: translateX(0); } } المثال الرابع المثال الرابع: ستكبر العناصر المستعادة والجديدة وتظهر تدريجياً ضمن موقعها، أمّا العناصر المزالة فإنّها ستصغر وتختفي تدريجياً. وهذا المثال بسيط أيضاً. فكل من العناصر الجديدة والمستعادة ستكبر وتظهر تدريجياً في مواقعها، أما العناصر المزالة ستصغر وتختفي تدريجياً. هناك إطارين أساسيين keyframes لهاتين التحريكتين: li.removed-item { animation: removed-item-animation .6s cubic-bezier(.55,-0.04,.91,.94) forwards; } @keyframes removed-item-animation { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0); } } li.restored-item { animation: openspace .3s ease forwards, restored-item-animation .3s .3s cubic-bezier(0,.8,.32,1.07) forwards; } @keyframes openspace { to { height: auto; } } @keyframes restored-item-animation { from { opacity: 0; transform: scale(0); } to { opacity: 1; transform: scale(1); } } المثال الخامس المثال الخامس: تسقط العناصر الجديدة من الأعلى إلى الأسفل. العناصر المزالة تبقى معلّقة قليلاً ثم تسقط إلى الأسفل. أما العناصر المستعادة فتنزلق إلى الداخل من اليمين. في هذا المثال، عندما نُزيل أحد العناصر فإنّه يبقى معلّقاً قليلاً قبل أن يبدأ بالسقوط الفعلي ثم الإختفاء. في الحقيقة هذا هو الجزء الأهم في هذا المثال، لأنّ العناصر الجديدة ستسقط إلى الأسفل كما في المثال الأوّل، والعناصر المستعادة ستنزلق إلى الداخل من اليمين كما في المثال الثالث، ولكن مع فرق طفيف في دالة التوقيت timing function. لذلك فإنّ التحريكة الخاصة بإزالة العناصر هي التأثير الجديد الوحيد في هذا المثال. li.restored-item { transform: translateX(300px); animation: openspace .3s ease forwards, restored-item-animation .3s .3s cubic-bezier(0,.8,.32,1.07) forwards; } @keyframes openspace { to { height: auto; } } @keyframes restored-item-animation { to { opacity: 1; transform: translateX(0); } } li.removed-item { animation: removed-item-animation 2s cubic-bezier(.55,-0.04,.91,.94) forwards; transform-origin: 0% 100%; }يعطي تغيير زاوية الدوران للعنصر ضمن أُطر frames مختلفة (الأطر الرئيسية: 0% و 20% و 40% و 60% و 70% و 90% و 100%) انطباعاً بأنّ العنصر يتأرجح بينما يكون معلّقاً، وبعد ذلك يبدأ بالسقوط إلى الأسفل. @keyframes removed-item-animation { 0% { opacity: 1; transform: rotateZ(0); } 20% { opacity: 1; transform: rotateZ(140deg); } 40% { opacity: 1; transform: rotateZ(60deg); } 60% { opacity: 1; transform: rotateZ(110deg); } 70% { opacity: 1; transform: rotateZ(90deg) translateX(0); } 90% { opacity: 1; transform: rotateZ(90deg) translateX(600px); } 100% { opacity: 0; transform: rotateZ(90deg) translateX(600px); } } المثال السادس المثال السادس: ستختفي العناصر المزالة تدريجياً وتسقط إلى الأسفل باتجاه اليسار، أما العناصر الجديدة والمستعادة فستنزلق إلى الداخل من اليمين. سيكون لكل من العناصر الجديدة والمستعادة نفس السلوك في هذا المثال، حيث ستنزلق هذه العناصر إلى الداخل من اليمين ثم تخرج بشكل طفيف من الجهة اليسرى قبل أن تستقرّ في مكانها. li.restored-item { transform: translateX(300px); animation: openspace .3s ease forwards, restored-item-animation .5s .3s cubic-bezier(.14,.25,.52,1.56) forwards; } @keyframes openspace { to { height: auto; } } @keyframes restored-item-animation { 0% { opacity: 0; transform: translateX(300px); } 70% { opacity: 1; transform: translateX(-50px); } 100% { opacity: 1; transform: translateX(0); } }ستنزلق العناصر المزالة ببطء نحو اليسار، وبعد ذلك ستسقط إلى الأسفل باتجاه اليسار وتتلاشى. من المهم الآن أن نعمل على إعداد تحويل مناسب لموضع نقطة الأصل origin (مبدأ الإحداثيات)، بحيث أنّ التأثير المسؤول عن السقوط إلى أسفل يبدو أكثر واقعية. بغية ذلك، أجريت تحويلاً على نقطة الأصل لكي تنطبق على آخر نقطة تماس بين العنصر والسطر الذي ينتمي إليه، وذلك قبل أن يبدأ بالدوران والسقوط إلى أسفل، يعطي ذلك انطباعاً بأنّ العنصر يسقط بسبب وزنه. li.removed-item { animation: removed-item-animation 1s linear; transform-origin: 390px 100%; } @keyframes removed-item-animation { 0% { opacity: 1; transform: translateX(0) rotateZ(0); } 50% { opacity: 1; transform: translateX(-400px) rotateZ(0); } 75% { opacity: 1; transform: translateX(-420px) rotateZ(-30deg); } 100% { opacity: 0; transform: translateX(-800px) rotateZ(-60deg) translateY(400px); } } خاتمةفي الواقع، الإمكانيات المتاحة لا حدّ لها تقريباً، هناك الكثير من الطرق الأكثر الإبداعية لإضافة وإزالة عناصر قائمة، وأنا على ثقة بأنّك تستطيع ابتكار تأثيرات خاصة بك، وأرجو أن يكون هذا الدرس مثيراً وملهماً. لم أدخل في القسم المتعلّق بـ JavaScript لأنّ ذلك ليس من محور اهتمام الدرس. من الملاحظ وجود خطأ ضمن متصفح Firefox (ربما يُصحّح في النسخ اللاحقة) ,والذي يسبب وميضاً للصفحة كلّما تمّ وضع التركيز على العنصر أو حتى إزالة التركيز عنه (أي عندما تضغط زر edit/save). لا أدري إذا كانت توجد طريقة لتجاوز هذا الأمر، لذلك فمن فضلك أعلمني إذا استطعت تحديد سبب ذلك الوميض وإذا كانت هناك أي طريقة لمنعه. على أية حال جُرّبت الأمثلة السابقة بشكل جيّد على المتصفحات التي تدعم Webkit. شكراً لقراءتك هذا الدرس، وأرجو أن تكون قد استمتعت فيه. بإمكانك الاطّلاع على هذه الأمثلة من هنا، أما الشيفرة المصدرية فهي متُوفّرة على هذا المُستودع. ترجمة -وبتصرّف- للمقال Creative Add/Remove Effects For List Items with CSS3 Animations لصاحبته Sara Soueidan.
  7. في هذا الدرس سوف تتعلم كيفية إنشاء قائمة تنقل (navigation) دائرية الشكل باستخدام CSS Transforms. سوف أريك كيف تقوم بذلك خطوة بخطوة وسوف أقوم بشرح الحسابات والمنطق البسيط وراء هذه الطريقة حتى يتسنى لك فهم كيفية عمل هذه الطريقة. وكما ذكرت قبل قليل فسوف يكون هناك بعض الحسابات، ولكن لا تقلق فلن يكون هناك أي شيء معقد أو يصعب فهمه وسوف أشرح كل شيء خطوة بخطوة. بنية HTMLبما أننا سنقوم ببناء قائمة تنقل فسوف نبدأ بالبنية المعتادة لأي قائمة، أي أننا سوف نحتاج إلى عنصر div ليحتوي على قائمة العناصر المتمثلة بعنصر ul وسوف نحتاج أيضًا إلى عناصر القائمة المتمثلة بعناصر li كما أننا سوف نحتاج إلى زر (button) ليعمل على فتح وإغلاق القائمة عند الضغط عليه. وسوف نحتاج في المثال الأول إلى شيء اضافي وهو عنصر div ليعمل كغشاء (overlay) يمنعنا من الضغط على أي مكان آخر في الصفحة طالما أنّ القائمة مفتوحة. ملاحظة: جميع الأيقونات المستخدمة في هذا الدرس هي من خدمة Font Awesome. شرح بعض الحسابات خلف CSS Transformsأفضل طريقة للشرح هنا ستكون باستعمال الصور بدلًا من الكلمات، لذلك سوف أبدأ بالمنطق الكامن وراء هذه الطريقة كما أننا سنقوم بتطبيق بعض الحسابات وبعد أن ننتهي من الشرح سوف نتوجه إلى جزء التكويد مما سيسمح لك بأن تعرف ما الذي تفعله كل واحدة من خصائص CSS التي سوف نستعملها. لنبدأ أولًا بتعريف ما هو المقصود بالزاوية المركزية. أنظر إلى الصورة التالية متبوعة بشرح بسيط: لنفرض أنك تريد توزيع عناصر القائمة على نصف دائرة كما هو الحال بالنسبة لما نريد إنشائه هنا، ولنفرض أيضًا أنّ لدينا 6 عناصر، إذًا كل زاوية سوف يكون لها زاوية مركزية بقيمة: 180 درجة / 6 = 30 درجة ماذا لو أردت توزيع العناصر على دائرة كاملة؟ بسيطة كل ما سنقوم بفعله هو التالي: 360 درجة / 6 = 60 درجة وهكذا دواليك. إذًا فكل ما يجب علينا فعله هو حساب الزاوية المركزية التي تناسبنا وبعدها نبدأ بتطبيق بعض الحسابات على خصائص CSS Transforms لنقوم بتطبيق هذه الزوايا عليها. وحتى يكون بامكاننا إنشاء زاوية مساوية للزوايا المركزية التي نريدها فإننا سوف نحتاج إلى حرف/تمييل (skew) العناصر وذلك باستخدام دالّة ()skew، وسوف تكون القيمة كالتالي: 90 درجة - x درجة ، بحيث تكون x هي الزاوية المركزية التي نريدها ولكن في هذه الحالة فإنّ جميع محتوى عناصر القائمة سوف تتم إمالتها وسوف تظهر بشكل غير مناسب، لذلك يجب علينا أن نقوم بتعديلها (أي نعمل لها unskew) حتى يظهر كل شيء بشكل جيد. يمكنك الدخول إلى هذا المثال لرؤية كيف يتم تطبيق التحويلات (transforms) إلى عناصر القائمة خطوة بخطوة حتى يتضح لك ما الذي سوف نقوم بفعله في الشيفرة البرمجية (ضع في الحسبان أن التسلسل الموجود في المثال قد يختلف بشكل بسيط عن الخطوات الفعلية التي سوف نتبعها في هذا الدرس). وهذه لقطات لكل خطوة سوف تراها في هذا المثال: الحالة الأولية: الخطوة الأولى: الخطوة الثانية: الخطوة الثالثة: الخطوة الرابعة: الخطوة الخامسة: الخطوة السادسة: إذًا هذا ما سوف نفعله: سوف نحتاج إلى موضعة عناصر القائمة بشكل مطلق (absolute positioning) داخل الحاوي الخاص بها. سوف نستعمل الخاصية transform-origin على كل عنصر وبالقيمة bottom right corner .بعد ذلك سوف نقوم بتحريك العناصر إلى الأعلى وإلى اليسار بشكل كافٍ حتى تتطابق مراكز التحريك الخاصة بها مع مركز العنصر الذي يحويها. سوف نقوم بتدوير العناصر في مواضعها باستخدام المعادلة التالية: كل عنصر له index بقيمة i سوف يتم تدويره بقيمة i*x حيث أن x كما سبق وذكرنا هي قيمة الزاوية المركزية. بعد ذلك نقوم بتمييلها (skew) للحصول على الزاوية المركزية التي نريدها (باستخدام المعادلة الموجودة في الأعلى). في مثالنا سوف يكون هناك 5 عناصر مما يعني وجود 5 زوايا مركزية وبالتالي سوف نقوم بتغطية الجزء العلوي فقط من الدائرة، واعتمادًا على المعادلات التي طرحناها سابقًا فسوف تكون الزاوية المركزية لكل عنصر هي 36 درجة (180 / 5) ولكننا سوف نجعل الزاوية المركزية تساوي 40 درجة مما يمنحنا منطقة أكبر قابلة للنقر، وبالتالي يكون مجموع الزوايا هو 40*5=200 وهو رقم أكبر من 180. في هذه الحالة سوف نحتاج إلى تدوير العناصر عكس عقارب الساعة بقيمة (200-180)/2 لنتأكد من أنها متوازنة على كلا الجانبين. قمنا إلى هذه النقطة بإنشاء الزوايا المركزية المناسبة، ولكن تمييل عناصر القائمة أدى أيضًا إلى تمييل المحتوى الذي بداخلها وبالتالي تشويهه، لذلك سوف نحتاج إلى تطبيق قاعدة رياضية أخيرة حتى نتأكد من ظهور المحتوى بشكل سليم وغير مشوه، وهذه القاعدة هي: نقوم بتمييل عناصر a الموجودة داخل عناصر القائمة وذلك بتطبيق قيمة معاكسة للقيمة المستخدمة لتمييل عناصر القائمة وبعد ذلك نقوم بتدويرها بالقيمة: -[90 - (x/2)] (لاحظ أن القيمة سالبة) ، مرة أخرى x هي الزاوية المركزية إذًا بما أن الزاوية المركزية هي 40 درجة فإننا سوف نحتاج إلى تمييل العناصر aبالقيمة -40 درجة وتدويرها بالقيمة -70 درجة (90 - (40/2)). سوف يتم موضعة عناصر a بشكل مطلق داخل الحاوي الخاص بها (الحاوي هنا هي عناصر القائمة) وسوف يتم إعطاء عناصر القائمة الخاصية overflow: hidden مما يعني أنّه سوف يتم قطع جزء من كل عنصر من عناصر a، وحتى نتأكد بأنّ محتوى العناصر (سواء كانت نص أو أيقونة) يبقى ضمن المحتوى الظاهر منها فإننا سوف نستعمل الخاصية text-align: center. وهذه هي كل الحسابات التي سوف نحتاجها إلى الآن. تبقى علينا الآن أن نقوم باستعمال تنسيقات CSS المناسبة حتى يصبح كل شيء بشكله المناسب. تنسيقات CSSسوف نقوم أولًا بتنسيق المثال الأول. سوف نستخدم Modernizr حتى نستطيع تحديد المتصفحات التي تدعم CSS Transforms والمتصفحات التي لا تدعمها ونقوم بتوفير fallback للمتصفحات القديمة التي لا تدعمها. لنبدأ أولًا بتنسيق العنصر الذي سوف يحتوي على القائمة ، بحيث سوف يكون ثابت إلى الأسفل ومتوسط للصفحة وسوف يكون متقلصًا/مخفيًا ويظهر/يتمدد عندما يتم النقر على الزر. .csstransforms .cn-wrapper { font-size:1em; width: 26em; height: 26em; overflow: hidden; position: fixed; z-index: 10; bottom: -13em; left: 50%; border-radius: 50%; margin-left: -13em; transform: scale(0.1); transition: all .3s ease; } /* class applied to the container via JavaScript that will scale the navigation up */ .csstransforms .opened-nav { border-radius: 50%; transform: scale(1); }سوف نقوم كذلك بتنسيق الزر الذي سوف يفتح ويغلق القائمة: .cn-button { border:none; background:none; color: white; text-align: Center; font-size: 1.5em; padding-bottom: 1em; height: 3.5em; width: 3.5em; background-color: #111; position: fixed; left: 50%; margin-left: -1.75em; bottom: -1.75em; border-radius: 50%; cursor: pointer; z-index: 11 } .cn-button:hover, .cn-button:active, .cn-button:focus{ background-color: #222; }عندما يتم فتح القائمة فإنه سوف يظهر غطاء/غشاء شفاف يغطي الصفحة، وهذه هي التنسيقات الخاصة به: .cn-overlay{ width:100%; height:100%; background-color: rgba(0,0,0,0.6); position:fixed; top:0; left:0; bottom:0; right:0; opacity:0; transition: all .3s ease; z-index:2; pointer-events:none; } /* Class added to the overlay via JavaScript to show it when navigation is open */ .cn-overlay.on-overlay{ pointer-events:auto; opacity:1; }سوف نقوم الآن بتنسيق عناصر القائمة وعناصر a الموجودة بداخلها وذلك بتطبيق المنطق والالحسابات التي تحدثنا عنها في البداية: .csstransforms .cn-wrapper li { position: absolute; font-size: 1.5em; width: 10em; height: 10em; transform-origin: 100% 100%; o verflow: hidden; left: 50%; top: 50%; margin-top: -1.3em; margin-left: -10em; transition: border .3s ease; } .csstransforms .cn-wrapper li a { display: block; font-size: 1.18em; height: 14.5em; width: 14.5em; position: absolute; bottom: -7.25em; right: -7.25em; border-radius: 50%; text-decoration: none; color: #fff; padding-top: 1.8em; text-align: center; transform: skew(-50deg) rotate(-70deg) scale(1); transition: opacity 0.3s, color 0.3s; } .csstransforms .cn-wrapper li a span { font-size: 1.1em; opacity: 0.7; } /* for a central angle x, the list items must be skewed by 90-x degrees in our case x=40deg so skew angle is 50deg items should be rotated by x, minus (sum of angles - 180)2s (for this demo) */ .csstransforms .cn-wrapper li:first-child { transform: rotate(-10deg) skew(50deg); } .csstransforms .cn-wrapper li:nth-child(2) { transform: rotate(30deg) skew(50deg); } .csstransforms .cn-wrapper li:nth-child(3) { transform: rotate(70deg) skew(50deg) } .csstransforms .cn-wrapper li:nth-child(4) { transform: rotate(110deg) skew(50deg); } .csstransforms .cn-wrapper li:nth-child(5) { transform: rotate(150deg) skew(50deg); } .csstransforms .cn-wrapper li:nth-child(odd) a { background-color: #a11313; background-color: hsla(0, 88%, 63%, 1); } .csstransforms .cn-wrapper li:nth-child(even) a { background-color: #a61414; background-color: hsla(0, 88%, 65%, 1); } /* active style */ .csstransforms .cn-wrapper li.active a { background-color: #b31515; background-color: hsla(0, 88%, 70%, 1); } /* hover style */ .csstransforms .cn-wrapper li:not(.active) a:hover, .csstransforms .cn-wrapper li:not(.active) a:active, .csstransforms .cn-wrapper li:not(.active) a:focus { background-color: #b31515; background-color: hsla(0, 88%, 70%, 1); } .csstransforms .cn-wrapper li:not(.active) a:focus { position: fixed; /* fix the "displacement" bug in webkit browsers when using tab key */ }سوف نقوم بتوفير fallback بسيط للمتصفحات التي لا تدعم CSS Transforms. .no-csstransforms .cn-wrapper{ font-size:1em; height:5em; width:25.15em; bottom:0; margin-left: -12.5em; overflow: hidden; position: fixed; z-index: 10; left:50%; border:1px solid #ddd; } .no-csstransforms .cn-button{ display:none; } .no-csstransforms .cn-wrapper li{ position:static; float:left; font-size:1em; height:5em; width:5em; background-color: #eee; text-align:center; line-height:5em; } .no-csstransforms .cn-wrapper li a{ display:block; width:100%; height:100%; text-decoration:none; color:inherit; font-size:1.3em; border-right: 1px solid #ddd; } .no-csstransforms .cn-wrapper li a:last-child{ border:none; } .no-csstransforms .cn-wrapper li a:hover, .no-csstransforms .cn-wrapper li a:active, .no-csstransforms .cn-wrapper li a:focus{ background-color: white; } .no-csstransforms .cn-wrapper li.active a { background-color: #6F325C; color: #fff; }وبالطبع فنحن نريد أن تكون القائمة متجاوبة مع جميع الأجهزة وتتقلص للشاشات الصغيرة: @media screen and (max-width:480px){ .csstransforms .cn-wrapper{ font-size:.68em; } .cn-button{ font-size:1em; } .csstransforms .cn-wrapper li { font-size:1.52em; } } @media screen and (max-width:320px){ .no-csstransforms .cn-wrapper{ width:15.15px; margin-left: -7.5em; } .no-csstransforms .cn-wrapper li{ height:3em; width:3em; } }هذا كان كل شيء يخص المثال الأول. دعونا الآن ننتقل لتنسيق المثال الثاني. في المثال الثاني سوف تكون القائمة مختلفة قليلًا عن القائمة في المثال الأول، ولكن كل المنطق والحسابات التي يخص القائمة الأولى سينطبق على هذه القائمة مع وجود ثلاثة اختلافات. لن نقوم بالرجوع وتوضيح كل شيء مرة أخرى وإنما سوف نكتفي بالتحدث عن الاختلافات الثلاثة فقط. دعونا نأخذ نفس المثال الموجود في الأعلى ونقوم بتغيير خاصية CSS واحدة فقط ونرى ما التغيير الذي سوف تصنعه على شكل عناصر القائمة. سوف نقوم بتطبيق تدرج لوني دائري على عناصر a مع لون خلفية شفاف، وسوف تظهر النتيجة كالتالي: سوف نقوم الآن بإضافة بعض المسافة بين عناصر القائمة وذلك بتغيير درجة الدوران لكل عنصر. سوف نقوم كذلك بإزالة لون الخلفية لعناصر القائمة وللحاوي وللحدود وسوف نقوم بتقليص قيمة الخاصية padding-top لعناصر a حتى نجعل الأيقونات متوسطة بشكل مثالي داخل العناصر. النتيجة النهائية ستكون كما في الصورة التالية: يمكنك أن ترى بأنّ القائمة بدأت تبدو بمظهر مختلف، وبقي شيء واحد مهم يجب أن نقوم به. ففي حالة بقاء التنسيقات كما هي عليه في الوقت الحالي فإنّ المناطق القابلة للنقر الخاصة بعناصر a ستكون أكبر مما نريده، فما نريده هو أن يكون الجزء الملون من القائمة هو فقط القابل للنقر. الصورة التالية توضح الجزء الزائد القابل للنقر والذي لا نريده: عندما تقوم بوضع مؤشر الفأرة فوق المنطقة الحمراء الموضحة في الصورة الموجودة في الأعلى فإنّه سيتم تفعيل حالة الـhover الخاصة بعناصر a وهو شيء طبيعي الحدوث ولكننا لا نريده لأننا نريد أن تظهر العناصر وكأنها هي فقط الجزء البنفسجي، وبالتالي سوف نحتاج إلى منع تفعيل أحداث الفأرة على الجزء الملون باللون الأحمر. لذلك ما سنقوم به هو استخدام pseudo-element (سوف تفي بالغرض لأننا لا نريد استعمال وسم فارغ) ليعمل كغطاء/غشاء يقوم بتغطية منطقة اللون الأحمر وبالتالي نمنع تفعيل أحداث الماوس على هذه المنطقة. إذًا سوف نقوم بتطبيق الخطوات الثلاثة التي ذكرناها مع تغيير بعض التنسيقات (كاللون والحجم) بالنسبة للقائمة ولعناصر القائمة ليظهر كل شيء كما في الصورة: .csstransforms .cn-wrapper { position: absolute; top: 100%; left: 50%; z-index: 10; margin-top: -13em; margin-left: -13.5em; width: 27em; height: 27em; border-radius: 50%; background: transparent; opacity: 0; transition: all .3s ease 0.3s; transform: scale(0.1); pointer-events: none; overflow: hidden; } /*cover to prevent extra space of anchors from being clickable*/ .csstransforms .cn-wrapper:after{ color: transparent; content:"."; display:block; font-size:2em; width:6.2em; height:6.2em; position: absolute; left: 50%; margin-left: -3.1em; top:50%; margin-top: -3.1em; border-radius: 50%; z-index:10; } .csstransforms .cn-wrapper li { position: absolute; top: 50%; left: 50%; overflow: hidden; margin-top: -1.3em; margin-left: -10em; width: 10em; height: 10em; font-size: 1.5em; transition: all .3s ease; transform: rotate(76deg) skew(60deg); transform-origin: 100% 100%; pointer-events: none; } .csstransforms .cn-wrapper li a { position: absolute; position: fixed; /* fix the "displacement" bug in webkit browsers when using tab key */ right: -7.25em; bottom: -7.25em; display: block; width: 14.5em; height: 14.5em; border-radius: 50%; background: #429a67; background: radial-gradient(transparent 35%, #429a67 35%); color: #fff; text-align: center; text-decoration: none; font-size: 1.2em; line-height: 2; transition: all .3s ease; transform: skew(-60deg) rotate(-75deg) scale(1); pointer-events: auto; } .csstransforms .cn-wrapper li a span { position: relative; top: 1.8em; display: block; font-size: .5em; font-weight: 700; text-transform: uppercase; } .csstransforms .cn-wrapper li a:hover, .csstransforms .cn-wrapper li a:active, .csstransforms .cn-wrapper li a:focus { background: radial-gradient(transparent 35%, #449e6a 35%); }نريد للعناصر في المثال الثاني أن تظهر بتأثير شبيه لحركة المروحة عندما نقوم بفتح القائمة (يمكنك الذهاب إلى المثال الثاني لترى ما الذي أقصده). وللحصول على هذا التأثير فإننا قمنا بموضعة العناصر بنفس المكان وبنفس التمييل والتدوير بقيمة (rotate(76deg) skew(60deg. يمكننا باستعمال تأخيرات التنقل (transition delays) بأن نسمح للعناصر بأن تتمدد/تتفرق عن بعضها بعد أن يظهر الحاوي ويتمدد. وعندما نقوم بغلق القائمة فسوف ننتظر حتى تعود عناصر القائمة قبل أن نقوم بتقليص الحاوي وإخفائه. عند الضغط على زر الفتح فسوف نقوم بإبعاد عناصر القائمة عن بعضها وذلك عن طريق تدوير كل عنصر إلى مكانه النهائي في الدائرة. .csstransforms .opened-nav { border-radius: 50%; opacity: 1; transition: all .3s ease; transform: scale(1); pointer-events: auto; } .csstransforms .opened-nav li { transition: all .3s ease .3s; } .csstransforms .opened-nav li:first-child { transform: rotate(-20deg) skew(60deg); } .csstransforms .opened-nav li:nth-child(2) { transform: rotate(12deg) skew(60deg); } .csstransforms .opened-nav li:nth-child(3) { transform: rotate(44deg) skew(60deg); } .csstransforms .opened-nav li:nth-child(4) { transform: rotate(76deg) skew(60deg); } .csstransforms .opened-nav li:nth-child(5) { transform: rotate(108deg) skew(60deg); } .csstransforms .opened-nav li:nth-child(6) { transform: rotate(140deg) skew(60deg); } .csstransforms .opened-nav li:nth-child(7) { transform: rotate(172deg) skew(60deg); }وبالطبع سوف نوفر fallback بسيط للمتصفحات غير الداعمة: .no-csstransforms .cn-wrapper{ margin:10em auto; overflow:hidden; text-align:center; padding:1em; } .no-csstransforms .cn-wrapper ul{ display:inline-block; } .no-csstransforms li{ font-size:1em; width:5em; height:5em; float:left; line-height:5em; text-align:center; background-color: #fff; } .no-csstransforms li a{ display:block; height:100%; width:100%; text-decoration: none; color: inherit; } .no-csstransforms .cn-wrapper li a:hover, .no-csstransforms .cn-wrapper li a:active, .no-csstransforms .cn-wrapper li a:focus{ background-color: #f8f8f8; } .no-csstransforms .cn-wrapper li.active a { background-color: #6F325C; color: #fff; } .no-csstransforms .cn-button{ display:none; }وكذلك سوف نجعل القائمة متجاوبة وذلك بتقليصها للشاشات الصغيرة: @media only screen and (max-width: 620px) { .no-csstransforms li{ width:4em; height:4em; line-height:4em; } } @media only screen and (max-width: 500px) { .no-ccstransforms .cn-wrapper{ padding:.5em; } .no-csstransforms .cn-wrapper li{ font-size:.9em; width:4em; height:4em; line-height:4em; } } @media only screen and (max-width: 480px) { .csstransforms .cn-wrapper{ font-size: .68em; } .cn-button{ font-size:1em; } } @media only screen and (max-width:420px){ .no-csstransforms .cn-wrapper li{ width:100%; height:3em; line-height:3em; } }هذا كان كل ما يخص تنسيقات CSS. دعونا الآن نرى ما الذي سوف نحتاج لعمله باستخدام الجافاسكربت. بعض الجافاسكربتلن نستخدم أي إطار عمل للجافاسكربت هنا ولكننا سوف نستخدم Classie.js لإضافة وإزالة الفئات (classes). وأما بالنسبة للمتصفحات التي لا تدعم addEventListener و removeEventListener فإننا سوف نستخدم EventListener polyfill. سوف نقوم بإضافة مدير أحداث (event handler) إلى الزر بحيث يتم فتح/اغلاق القائمة عند النقر عليه أو استخدام زر tab في لوحة المفاتيح (أي عند حدوث focus على العنصر). نريد أيضًا فيالمثال الأول أن يتم اغلاق القائمة عند النقر على أي مكان خارج القائمة. لنبدأ أولًا بالجافاسكربت الخاص بالمثال الأول: (function(){ var button = document.getElementById('cn-button'), wrapper = document.getElementById('cn-wrapper'), overlay = document.getElementById('cn-overlay'); //open and close menu when the button is clicked var open = false; button.addEventListener('click', handler, false); button.addEventListener('focus', handler, false); wrapper.addEventListener('click', cnhandle, false); function cnhandle(e){ e.stopPropagation(); } function handler(e){ if (!e) var e = window.event; e.stopPropagation(); //so that it doesn't trigger click event on document if(!open){ openNav(); } else{ closeNav(); } } function openNav(){ open = true; button.innerHTML = "-"; classie.add(overlay, 'on-overlay'); classie.add(wrapper, 'opened-nav'); } function closeNav(){ open = false; button.innerHTML = "+"; classie.remove(overlay, 'on-overlay'); classie.remove(wrapper, 'opened-nav'); } document.addEventListener('click', closeNav); })();الجافاسكربت الخاص ب المثال الثاني مشابه للأولى نوعًا ما ولكن مع وجود بعض الاختلافات: (function(){ var button = document.getElementById('cn-button'), wrapper = document.getElementById('cn-wrapper'); //open and close menu when the button is clicked var open = false; button.addEventListener('click', handler, false); button.addEventListener('focus', handler, false); function handler(){ if(!open){ this.innerHTML = "Close"; classie.add(wrapper, 'opened-nav'); } else{ this.innerHTML = "Menu"; classie.remove(wrapper, 'opened-nav'); } open = !open; } function closeWrapper(){ classie.remove(wrapper, 'opened-nav'); } })(); خاتمةهذا كان كل شيء فيما يتعلق بهذا الدرس، أتمنى أن تكون قد استفدت منه وتعلمت شيئًا جديدًا. ترجمة -وبتصرّف- للمقال Building a Circular Navigation with CSS Transforms HTML/CSS لصاحبته Sara Soueidan.
  8. وقعت عيني في أحد الأيام على مبدأ جميل لقائمة في أحد المواقع، وكان أحدهم قد قام ببناء شيء شبيه بها ولكني أردت أن أضيف لها تأثيرات إضافية وأجعلها متناسبة مع متصفحات سطح المكتب. لذلك أردت أن أريك في هذا الدرس كيفية القيام بشيء مشابه. سوف نقوم في هذا الدرس ببناء شيء شبيه بهذه المعاينة (أنظر إلى Demo2) بحيث تكون الأيقونة التي تفتح القائمة موجودة في أعلى يسار الشاشة والحد (border) يكون أسمك من جهة اليسار. ملاحظة: ضع في الحسبان أننا سوف نستعمل تأثيرات التنقل (transitions) والحركة (animation) على عناصر زائفة (pseudo-elements) والتي لن تعمل في بعض المتصفحات (مثل Safari وMobile Safari). بنية ملف HTMLسوف تحتوي بنية HTML الخاصة بالقائمة على عنصر nav وعلى عنصر a وأيضًا على عنصر ul يوجد بداخله مجموعة من عناصر li وبداخل كل عنصر من هذه العناصر يوجد أيقونة: <nav id="bt-menu" class="bt-menu"> <a href="#" class="bt-menu-trigger"><span>Menu</span></a> <ul> <li><a href="#" class="bt-icon icon-zoom">Zoom</a></li> <li><a href="#" class="bt-icon icon-refresh">Refresh</a></li> <li><a href="#" class="bt-icon icon-lock">Lock</a></li> <li><a href="#" class="bt-icon icon-speaker">Sound</a></li> <li><a href="#" class="bt-icon icon-star">Favorite</a></li> </ul> </nav>لنبدأ الآن بتنسيق العناصر باستخدام CSS. تنسيقات CSSملاحظة: التنسيقات التي سوف نقوم بكتابتها لن تحتوي على أي بادئات للمتصفحات (vendor prefixies) ولكنك سوف تجدها في الملفات المصدرية لهذا الدرس. سوف نقوم في البداية بتطبيق الخاصية box-sizing: border-box على جميع العناصر: *, *:after, *::before { box-sizing: border-box; }ودعنا نقوم أيضًا بإعطاء بعض التنسيقات لجسم الصفحة (العنصر body) والحاوي الرئيسي: body { background: #04a466; } .container { padding: 80px; }سوف تساعد خاصية padding: 80px على توفير بعض المساحة حول المحتوى حتى نضمن وجود بعض المساحة عندما تظهر الحدود (border). سوف تكون القائمة الرئيسية متموضعة بشكل ثابت في الصفحة (باستخدام position: fixed) وسوف تكون الحدود دائمًا موجوة حول مجال الرؤية (viewport). سوف نستخدم تنسيقات مبدئية للحدود والتي سوف تتحول إلى حدود أكبر عند النقر على أيقونة القائمة. لاحظ أننا جعلنا الارتفاع يساوي صفر (height: 0) وذلك حتى نضمن بأنّ القائمة لا تُغطّي أي شيء قبل ظهورها على الشاشة، كما أنّه سيكون هناك تأخير بمقدار 0.3s بالنسبة للارتفاع عندما نقوم بإغلاق القائمة: .bt-menu { position: fixed; top: 0; left: 0; width: 100%; height: 0; border-width: 0px; border-style: solid; border-color: #333; background-color: rgba(0,0,0,0); transition: border-width 0.3s, background-color 0.3s, height 0s 0.3s; }عندما نقوم بفتح القائمة فإننا نريد أن نجعل الارتفاع يساوي 100% (ولكننا لن نستخدم الخاصية transition) وسوف نجعل الحدود تساوي 90px على الجهة اليسرى و30px على الجهات الأخرى، كما أنّ لون الخلفية سوف يكون شبه شفاف وذلك باستخدام RGBA: .bt-menu.bt-menu-open { height: 100%; border-width: 30px 30px 30px 90px; background-color: rgba(0,0,0,0.3); transition: border-width 0.3s, background-color 0.3s; }سوف نستعمل الآن خدعة بسيطة بحيث سوف نقوم بإضافة عنصر باستخدام الجافاسكربت ليعمل وكأنّه حاوٍ يغطي كافة الصفحة باستثناء الحدود، وهذا سوف يسمح لنا بأن نعرف أين ننقر حتى نقوم بإغلاق القائمة فنحن لا نريد للقائمة أن تغلق عندما ننقر على الحدود وإنما فقط عند النقر على المساحة الأخرى. .bt-overlay { position: absolute; width: 100%; }وعندما نقوم بفتح القائمة فإنّ هذا العنصر سوف يكون بارتفاع كامل (height: 100%): .bt-menu-open .bt-overlay { height: 100%; }لنقم الآن بتنسيق الأيقونة التي تفتح القائمة عند النقر عليها. سوف نعطيها تموضعًا ثابتًا (position: fixed) وسوف نجعلها تظهر في الجزء العلوي من الجهة اليسار للصفحة: .bt-menu-trigger { position: fixed; top: 15px; left: 20px; display: block; width: 50px; height: 50px; cursor: pointer; }سوف يعمل العنصر a كحاوٍ والعنصر span سيكون الخط المتوسط في أيقونة الهامبرجر (تم تسميتها بهذا الاسم لأنّ شكلها يشبه سندويشة الهامبرجر). لذلك سوف نقوم بموضعة العنصر span في الوسط وذلك بإعطائه الخاصية top: 50% والخاصية margin-top: -2px (هذه خدعة قديمة بحيث نقوم بإعطاء العنصر قيمة margin سالبة بنصف ارتفاع العنصر حتى نجعله يتوسط كما نريد): .bt-menu-trigger span { position: absolute; top: 50%; left: 0; display: block; width: 100%; height: 4px; margin-top: -2px; background-color: #fff; font-size: 0px; user-select: none; transition: background-color 0.3s; }وعند فتح القائمة سوف يتغير شكل الأيقونة التي تفتح القائمة وتغلقها إلى حرف "X"، بحيث سوف نستعمل العناصر الزائفة لإنشاء الخطين المتقاطعين على شكل "X" وسوف نقوم بإخفاء الخط الذي في الوسط (والذي قلنا بأنّه هو نفسه العنصر span): .bt-menu-open .bt-menu-trigger span { background-color: transparent; }لنقم الآن بإنشاء الخطين المتقاطعين كما قلنا باستخدام العناصر الزائفة، بحيث سوف نعطيها الخاصية position: absolute وسوف يكون ارتفاعها بنفس ارتفاع العنصر الذي يحتويها (العنصر span في هذه الحالة) وذلك عن طريق إعطائها الخاصية height: 100%: bt-menu-trigger span:before, .bt-menu-trigger span:after { position: absolute; left: 0; width: 100%; height: 100%; background: #fff; content: ''; transition: transform 0.3s; }وسوف نستخدم الخاصية translateY لموضعتها بشكل صحيح: .bt-menu-trigger span:before { transform: translateY(-250%); } .bt-menu-trigger span:after { transform: translateY(250%); } سوف ينتج الشكل "X" عندما نقوم بفتح القائمة وذلك عن طريق الخاصية (translateY(0 وعن طريق تدوير العناصر الزائفة باستخدام الخاصية (rotate(45deg: .bt-menu-open .bt-menu-trigger span:before { transform: translateY(0) rotate(45deg); } .bt-menu-open .bt-menu-trigger span:after { transform: translateY(0) rotate(-45deg); }كما أنّ العنصر ul الذي سوف يحتوي على الأيقونات سوف يتم موضعته بشكل ثابت (باستخدام position: fixed) وإلى يسار الشاشة: .bt-menu ul { position: fixed; top: 75px; left: 0; margin: 0; padding: 0; width: 90px; list-style: none; backface-visibility: hidden; }لنقم الآن بإعطاء العناصر li و a الخاصية display: block ونجعلها بعرض كامل باستخدام الخاصية width: 100%: .bt-menu ul li, .bt-menu ul li a { display: block; width: 100%; text-align: center; }كل عنصر li سوف يكون مخفيًا بشكل مبدئي وبشفافية منعدمة (opacity: 0)، كما أنّ التنقل الرجعي (backward transition) لخاصية visibility سوف يتم تأخيره حتى تنتهي خاصية التنقل الخاصة بالخاصيتين transform و opacity: .bt-menu ul li { padding: 16px 0; opacity: 0; visibility: hidden; transition: transform 0.3s, opacity 0.2s, visibility 0s 0.3s; }سوف نقوم الآن بتحويل جميع عناصر li بشكل مختلف عن بعضها بحيث تكون جميعها موجودة في الوسط وإلى اليسار إلى أن تختفي (وذلك باستعمال القيمة -100% على المحور العمودي): .bt-menu ul li:first-child { transform: translate3d(-100%,200%,0); } .bt-menu ul li:nth-child(2) { transform: translate3d(-100%,100%,0); } .bt-menu ul li:nth-child(3) { transform: translate3d(-100%,0,0); } .bt-menu ul li:nth-child(4) { transform: translate3d(-100%,-100%,0); } .bt-menu ul li:nth-child(5) { transform: translate3d(-100%,-200%,0); }عند فتح القائمة سوف تظهر العناصر li بشكل فوري لأننا لم نقم بإعطائها خاصية transition. وسوف يتحركوا أيضًا إلى مواقعهم الرئيسية وذلك عن طريق استخدام الخاصية (transform: translate3d(0,0,0: .bt-menu.bt-menu-open ul li { visibility: visible; opacity: 1; transition: transform 0.3s, opacity 0.3s; transform: translate3d(0,0,0); }لنقم الآن بتنسيق عناصر a. سوف نستخدم خط أيقوني (icon font) ونضيف مصدر الخط وفئات الأيقونات (icon classes) الموجودة في ملف CSS آخر والذي توفره خدمة مثل Fontastic أو IcoMoon app. سوف نقوم بإخفاء النص الموجود في العناصر وذلك باستعمال الخاصية font-size: 0px ونجعل اللون شفاف باستخدام color: transparent: .bt-menu ul li a { display: block; outline: none; color: transparent; text-decoration: none; font-size: 0px; }وحتى نجعل الأيقونات تظهر (لأننا قمنا بإخفائها عن طريق font-size: 0 و color:transparent) فإننا سوف نُعطي العناصر الزائفة الخاصية font-size: 48px وcolor: #04a466. لاحظ أننا استعملنا "px" بدل "em" وذلك لأننا أعطينا العنصر الرئيسي القيمة font-size: 0 وبالتالي فإنّ "em" لن تعمل هنا: .bt-menu ul li a:before { color: #04a466; font-size: 48px; transition: color 0.2s; }وعندما يقوم المستخدم بوضع مؤشر الفأرة فوق العنصر (hover) فإننا نريد أن يكون لون الخط أبيضًا: .bt-menu ul li a:hover:before, .bt-menu ul li a:focus:before { color: #fff; }وأخيرًا وليس آخرًا، نريد أن تظهر الأيقونات بحجم أصغر في شاشات الهواتف المحمولة: @media screen and (max-height: 31.125em) { .bt-menu ul li a:before { font-size: 32px; } }هذا كان كل ما يتعلق بتنسيقات CSS. لننتقل الآن إلى الجافاسكربت. بعض الجافاسكربتما نريد فعله بالجافاسكربت بسيط ومباشر، ما نريده هو أنّه عند النقر على أيقونة القائمة فإنّه يتم إضافة/إزالة الفئة (class) التي تحمل الاسم "bt-menu-open" والفئة "bt-menu-close على العنصر nav (إضافة الفئة "bt-menu-close" هي فقط ضرورية إذا كنت سوف تستخدم التحريك (animation) للتأثير الخاص بأيقونة القائمة، تمامًا كما فعلنا في Demo1. وهذا سوف يسمح لنا بتشغيل التحريك الرجعي عندما نقوم بإغلاق القائمة). عندما نقوم بالنقر على عنصر الـoverlay الذي تحدثنا عنه سابقًا نريد أن يتم إغلاق القائمة. وسوف نقوم أيضًا بإضافة بعض الدعم للأجهزة التي تعمل باللمس: (function() { // http://stackoverflow.com/a/11381730/989439 function mobilecheck() { var check = false; (function(a) {if(/(android|ipad|playbook|silk|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); return check; } function init() { var menu = document.getElementById( 'bt-menu' ), trigger = menu.querySelector( 'a.bt-menu-trigger' ), // event type (if mobile, use touch events) eventtype = mobilecheck() ? 'touchstart' : 'click', resetMenu = function() { classie.remove( menu, 'bt-menu-open' ); classie.add( menu, 'bt-menu-close' ); }, closeClickFn = function( ev ) { resetMenu(); overlay.removeEventListener( eventtype, closeClickFn ); }; var overlay = document.createElement('div'); overlay.className = 'bt-overlay'; menu.appendChild( overlay ); trigger.addEventListener( eventtype, function( ev ) { ev.stopPropagation(); ev.preventDefault(); if( classie.has( menu, 'bt-menu-open' ) ) { resetMenu(); } else { classie.remove( menu, 'bt-menu-close' ); classie.add( menu, 'bt-menu-open' ); overlay.addEventListener( eventtype, closeClickFn ); } }); } init(); })();خاتمةهذا كان كل شيء. أتمنى أن تكون قد استمتعت بهذا الدرس ووجدته مفيدًا وعمليًا، وتأكد بأن تلقي نظرة على جميع المعاينات (demos) فالمعاينة الأخيرة يوجد بها مبدأ لمشغّل فيديو يأخذ كامل الشاشة. ترجمة -وبتصرّف- للمقال Animated Border Menus لصاحبته Mary Lou.
  9. سنقوم اليوم بإذن الله ببناء قائمة أخرى مُطَعَّمة بتأثيرات fancy hover‏. وسأعتمد التصميم ‏المُسطح الشائع مُستخدمًا الألوان الزاهية والأيقونات الرائعة، وأُطبّق تقنيات CSS‏ المتعددة، وبذلك يُصبح هذا الدرسُ ‏مقالةً رائعةً لمصممي الويب.‏ مفهوم القائمةقبل أن نبدأ بأي تنسيقات، سنقوم بإنشاء الهيكل الأساسي للقائمة بـ HTML‏. هناك عناصر جديدة في HTML5‎‏ مثل ‏nav‏ مُتاحة هذه الأيام، حتى أنها تعمل على إنترنت إكسبلورر بمساعدة بعض الإضافات مثل ‏ html5shiv. ‏ <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Flat Nav</title> <link href="style.css" rel="stylesheet" /> <link href='http://fonts.googleapis.com/css?family=Dosis' rel='stylesheet' type='text/css'> </head> <body> <div id="demo"> <nav> <ul> <li> <a href="#"> <span>Home</span> </a> </li> <li> <a href="#"> <span>About</span> </a> </li> <li> <a href="#"> <span>Portfolio</span> </a> </li> <li> <a href="#"> <span>Contact</span> </a> </li> </ul> </nav> </div> </body> </html>يبدأ الكود بتعريف ‏HTML5‎‏ كمرجعية للصفحة عن طريق <doctype html!>‏، عنوان الصفحة ‏‏title‏‏ ورابط ‏ملف CSS‏ ‏الذي سنقوم بإنشائه بعد ذلك ‏‏link>‎>. يأتي بعد ذلك ربط صفحة الويب بخط ‏ ‏Dosis‏‏ من ‏‏‏Google Webfonts‏. تبدأ ‏البُنية الرئيسة في القائمة بعنصر ‏‏nav‏، تكون داخله قائمة ‏ul‏. وكل ‏عنصر داخل ‏ul‏ يحتوي على رابط مرفقًا معه ‏عنصر ‏‎span‎‏ لإظهار تسمية الزر على يمين القائمة عند ‏مرور الماوس عليه.‏ تنسيقات CSSnav ul { list-style: none; overflow: hidden; position: relative; } nav ul li { float: left; margin: 0 20px 0 0; }نبدأ بعمل تنسيق ‏CSS‏ بأن نُزيل رمز النقطة من أمام كل عنصر من عناصر القائمة ‏ul‏، نجعل تلك العناصر ‏‏‏li‏ بجوار بعضها البعض. نضع تعريف التنسيق ‏overflow: hidden‏ للقائمة ‏ul‏ لتظهر تسميات ‏الأزرار متناسقة وغير متداخلة، من ثم وضع التنسيق الخاص بتسميات الأزرار ‏span‏ حتى تكون كلٌ منها ‏متمركزة في مكانها الصحيح على ‏li‏ الخاص بها ضمن القائمة ‏ul‏ الأب. nav ul li a { display: block; width: 120px; height: 120px; background-image: url(icons.png); background-repeat: no-repeat; } nav ul li:nth-child(1) a { background-color: #5bb2fc; background-position: 28px 28px; } nav ul li:nth-child(2) a { background-color: #58ebd3; background-position: 28px -96px; } nav ul li:nth-child(3) a { background-color: #ffa659; background-position: 28px -222px; } nav ul li:nth-child(4) a { background-color: #ff7a85; background-position: 28px -342px; }كل نقطة من نقاط القائمة الأربعة يتم تنسيقها لتظهر كمربع بعد إضافة الطول والعرض لها وهو ‏‎120px‎‏، قابلة ‏للتحول من تنسيق ‏inline‏ إلى تنسيق ‏block‏ باستخدام ‏display: block;‎‏. يتم تصدير جميع الأيقونات ‏من الفوتوشوب في صورة ‏sprite‏ واحدة تحوي خلفيات الصور الأربعة في نفس الملف، لذا فإن ملف ‏‏‏icons.png‏ يُعتبر صورة خلفية لجميع نقاط القائمة باستخدام مُحدِّد ‏nav ul li‏. حيث تقوم بتحديد موقع ‏الخلفية من داخل الملف الواحد حسب تموضعها فيه.‏ يُمكنك إضافة تنسيق مُحدَّد لكل نقطة من النقاط الأربعة على حِدة باستخدام مُحدِّد ‏li :nth-child‏. حيث تستطيع ‏أن تضيف ‏classes‏ لكل عنصر لوحده من عناصر القائمة ‏li‏ حسب رقم هذا العنصر. بالضبط كما حددنا لون ‏الخلفية لكل عنصر منفردًا.‏ nav ul li a span { font: 50px "Dosis", sans-serif; text-transform: uppercase; position: absolute; left: 580px; top: 29px; display: none; }نأتي الآن إلى ضبط موقع تسمية النص لعناصر القائمة، بتطبيق حدث التنسيق ‏on hover‏ لجميع العناصر مرة ‏واحدة، وذلك على ‏span‏ التي أضفناها لكل عنصر من عناصر القائمة. أولاً: نقوم بإضافة خصائص الخط ‏‏‏Dosis‏، وهي حجم الخط، و‏uppercase‏ (تحويل الحروف الصغيرة إلى حروف كبيرة) باستخدام خاصية ‏‏‏text-transform‏. ‏ افتراضيًا، فإن كل تسمية عنصر تتموضع في الزاوية العلوية يسارًا على بلوك عنصر القائمة، ولكننا نريدها أن تكون ‏على يمين القائمة ‏ul‏ خارج إطار العناصر. ببساطة، نضيف خاصية الموضع ‏position: absolute;‎‏ لعمل ‏ذلك. قمنا قبل ذلك بوضع الخاصية ‏position: relative;‎‏ إلى ‏nav ul‏ حتى يكون التموضع الحر مرتبطًا ‏بالقائمة ‏ul‏ (الأب)، عدا عن كونها مرتبطة بالعرض الكامل لشاشة المتصفح.‏ nav ul li a:hover span { display: block; } nav ul li:nth-child(1) a span { color: #5bb2fc; } nav ul li:nth-child(2) a span { color: #58ebd3; } nav ul li:nth-child(3) a span { color: #ffa659; } nav ul li:nth-child(4) a span { color: #ff7a85; }نرى الآن جميع تسميات العناصر ظاهرة فوق بعضها البعض في نفس الوقت، لذا سنقوم بإخفائها باستخدام الخاصية ‏‏‏display:none;‎‏ حتى لا تظهر أي من التسميات إلا حين يمر الماوس فوق عنصرها فقط، بإضافة ‏‏‏display:block;‎‏ إلى حدث التنسيق ‏on hover‏ الخاص بكل عنصر. بقي أن نُعطيَ كلَّ تسمية عنصر لونها ‏الخاص بها والمطابق للون خلفية عنصرها، هذا الأمر يتم في مُحدِّد ‏‎:nth-child‏ لكل عنصر على حدة.‏ النص الكامل لملف CSS‏، بعد أن انتهينا من الخطوات جميعها، سوف يصبح لدينا ملف CSS‏ جاهزًا كما يلي، بإمكانك نسخه من هنا: nav ul { list-style: none; overflow: hidden; position: relative; } nav ul li { float: left; margin: 0 20px 0 0; } nav ul li a { display: block; width: 120px; height: 120px; background-image: url(icons.png); background-repeat: no-repeat; } nav ul li:nth-child(1) a { background-color: #5bb2fc; background-position: 28px 28px; } nav ul li:nth-child(2) a { background-color: #58ebd3; background-position: 28px -96px; } nav ul li:nth-child(3) a { background-color: #ffa659; background-position: 28px -222px; } nav ul li:nth-child(4) a { background-color: #ff7a85; background-position: 28px -342px; } nav ul li a span { font: 50px "Dosis", sans-serif; text-transform: uppercase; position: absolute; left: 580px; top: 29px; display: none; } nav ul li a:hover span { display: block; } nav ul li:nth-child(1) a span { color: #5bb2fc; } nav ul li:nth-child(2) a span { color: #58ebd3; } nav ul li:nth-child(3) a span { color: #ffa659; } nav ul li:nth-child(4) a span { color: #ff7a85; } التصميم النهائي لقائمتنا ذات السِمة المسطحة: يمكن معاينة مثال حي عن الدرس، أو تصفح ملفات العمل الخاصة بالدرس. ترجمة وبتصرف للمقال: How To Create a Trendy Flat Style Nav Menu in CSS.