Bian Alabras

الأعضاء
  • المساهمات

    22
  • تاريخ الانضمام

  • تاريخ آخر زيارة

السُّمعة بالموقع

10 جيدة

1 متابع

  1. إن العديد من حزم بايثون تساعد مطوري جانغو Django على العمل بسرعة وفعالية أكبر، حيث أن مكتبات Django هي المفضلة لدى أغلب المطورين فهي توفر الوقت وتقلل من عملية البرمجة وبالتالي تبسط عملهم. سنتكلم في هذا المثال عن ستة حزم لبرامج Django واثنين لهيكلية Django REST، حيث تظهر هذه الحزم في غالبية المشاريع التي نعمل عليها. إضافة جانغو الموفرة للوقت: Django-extensions تعتبر Django-extensions من الحزم المليئة بالأدوات المفيدة مثل أوامر الإدارة التالية: shell_plus: تفتح Django shell مع جميع النماذج الخاصة بقاعدة البيانات والمُحمَّلة بشكل مُسبق، وبالتالي لاتحتاج إلى عمل أي استيراد من التطبيقات المختلفة والتي تحتاجها لعمل اختبار لعلاقة معقدة clean_pyc: تعمل على محي جميع المشاريع ذات اللاحقة .pyc في جميع الأماكن التي تظهر بها داخل مسار المشروع. create_tamplate_tags: تنشئ بنية مجلد خاص بالقوالب داخل التطبيق الذي تحدده. describe_form: تظهر تعريف الواجهة للنموذج ويمكن نسخه فيما بعد إلى forms.py (ولكن من المهم الانتباه إلى أن هذا الأمر ينشئ واجهة جانغو عادية وليست من نوع ModelForm). notes: يظهر هذا الأمر جميع التعليقات من نوع TODO أو FIXME وغيرها خلال المشروع. كما تحوي حزمة Django-extensions على أصناف مجردة مفيدة تستخدم للنماذج الشائعة، حيث تستطيع توريث هذه الأصناف الأساسية عند إنشاء نماذج خاصة بك: TimeStampeModel: يحوي هذه الصنف على حقول created و modified بالإضافة للتابع ()save الذي يحدث هذه الحقول تلقائيًّا. ActivatorModel: في حال كان النموذج الخاص بك يحتاج إلى حقول مثل activate_state، status و deactivate_date عندها يمكنك استخدام هذا الصنف حيث أنه يحوي أيضًا على مدير يوفر لك استعلامات ()active. و ()inactive. TitleSlugDescriptionModel و TitleDescriptionModel: يحوي الصنفان على حقول title، description كما يحوي الصنف الأول على حقل إضافي هو slug الذي يُحدَّث تلقائيًّا بالاعتماد على حقل title. حزمة Django-environ تتيح Django-environ تطبيق المنهجية 12-factor app لإدارة إعداداتك في مشروع جانغو الذي تنشئه، وتجمع هذه الحزمة مكتبات أخرى مثل envparse و honcho والآن وبعد تحميلك لحزمة Django-environ تستطيع إنشاء ملف .env في جذر مشروعك وتعرف في داخله متغيرات الإعدادات التي من الممكن أن تتغير بين البيئات أو التي يجب أن تبقى سرية مثل مفاتيح API أو حالة Debug أو عناوين قاعدة البيانات. بعد ذلك في الملف الخاص بإعدادات المشروع setting.py يمكنك عمل استيراد للبيئة ثم ضبط المتغيرات المتعلقة بـ ()environ.PATH و ()environ.Env كما ورد في المثال. مع العلم أنه يمكن الوصول إلى المتغيرات المُعرّفة في ملف .env باستخدام (’env(‘VARIABLE_NAME إنشاء أوامر الإدارة: Django-click أُنشِئت حزمة Django-click بالاعتماد على حزمة click وهي تساعدك لكتابة أوامر الإدارة جانغو، لكن لاتمتلك هذه الحزمة الكثير من التوثيقات وإنما لديها مجلد من الأوامر التجريبية test commands. لنتعرف على طريقة كتابة الأمر Hello World باستخدام هذه الحزمة: # app_name.management.commands.hello.py import djclick as click @click.command() @click.argument('name') def command(name): click.secho(f'Hello, {name}') ثم نكتب سطر التشغيل الذي يعطي أمر التنفيذ: >> ./manage.py hello Lacey Hello, Lacey التعامل مع آلة ذات حالات منتهية : Django-fsm تضيف حزمة Django-fsm الدعم لحالة الآلات ذات حالات منتهية للنموذج الموجود بالجانغو الخاص بمشروعك، ففي حال كنت تنفذ موقع ويب أخباري و تحتاج إلى مقالات تمر بحالات states مثل الكتابة، التحرير، والنشر، هنا يأتي دور حزمة Django-fsm التي تساعد في تعريف هذه الحالات وإدارة القواعد التي تحكُم الانتقال من حالة إلى أخرى. توفر Django-fsm مايدعى FSMField والذي يستخدم كصفة التي تعرف حالة النموذج وبعد ذلك تستطيع استخدام transition@ من هذه الحزمة من أجل تعريف التوابع التي تحرك النموذج من حالة إلى أخرى وللتعامل مع أي تأثيرات جانبية لذلك الانتقال. وعلى الرغم من أن حزمة Django-fsm لا تملك توثيقات التي تعرفها بشكل جيد ولكن تحوي workflows(states) in Django الذي يمكن أن يعتبر مقدمة جيدة لتعريف مفهومي آلة ذات حالات منتهية وحزمة django-fsm. استمارات التواصل: #django-contact-form إن استمارات التواصل أمر أساسي يتواجد في مواقع الويب، ولكن لا داع لتكتب ترميزها بنفسك إذ يمكن إنشاؤها بدقائق من خلال استخدام الحزمة django-contact-form والتي يأتي معه صنف اختياري spam-filtering contact form (و صنف نظامي، و non-filtering) بالإضافة إلى صنف ContactFormView الذي يحوي على توابع يمكن تخصيصها وإعادة كتابتها وذلك لإنشاء النموذج الخاص بك. تسجيل واستيثاق المستخدمين: django-allauth إن Django-allauth هو برنامج يوفر الواجهات، والروابط لتسجيل المستخدمين، ولتسجيل الدخول والخروج، وإعادة تعيين كلمات السر و استيثاق المستخدمين مع مواقع خارجية مثل GitHub و Twitter. كما أنه يدعم الاستيثاق عن طريق email-as-username وهو مُوثَّق بشكل كبير، لأنه قد يكون مربك قليلًا التعامل معه لأول مرة لذلك يمكنك اتباع تعليمات التثبيت (installation instructions)بحرص، ومن الممكن أن تفيدك أيضًا مقالة تخصيص إعداداتك (customize your setting) للتأكد من أنك قد استخدمت كل الإعدادات التي تحتاجها لتفعيل ميزة محددة. التعامل مع استيثاق المستخدمين ضمن هيكيلية REST Framework: django-rest-auth في حال كان عملك كمطور جانغو يتطلب منك كتابة تطبيقات APIs فإنك على الأرجح تستخدم هيكلية Django REST Framework (DRF)، وعلى الأغلب تعاملت مع حزمة django-rest-auth وهي حزمة تمكننا من تسجيل المستخدمين، تسجيل عمليات الدخول والخروج، إعادة تعيين كلمات المرور، استيثاق وسائل التواصل الاجتماعي (وذلك من خلال إضافة django-allauth والتي تعمل بشكل جيد مع django-rest-auth). تطبيقات الإظهار ضمن هيكلية Django REST Framework API: django-rest-swagger يوفر Django REST Swagger واجهة تعامل بالميزات للتفاعل مع Django REST Framework API، فبعد تثبيت Django REST Swagger وإضافته للبرنامج المثبت مسبقًا يمكنك إضافة Swagger View بالإضافة إلى نمط عنوان URL pattern للملف ruls.py الخاص بـواجهتك البرمجية. تتضمن واجهة المستخدم الخاصة بتطبيقك كل نقط النهاية والتوابع المتاحة مصنفة حسب التطبيق، كما تظهر أيضًا قائمة بالعمليات المتاحة لنقط النهاية هذه والتي تسمح لك بالتفاعل مع الواجهة البرمجية (عمليات الإضافة، الحذف، جلب السجلات على سبيل المثال) كما يمكن توليد توثيق لكل نقطة نهاية وبالتالي سينتج لدينا مجموعة مستندات توثيق خاصة بالمشروع والتي تفيدك كمطور وتفيد مطوري الواجهات الأمامية والمستخدمين. ترجمة –وبتصرّف– للمقال 8 Python packages that will simplify your life with Django لصاحبيه Lacey Williams Henschel و Jeff Triplett
  2. سنتكلم في هذا المقال عن أفضل مكاتب التحريك في ‏CSS، والتي تساعدك في حفظ وقتك عند تصميم ‏موقع الويب الخاص بك من خلال استخدامك لهذه المكاتب التي تضفي بعض الديناميكية والتفاعل إلى ‏عملك، حيث تحوي هذه المكتبات على العديد من عناصر ‏التحريك والمؤثرات لأي نوع من أنواع عناصر صفحات الويب كالنصوص، الصور، الإطارات، الأزرار، والبطاقات والعديد غيرها. إن لمكاتب التحريك في CSS العديد من الأنماط styles والتي تستخدم تأثيرات خاصة بها (يمكن الإطلاع على المزيد في CSS effects)، وبالتالي مهما كان النمط الذي تبحث عنه سواء بسيط وسهل، أو ممتع مؤثر ستجد مكتبة مفيد لك. تتنوع المؤثرات من المنزلقات إلى التلاش والدوران كما يوجد العديد من التأثيرات الأكثر عمقًا وتخصيصًا مثل مؤثرات النبض، ودقات القلب وغيرها... باختصار هذه المكتبات توفر لك ماتريده، ويمكن تثبيتها بسهولة داخل موقعك كما أن غالبيتها خفيفة الحجم ومُتجاوبة responsive. Hexa تعتبر Hexa مكتبة مذهلة للتحريك، بسيطة وخفيفة الحجم. يمكن استخدام مؤثرات التحريك التي تقدمها لإحياء عناصر صفحة الويب من نصوص، صور، أيقونات وبقية العناصر الأخرى. Vivify إن Vivify هي مكتبة تحريك CSS مجانية تستطيع من خلالها إضافة المؤثرات إلى الأزرار، الصور وغيرها، وتحوي على العديد من المؤثرات التي يمكن الاختيار فيما بينها مثل الكرة، النبض، الاصطدام من اليمين واليسار، الاهتزاز، الانفلات، القفز، التلاشي، الدوران الشاقولي، السحب، الطي... Tachyons-animate تعتبر Tachyons-animate مجموعة من صفوف CSS ذات الهدف المحدد وهو تنسيق الحركة باستخدام CSS. في حال كنت تريد الحصول على أكثر من مجرد تحريك فباستطاعتك استخدام هذه الأداة لأنها لاتحوي على أي مؤثرات مُحددة مسبقًا وإنما تحوي على صفوف CSS فقط تستخدمها ضمن موقعك تبني من خلالها الحركة التي ترغب بها ولكنها توفر عليك الكثير من الوقت. ‏Infinite – useful CSS animations تعتبر Infinite-useful CSS animations مجموعة صغيرة من عناصر التحريك المفيدة للغاية والتي يمكن استخدامها لتوليد عناصر نابضة بطريقة التحكم بشفافية ومقدار التوسع في العناصر، إنشاء رسوم متحركة للتنبيه، تدوير العناصر وغيرها. Motion UI تعتبر Motion UI مكتبة Sass قوية تفيد في إنشاء التحريكات والانتقالات، وقد أوجدت هذه المكتبة بالأساس كمكون ضمني في Foundation لتصميم البرامج، ولكن يمكن استخدامها كمكتبة منفصلة الآن، حيث يمكن استخدامها في إضافة تأثيرات السرعة، الانزلاق، تغيير الحجم وغيرها.. DynCSS إن DynCSS هي مكتبة عظيمة تستخدم في التحريك والتي تقوم بتفسير الـ CSS الخاص بك من أجل إنشاء قواعد dyn بسيطة وديناميكية تبث الحياة في موقعك. All animation تعتبر All animation.css أداة تحوي العديد من عناصر التحريك الممتعة لجعل مشروعك أكثر تفاعلية وجاذبية بالنسبة للمستخدم، وهي عبارة عن عدة مؤثرات وعناصر تحريك ممكن استخدامها لإضافة تأثيرات ‏‎3D‎‏ وغيرها. Animate.CSS إن Animate.CSS هي مكتبة تحريك أخرى تعمل على جميع المتصفحات وتُستخدم لإضافة مؤثرات مثل الاهتزاز، ‏الاضطراب، النبض، التأرجح، التمايل..‏ Hover.CSS تعتبر Hover.CSS مجموعة CSS3 خاصة بتأثيرات التأرجح hover effects وتُستخدم على الروابط ، الأزرار، الشعارات، SVG، الصور وغيرها من العناصر. كما أنها متوفرة كـ CSS، Sass، و LESS. Effect.CSS تعتبر مكتبة Effect.CSS مكتبة CSS تُستخدم لإضاف انتقالات حساسة للموقع بهدف تحسين تجربة المستخدمين، حيث تقوم بإنشاء انتقالات CSS وتحريكات للأزرار، القوائم، الانتقالات بين الصفحات، العناوين، التلميحات، علامات التبويب، الإطارات وغيرها... ‎Magic CSS تحوي Magic CSS على عناصر تحريك CSS3 بمؤثرات خاصة تضفي روحًا خاصة للموقع مثل المؤثرات السحرية، المؤثؤات الساكنة، المناظير، الدوران، التحجيم، الانزلاق.. CSSHAKE إن CSSHAKE هي مجموعة من صفوف CSS لتحريك عناصر الصفحة DOM، وتثبيت هذه المكتبة سهل للغاية حيث يمكن تنزيل ملفات منفصلة لكل تأثير تحريك، توسيع، أو تصغير. Repaintless CSS تعتبر Repaintless CSS مكتبة CSS خاصة بالتحريك خفيفة الحجم تحوي عشرات المؤثرات التي تجعل الموقع أكثر فاعلية بدون التأثير على سرعته، حيث يمكنك إضافة مؤثرات الانزلاق بكل سهولة. Tuesday إن Tuesday هي مكتبة CSS منفصلة تستخدم لإضافة تأثيرات الدخول والخروج مثل التمدد والتقلص، الهبوط، نزول المطر، المفصلات. Obnoxious تحوي Obnoxious تأثيرات ممتعة ولكن قد لاتناسب جميع المواقع ولكن بالتأكيد في حال استخدامها ستجعل موقعك يبرز بين المواقع الأخرى. من هذه المؤثرات الاهتزاز، زيادة الحجم، الطي، الالتواء... Flutter – CSS Image Hover Effects & Lightbox تحوي مكتبة Flutter على 12 مؤثر تحريك خاص بالتأرجح hover فوق الصور، أنظمة الأعمدة الثنائية أو الثلاثية، بالإضافة إلى 4 أنواع من الإضاءة وغيرها. Skloading – CSS3 Animations تعتبر Skloading مكتبة تحريك CSS3 بتصميم قابل للتخصيص بشكل كامل حيث يمكنك تحميل واستخدام هذه التحريكات الخاصة بهذه المجموعة ضمن مشروعك. Viavi CSS3 Scroll Effects يمكنك من خلال Viaviتجربة مجموعة المغازل ‏spinners‏ والمحملات ‏loaders‏ التي يمكن استخدامها لجذب انتباه ‏المستخدم، وهي تصميمات مُتجاوبة بشكل كامل كما أنها تتكيف بشكل رائع مع أي حجم للشاشة. CSS Bubbles and Tooltips إن هذه المكتبة ( يمكن الإطلاع على المزيد من خلال CSS Bubbles and Tooltips) تقدم لك التلميحات والفقاعات في CSS والتي ستساعدك في حفظ الوقت، حيث ستجد مجموعة من أشكال الملاحظات جاهزة على شكل مربع أو غيوم أو فقاعات على شكل قلب وكذلك تلمحيات للموقع، النصوص، الواجهات، الشرائط والأزرار. ‎ Animatia – CSS Image Hover Effects إن Animatia هي مكتبة خاصة بتأثيرات التأرجح hover التي تحدث للصور أو عناصر CSS أخرى مثل الأزرار وغيرها. CSS Box and Text Shadows يمكنك تنزيل صناديق CSS و ظلال النصوص (للإطلاع CSS Box and Text Shadows) واستخدامها مباشرة في موقعك وهي عبارة عن مجموعة فيها 40 تأثير ظلال يمكن تطبيقها على النصوص، الأزرار، الصور وغيرها. HoverMe تعد HoverMe مجموعة رائعة من عناصر تحريك CSS3 التي تحوي 4 صور أزرار، 10 أزرار تحميل، و 8 تحريكات متقدمة وغيرها. ‎Wickedcss إن Wickedcss هي مكتبة تحريك CSS3 تمنحك المتعة في التعامل معها فهي تحوي العديد من المؤثرات مثل التقافز pound، الانزلاق، النبض، دقات القلب، الدوران Woah إن مكتبة Woah هي لمطوري الويب غريبي الأطوار، حيث يمكن استخدامها لإضافة تحريكات مميزة لأي عنصر من عناصر صفحة الويب. Motion CSS تعتبر Motion CSS مكتبة تحريك ممتعة وسهلة التثبيت فلا تحتاج لاستخدامها سوى إلى ربطها مع ملف CSS ثم يمكنك استخدام صفوف محددة لأي عنصر من عناصر الصفحة لتحريكه. ترجمة -وبتصرّف- للمقال 25 Best CSS Animation Libraries لصاحبه Iggy مصمم موقع Line25
  3. إن الحصول على المواضيع من قاعدة بيانات الوورد بريس هي إحدى المواضيع المفضلة لدي، وذلك بسبب المرونة الرائعة التي يتيحها صنف WP_Query التي تسمح لنا بالحصول على مانريده، وتُعتبر هذه الطريقة هي إحدى الميزات الأساسية في أنظمة إدارة المحتوى CMS التي نراها عند العمل في الواجهات الأمامية front-end. قد تظهر أمامنا العديد من الحالات التي نحتاج فيها إلى معلومات محددة مثل قائمة بالمهام المدخلة من قبل مستخدم محدد بين تاريخين معينين، أو قائمة بكل المواضيع والصفحات المُدرجة ضمن تصنيف معيّن، والتي أُدخِل عليها تعديلات خلال أقل من أسبوعين. يمكن الحصول على جميع هذه المعلومات باستخدام صنف WP_Query، والذي يمتاز بسهولة استخدام وتطبيقه، حيث سنشرح في هذا المقال طريقة استخدامه وسندرج أيضًا العديد من الأمثلة البرمجية العملية. ماهو استعلام الوورد بريس؟ لنستذكر أولًا بعض المعلومات العامة المتعلقة بالاستعلامات queries، حيث يمكن تعريف الاستعلام من قاعدة البيانات بأنه السؤال عن بعض المعلومات الموجودة ضمنها، والتي يمكن أن تكون أي معلومة ابتداءًا من أرقام الهواتف لجميع المستخدمين ومرورًا بكل التصنيفات الأخرى المتاحة. كما ننوّه إلى أن ذكرنا لكلمة استعلام query، أو استعلام وورد بريس wordpress query نقصد بها الاستعلام الذي يقوم باسترجاع بعض المواضيع posts من قاعدة البيانات الخاصة بالوورد بريس. تقوم كل صفحات الوورد بريس تقريبًا بإنشاء الاستعلام الخاص بها، فصحة الفهرسة (وهي الصفحة الرئيسية التي تظهر في المدونة) هي عبارة عن إظهار نتيجة الاستعلام عن آخر المواضيع المنشورة، كما أن فئة الأرشيف تحوي على استعلام يقوم باستعادة أحدث المشاركات داخل هذه الفئة وتُظهر النتيجة ضمن الفئة المحددة، أما بالنسبة لصفحات المؤلف فإنه تُظهر آخر المواضيع التي نشرها المؤلف. كما أن المنشورات الوحيدة والصفحات المفردة تستخدم استعلامات خاصة بها، ولكن في هذه الحالة تستعاد دومًا نتيجة واحدة فقط من خلال الاستعلام. وبالتالي نجد أن أي صفحة افتراضية نُحمّلها تحوي في الغالب على استعلام يُجرى تلقائيًا. الحلقة: كيفية استخدام الاستعلامات تُعتبر الحلقة طريقة لاستعادة مجموعة من المواضيع معًا، ولكن المسألة الأساسية هنا هي كيفية عرض المواضيع المُسترجعة، حيث أن كل موضوع يستخدم عناصر الإظهار و HTML الخاصة به، مع أنها جميعها تشترك في حلقة واحدة. ببساطة تقوم الحلقة باستخدام آلية خاصة في الوورد بريس تُمكّنها من الانتقال عبر المواضيع المُسترجعة مع تمكين الموضوع من عرضه بطريقته الخاصة. يمكن شرح هذه الآلية من خلال الأسطر البرمجية: <?php if ( have_posts() ) : ?> <?php while ( have_posts() ) : the_post() ?> <div <?php post_class() ?>> <h2><?php the_title() ?></h2> <?php the_content() ?> </div> <?php endwhile ?> <?php else : ?> <h2>Ooops, no posts here!</h2> <?php endif ?> إن المثال السابق هو مثال مبسط عمّا يمكن إيجاده في أي صفحة تحوي قوائم يوجد داخلها مجموعة من المواضيع، حيث نلاحظ في المثال أن الحلقة تحوي أيضًا على تعليمة if/else . حيث قمنا باستخدام ()have_post للتأكد من أنه في حالة وجود موضوع مُستعاد، نُنشئ عنصر من هذا الموضوع ويكون الخرج عبارة عن العنوان ومحتوى الموضوع. أما تابع ()the_post فإنه هو المسؤول عن معالجة حالة "هل يوجد مواضيع أخرى بعد الموضوع الحال أم لا". وفي النهاية عند المرور على جميع المواضيع، تُنفّذ تعليمة else وهي إظهار نص محدد قد يشير كما في المثال السابق إلى انتهاء المواضيع. الآن قبل المتابعة في هذه المقالة، أود توضيح بعض المصطلحات التي مرت معنا، أولا مصطلح الحلقة "the loop" والذي يستخدم لوصف حلقة تمر على قائمة مو المواضيع الموجودة في الصفحة والمنشورة على الوورد بريس، لنستعرض مثالًا توضيحيًا، في حال كان لدينا قسم في المدونة نستعرض فيه المواضيع المتعلقة بمدونتنا والتي نستعرضها في قسم "Related articles on our blog" إن هذا القسم عبارة عن مجموعة من قوائم المواضيع التي تم الحصول عليها من خلال استخدام استعلام مخصص وحلقة خاصة بها، ولكن لايمكننا اعتبار هذه القوائم تشبه القوائم التي نتجت عن الاستعلام السابق (في المثال البرمجي) وذلك لأنه العناصر الموجودة داخل هذا القسم ليست إحدى القوائم الافتراضية الخاصة بهذه الصفحة، إنما هي عبارة عن ناتج عدة استعلامات وحلقات وُضعت في نفس الصفحة . بناء استعلام مخصص سنتعلم في هذه الفقرة طريقة بناء استعلام مخصص، لنفترض أننا نريد الحصول على قائمة بالمواضيع الخاصة بمؤلف معين ومُدرجة ضمن فئة معينة. هذا المثال يطرح طريقة برمجية للوصول إليها: <?php $args = array( 'author_name' => 'danielpataki', 'category_name' => 'wordpress' ); $author_posts = new WP_Query( $args ); if ( $author_posts->have_posts() ) : ?> <?php while ( $author_posts->have_posts() ) : $author_posts->the_post() ?> <div <?php post_class() ?>> <h2><?php the_title() ?></h2> <?php the_content() ?> </div> <?php endwhile ?> <?php else : ?> <h2>Ooops, no posts here!</h2> <?php endif ?> يتعامل القسم الأول من التعليمات مع الاستعلام في قاعدة البيانات حيث ترى أنني قمت ببناء غرض WP_Query من خلال تمرير مجموعة من المعاملات arguments، ولكن لاتقلق في حال لست من المهتمين بالبرمجة غرضية التوجه لأننا سنستخدمها بطريقة بسيطة جدًا، حيث مررنا اسم المؤلف من خلال المعامل author_name والفئة المختارة من خلال المعامل category_name. الآن لترتيب هذه المواضيع ضمن قائمة واحدة نحتاج إلى تعديل الحلقة قليلًا وذلك من خلال استخدام التوابع الخاصة بالغرض WP_Query بدلًا من استخدام التوابع ()have_post و ()the_post وذلك يعني أن نسبق اسم التابع باسم المتغير بالشكل ()variable_name->function$. مع العلم أنني لن أعرض الحلقة في الأمثلة التالية وذلك لأن الفكرة نفسها ستتكرر دومًا، والآن اسم متغيرنا هو $custom_post لذلك سنستخدم التوابع ()custom_posts->have_post$ و ()custom_posts->the_post$. معاملات WP_Query إن بناء استعلام مخصص يعتمد بالأساس على تغيير المعاملات parameters، ومن الأمثلة على الاستعلامات المخصصة استعلام الحصول على المواضيع من تاريخ محدد، المؤلفين، نمط نشر معين، تصنيفات مخصصة، وحالات وأمور أخرى. الآن لنلق نظرة على جميع المعاملات من خلال بعض الأمثلة الأساسية البسيطة. معامل المؤلف Author Parameters يوجد أربع طرق مختلفة يمكن استخدامها لتجميع المؤلفين، حيث أن المعامل author يقبل رقم تعريفي وحيد لمؤلف محدد، أو قائمة من الأرقام المعرفة للمؤلفين مفصولة عن بعضها بفاصلة، كما يمكن استخدام السالب أمام الأرقام التعريفية للمؤلفين لاستبعادها من ناتج الاستعلام: // Posts from the author with the ID of 42 $args = array( 'author' => 42 ); // All posts except the ones from author 42 $args = array( 'author' => -42 ); // Posts from author 42, 38 and 55 $args = array( 'author' => '42,38,55' ); // Posts from everyone except author 42 and 38 $args = array( 'author' => '-42,-38' ); يمكن استخدام المعامل author_name في حال كنا نريد تمرير اسم المؤلف بدلًا من رقمه التعريفي، ولكن من المهم الانتباه إلى استخدام اسم المستخدم username الصحيح. $args = array( 'author_name' => 'danielpataki' ); المعاملان المتبقيان والمتعلقان بالمؤلف هما author_in و author_not_in حيث أُضيف هذين المعاملين في النسخ اللاحقة من الوورد بريس في 3.7 حيث تعرّف المؤلفين من خلال إدخال أرقام المؤلفين على شكل مصفوفة. // Posts from author 42, 38 and 55 $args = array( 'author__in' => array( 42, 38, 55 ) ); // Posts from everyone except author 42 and 38 $args = array( 'author__not_in' => array( 42, 38 ) ); إن هذه الطريقة لاتبدو مختلفة كثيرا عن استخدام المعامل author ولكن هذه الطريقة أفضل من حيث الطريقة التنظيمية. حالة استخدام: إعداد قائمة بمقالات المؤلفين الأعلى تصنيفًا لنلق نظرة على كيفية بناء قائمة بمقالات المؤلفين الأعلى تصنيفًا. حيث نفترض أن لدينا نظام تصنيف في مكان ما يُخزن فيه التقييم العالمي لكل مؤلف ضمن جدول أساسي مع معرف خاص به هو التصنيف rating، ونستيطع الحصول على المطلوب من خلال ‏التابع ()get_user الذي يحوي حلقة مخصصة لجمع مشاركات المؤلفين الأعلى تصنيفًا وإعادتها كخرج للتابع المذكور. <?php $args = array( 'meta_key' => 'rating', 'meta_value' => 4, 'meta_compare' => '>', 'fields' => 'id', ); $top_users = get_users( $args ); $args = array( 'author__in' => $top_users; ); $top_author_posts = new WP_Query( $args ); ?> معامل الفئة Category Parameters يمكن تقييد إضافة المواضيع على أساس الفئة باستخدام ما لا يقل عن خمس توابع منفصلة. إن المعامل cat يشبه المعامل author الذي شرحناه بالأعلى، من حيث إسناد الأرقام الصحيحة والتي تستخدم كمفتاح تعريف للفئة، كما يمكن إضافة إشارة السالب (-) من أجل استبعاد الفئة التي تُعرّف بالرقم الصحيح الذي يلي الإشارة. // Posts from the category with the ID of 42 $args = array( 'cat' => 42 ); // All posts except the ones from category 42 $args = array( 'cat' => -42 ); // Posts from categories 42, 38 and 55 $args = array( 'cat' => '42,38,55' ); // Posts from all categories except 42 and 38 $args = array( 'cat' => '-42,-38' ); من الأفضل عدم استخدام category_name وذلك لأن اسم الفئة غالبًا يستخدم في توليد رابط URL الخاص بالمقالة، من خلال استخدام اسم الفئة وعنوان المقالة مع بعض التعديلات الخاصة مثل تحويل جميع الأحرف إلى أحرف صغيرة، وحذف جميع الكلمات الخاصة، واستبدال الفراغات بـ (-)، فمثلًا الفئة المخصصة لمراجعات الكتب والمسماة "Book Reviews" غالبًا ستجد قسم من رابط المقالة يحوي على "book-reviews". $args = array( 'category_name' => 'book-reviews' ); كما يمكننا استخدام مصفوفة من الفئات تحوي ثلاثة معاملات هي category_in، و category_not_in والتي تماثل في طريقة عملها مماثلاتها في معامل author، حيث يمكن من خلالها التأكد من أن المواضيع التي تم تحديدها من خلال الفئات المدخلة فقط هي التي استُرجِعت بواسطة الاستعلام. $args = array( 's' => 'awesome+wordpress+plugins', 'posts_per_page' => 8 ); $results = new WP_Query( $args ); echo "A total of " . $results->found_posts . " posts were found.<br />"; echo "We will be displaying " . $results->query_vars['posts_per_page'] . " posts per page if possible.<br />"; echo "We need a total of " . $results->max_num_pages . " pages to display the results". حالة استخدام: الحصول على مقالات الفئات الأكثر استخدمًا نقصد بالفئات الأكثر استخدمًا هي الفئات التي تحوي أكبر عدد من المواضيع المنشورة، وسنقوم بهذه العملية من خلال استرجاع قائمة بالفئات اعتمادًا على عدد العناصر (المواضيع) الموجودة داخل الفئة، ثم نقوم بتمرير هذه المعلومة للاستعلام الذي نبنيه للحصول على المواضيع المنشورة في الفئات الثلاثة الأعلى استخدامًا. $args = array( 'number' => 5, 'orderby' => 'count', 'order' => 'DESC', 'fields' => 'ids' ); $top_categories = get_terms( array( 'category' ), $args ); $args = array( 'category__in' => $top_categories; ); $top_category_posts = new WP_Query( $args ); معامل الوسم Tag Parameters يمرر لمعامل الوسم خمس متغيرات هي tag، tag_id، tag_and، tag_in، tag_not_in نلاحظ وجود ثلاث منها ذُكرت مع المعاملات السابقة، وسنشرحها من خلال المثال: // Posts from the tag named "Awesome Colors" $args = array( 'tag' => 'awesome-colors' ); // Posts from the tag named "Awesome Colors" or "Awesome People" $args = array( 'tag' => 'awesome-colors,awesome-people' ); // Posts from the contain both "Awesome Colors" and "Awesome People" $args = array( 'tag' => 'awesome-colors+awesome-people' ); // Posts from the tag with the ID of 23 $args = array( 'tag_id' => 23 ); // Posts from tag 42, 38 and 55 $args = array( 'tag__in' => array( 42, 38, 55 ) ); // Posts from all tags except 42 and 38 $args = array( 'tag__not_in' => array( 42, 38 ) ); // Posts that are assigned both tag 42 and 38 $args = array( 'tag__and' => array( 42, 38 ) ); أود أن أشير إلى أمرين، الأول أن نواة الوورد بريس ليست متناسقة تمامًا، فمثلًا معامل category يتوقع أن يمرر لها معرف ID، أما معامل tag و category_name فيتوقع قسم من الرابط، لذلك فالتحدي الذي يواجهك في البرمجة هنا هو أن تجعل المعلومات التي تدخلها دومًا متناسقة قدر الإمكان، مع أنه في بعض الأحيان تُجبر على تخطي بعض هذه القواعد لأسباب قديمة كانت موجودة سابقًا في النواة. أما الأمر الثاني هو أنه من الأفضل استخدام المصفوفات في حال كان ذلك مسموحًا، فمثلًا على الرغم من أن معامل tag يتيح لك إدخال متغيرات متعددة ولكن من الأفضل استخدام tag_in والمتغيرات الأخرى التي تسمح بتمرير القيم على شكل مصفوفة. ولتسهيل الأمور أكثر في التعامل مع الوسوم يوجد أيضًا معاملين آخرين هما tag_slug_and و tag_slug_in ويكون التعامل معها بالشكل التالي: // Posts from the tag named "Awesome Colors" or "Awesome People" $args = array( 'tag_slug__in' => array( 'awesome-colors', 'awesome-people' ) ); // Posts from the contain both "Awesome Colors" and "Awesome People" $args = array( 'tag_slug__and' => array( 'awesome-colors', 'awesome-people' ) ); حالة استخدام: الدمج بين معاملات الوسم، الفئة، المؤلف بما أننا تعرفنا على بعض المعاملات يمكننا الدمج فيما بينها ضمن استعلام واحد، على سبيل المثال لنجمع المقالات المتميزة (التي أُضيف لها وسم "featured") لمؤلفين محددين وذلك في فئتي الكتب والأفلام. $args = array( 'tag' => 'featured', 'category__in' => array( 43, 52 ), 'author__in' => array( 255, 930 ) ); $author_featured_posts = new WP_Query( $args ); أنواع المواضيع Post Type تحوي الوورد بريس على عدد من أنواع المواضيع المبنية داخله بالأساس وهي موضوع post، الصفحة page، مرفقات attachment، مراجعة revision، و عنصر قائمة التنقل، ولكن أصبح من الشائع إضافة أنواع أخرى عند إدارة المشاريع مثل الأعمال، المنتجات، المنتديات وهكذا بحسب مايحتاجه المشروع من أنواع. يسمى المعامل المخصص للتعامل مع أنواع المواضيع الجديدة post_type والذي يساعدنا في تحديد النوع الذي ينتمي له الموضوع المُراد نشره. عند تمرير هذا المعامل سيقوم الاستعلام بالبحث عن المنشورات المنتمية لهذا النوع فقط، ولكن في حالة تمرير القيم على شكل مصفوفة فإن البيانات المُعادة ستكون جميع المنشورات التي تطابق هذا النوع. // Return posts with the 'page' post type $args = array( 'post_type' => 'page' ); // Return posts from two custom post types $args = array( 'post_type' => array( 'product', 'post' ) ); // Returns posts from all post types $args = array( 'post_type' => 'any' ); مع العلم أنه هذا المعامل له قيمة افتراضية هي post، ولذلك في حال كنت ترغب في استعادة أنواع أخرى من المنشورات عليك تحديدها بشكل واضح. في حال استخدام المعامل tax_query (سنتكلم عنه لاحقًا) ستصبح القيمة الافتراضية هي any. حالة المنشور Post Status كما في الفقرة السابقة الخاصة بأنواع المواضيع، يمكننا تحديد حالة منشور محدد أو حالات لعدة مواضيع باستخدام المعامل post_status والذي يمرر له قيمة من نوع نص string في حال المنشور وحيد أو مصفوفة في حال اخترنا أكثر من واحدة،. تحوي الوورد بريس على ثماني حالات مبنية في النواة هي: publish الموضوع Post أو الصفحة page منشورة pending الموضوع قيد الانتظار للمراجعة draft الموضوع حُفظ كمسودة auto-draft الموضوع المُنشأ حديثًا ولكن بدون محتوى future موضوع سينشر في المستقبل private ليس معروضًا أمام المستخدمين غير المسجلين الدخول inherit مراجعة (له علاقة بـ get_children) trash الموضوع في سلة المهملات (هذه الحالة متاحة في النسخة رقم 2.9) يمكننا أيضًا استخدام القيمة any لنقول بأننا نريد تضمين جميع حالات الموضوع، أما القيمة الافتراضية فهي Publish لذلك من الأفضل أن تتأكد بأنك حددت الحالة في حال كنت ترغب بحالة مختلفة. حالة استخدام: الإعلان عن المنتجات القادمة لنفترض أنك تدير متجر كتب وتعلم بأن الكتب الأكثر مبيعًا المحتملة ستصدر قريبًا، حيث يمكنك بناء استعلام وورد بريس مخصص لبناء قائمة بهذه الكتب. في هذه الحالة لاتحتاج إلى نشر الكتب على شكل مواضيع ثم استخدام بعض الخدع لتستثنيها من العرض حتى يُؤمّن ويستطيع المستخدم طلبها، يمكنك فقط ترتيب قائمة مباشرة بها بالشكل التالي: $args = array( 'post_status' => 'future', 'post_type' => 'product', 'category_name' => 'books' ); معامل البحث The Search Parameter يسمى المعامل الخاص بعملية البحث s، ويأخذ قيمة نصية string هي نفسها المُستخدمة في عملية البحث داخل الموضوع. ولكن يجب تأكد من ترميز العبارة النصية المُستخدمة في البحث، حيث يمكنك القيام بذلك من خلال استخدام التابع urlencode الموجود في PHP والذي يساعدك في تحديد عدد الفراغات والمحارف الخاصة الأخرى وتحويل العبارة إلى كلمات يمكن البحث عنها. $args = array( 's' => 'awesome+wordpress+plugins' ); حالة استخدام: البحث في موضوع فرعي يمكن البحث في المحتوى كاملًا من خلال شريط البحث الافتراضي الموجود في الوورد بريس باستخدام المعامل s، ولكن يمكن أن يبحث بطريقة أكثر مهنية من خلال الدمج بين معامل البحث والمعاملات الأخرى، سنطرح في المثال التالي طريقة للبحث في مواضيع فرعية متعددة: // Show posts about ACF (Advanced Custom Fields) from the news category $args = array( 's' => 'ACF', 'category_name' => 'news' ); // Search an author's posts $args = array( 's' => 'custom post types', 'author_name' => 'danielpataki' ); // Search an array of custom post types $args = array( 's' => 'wordpress', 'post_type' => array( 'project', 'portfolio' ) ); المواضيع المحمية بكلمة مرور تستطيع اختيار الموضوع الذي ترغب بحمايته بكلمة مرور من المربع الخاص بإعدادات النشر، حيث يوفر لك هذا الخيار المزيد من حماية خصوصية المحتوى، أو يمكن استخدامه لبناء نموذج اشتراك. في جميع الحالات التي تجعلك ترغب بإضافة كلمة مرور للموضوع الخاص بك، يمكنك استخدام المعامل has_password لتحديد فيما إذا كنت ترغب بإدراج الموضوع إلى قائمة المواضيع المحمية بكلمة المرور. يمكن أن يُمرَّر للمعامل has_password قيمة true عندها سيقوم الاستعلام بجمع المواضيع التي تم إعطاؤها كلمة مرور، أو قيمة false وبذلك يختار الاستعلام المواضيع التي لم تُزود بكلمة مرور، ولكن في حال كانت قيمة المعامل محذوفة أو تم إسناد null لها عندها ستُنشر جميع المواضيع. كما يمكن استخدام المعامل post_password من أجل إعداد قائمة بالمواضيع المحمية بكلمة مرور محددة. // Return only password protected posts $args = array( 'has_password' => true ); // Return posts protected with the password: qweasd $args = array( 'post_password' => 'qweasd' ); حالة استخدام: لعبة صيد الكنز أونلاين يمكن بناء لعبة مرحة باستخدام الوورد بريس بالشكل التالي، يقوم المستخدمون بحل الأحجية والحصول على كلمة المرور الخاصة بقائمة المواضيع التي تحوي على أدلة تقود للحل الأخير. الآن يجب على المستخدم وضع كلمة المرور في المكان المخصص لها وعند إدخالها وتثبيت الجواب، سينتقل المستخدم إلى صحفة تستخدم كلمة المرور المُدخلة كمعامل مُمرر لاستعلام بالشكل التالي: $args = array( 'post_type' => 'clue', 'post_password' => 'treasurehunt2015' ); وباستخدام حلقة كاملة تمر على جميع المواضيع الموجودة ستكون لدينا حالتان، الأولى في حال كانت كلمة المرور المُدخلة خاطئة عندها ستظهر رسالة للمستخدم محتواها "لم يعثر على مواضيع"، أما الحالة الثانية فهي في حال كانت كلمة المرور المدخلة صحيحة عندها ستُعرض قائمة بالمواضيع المحمية بكلمة المرور هذه. تضمين، استبعاد، واستهداف مواضيع محددة يوجد تسعة معاملات مختلفة تُستخدم للحصول على مواضيع معينة في الاستعلام: المعامل p: يُستخدم في تجميع المواضيع بالاعتماد على معرف الموضوع ID. المعامل name: يُستخدم في تجميع تجميع المواضيع من خلال تمرير slug كقيمة نصية له. المعاملين page_id و page_name: الأول يشبه معامل p والثاني يشبه name ولكنها تعمل بالنسبة للصفحات، مع بعض الاختلاف بينهما. حيث سيعملان بشكل متشابه تمامًا في حال تم تحديد قيمة للمعامل post_type أما في حال عدم تحديد هذه القيمة فإن معامل page_id سيعمل كما هو مُتوقع منه على عكس معامل p المعاملين post_in و post_not_in: تعمل بنفس الطريقة التي تعمل بها شبيهاتها مع الفئات والوسوم حيث تقبل تمرير مصفوفة من مُعرِّفات المواضيع. المعامل post_parent: يأخد معِّرف وحيد عندها يقوم الاستعلام بإعداد قائمة بالمواضيع التي تكون أبناء تابعة للموضوع المُحدد بالمعرف المذكور. المعاملين post_parent_int و post_parent_not_int: كما في شبيهاتها يُمرَّر لها مصفوفة من معرِّفات المواضيع. من المهم الأخذ بالحسبان نوع المنشور post_type عند التعامل مع هذه المعامل، ففي حال قمت بتضمين المُعرِّفات لمجموعة من أنواع المواضيع عندها ستحتاج لتمرير القيمة any للمعامل post_type، أو تدخل النوع المحدد الذي تبحث عنه. // Grab post 532 only $args = array( 'p' => 532 ); // Grab the post with the given slug $args = array( 'name' => 'guide-to-wp-query' ); // Retrieve page 55 $args = array( 'page_id' => 55 ); // Retrieve the about page $args = array( 'pagename' => 'about' ); // Get the specified 5 posts $args = array( 'post__in' => array( 31, 36, 39, 91, 119 ) ); // Get all posts except the specified 5 $args = array( 'post__not_in' => array( 31, 36, 39, 91, 119 ) ); // Get all child posts of post 6 $args = array( 'post_parent' => 6 ); // Get posts which are the children of the listed posts $args = array( 'post_parent__in' => array( 6, 23, 55 ) ); // Get posts which do not have the listed parents $args = array( 'post_parent__not_in' => array( 6, 23, 55 ) ); حالة استخدام: الوصول إلى جميع المرفقات في الموضوع في حال كنت ترغب بالحصول على قائمة للصور والمرفقات الأخرى الموجودة في المنشور، ستحتاج لاستخدام معاملات الآباء للوصول إلى الهدف المحدد، حيث تعد هذه الطريقة المثالية لتنفيذ فكرة التوليد التلقائي للمعرف أو قوائم التحويل. global $post; $args = array( 'post_parent' => $post->ID, 'post_type' => 'attachment', 'post_status' => 'any' ); استعلامات التصنيف بعد أن تعرفنا بشكل مفصل عن المعاملات المُستخدمة في الاستعلام وكيفية التعامل معها، يمكننا الانتقال إلى موضوع متقدم أكثر هو tax_query. إنّ ميزة هذا المعامل بأنه عبارة عن مصفوفة، يمكن استخدمها لبناء شروط أكثر تعقيدًا، أو في بناء تصنيفات مخصصة بشكل أكبر. سنأخذ نظرة عامة في المثال المقتبس من WordPressCodex: $args = array( 'post_type' => 'post', 'tax_query' => array( array( 'taxonomy' => 'movie_genre', 'field' => 'slug', 'terms' => array( 'action', 'comedy' ), ), array( 'taxonomy' => 'actor', 'field' => 'id', 'terms' => array( 103, 115, 206 ), 'operator' => 'NOT IN', ), 'relation' => 'AND', ), ); يحوي استعلام التصنيف على شرطين condition وعلاقة relation، وهذه العلاقة هي التي تحدد إما أنه يجب أن يتحقق شرط واحد على الأقل (OR) أو يجب تحقيق كلا الشرطين (AND)، أما بالنسبة للشرطين نفسهما فإنهما عبارة عن مصفوفات تحدد فيها قيود التصنيف. وبالعودة إلى المثال المُرفق في الأعلى سنجد أن الشرط الأول ينص على أن المواضيع التي نبحث عنها يجب أن تندرج تحت تصنيف أفلام حركة أو كوميدية، بينما ينص الشرط الثاني على أن المواضيع المُختارة (أي الأفلام) يجب ألا يكون أي من الممثلين الثلاثة الذين تم إدخالها إلى الشرط عن طريقة مصفوفة بأرقام معرفاتها ID مشاركين في الفيلم، فيما العلاقة بين الشرطين هي AND وبالتالي يجب أن يتوفر الشرطان معًا أي أننا نبحث عن أفلام الحركة أو الكوميدية والتي لايشارك بها أي من الممثلين الثلاثة. يحوي كل شرط في استعلام التصنيف على ثلاثة معاملات خاصة به هي: taxonomy : يُحدَّد من خلاله المقطع slug الخاص بالتصنيف. field : يمكن من خلاله الاختيار فيه بين المعاملات الثلاثة وهي term_id، أو name أو slug. operator : يستخدم لتحديد كيفية التعامل مع المصطلحات المتواجدة في الشرط، حيث IN سيسمح لأي من المواضيع التي تنتمي لأي من الشروط المدرجة بالتواجد في القائمة النهائية، أما NOT IN سيسمح لأي من المواضيع التي لاتتضمن أيًا من الشروط المدرجة، أما AND فسيسمح للمواضيع التي تحقق جميع الشروط المدرجة. Include_children : يحدد فيما إذا كان بالإمكان السماح أو عدم السماح بتضمين المواضيع التي تنتمي للتصنيف الابن في التصنيفات الهرمية حالة استخدام: تصفية محتوى متقدمة تحتوي فلاتر التصنيف على نطاق واسع من الاستخدامات، مثل التعامل مع تصنيفات المنتجات المخصصة، أو إنشاء قاعدة بيانات للأفلام تشبه IMDB، وسنطرح مثال جديد لتصفية كتب متقدمة. $args = array( 'post_type' => 'book', 'tax_query' => array( array( 'taxonomy' => 'mood', 'field' => 'slug', 'terms' => array( 'negative', 'bad', 'sad' ), 'operator' => 'NOT IN' ), array( 'taxonomy' => 'genre', 'field' => 'slug', 'terms' => array( 'comedy', 'romance' ), 'operator' => 'NOT IN', ), 'relation' => 'AND', ), ); في هذه الحالة بالتحديد يريد المستخدم النظر إلى جميع الكتب التي تحوي أفكار إيجابية(التي لاتحوي على وسوم سلبية تم إعطاؤها إياها) ولكن لايمكن أن تكون مُصنفة ككتب كوميدية أو رومانسية. الاستعلامات من نمط Meta Query تشبه بنية الاستعلام من نمط Meta query استعلام التصنيف. حيث يُسمح للاستعلام عن المواضيع بالاعتماد على القيم الخاصة بها من الجداول التعريفية الأساسية الخاصة بالوورد بريس meta table. يقدم الوورد بريس أربع معاملات يمكن استخدامها هي meta_key، meta_value، meta_value_num، و meta_compare ولكن يمكن استبدالها بالمعامل meta_query وهو الأفضل. لنلق نظرة على المثال: $args = array( 'post_type' => 'book', 'meta_query' => array( array( 'key' => 'pages', 'value' => 500, 'compare' => '>', 'type' => 'NUMERIC' ), ), ); في هذا المثال يقوم استعلام meta query باسترجاع الكتب التي عدد صفحاتها أكثر من 500 صفحة. يُعتبر هذه الطريقة أسهل بكثير من استخدام استعلام التصنيف. لنتعرف أكثر على المعاملات الفرعية الموجودة في المثال السابق: key: مفتاح الحقل المُدخل value: القيمة التي نبحث عنها compare: نوع عملية المقارنة ويمكن أن تكون أحد قيم المساواة(=)، اللامساواة (!=) أكبر (>)، أكبر أو يساوي (>=)، أصغر (<)، أصغر أو يساوي (<=)، يشابه (LIKE)، لايشبه(NOT LIKE)، داخل(IN)، ليس ضمن (NOT IN)، بين (BETWEEN)، ليس بين (NOT BETWEEN)، موجود (EXISTS)، وليس موجود (NOT EXISTS). type: نوع البيانات الموجودة في الحقل. ومن الممكن نوع القيمة ‘NUMERIC‘, ‘BINARY‘، ‘CHAR‘، ‘DATE‘، ‘DATETIME‘، ‘DECIMAL‘، ‘SIGNED‘، ‘TIME‘، ‘UNSIGNED‘. ومن المهم الانتباه دومًا إلى العلاقة بين المعاملات التي تشير إلى العلاقة بين جمل الاستعلام المنفصلة، حيث أنها تعمل تمامًا مثل استعلامات التصنيف الذي شرحناه بالأعلى. حالة استخدام: المواضيع المُرفقة بصور مميَّزة يوجد الكثير من الطرق لاستخدام استعلام meta queries. من أهم الأمثلة عليه هي جمع المواضيع التي تحوي صور مميزة. كل معرف ID للصور المميزة يخزّن في أحد الجداول التعريفية الأساسية للوورد بريس باسم _thumbnail_id ويمكن من خلال استعلام meta query للبحث عن المواضيع التي تكون فيها قيمة هذا المفتاح المعرف meta key الذي لايكون فارغًا. $args = array( 'meta_query' => array( array( 'key' => '_thumbnail_id', 'value' => '', 'compare' => '!=', ), ), ); استعلامات التاريخ تشبه طريقة عمل استعلامات التاريخ استعلامات meta queries، حيث تقدم WP_Query ثمانية معاملات لتضيق قائمة المواضيع بالاعتماد على التاريخ ويمكن تعويضها جميعًا بمعامل date_query وسنشرحه بتفصيل بسبب أهميته وكثرة استخدامه. لنتعرف أولًا على المعاملات الخاصة بالتاريخ، والمقتبسة من WordPressCodex: year - وهي عبارة عن السنة مؤلفة من 4 أرقام مثل 2011. month - رقم الشهر من 1 إلى 12. week - رقم الأسبوع ضمن السنة من 0 إلى 53. day - رقم اليوم ضمن الشهر من 1 إلى 31. hour - الساعة من 0 إلى 23. minute - الدقيقة من 0 إلى 59. second - الثانية من 0 إلى 59. after - التاريخ الذي نريد استرجاع المواضيع بعده. حيث يمرر التاريخ من خلال ()strtotime والذي يمكن أن يأخذ التاريخ كقيمة نصية أو من خلال مصفوفة تحوي القيم السنة ‘year‘، الشهر ‘month‘، واليوم ‘day‘ على النحو التالي: year – تقبل السنة بصيغة الأربع أرقام ولكن القيمة الافتراضية هي أن تكون فارغة. month - الشهر من السنة ويقبل الأرقام من 1 إلى 12 والقيمة الافتراضية هي 12. day - هو اليوم من السنة وتأخذ قيمة من 1 إلى 31 أما القيمة الافتراضية هي آخر يوم من الشهر. before - التاريخ الذي نريد استرجاع المواضيع التي قبله. حيث يمرر التاريخ من خلال ()strtotime والذي يمكن أن يأخذ التاريخ كقيمة نصية أو من خلال مصفوفة تحوي القيم السنة ‘year‘، الشهر ‘month‘، واليوم ‘day‘ على النحو التالي: year – تقبل السنة بصيغة الأربع أرقام ولكن القيمة الافتراضية هي أن تكون فارغة. month - الشهر من السنة ويقبل الأرقام من 1 إلى 12 والقيمة الافتراضية هي 1. day - هو اليوم من السنة وتأخذ قيمة من 1 إلى 31 أما القيمة الافتراضية هي 1. inclusive - تستخدم مع before/after من أجل تحديد فيما إذا كانت قيمة التاريخ متضمنة ضمن المجال أو لا. compare -يمكن الإطلاع على ()WP_Date_Query::get_compare. column - القيمة الافتراضية له post_date. relation - وتأخذ إحدى القيمتين OR أو AND والتي تحدد كيف ستتعامل المصفوفات الفرعية فيما بينها لإتمام عملية المقارنة، والقيمة الافتراضية له هي AND. وهكذا نكون قد شرحنا القليل فقط عن استعلامات التاريخ، ولكنه يساعدنا في التعامل بمرونة كبيرة مع النطاقات الزمنية باستخدام المعاملات السبعة الأولى معًا. // Return all posts from 2014 $args = array( 'date_query' => array( array( 'year' => 2014, ), ), ); // Return all posts from January 2015 $args = array( 'date_query' => array( array( 'year' => 2015, 'month' => 1 ), ), ); // Return all Valentine's Day posts, regardless of year: $args = array( 'date_query' => array( array( 'month' => 2 'day' => 14 ), ), ); وللتحكم أكثر بالنطاق الزمني يمكن استخدام المعاملين before و after، ولكنك تحتاج لتحديد السنة، الشهر، واليوم كعناصر في المصفوفة الفرعية. كما يمكن استخدام المعامل inclusive لتحديد حدود النطاق الزمني بشكل دقيق أكثر. $args = array( 'date_query' => array( array( 'after' => 'January 1st, 2013', 'before' => array( 'year' => 2013, 'month' => 2, 'day' => 28, ), 'inclusive' => true, ), ), ); سيعيد هذا المثال المأخوذ من Codex كل المواضيع المنشورة بين الأول من كانون الثاني عام 2013 و الثامن والعشرين من شباط عام 2013. وبما أن القيمة الممررة للمعامل inclusive هي true فستكون المواضيع المنشورة في التواريخ الحدودية مُتضمَّنة في النتيجة. إن وظيفية التاريخ هنا تعتمد على صنف WP_Date_Query، لذلك أقترح الإطلاع أكثر على الموضوع. حالة استخدام: المنشورات الموسمية السابقة تمتلك العديد من المواقع محتوى موسمي، مثل المواضيع الخاصة بالميلاد، وبالسنة الجديدة، وأمور عيد الحب وهكذا. ومن خلال استعلام مخصص لتاريخ معين يمكننا إضافة محتوى خاص بهذا التاريخ إلى موقعك من الأرشيف. المثال القصير التالي سيضيف موضوعا خاصا بخدعة الأول من نيسان: $args = array( 'date_query' => array( array( 'month' => 4, 'day' => 1 ), ), ); قد تعتقد أن عدد المعاملات المستخدمة في عملية الترقيم هي واحد أو اثنين، ولكن في الحقيقة يوجد أكثر من سبعة معاملات للتعامل مع الترقيم فقط. المعامل الرئيسي الأكثر استخدامًا هو posts_per_page والذي يحدد بشكل رئيسي عدد المواضيع التي يتم اختيارها من قاعدة البيانات ووضعها في الصفحة، ويمكن ضبط الإعداد على القيمة -1 حيث سيقوم بجمع كل المواضيع المحتملة. المعامل paged هو أحد المعاملات المستخدمة بكثرة حيث يحدد أي صفحة يمكن عرض نتائجها. ويمكن ربطها مع المعامل posts_per_page. مثلًا إذا تم عرض 10 مواضيع في الصفحة وأُسنِد للمعامل paged القيمة 4 فستعدل قيمة 4 إلى 10. المعامل nopaging يسمح لك بتجاهل ترقيم الصفحات بشكل كلي إذا عدلت قيمته إلى true. كما يوجد معامل offset والذي يعمل كما في MySQL حيث تزاح المواضيع المسترجعة من خلال القيمة الممرة للمعامل وبالتالي ستكسر قاعدة ترقيم الصفحات مالم تكن تعرف ماتفعله، وسنشرح عن هذا الموضوع بشكل بسيط. وفي حال وجدت نفسك لاتحتاج للمواضيع الموسومة على أنها ثابتة stick posts، يمكنك استخدام المعامل ignore_sticky_posts لتجاهلها. حيث أن المواضيع الموسومة بأنها ثابتة تتواجد دائما في أعلى القائمة وهو المكان الطبيعي لها ولتجاهل هذا التصرف تسند القيمة false للمعامل المذكور. يحدد المعامل posts_per_archive_page عدد الصفحات التي توجد في صفحات الأرشيف، والتي ستكون هي الصفحات التي تُعطى القيمة true للتوابع ()is_archive و i()s_search، مع العلم أن هذا المعامل لايُستخدم مع معامل posts_per_page. أخيرًا، المعامل page يُظهر المواضيع التي عادةً ما تظهر فقط في الصفحة X من الصفحة الأمامية الثابتة. $args = array( 'posts_per_page' => 20, 'offset' => 2, 'ignore_sticky_posts' => true ); يعيد المثال في الأعلى المواضيع العشرين الثانية (كانت قيمة الإزاحة 2) من قاعدة البيانات مع تجاهل المواضيع المثبتة. ترتيب النتائج إن ترتيب النتائج هو بنفس أهمية استرجاع النتائج. ويمكن التحكم في ترتيب النتائج من خلال معاملين هما order و orderby. إن معامل orderby يحدد ترتيب المواضيع التي ستُستعاد. ويوجد لدينا العديد من الخيارات منها: none - لايوجد ترتيب وهو القيمة الافتراضية في MySQL ID - ترتيب المواضيع من خلال المفتاح المعرف author - ترتيب المواضيع بحسب معرف المؤلف title - ترتيب المواضيع بحسب العنوان name - ترتيب المواضيع بالاعتماد على slug type - ترتيب المواضيع بحسب نوع الموضوع date - ترتيب المواضيع بحسب التاريخ modified - ترتيب المواضيع بالاستناد إلى تاريخ آخرت تعديل parent - ترتيب المواضيع بالاستناد إلى معرف الأب rand - ترتيب عشوائي للمواضيع comment_count - ترتيب المواضيع بحسب عدد التعليقات على الموضوع menue_order - ترتيب المواضيع بحسب ترتيب الصفحات meta_value - ترتيب المواضيع بالاستناد إلى القيمة meta value meta_value_num ترتيب المواضيع بالاستناد إلى القيمة الرقمية ل meta value post_in الحفاظ على الترتيب المدخل في هذا المعامل تقريبًا جميع هذه المعاملات تشرح نفسها بنفسها، ولكن لنتعمق أكثر في طريقة ترتيب المواضيع باستخدام meta value. للقيام بهذا العمل، علينا أول تحديد معامل meta_key كما يعرّفه الوورد بريس بأنه المفتاح الذي يحوي قيمة meta value، ولكن يجب عليك الاهتمام بتحديد النمط الصحيح عند الترتيب وإلا فإننا سنحصل على نتائج غير مُتوقعة. مع الملاحظة أنه في في حال كنا نرتب معلومات رقمية عندها سنستخدم المعامل meta_value_num بدلًا من meta_value. ويمكن أيضًا استخدام المعامل meta_type الذي يسمح لنا بتحديد نوع البيانات. إن المعامل order يحدد اتجاه الترتيب ففي حال كانت القيمة المُمررة له هي ASC عندها ستكون نتيجة الترتيب تصاعديًا، فيما ستكون نتيجة الترتيب تنازلية في حال كانت قيمة المعامل DESC ، والآن لنلق نظرة على المثال التالي الذي يعطينا نظرة عملية عن طريقة الترتيب: // Reverse order by post title $args = array( 'post_type' => 'post', 'orderby' => 'title', 'order' => 'DESC' ); // List specific posts, preserve given order $custom_posts = array( 56, 928, 2271, 22, 491, 883 ); $args = array( 'post_type' => 'any', 'post__in' => $custom_posts, 'orderby' => 'post__in', ); // Order by meta data (grab posts with thumbnails first) $args = array( 'post_type' => 'post', 'meta_key' => '_thumbnail_id', 'orderby' => 'meta_value', 'order` => 'DESC' ); ولتوخي الدقة في المعلومات فإن خيار الترتيب باستخدام عدة أعمدة كان على شكل تمرير قيم منفصلة في نسخ الوورد بريس التي سبقت الإصدار 4.0 ولكن من الإصدار 4.0 ومافوق، حُسّن الأمر من خلال استخدام مصفوفة يمكن تحديد طريقة الترتيب من خلالها بشكل منفصل: $args = array( 'orderby' => array( 'title' => 'DESC', 'menu_order' => 'ASC' ) ); حالة استخدام: ترتيب المنتجات حسب السعر تتطلب نماذج مواقع التجارة الالكترونية طريقة لترتيب المنتجات بحسب السعر. سنبحث في المثال التالي عن المنتجات التي يتجاوز سعرها 20 دولارًا وترتيبها في قائمة حسب السعر من الأدنى إلى الأعلى: $args = array( 'meta_query' => array( array( 'key' => 'price', 'value' => '20', 'compare' => '>=', ), ), 'post_type' => 'product', 'meta_key' => 'price', 'orderby' => 'meta_value_num', 'order` => 'ASC' ); القيم المُسترجعة يبدو أن المعامل fields ليس معروفًا بشكل جيد، بالرغم من أنه أحد الميزات الهامة في الاستعلامات التي تسمح بحمل القيم المُسترجعة من الاستعلامات بسهولة، وذلك من خلال تمرير قيمتين لهذا المعامل. في حال حذف المعامل fields فستُسترجع البيانات من الاستعلام بالطريقة المُعتادة، أما في حال ممرنا القيمة ids لهذا المعامل فستكون النتيجة المُرجعة من الاستعلام على شكل مصفوفة تحوي أرقام المعرفات الخاصة بالمواضيع post IDs، وفي حال كانت قيمة المعامل هي id=>parent عندها ستكون النتيجة المُرجعة من الاستعلام هي مصفوفة ولكنها تحوي أرقام المعرفات الخاصة بالآباء المرتبطة بالعلاقة المذكورة في الاستعلام. أعتقد أن هذا المعامل يضيف ميزات كبيرة في تحسين الاستعلام، وذلك لأن بعض الإضافات المستخدمة في WP_Query المستخدمة لجمع معرفات المواضيع لاتستخدم معامل fields لحمل القيم المُرجعة وبالتالي ستتم عملية استرجاع البيانات من خلال إعطاء الخادم server الكثير من العمل ليقوم به، وفي هذه العملية صرف لموارد الخادم ووقت العمل نحن بغنى عنه. حالة استخدام: إضافة معرض صور برمجيًا لنفترض أننا نريد إضافة معرض خاص بالصور التي مرت في الموضوع بعد الانتهاء من عرضه، سنقوم بعمل ذلك من خلال الاستعلام التالي: // Programmatically placing a gallery shortcode global $post; $args = array( 'post_parent' => $post->ID, 'post_type' => 'attachment', 'post_status' => 'any', 'fields' => 'ids' ); $images = new WP_Query( $args ); $image_id_string = implode( ',', $images ); echo do_shortcode( '[gallery ids="' . $image_id_string . '"]' ); لقد قمنا أولًا بجمع كل المرفقات من الموضوع ثم التأكد من أن القيمة المُسترجعة هي المعرف ID فقط. ثم قمنا ببناء قيمة نصية string تحوي على جميع عناصر المصفوفة (وهي المعرفات ID) مع إضافة فاصلة بين كل معرف والذي يليه، بعدها استخدمنا التابع ()do_shortcode لإنشاء المعرض. الإذن Permission إن هذا الجزء غامض قليلًا لأنه لم ينفذ وفق تصميم معين. الفكرة فيه هو استخدام المعامل perm وإعطاؤه القيمة readable كقيمة وعندها ستظهر فقط المشاركات التي يمكن للمستخدم الحالي قراءتها. هذه الطريقة عملية في حال كنت تريد إعداد قائمة من المواضيع الخاصة أو العادية ولكن يجب أن تتأكد من أنك قمت بإدراج هذه المواضيع الخاصة في المكان المخصص لها. $args = array( 'post_status' => array( 'publish', 'private' ), 'perm' => 'readable', ); التخزين المؤقت Caching هذه أحد التحسينات المتقدمة التي تسمح بالتحكم بطريقة التخزين المؤقت التي تستخدمها الاستعلامات. بشكل عام إن تخزين نتاج الاستعلام بشكل مؤقت يعتبر فكر جيدة لأن لديها فرصة كبيرة في استخدمها عدة مرات في نفس الصفحة. مثلا قائمة بأفضل 10 مواضيع على الأغلب لن تتغير من استعلام إلى آخر وبالتالي يمكن الاستفادة من تخزينها. يوجد حالتين يمكن فيها التحكم بهذا التخزين المؤقت. إذا قمت بتخزين قائمة ولاتحتاج إلى البيانات الأساسية منها metadata أو إلى تصنيفها يمكن أن تستخدم المعاملين update_post_meta_cache و update_post_term_cache وذلك لإيقاف التخزين المؤقت الخاص به. كما أنه في حال كنت تقوم باستعلام لمرة وحيدة من أجل اختبار ما سريع يمكن استخدام المعامل cache_results لإيقاف تخزين نتايج الموضوع نفسه. // Do not cache results $args = array( 'posts_per_page' => -1, 'cache_results' => false ); // Don't cache taxonomy data $args = array( 'posts_per_page' => 10, 'update_post_term_cache' => false ); قمت في هذا المثال ببناء جدول تصدير سريع لجميع المواضيع المتواجدة في قاعدة البيانات تحوي جميع معرفات المواضيع، عناوينها، الفئات، والوسوم المُسندة إليها، حيث أن تخزين هذا الاستعلام ذو الاستخدام لمرة واحدة غير منطقي، فما الفائدة من تخزين بيانات بدائية meta date عن 8000 موضوع أو أكثر. خصائص استعلامات الوورد بريس WP_Query حتى الآن كنا نتكلم عن المعاملات التي يمكن تمريرها لاستعلام WP_Query، ولكن الآن سنتكلم عنه كصنف Class وسنشرح الخصائص properties و التوابع methods التي يمكن أن نتعامل معها. يحوي WP_Query Properties Documentation على شرح وافٍ لجميع الخصائص الخاصة بهذا الصنف، ولكن سنتحدث هنا عن أكثر الخصائص فائدة وكيفية الاستفادة منها. لنبدأ أولًا بالخاصية $found_posts التي تحمل قيمة عدد المواضيع التي تم العثور عليها كإجمالي ناتج الاستعلام المدخل. أما الخاصية $post_count فإنها تحمل عدد المواضيع التي تظهر حاليًا، وهي قيمة غير ثابتة منذ استخدم ترقيم الصفحات، فمثللًا يمكن أن يكون هناك 52 موضوع أوجده الاستعلام ولكن تعرض 10 فقط في الوقت الحالي. كما أنه يجب الانتباه إلى أن القيمة التي يحملها $post_count ليس من الضروري أن تكون نفسها الموجودة في post_per_page فعلى سبيل المثال إذا وُجد 52 موضوع وكانت كل صفحة تعرض 10 مواضيع في المرة الواحدة ستكون الصفحة الأخيرة تحوي موضوعين فقط . أخيرًا سنتحدث عن الخاصية $max_num_pages والتي تحمل عدد الصفحات التي نحتاجها لإظهار النتائج، وهي عمليًا ناتج العملية $found_posts\$posts+per_page ويقرّب الناتج إلى أكبر قيمة صحيحة. $args = array( 's' => 'awesome+wordpress+plugins', 'posts_per_page' => 8 ); $results = new WP_Query( $args ); echo "A total of " . $results->found_posts . " posts were found.<br />"; echo "We will be displaying " . $results->query_vars['posts_per_page'] . " posts per page if possible.<br />"; echo "We need a total of " . $results->max_num_pages . " pages to display the results". توابع استعلامات الوورد بريس WP_Query Methods بما أننا نتكلم عن الأصناف فإن الخصائص تشابه بشكل كبير المتغيرات، والتوابع methods تشبه أيضًا الدوال functions. يمتلك استعلام الوورد بريس العديد من التوابع رغم أنك لاتحتاج إلى استخدامها مباشرة بشكل عام. ولكن أهم تابعين ذكرناهما في الأعلى هما ()have_post و ()the_post. بعض التوابع الأخرى المفيدة هي ()rewind_posts والذي يعيد ضبط الحلقة ويمكن استخدامها في حال مثلًا كان الخرج عبارة عن قائمتين متماثلتين تمامًا في مكانين مختلفين. بالنسبة للتوابع ()get و ()set ستسمح لك بالوصول إلى متغيرات الاستعلام ولانستخدمها إلا في حالات قليلة جدًا. لمزيد من المعلومات حول توابع استعلام الوورد بريس، يمكن الإطلاع على Method Documentation. سلامة البيانات: التحقق Validating، التنظيف Sanitizing، والتهريب Escaping تقترن بعض الاستعلامات المخصصة مع واجهات المستخدمين. والسيناريو الأمثل لضمان سلامة المعلومات هو إضافة فلتر مخصص يتحكم بصلاحيات الوصول للمستخدم للمواضيع. يمكن الإطلاع على المزيد من المعلومات في المقالة Adding Post Filters To Your WordPress Site. وفي حال كنت تملك مكان من الواجهة يستطيع المستخدم فيه إدخال بيانات ما، فيجب دوما التحقق وتهريب البيانات المدخلة لمنع الهجومات التي تحصل على قاعدة البيانات. ويمكن الإطلاع على معلومات أكثر في المقالة Validating, Santitizing And Escaping User Data. تعديل استعلامات SQL يوجد عدد من الفلاتر التي تسمح لك بالنفاذ إلى استعلامات SQL الفعلية والتي تُنفّذ بناء على استعلامات WP_Query. وغالبًا لا نستخدم هذه الفلاتر لذلك من الأفضل أن تتعامل مع المعاملات التي ذكرناها سابقًا. تسمح الفلاتر مثل posts_where و posts_join بتعديل شروط معينة في استعلام SQL بينما تكون استعلامات الوورد بريس أكثر مرونة ولكنها لاتسمح بوجود شروط معقدة بعد WHERE بما في ذلك التصنيفات المتعددة أو العلاقات المتعددة. في هذه الحالة يمكن استخدام الفلاتر التي ذكرناها. يمكن الإطلاع على WP_Query Filters للمزيد من المعلومات. الخلاصة أرجو أن تكون قد كونت فكرة أفضل عن صنف استعلام الوورد بريس WP_Query Class، وكيفية الحصول على المعلومات التي نريدها عبر بناء قوائم من المواضيع، كما يمكنك الدمج بين أنواع المواضيع المختلفة، التصنيفات والبيانات الأساسية لبناء نظام إدارة محتوى قوي. إذا كنت مهتمًا ببرمجة الوورد بريس، يوجد مجموعة من الأصناف التي تعمل بشكل مشابه لصنف WP_Query مثل صنف WP_Comment_Query الذي يُستعمل لتجميع التعليقات بنفس الطريقة التي يعمل بها صنف WP_Query. كما أن صنف WP_Meta_Query يتعامل مع الاستعلامات الأساسية meta_queries، بينما يتعامل صنف WP_Date_Query مع كل حدود التواريخ. ترجمة -وبتصرّف- للمقال An In-Depth Guide to Conquering WP_Query لصاحبه Daniel Pataki
  4. لدينا مقولة في عالم برمجة البايثون هو أننا كمطورين ندخل عالم البايثون من أجل تعلم اللغة ولكن نبقى عالقين فيها بسبب مجتمع مطوريها، وهذا صحيح بالنسبة لأغلبنا فالأمر الذي يبقينا في عالم البايثون سهولة أن تخطر فكرة لك وتبدأ بتنفيذها فتنتهي من تنفيذها سريعًا على الأرجح خلال عدة ساعات من الليل بسبب كل تلك المساعدات والمكتبات التي يقدمها لك مجتمع مطوري البايثون. في هذا المقال سنتكلم عن أهم مكتبات البايثون التي نحبها ونستخدمها كثيرًا أثناء تطوير مشاريعنا. لحفظ البيانات في قاعدة البيانات بشكل سريع: Dataset تُستخدم Dataset عندما نحتاج إلى جمع البيانات وحفظها في قاعدة البيانات قبل أن نكتشف ماهو الشكل النهائي لجداول قاعدة البيانات. حيث تُعتبر Dataset بسيطة ولكنها مع ذلك API قوي يقدم طريقة سهلة لحفظ البيانات داخلها ثم تصنيفيها لاحقًا. بنيت Dataset فوق SQLAlchemy ويمكن استخدامها ضمن جانغو من خلال تعليمات الإدارة inspectdb المبنية داخل جانغو. لالتقاط البيانات من صفحات الويب: Beautiful Soup تستخرج مكتبة Beautiful Soup (ويمكن كتابتها BS4) المعلومات من صفحات HTML كما تستخدم في تحويل البيانات غير المنظمة أو المهيكلة في HTML إلى بيانات مهيكلة، كما أنها تعمل بشكل رائع مع بيانات XML التي من الممكن أن تكون غير قابلة للقراءة. للعمل مع محتوى HTTP: Requests يمكن القول أن Requests هي من أفضل المكتبات المعيارية التي تعمل على محتوى HTTP. ففي أي وقت نحتاج لطلب صفحة HTML أو حتى API ستقوم مكتبة Requests بتغليف هذا الطلب وتوثيقه بشكل جيد. لكتابة الأوامر الخاصة بالـ command-line: Click عندما نحتاج لكتابة نص برمجي بلغة البايثون تعد مكتبة Click من المكتبات المفضلة في كتابة أوامر command-line، حيث تعتبر هذه المكتبة API بسيطة ومدروسة جيدًا كما أنها لاتحوي إلا القليل من النماذج التي نحتاج لتذكرها بالإضافة إلى أنها تمتلك مستندات توثق طريقة عملها بشكل جيد وهذا يسهل عملية البحث عن الميزات المتقدمة ضمن المكتبة. لتسمية الأشياء: Python Slugify كما نعلم تسمية الأشياء أمر صعب لذلك تقدم لنا مكتبة Python Slugify خدمة تحويل العنوان أو الوصف إلى مُعرِّف مميز. وفي حال كنت تعمل على مشروع ويب فإنك تحتاج إلى استخدام روابط صديقة لعمليات تحسين نتائج البحث SEO. إذا مهمة مكتبة Python Slugify أن تجعل عملية التسمية أمرًا سهلًا بالنسبة لنا. للعمل مع الإضافات: Pluggy تعتبر مكتبة Pluggy جديدة نسبيًا ولكنها من أفضل وأسهل الطرق لإضافة نظام الملحقات plugin إلى تطبيقك الموجود حاليًا. في حال تعاملت سابقًا مع pytest فإنك بالتأكيد استخدمت Pluggy من دون أن تعرف. لتحويل ملفات CSV إلى API: Datasette إن Datasette أداة رائعة وسهلة لتحويل ملفات CSV إلى تطبيق REST JSON كامل المميزات ولكنها تطبيقات قراءة فقط. تمتلك Datasette الكثير من الميزات بما في ذلك المخططات والخرائط التفاعلية، وهي سهلة التطبيق ضمن البرامج باستخدام إطار حاوي container أو مضيف ثالث في الويب. للتعامل مع متغيرات البيئة وغيرها: Envparse في حال كنت تحتاج إلى تفسير متغيرات البيئة environment variables مباشرة لأنك لاتريد حفظ مفاتيح API، أو أي معلومات أخرى هامة عن قاعدة البيانات ضمن النص البرمجي source code، عندها تحتاج إلى استخدام Envparse وهي من أفضل المكتبات التي تعالج متغيرات البيئة، ملفات ENV، أنماط المتغيرات، even pre-، و post-processors ترجمة وبتصرّف للمقال 8 8 great Python libraries for side projects لصاحبيه Lacey Williams Henschel و Jeff Triplett
  5. تعتبر المخططات والجداول البيانية charts طريقة لعرض المعلومات information المُستخرجة من البيانات الخام data بشكل بصري يساهم في تقديم المعلومة بشكل سهل للمستخدم ويجعل تحليل الأجزاء الكبيرة من البيانات أكثر سهولة، بالإضافة إلى الجاذبية التي تضيفها هذه المخططات على الموقع بسبب الطريقة التفاعلية التي تُقدَّم بها المعلومات. تُؤمّن مكتبات الجافا سكربت لنا تضمين المخططات إلى موقعنا، والتي تمتاز بسهولة الفهم والتعامل، حيث سنتكلم في هذه المقالة عن أهم 3 مكتبات مفتوحة المصدر مكتوبة بلغة الجافا سكربت ومتخصصة في المخططات البيانية. في هذه المقالة سنتعلم عن أهم 3 مكتبات مفتوحة المصدر متخصصة في المخططات في الجافاسكربت مكتبة chart.js إن مكتبة chart.js هي مكتبة مفتوحة المصدر مكتوبة بالجافا سكربت تسمح لنا ببناء مخططات بيانية جميلة متحركة وتفاعلية نستخدمها في تطبيقات الويب، وهي متاحة تحت ترخيص MIT. يمكن يمكن من خلال هذه المكتبة بناء العديد من المخططات والرسوم البيانية المثيرة للإعجاب بمافيها مخططات الأعمدة bar charts، المخططات الخطية line charts، مخططات المساحة area charts، المقاييس الخطية linear scale، المخططات البيانية المبعثرة scatter charts. تمتاز المخططات المبنية بواسطة هذه المكتبة بأنها متجاوبة بشكل كامل مع غالبية الأجهزة، حيث يتم استدعاؤها وتضمينها في صفحة الويب باستخدام عناصر HTML5. سنطرح مثالًا توضيحيًا لطريقة رسم مخطط أعمدة باستخدام هذه المكتبة، حيث سنُضمِّمن مكتبة chart.js الموجودة في content delivery Network (اختصارًا CDN)، مع العلم أن البيانات المذكورة في المثال توضيحية فقط. <!DOCTYPE html> <html> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script> </head> <body> <canvas id="bar-chart" width=300" height="150"></canvas> <script> new Chart(document.getElementById("bar-chart"), { type: 'bar', data: { labels: ["North America", "Latin America", "Europe", "Asia", "Africa"], datasets: [ { label: "Number of developers (millions)", backgroundColor: ["red", "blue","yellow","green","pink"], data: [7,4,6,9,3] } ] }, options: { legend: { display: false }, title: { display: true, text: 'Number of Developers in Every Continent' }, scales: { yAxes: [{ ticks: { beginAtZero:true } }] } } }); </script> </body> </html> كما نرى في المثال، يتم إنشاء المخطط وتسمية نوعه عندما أسندنا القيمة bar إلى type (يمكن اختيار نوع المخطط هنا مثلًا يمكن تغيير نوع المخطط إلى horizontalBar) نسند قيم ألوان الأعمدة من خلال مصفوفة إلى backgroundColor ونلاحظ أن كل لون أسند إلى اسم العمود الذي يحمل نفس رقم الفهرسة في مصفوفة labels وسيكون الخرج بالشكل التالي: مكتبة Chartist.js مكتبة Chartist.js هي هي مكتبة تحريك بسيطة مكتوبة بلغة جافا سكربت تُستخدم لبناء مخططات وتصميمات جميلة متجاوبة وقابلة للتخصيص، وهي مكتبة مفتوحة المصدر تندرج تحت ترخيص MIT و WTFPL. صممت هذه المكتبة من قبل مجموعة من المطورين الذين كانوا غير راضيين عن الأدوات التي تُستخدم لتصميم المخططات، لذلك قاموا ببناء هذه المكتبة التي توفر ديناميكية رائعة للمصممين والمطورين. للبدء باستخدام هذه المكتبة ضمن تطبيقنا، نحتاج أولًا لتضمين ملف المكتبة Chartist.js والملف CSS، ويمكننا بعدها البدء ببناء العديد من أنواع المخططات والرسوم البيانية مثل مخططات الأعمدة، المخططات الخطية، بالإضافة إلى أنها إضافة بعض التحريك animation، حيث تقوم باستخدام SVG لاستدعاء وتحريك المخططات ديناميكيًا. سنطرح هنا مثالًا عن طريقة استخدام هذه المكتبة لبناء مخطط الفطيرة pie chart: <!DOCTYPE html> <html> <head> <link href="https//cdn.jsdelivr.net/chartist.js/latest/chartist.min.css" rel="stylesheet" type="text/css" /> <style> .ct-series-a .ct-slice-pie { fill: hsl(100, 20%, 50%); /* filling pie slices */ stroke: white; /*giving pie slices outline */ stroke-width: 5px; /* outline width */ } .ct-series-b .ct-slice-pie { fill: hsl(10, 40%, 60%); stroke: white; stroke-width: 5px; } .ct-series-c .ct-slice-pie { fill: hsl(120, 30%, 80%); stroke: white; stroke-width: 5px; } .ct-series-d .ct-slice-pie { fill: hsl(90, 70%, 30%); stroke: white; stroke-width: 5px; } .ct-series-e .ct-slice-pie { fill: hsl(60, 140%, 20%); stroke: white; stroke-width: 5px; } </style> </head> <body> <div class="ct-chart ct-golden-section"></div> <script src="https://cdn.jsdelivr.net/chartist.js/latest/chartist.min.js"></script> <script> var data = { series: [45, 35, 20] }; var sum = function(a, b) { return a + b }; new Chartist.Pie('.ct-chart', data, { labelInterpolationFnc: function(value) { return Math.round(value / data.series.reduce(sum) * 100) + '%'; } }); </script> </body> </html> تتيح لنا هذه المكتبة استخدام العديد من الأنماط Style المُعرَّفة مسبقًا في ملفات CSS التي قمنا بتضمينها في البداية بدلًا من كتابة الأنماط بشكل كامل يدويًا حيث يمكننا استخدام هذه الأنماط في التحكم بشكل المخطط الذي قمنا ببنائه. يُستخدم الصف .ct-chart المبني مُسبقًا في CSS لبناء حاوية container للمخطط، ويستخدم الصف .ct-golden-section للحصول على النسبة aspect ratios المستخدمة في تكبير وتصغيير التصميم ليتلائم مع التصميم المتجاوب responsive design وبالتالي يخفف عنا حساب الأبعاد. واستخدمنا الصف الافتراضي .ct-series-a لتحديد شكل الأقسام الموجودة في المخطط، حيث يمكن استبدال الحرف a للحصول على أشكال أخرى مبنية مُسبقًا. وقد استخدمنا التابع Chartist.Pie لبناء مخطط الفطيرة، وفي حال كنا نريد بناء شكل نوع آخر من المخططات مثل المخطط الخطي يمكن استخدام التابع Chartist.Line وسيكون الخرج لهذا الكود بالشكل: مكتبة D3.js تُعتبر المكتبة D3.js مكتبة أخرى مهمة مفتوحة المصدر مكتوبة بالجافا سكربت. وهي متاحة تحت رخصة BSD. وتقوم هذه المكتبة التي توفر أيضا تحريك ثلاثي الأبعاد 3D animation باستخدام HTML5، SVG، و CSS كإطار لعملها ضمن صفحة الويب. تقوم هذه المكتبة على على مبدأ أساسي في العمل وهو بناء مستند (Document Object Model) DOM ثم معالجته بالاعتماد على التوابع والبيانات المتوفرة داخله لنحصل منها على المخطط البياني الذي نريد بناؤه من خلال إحداث تغييرات في المستند. سنطرح هنا مثال عن مخطط الأعمدة باستخدام هذه المكتبة: <!DOCTYPE html> <html> <head> <style> .chart div { font: 15px sans-serif; background-color: lightblue; text-align: right; padding:5px; margin:5px; color: white; font-weight: bold; } </style> </head> <body> <div class="chart"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.5.0/d3.min.js"></script> <script> var data = [342,222,169,259,173]; d3.select(".chart") .selectAll("div") .data(data) .enter() .append("div") .style("width", function(d){ return d + "px"; }) .text(function(d) { return d; }); </script> </body> </html> حيث قمنا أولًا ببناء وسم style يحوي جميع اختياراتنا لشكل المخطط ويتم ربطها مع عقدة DOM لمعالجتها بنفس الطريقة التي يعمل بها jQuery. بعد حفط البيانات في المستند document يقوم التابع .enter() بالإذن ببدء التطبيق invoke لبناء العقدة الجديدة التي تحمل البيانات بالنسبة لجميع وكل التوابع التي تلي التابع .enter() فسيتم استدعاؤها من أجل كل عنصر من البيانات. وسيكون الخرج بالشكل التالي: الخلاصة توفر مكتبات المخططات في الجافا سكربت أدوات فعالة لإظهار البيانات بشكل بصري في الموقع الالكتروني الخاص بك، وهذا يزيد من جمالية وتفاعل الموقع. ترجمة وبتصرّف للمقال 3 top open source JavaScript chart libraries لصاحبه Dr. Michael J. Garbade
  6. استُخدم وسم img لتزويد صفحات الويب بالصور، التي تضفي للموقع المزيد من التألق. وقد كان هذا الوسم مفيدًا وغير معقد الاستخدام، إذ يقوم وعبر الرابط المُمرَّر في الصفة src الملحقة بالـ img بطلب ملف الصورة، كما أُضيفت بعض التقنيات المساعدة للحالات البديلة التي لاتظهر فيها الصورة بالشكل الافتراضي، وبالتالي نستنتج أن وسم img يمتاز بالسرعة، الفعالية والسلاسة. لذلك أودّ التنبيه بأن هذه المقالة لم تأت للتقليل من أهمية وسم img ، وإنّما لإبراز الطرق الجديدة في استخدامها مع تصميم الويب المتجاوب Responsive Web Design (اختصارًا هو RWD). شهد تصميم صفحات الويب الكثير من المنافسة في السنوات السابقة، حيث كانت متصفحات الويب تعمل على تحسين أداء عرض الصور، من خلال تحسين التعامل مع الوسم الخاص بالصور img، والتي تمتاز بالوثوقية ولكن أيضًا قابليتها الضعيفة للتغيير. يمتاز تصميم الويب المُتجاوب RWD بأنه يعمل على بناء صفحات ويب مُتجاوبة مع أي طريقة عرض، وهي ميزة جميلة جدًا، ولكن الميزة الأكبر هي سعيه لإجياد تقنيات يمكنها التكيُّف مع أجهزة العرض المجهولة التي تظهر أمامها، ومحاولة إيجاد طريقة عرض جذابة تشابها، وسنختصر ذلك بالقول أن RWD يمتاز بالمرونة وأنه غير قابل للتنبؤ. من المهم هنا وقبل أن أبدأ بالتفصيل في موضوع الصور المُتجاوبة أن ألفت انتباهكم إلى أن صفحات الويب التي نفتحها عبر متصفحات الويب الموجودة في الأجهزة الخلوية، أي تلك الروابط التي تحوي على الحرف m متبوعًا بنقطة (ونسميها اختصارًا مواقع m-dot)، تطبّق الكثير من أفكار التصميم المتجاوب RWD، ولكنها لاتندرج تحت اسمه، إذ أنها تستخدم طرق أخرى تكيّف من خلالها البيانات الأساسية (الأصول) المتوافرة في صفحة الويب لتناسب الجهاز الذي تُعرض عليه وهذا مانسميه tailored assets. تكييف الأصول Tailored Assets بمجرد إضافة الإعداد max-width: 100% ضمن ملف CSS، ستظهر الصور بالشكل المناسب لشاشة العرض viewport، فمثلًا إذا كان لدينا صورة بعرض 300 بكسل ونريد عرضها على شاشة بعرض 200 بكسل فستُكبّر الصورة لتتناسب معها، والعكس أيضًا صحيح ففي حال كان العرض على شاشة بحجم صغير فستُصغّر الصورة للحجم المناسب، وهذا يعني هدر كبير في كلفة نقل الصورة، وذلك لأنه ستُنقل الصورة من مصدرها بحجمها الأصلي ثم ستُصَغّر عند العرض. أي أن عرض الصور عالية الدقة high resolution على شاشات منخفضة الدقة low resolution، يتسبب دومًا في كلفة عالية في عرض النطاق bandwidth cost بدون فائدة تعود على المستخدم من نقل هذه الحزم الكبيرة، بالإضافة إلى وقت أطول لمعالجة هذه البيانات ثم إظهارها. وقد كانت عملية النقل تتم بهذه الطريقة حتى في الأيام الأولى لاستخدام تصميم الويب المتجاوب RWD، لذلك كان من الشائع عرض أو إخفاء كتلة كاملة من المحتوى بالاعتماد على حجم وطريقة العرض viewport، ولكن هذه الطريقة أصبحت أقل شيوعًا. لكن للوسم img اعتبارات خاصة في عملية الإظهار بسبب خصوصية المحتوى الذي يحمله، وذلك لأن طريقة العمل في الويب هي تحليل وترجمة HTML كاملًا ثم تحليل CSS. وبما أن وسم img هو تابع للغة HTML ،ستُحمّل دومًا الصورة بحجمها الأصلي دون أي إمكانية لمعرفة حجم العرض المطلوب حتى لو استخدمنا السطر display:none في تعريف الوسم img أو القالب container الذي يحوي هذا الوسم، أي غالبًا سيكون هناك ضياع في عرض النطاق bandwidth من دون أي فائدة تعود على المستخدم. المحاولات الأولى كان انتشار مواقع m-dot في عام 2011 واسعًا على اعتباره أفضل حل عملي لعرض مواقع الويب على الأجهزة الخلوية، ولكن ظهر تصُّور في تلك المرحلة بأن استخدام التصميم المتجاوب قد يكون أيضًا حلًا عمليًا لبناء المناسبة لعرض الموقع على جميع أحجام الشاشات، وقد كانت مجرد فكرة صعبة التنفيذ إلى أن استُخدمت في إعادة تصميم موقع Globe، فانتشرت طريقة استخدام تصميم الويب المتجاوب RWD في المواقع الإخبارية الضخمة. كما كانت هناك محاولات لتطوير نقل صورة كبيرة للشاشات الكبيرة، وذلك من خلال الفكرة التي طُرحت بإمكانية أن تكون الصورة بحجم شاشة الموبايل مع الاحتفاظ بالصيغة الأساسية format للصورة الأصلية ثم نكبرها في حال كانت شاشة المستخدم أكبر، وبهذه الطريقة لن يكون عرض الصورة بنفس الجودة ولكننا حافظنا على فكرة عرض الصور المتجاوبة. أما الطريقة التقنية لتنفيذ هذه الفكرة فهي من خلال الحصول على عرض الشاشة باستخدام الجافا سكربت في قسم head الموجود في المستند، ثم نقل المعلومات إلى الخادم في الوقت المناسب وونؤجّل طلب الحصول على الصور إلى أسفل الصفحة، ثم وفي الوقت الذي تقوم به الجافا سكربت بتنفيذ الطلبات الموجودة في قسم body نكون قد أعددنا ملف تعريف الارتباط وحفظنا حجم شاشة العرض لدى المستخدم فيه وبهذا يمكن استخدام هذه المعلومات في نفس طلب تحميل الصورة من خلال الوسم img حيث تتم قراءة التعليمات المرفقة بملف تعريف الارتباط وتحديد ماهية البيانات التي يجب إرسالها في الرد. وقد كانت تعمل هذه الطريقة بشكل جيد ولكن سرعان ما استولى عليها المخترقون hackers وقاموا بتحليل السلوك بين الطلب والرد وفهموا محتواه جيدًا رغم أنه لم يكن معرَّفًا صراحة في أي من المواصفات المذكورة، وبالتالي لم يعد هناك جدوى من استخدام هذه الطريقة. تمّ بعد ذلك تطوير فكرة الجلب المسبق prefetching كما سميت أيضًا speculative preparsing، وقد كانت فكرة ضخمة جعلت المتصفحات تعمل بشكل أسرع وذلك لأن الفكرة في أساسيها هي أن يبدأ المتصفح بطلب البيانات الأساسية حتى قبل أن يبدأ عرض الصفحة، وبالتالي ستكون البيانات أقرب إلى الجاهزة عندما يحين وقت ظهور الصفحة. وقد قامت المتصفحات الرئيسية بعدة تغييرات رئيسية للتعامل مع الجلب المُسبَق، حيث كانت الفكرة "بما أنه طُلبت الصورة من مصدرها قبل فترة بالتالي سيكون لدينا فرصة لتنفيذ أي منطق مخصص نريده". وقد كانت المتصفحات تتنافس فيما بينها للوصول إلى أداء أفضل، وقد ساعدها في تنفيذ ذلك الجلب المسبق (كما هو موضح في المقالة improving load time by as much as 20 percent). منطقيًا إن أسرع تنفيذ طلب هو الطلب الذي لا يُنشأ أو يُعدّل عليه، وبالتالي فإن طلب صورة من خلال رابطها الموجود في src المُرفق بالوسم img هو أسرع طلب، ولكن غالبًا سيكون محتوى هذه الطلبات غير فعال منذ البداية بغض النظر عن السرعة التي ينجح المتصفح في طلبها وتحليلها وعرضها وذلك بسبب كبر البيانات الأساسية التي نحتاج إليها في أي وقت، لذلك وُجدت تقنية جديدة هي عبارة عن دمج الوسم noscript ديناميكيًا مع الوسم base ضمن document.write و eval، وبتحليل وعرض كل القسم المكتوب بـ HTML في صفحتنا ضمن عنصر head نكون قد تغلبنا على المشكلة السابقة. ولكن تعتبر هذه الطريقة صعبة التعامل معها دومًا لأنها تعتمد على طريقة عمل المتصفح وذلك أمر غير مُوثَّق وبالتالي غير موثوق به. نلاحظ أن الحلول التي طُرحت كانت دومًا حلولًا وسطية مع تنازلات كبيرة، فمرة لا تُعرض الصورة ويكون هناك هدر في طلبات الصور، ومرة تكون الصور المعروضة باهتة، وبالتالي لم يكن الحل المُنفَّذ باستخدام الجافا سكربت ذكيًا بما فيه الكفاية وذلك لأن التحسينات جميعها كانت تتم على مستوى المتصفح بدلًا من الاستفادة من تلك البيانات القادمة. وقد وُجد الحل الجذري لهذه المشكلة في HTML5 بما فيها من إمكانات. الحل الجذري يعد W3C المجتمع الأهم لمطوري الويب الذي يشاركون فيه اقتراحاتهم وأفكارهم لتطوير الويب. وفي نقاش كبير حول الصور المُتجاوبة والذي كان بعنوان "Responsive Images Community Groups" قدم Bruce Lawson اقتراحًا لتحديث طريقة تقديم الصور لتصبح ملائمة مع عناصر الوسائط المتعددة media التي أصبحت متنوعة وغنية في HTML5 وقد اقترح صيغة تشبه كثيرًا صيغة الوسم video حتى أنه استعار الصفة media منه، حيث كان الوسم الجديد المقترح لتوصيف الصور هو picture، والمميز في هذا الاقتراح هو أنه أبقى على الوسم img المعروف أساسًا بوثوقيته وعمل الوسم الجديد picture على تأطير –تغليف- الوسم السابق بالصيغة التالية: <picture> <source …> <img src="source.jpg" alt="…"> </picture> إنّ وجود وسم img داخل وسم img يؤمن لنا دعمًا احتياطيًّا قويًّا، فالوسم الجديد ليس أحد الوسوم المعيارية التي تحتم علينا أن ننتظر حتى يُدعم من قبل المتصفحات ثم نستطيع بعدها الاستفادة منه، وإنما يمكننا استخدامه مباشرة وستقوم المتصفحات التي لاتدعمه وبالتالي لاتفهم الموجود في عنصر picture و source بتجاهلهما وتنفيذ الوسم الداخلي img، أما المتصفحات اللواتي تدعم picture فستستخدم المعايير المرفقة في العنصر source لتحدد للوسم الداخلي img ماهو الملف الموجود في المصدر والذي نريد الحصول عليه. وبالتالي فنحن لانحتاج إلى بناء جميع الميزات الخاصة بالـ img ومن ثم إرفاقها بالعنصر الجديد picture، وذلك لأنها لم تقدم أي شيء جديد في حد ذاتها وإنما طوّرت أداء وإمكانية الوصول إلى الوسم img، ولكن مع المزيد من الأبحاث سنجد أن picture لا تقدم لنا جميع الخصائص التي تحتاجها الصورة في ظل التطور المستمر للويب المتجاوب وللطرق المتقدمة الجديدة المستخدمة في عرض الصور، ومع ذلك حصلنا على العديد من التحسينات لعنصر img منها خيارات جديدة للتعامل مع الشاشات عالية الدقة من خلال تغيير حجم الصورة ليناسب المكان المخصص، وتأمين صيغ بديلة للصورة لم نكن قادرين على الحصول عليها من قبل. ترجمة -بتصرّف- للمقال Responsive Images لصاحبه Mat Marquis
  7. مقدمة تعلمنا في المقال السابق طريقة الوصول إلى العناصر في DOM عن كيفية استخدام التوابع الملحقة بالكائن document بهدف الوصول إلى عناصر html بإحدى الطرق التالية: معرف العنصر ID، صف العنصر class، اسم الوسم tag name، أو محددات الاستعلام query selectors. كما شرحنا في مقال البنية الشجرية لـ DOM عن العقدة الأب لـ DOM الممثلة بالكائن document، كما تطرقنا إلى شرح مفصل عن العقد الأبناء التي تبنى من خلالها الشجرة وعن أنواعها (العناصر، التعليقات، والنصوص). ولكننا نحتاج غالبًا للانتقال بين عقد شجرة DOM بدون المرور على جميع العقد التي تسبق العقدة المطلوبة، لذلك سنتحدث في هذه المقالة عن كيفية التنقل في شجرة DOM من الأعلى إلى الأسفل وبالعكس، إضافة إلى التنقل بين أفرع الشجرة من خلال باستخدام تعليمات الجافا سكربت و html. الإعداد: لنبدأ أولًا بإنشاء ملف nodes.html والذي يحوي التعليمات التالية: <!DOCTYPE html> <html> <head> <title>Learning About Nodes</title> <style> * { border: 2px solid #dedede; padding: 15px; margin: 15px; } html { margin: 0; padding: 0; } body { max-width: 600px; font-family: sans-serif; color: #333; } </style> </head> <body> <h1>Shark World</h1> <p>The world's leading source on <strong>shark</strong> related information.</p> <h2>Types of Sharks</h2> <ul> <li>Hammerhead</li> <li>Tiger</li> <li>Great White</li> </ul> </body> <script> const h1 = document.getElementsByTagName('h1')[0]; const p = document.getElementsByTagName('p')[0]; const ul = document.getElementsByTagName('ul')[0]; </script> </html> عند فتح هذا الملف من خلال متصفح الانترنت، سنشاهد الصفحة التالية: في صفحة الويب هذه لدينا مستند html يحوي على عدد قليل من العناصر وأساسيات css مرفقة به من خلال الوسم style من أجل تحسين طريقة إظهار العناصر، كما قمنا ببناء عدد قليل من المتحولات باستخدام تعليمات الجافا سكربت والتي ذكرت ضمن الوسم script حيث ستساعدنا بالوصول إلى العناصر. كما نلحظ وجود عنصر واحد من العناصر التالية h1، p، ul لذا يمكن الوصول إليها من خلال الدليل الأول index للتابع getElementsByTagName. عقدة الجذر Root Node يعتبر الكائن doucment العقدة الجذر root node لجميع العقد في DOM. ولكن في الحقيقة كائن document هو إحدى خصائص الكائن window والذي يمكن تعريفه بأنه كائن عام global وذو مستوى عالي top-level object حيث يمثل تبويب المتصفح الذي يظهر فيه صفحة الويب. يمكننا من خلال كائن window أن نصل إلى معلومات عن شريط الأدوات، طول وارتفاع النافذة، الرسائل الظاهرة في المتصفح، والإنذارات، في حين أن كائن document يتضمن كل الأحداث والخصائص الأخرى الموجودة داخل window. يتضمن الجدول المرفق عناصر الجذر التي يمتلكها كل document حتى لو كان ملف html المحمل إلى المتصفح فارغ وهي عبارة عن 3 عقد سيتم إضافتها وتفسيرها في DOM. table { width: 100%; } td, th { border: 1px solid #dddddd; text-align: left; padding: 8px; } tr:nth-child(even) { background-color: #dddddd; } الخاصية العقدة نوع العقدة document #document DOCUMENT_NODE document.documentElement html Element_NODE getElementsByTagName() head Element_NODE querySelector() body Element_NODE وبما أن عناصر html، head، body شائعة جدًا فإنها تمتلك الخصائص الخاصة بها في المستند. سنقوم بفتح تبويب console ضمن أدوات المطورين DevTools حتى نختبر هذه الخصائص الأربع من خلال كتابتها في console ومشاهدة النتائج. كما يمكننا أيضًا اختبار العناصر h1، p، و ul. عقد الآباء Parent Nodes لدينا في DOM ثلاث أنواع من العقد معرفة حسب نوع العلاقة فيها بينها وهي الآباء parents، الأبناء children، والشقيقة siblings. تعرف العقدة الأب parent بأنها أي عقدة تمتلك عقدًا أقل في مستوى level أقل منها في التدرّج الهرمي للـ DOM، أو يمكن تعريفها بأنها العقد الأقرب لعقدة الجذر document. يمكن الوصول دومًا للعقدة الأب بواسطة إحدى الخاصيتين parentNode أو parentElement. Gets Property ParentNode parentNode ParentElementNode parentElement في مثالنا nodes.html نلاحظ أن: html هي العقدة الأب لكل من عقد head، body، و script العقدة body هي العقدة الأب لكل من h1، h2، و ul ولكنها ليست العقدة الأب لـ li لأنها في المستوى الأقل منها بمستويين. يمكننا اختبار طريقة الوصول إلى العقدة الأب التي ذكرناها سابقًا، حيث سنقوم بمعرفة أب العنصر p من خلال الخاصية parentNode، وكما نذكر أننا قمنا ببناء المتحول p في التعليمات السابقة وإسناد القيمة document.getElementsByTagName('p')[0] وللوصول للأب نكتب التعليمة التالية في تبويب console > p.parentNode; وسيكون الخرج: Output ► <body>...</body> ويمكن استعمال parentNode مرتين للوصول إلى جد p. يمكن الاستعانة بالعديد من الخصائص التي تمكننا من الوصول إلى العقدة الأب ولكن يوجد فيما بينها القليل من الاختلافات كما سنبينها الآن > // Assign html object to html variable > const html = document.documentElement; > > console.log(html.parentNode); // > #document > console.log(html.parentElement); // > null إن الأب لأي عقدة هو من نوع عقدة العنصر element nodes بينما لايمكن للعقد من نوع نص أو تعليقات أن تكون أب لأي عقدة أخرى وبما أن العقدة الأب لـ html هي العقدة الجذر document node لذلك نلاحظ أن القيمة المرجعة هي Null في السطر الأخير من التعليمات. بشكل عام إن استخدام الخاصية parentNode شائع أكثر في الانتقال ضمن DOM. العقد الأبناء Children Nodes إن أبناء أي عقدة هي جميع العقد الموجودة في المستوى الذي يليه مباشرة أما العقد الموجود في المستوى الذي يلي مستوى الأبناء تسمى العقد الأحفاد. Gets Property Child Nodes childNodes First Child Node firstChild Last Child Node lastChild Element Child Nodes children First Child Element Node firstElementChild Last Child Element Node lastElementChild إن خاصية ChildNode ستعيد قائمة بأبناء العقدة. على سبيل المثال في ملف nodes.html السابق نلاحظ أن عنصر ul لديه 3 عناصر أبناء من li وبالتالي عند إدخال التعليمة: > ul.childNodes; وسيكون الخرج: Output ► (7) [text, li, text, li, text, li, text] نلاحظ أننا حصلنا على قائمة بأبناء العنصر ul والتي تحوي العناصر li الثلاث التي ذكرناها بالإضافة إلى عُقد النصوص text nodes ويعود السبب في ذلك لأننا كتبنا ملف html بطريقة يدوية ولم يتم توليده بواسطة الجافا سكربت. مع العلم أن كل ما يوجد بين العناصر يعتبره DOM عقدة نص text node، ولكن يعمل تبويب Elements الموجودة في أدوات التطوير الخاصة بالمتصفح DevTools بحذف الفراغات البيضاء من النص وبالتالي لا يعتبرها عقد نصوص. مثلًا إذا حاولنا تغيير لون الخلفية للابن الأول باستخدام خاصية firstChild سنفشل لأن أول عقدة هي نص. > ul.firstChild.style.background = 'yellow'; وستكون رسالة الخطأ هي Output > Uncaught TypeError: Cannot set property 'background' of undefined لذلك فإن الخصائص children، firstElementChild، و lastElementChild وجدت لكي نستطيع الحصول على العقدة من نوع عنصر element node فقط، أي في حال كتابة ul.children سنحصل على مصفوفة تحوي 3 عقد من نوع عنصر هي li. باستخدام الخاصية firstElementChild سنقوم بتغيير لون الخلفية للعنصر الأول li التي هي ابن للعقدة ul > ul.firstElementChild.style.background = 'yellow'; وعند تنفيذ هذه التعليمة سيتغير لون الخلفية عند معالجة شجرة DOM تظهر بعض الخصائص المفيدة للعناصر element properties. في تطبيقات الويب التي تم توليدها باستخدام الجافا سكربت الأكثر احتمالًا، هو استخدام الخصائص التي تختار جميع العقد، وفي هذه الحالة الخطوط والمساحات البيضاء لن تكون موجودة. بالعودة للمثال السابق يمكن استخدام حلقة for ... of من أجل تطبيق اللون الأصفر في الخلف على جميع العناصر الأبناء children. for (let element of ul.children) { element.style.background = 'yellow'; } بتطبيق هذه التعليمات سيظهر اللون الأصفر في خلفية جميع الأبناء وبما أن العنصر p يمتلك نوعي عقد من الأبناء وهي العناصر والنصوص في داخله، فإنه يمكن للخاصية childNodes أن تكون مفيدة في الوصول إلى لهذه المعلومات. for (let element of p.childNodes) { console.log(element); } حيث سيكون الخرج عبارة عن ثلاثة أسطر كل سطر يمثل ابن للعقدة p Output "The world's leading source on " <strong>shark</strong> " related information." إن خاصيتي children و childNode تعيد مصفوفة ولكنها لاتمتلك جميع خصائص وتوابع المصفوفات، ولكنها تظهر بشكل مشابه للمصفوفات في الجافا سكربت. ولكن يمكن الوصول إلى العقد من خلال رقم الدليل index أو من خلال خاصية طول المصفوفة length document.body.children[3].lastElementChild.style.background = 'fuchsia'; التعليمة السابقة هي عبارة عن إضافة اللون الفوشي لخلفية العقدة الابن الأخيرة (li) التابعة للعنصر الابن رقم 3 (ul) والتي هي بدورها ابن للعقدة body. إن استخدام خصائص الأب والابن تساعد في الوصول إلى أي عقدة ضمن شجرة DOM. العقد الشقيقة Sibling Nodes العقد الشقيقة sibling of nodes هي العقد الموجودة في نفس المستوى في شجرة DOM ويمكن أن تكون من أنواع مختلفة من العقد سواء عنصر، نص أو تعليق Gets Property Previous Sibling Node previousSibling Next Sibling Node nextSibling Previous Sibling Element Node previousElementSibling Next Sibling Element Node nextElementSibling إن خصائص العقد الشقيقة تعمل بنفس الطريقة التي تعمل فيها عند العقد الأبناء، حيث نجد مجموعة من الخصائص للتنقل بين العقد بكل أنواعها وهي previousSibling و nextSibling ومجموعة أخرى تساعد على التنقل بين العقد من نوع عناصر فقط وهي previousElementSibling و nextElementSibling. في مثالنا nodes.html سنختار الابن الأوسط للعنصر ul const tiger = ul.children[1]; وبما أننا بنينا DOM الخاص بنا من الصفر وليس بناء على تطبيق ويب باستخدام الجافا سكربت فإننا نحتاج لاستخدام خصائص العناصر الأشقاء للوصول إلى عقد العناصر السابقة واللاحقة مع العلم أن الفراغات البيضاء موجودة في DOM كعقد من نوع text tiger.nextElementSibling.style.background = 'coral'; tiger.previousElementSibling.style.background = 'aquamarine'; وبتنفيذ التعليمات السابقة سنلاحظ أن لون الخلفية عند Hammerhead وGreat White قد تغير ملخص تحدثنا في هذه المقالة عن كيفية الوصول إلى جميع عناصر مستند HTML وكيفية التحرك بين العقد في شجرة DOM من خلال خصائص العقد الأب، الابن، والشقيقة. وبهذا ستكون قادرًا على الوصول إلى أي عقدة ضمن شجرة DOM في أي موقع ويب. هذه المقالة ترجمة للمقالة How To Traverse the DOM للكاتبة Tania Rascia
  8. مقدمة تحدثنا في المقال السابق عن البنية الشجرية لـ DOM وعن العقد Nodes المكونة لها وهي عبارة عن كائنات objects من الممكن أن تكون نصوص أو تعليقات أو عناصر. في الغالب يمكن الوصول إلى محتوى DOM من خلال عقد عناصر html، ولمعرفة كيفية الوصول إلى عناصر DOM بشكل احترافي من الضروري امتلاك معلومات كافية عن قواعد وأساسيات css، لذلك سنقوم في هذه المقالة بتعلم كيفية الوصول إلى عناصر DOM من خلال ID والصف class والوسم tag، ومحددات الاستعلام query selectors. نظرة عامة سنستعرض في هذا المقال خمس توابع methods هي: الطرق محدد بناء الجملة يحصل على getElementById() #demo ID getElementsByClassName() .demo Class getElementsByTagName() demo Tag querySelector() Selector (single) querySelectorAll() Selector (all) من المهم عند دراستنا ل DOM أن تقوم بتطبيق الأمثلة على حاسبك للتأكد من فهم جميع المعلومات بشكل جيد. سنبدأ بإنشاء ملف access.html في مجلد المشروع والذي سنقوم بتطبيق جميع أمثلتنا عليه في هذه المقالة. <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Accessing Elements in the DOM</title> <style> html { font-family: sans-serif; color: #333; } body { max-width: 500px; margin: 0 auto; padding: 0 15px; } div, article { padding: 10px; margin: 5px; border: 1px solid #dedede; } </style> </head> <body> <h1>Accessing Elements in the DOM</h1> <h2>ID (#demo)</h2> <div id="demo">Access me by ID</div> <h2>Class (.demo)</h2> <div class="demo">Access me by class (1)</div> <div class="demo">Access me by class (2)</div> <h2>Tag (article)</h2> <article>Access me by tag (1)</article> <article>Access me by tag (2)</article> <h2>Query Selector</h2> <div id="demo-query">Access me by query</div> <h2>Query Selector All</h2> <div class="demo-query-all">Access me by query all (1)</div> <div class="demo-query-all">Access me by query all (2)</div> </body> </html> في ملف html هذا نلاحظ أن لدينا العديد من العناصر التي يمكن الوصول إليها من خلال توابع method الخاصة بـ document وعند فتح هذا الملف في المتصفح سيكون مشابه لهذه الصفحة: سنستخدم العديد من التوابع methods التي ذكرناها في المقال سابقًا للوصول إلى العناصر المتاحة في هذا الملف. الوصول للعناصر باستخدام ID أسهل طريقة للوصول إلى عنصر محدد في DOM هو المعرف الخاص بها ID(Identification). حيث يمكن الوصول إلى العنصر من خلال ID باستخدام التابع getElementById() مع العنصر document. document.getElementById(); ولكن للحصول على هذه الخاصية في الوصول إلى العنصر من خلال id يجب أولًا إضافة الصفة id للعنصر. مثلًا سنضيف المعرف demo للعنصر div <div id="demo">Access me by ID</div> الآن في تبويب console سنقوم بالوصول إلى العنصر واسناده للمتحول demoId > const demoId = document.getElementById('demo'); في حال تمرير المتحول demoId إلى تابع log سنحصل على عنصر html كاملًا. > console.log(demoId); سيكون الخرج عندها العنصر كاملًا <div id="demo">Access me by ID</div> للتأكد من وصولنا إلى العنصر الصحيح سنقوم بتغيير حدود العنصر border property إلى اللّون البنفسجي > demoId.style.border = '1px solid purple'; وعند تنفيذ هذه التعليمات سنلاحظ أن حدود العنصر قد تغيرت إلى اللّون البنفسجي: إن الوصول إلى أي عنصر من خلال ID الخاص به هي أكثر طريقة فعالة وسريعة للوصول إلى عنصر ضمن DOM، ومع ذلك هذه الطريقة لديها عدة عيوب وهي أن ID دائمًا يجب أن يكون متفردًا في الصفحة ولذلك لا يمكن الوصول إلا إلى عنصر واحد فقط من خلال التابع getElementById. ولذلك في حال الحاجة إلى إضافة تابع لأكثر من عنصر في الصفحة فإن إضافته باستخدام id سيؤدي إلى تكرار كتابة التابع لكل عنصر على حدى. الوصول للعناصر باستخدام class تستخدم خاصية class للوصول إلى عنصر أو أكثر في DOM وذلك من خلال التابع getElementByClassName() document.getElementsByClassName(); ونستطيع من خلال الخاصية class الوصول إلى أكثر من عنصر، كما في هذا المثال حيث سنعطي لعنصرين نفس اسم الصف demo <div class="demo">Access me by class (1)</div> <div class="demo">Access me by class (2)</div> الآن في تبويبة console سنبني متحول demoClass ونسند له العناصر التي استطعنا الوصول إليها من خلال اسم الصف demo > const demoClass = document.getElementsByClassName('demo'); والآن أصبح بالإمكان الوصول إلى العناصر وتعديلها باستخدام class كما فعلنا في المثال السابق باستخدم ID. سنقوم الآن بتغيير خاصية الحدود border من خلال المتحول demoClass، demoClass.style.border = '1px solid orange'; ولكن ستظهر لنا رسالة خطأ بأنه لا يمكن تحديد العنصر Output Uncaught TypeError: Cannot set property 'border' of undefined والسبب في هذا الخطأ أن المتحول demoClass لايحوي عنصرًا واحدًا فقط وإنما مصفوفة كائنات من هذه العناصر للتأكد من ذلك سنكتب التعليمة التالية لمعرفة عدد عناصر المصفوفة المُسند إلى deomClass console.log(demoClass); والخرج سيكون هو عدد العناصر في المصفوفة Output (2) [div.demo, div.demo] حتى نستطيع الوصول إلى العناصر بشكل صحيح يجب أن نتعامل مع المتحول demoClass على أنه مصفوفة جافا سكربت يتم الوصول إليها من خلال رقم دليل العنصر index number. الآن نستطيع تغيير حدود كل العنصر الأول في المصفوفة باستخدام رقم الدليل 0 demoClass[0].style.border = '1px solid orange'; بشكل عام للوصول إلى العناصر من خلال الصف calss سنقوم بتغيير جميع العناصر التي تمتلك نفس اسم الصف باستخدام حلقة for التي تقوم بالمرور على جميع العناصر التي تحويها المصفوفة for (i = 0; i < demoClass.length; i++) { demoClass[i].style.border = ‘lpx solid orange’; } وعند تنفيذ هذه التعليمات سيصبح شكل صفحة الويب إذًا استطعنا الوصول إلى جميع العناصر التي تمتلك الصف demo وتغيير خاصية حدودها border إلى اللّون البرتقالي. الوصول للعناصر باستخدام Tag طريقة الوصول للعنصر من خلال اسم الوسم html tag name هو أقل الطرق تخصيصًا حيث يمكننا الوصول إلى العديد من العناصر باستخدام تابع getElementsByTagName() document.getElementsByTagName(); سنستخدم كمثال هنا العنصر article للوصول إليه من خلال اسم الوسم، نلاحظ العنصرين الذين يحملان اسم الوسم article <article>Access me by tag (1)</article> <article>Access me by tag (2)</article> وبنفس الطريقة التي وصلنا للعناصر من خلال اسم الصف class فإن التابع getElementsByTagName تعيد لنا مصفوفة array من العناصر حيث يمكن تعديل كل tag في المستند من خلال حلقة for const demoTag = document.getElementsByTagName(‘article’); for (i = 0; i < demoTag.length; i++) { demoTag[i].style.border = ‘1px solid blue’; } وبمجرد تنفيذ هذه التعليمات سيصبح شكل صفحة الويب بالشكل وهكذا قمنا بتعديل خاصية الحدود border لجميع العناصر article إلى اللّون الأزرق. محددات الاستعلام Query Selector في حال كان لديك خبرة سابقة في jQuery API ربما ستكون لديك معرفة سابقة مع توابع jQuery التي تستطيع الوصول لعناصر DOM من خلال محددات css $('#demo'); // returns the demo ID element in jQuery يمكننا الحصول على نفس النتيجة في الجافا سكربت من خلال استخدام التوابع querySelector() و querySelectorAll() document.querySelector(); document.querySelectorAll(); للوصول إلى عنصر واحد نستخدم تابع querySelector. فمثلًا يمكننا الوصول إلى العنصر الذي لديه المعرف demo-query من خلال التابع querySelector() <div id="demo-query">Access me by query</div> حيث نمرر للتابع المعرف id مع إضافة الرمز #، وسنقوم بإسناد العنصر للثابت demoQuery const demoQuery = document.querySelector('#demo-query'); ولكن في حالة استخدمنا التابع السابق مع المحددات التي تعطينا أكثر من عنصر واحد مثل class أو tag فإن querySelector() سيعيد لنا العنصر الأول فقط الذي يتطابق مع الاستعلام، ولكن نستطيع استخدام التابع querySelectorAll() من أجل الحصول على جميع العناصر المتطابقة مع الاستعلام. في مثالنا نلاحظ أن لدينا عنصرين يملكان اسم الصف demo-query-all <div class="demo-query-all">Access me by query all (1)</div> <div class="demo-query-all">Access me by query all (2)</div> إن اسم المحدد للخاصية class يجب أن يسبق بنقطة عند تمريره للتابع querySelectorAll() أي سنمرر في هذا المثال .demo-query-all const demoQueryAll = document.querySelectorAll('.demo-query-all'); باستخدام التابع forEach() يمكننا تطبيق اللّون الأخضر على حدود هذه العناصر deomQueryAll.forEach(query => { query.style.border = ‘1px solid green’; }); تعتبر الفاصلة , في حال وضعناها بين القيم للتابع querySelector() كعملية OR مثلاً إذا قمنا بكتابة التابع بالشكل querySelector(‘div, article’) أي تعني أن يجد لنا العنصر div أو article بغض النظر عمن سيظهر أولًا في الملف. أما في حالة وجود الفاصلة بين القيم للتابع querySelectorAll فإنها تعني العملية AND أي في حال كتبنا التابع querySelectroAll(‘div, article’) فإنها تعني إعادة العناصر التي تحوي القيم articl و div الموجودة ضمن المستند. إن استخدام توابع محددات الاستعلامات يعطينا القدرة العالية في البرمجة للوصول إلى أي عنصر أو مجموعة عناصر ضمن DOM بنفس المحددات التي نستخدمها في ملف css. وفي هذه المقالة يوجد قائمة بجميع المحددات الخاصة بال css التي جمعت بواسطة شبكة مطوري Mozilla. تعليمات الجافا سكربت كاملة سأذكر هنا التعليمات التي كتبناها بلمف access.js بشكل كامل. حيث يمكن إضافة ملف js إلى ملف html قبل إغلاق body. // Assign all elements const demoId = document.getElementById('demo'); const demoClass = document.getElementsByClassName('demo'); const demoTag = document.getElementsByTagName('article'); const demoQuery = document.querySelector('#demo-query'); const demoQueryAll = document.querySelectorAll('.demo-query-all'); // Change border of ID demo to purple demoId.style.border = '1px solid purple'; // Change border of class demo to orange for (i = 0; i < demoClass.length; i++) { demoClass[i].style.border = '1px solid orange'; } // Change border of tag demo to blue for (i = 0; i < demoTag.length; i++) { demoTag[i].style.border = '1px solid blue'; } // Change border of ID demo-query to red demoQuery.style.border = '1px solid red'; // Change border of class query-all to green demoQueryAll.forEach(query => { query.style.border = '1px solid green'; }); وسيكون الشكل النهائي لملف html هو <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Accessing Elements in the DOM</title> <style> html { font-family: sans-serif; color: #333; } body { max-width: 500px; margin: 0 auto; padding: 0 15px; } div, article { padding: 10px; margin: 5px; border: 1px solid #dedede; } </style> </head> <body> <h1>Accessing Elements in the DOM</h1> <h2>ID (#demo)</h2> <div id="demo">Access me by ID</div> <h2>Class (.demo)</h2> <div class="demo">Access me by class (1)</div> <div class="demo">Access me by class (2)</div> <h2>Tag (article)</h2> <article>Access me by tag (1)</article> <article>Access me by tag (2)</article> <h2>Query Selector</h2> <div id="demo-query">Access me by query</div> <h2>Query Selector All</h2> <div class="demo-query-all">Access me by query all (1)</div> <div class="demo-query-all">Access me by query all (2)</div> <script src="access.js"></script> </body> </html> ملخص في هذه المقالة تعلمنا 5 طرق للوصول إلى عناصر html الموجودة في DOM وهي من خلال المعرف ID، الصف class، الوسم html tag name، ومن خلال المحددات selector. التوابع التي يمكن استخدامها للحصول على عنصر أو مجموعة عناصر تعتمد على مدى دعم المتصفح المستخدم لها وعدد العناصر التي تتم معالجتها. والآن يمكنك الوصول إل عناصر html الموجودة في المستند باستخدام الجافا سكربت و DOM. هذه المقالة هي ترجمة للمقالة How To Access Elements in the DOM وكاتبتها Tania Rascia. table { width: 100%; } td, th { border: 1px solid #dddddd; text-align: left; padding: 8px; } tr:nth-child(even) { background-color: #dddddd; }
  9. مقدمة عندما نتكلم عن DOM فإننا نتكلم عن بنية شجرية تحوي العديد من الكائنات objects التي تدعى العقد nodes. لقد تكلمنا في المقال السابق (مدخل إلى DOM) عن تعريف DOM وكيفية الوصول إلى الكائن document وطريقة تعديل خصائصه من خلال console بالإضافة إلى إيضاح الفرق بين ماهية HTML و DOM. في هذه المقالة فسنتحدث عن مصطلحات HTML التي تعتبر قواعد أساسية للتنسيق برمجة موقع الويب الذي يتضمن html، الجافا سكربت ومفاهيم DOM. حيث سنتعلم من خلال هذا المقال كيفية بناء شجرة DOM و كيفية إضافة العقد الخاصة بها Nodes حسب نوعها، أخيرًا سنقوم ببناء برنامج باستخدام الجافا سكربت بدلًا من استخدام console وذلك لتعديل DOM. مصطلحات HTML إن فهم مصطلحات Html و الجافا سكربت تعتبر أمرًا ضروريًا لفهم كيفية العمل مع DOM. ولذلك سنقوم بمراجعة بعض مصطلحات html سنبدأ أولًا بالنظر إلى عنصر html التالي <a href="index.html">Home</a> إن عنصر html السابق a هو عبارة عن عنصر يُضاف لربط صفحة الويب الخاصة بنا بصفحة أو محتوى آخر، في هذا المثال لتحقيق إمكانية الوصول إلى صفحة index.html a هو الوسم tag href هو الصفة attribute index.html هو قيمة الصف home هو النص الذي نضيفه للعنصر كل ماهو موجود بين علامتي <> هو عبارة عن عنصر html سنعمل على صفحة index.html التي بنيناها بالمقالة السابقة <!DOCTYPE html> <html lang="en"> <head> <title>Learning the DOM</title> </head> <body> <h1>Document Object Model</h1> </body> </html> إن الطريقة الأسهل للوصول إلى عنصر عبر الجافا سكربت هي استخدام الصفة id ويمكننا ربط أي عنصر في html بـ id خاص به، فمثلًا سنقوم بإضافة id خاص بالعنصر a في المثال السابق وقيمتها هي nav ... <body> <h1>Document Object Model</h1> <a id="nav" href="index.html">Home</a> </body> ... الآن سنقوم بتحميل إو إعادة تحميل صفحة index.html للتأكد بتعديل DOM الخاص بصفحتنا التي عدلناها. سنستخدم التابع getElementById() للوصول إلى العنصر كاملًا من خلال كتابة التعليمة التالية في تبويبة console document.getElementById('nav'); عندها سيكون الخرج في تبويبة console بالشكل <a id="nav" href="index.html">Home</a> حيث نلاحظ أننا حصلنا على العنصر كاملآ من خلال استخدام تابع getElementById() ، ولكن يمكننا إسناد عنصر html إلى متحول variable للوصول إليه بشكل أسرع من استخدام التابع السابق. let navLink = document.getElementById('nav'); وهكذا أصبح navLink هو عبارة عن متحول يحوي على عنصر a الخاص وبالتالي يمكننا تعديل الصفات attribute التي يحويها وقيمها بسهولة، فمثلًا يمكننا تعديل قيمة الصفة href بالشكل التالي navLink.href = 'https://www.wikipedia.org'; كما يمكننا تعديل المحتوى النصي للمتحول من خلال إعادة إسناد قيمة نصية جديدة للخاصية textContent navLink.textContent = 'Navigate to Wikipedia'; الآن عندما سنستعرض العنصر a إما من خلال console أو من خلال تمرير الفأة على الوسم a سنشاهد كيف تم تعديل العنصر navLink; وسيكون الخرج هو Output <a id="nav" href="https://www.wikipedia.org/">Navigate to Wikipedia</a> كما سنشاهد أن هذه سينعكس على شكل واجهة الموقع ولكن عند تحديث الصفحة سيتم التراجع عن كل التعديلات التي قمنا بها. وبنهاية هذه الفقرة يجب أن نكون قد تعلمنا كيفية التعامل مع توابع document وطريقة الوصول إليها وإسناد العنصر إلى متحول وتغيير الصفات الخاصة بالعنصر من خلال استخدام المتحول المُسند إليه. شجرة DOM والعقد المكونة لها تعرف جميع العناصر التي تنتمي لل DOM كعقد nodes ولها أنواع متعددة لكن يوجد 3 أنواع هي الأكثر استخداما عقد العنصر Element nodes عقد النص Text nodes عقد التعليقات Comment nodes عندما يتم تمثيل عنصر html كعنصر داخل DOM سنشير له بأنه عقدة العنصر element nodes، وأي نص خارج هذا العنصر سنشير له بأنه عقدة النص text node، وأخيرًا التعليقات التي نكتبها داخل html سنشي لها في DOM بأنها عقد التعليقات Comment node. بالإضافة إلى هذه الأنواع الثلاثة تعتبر document بأنها عقدة قائمة بذاتها تسمى document node والتي تعتبر بأنها جذر العقد الأخرى كافة التي يحتويها ملف DOM. إذًا يتضمن DOM بنية شجرية تحوي العديد من القعد المتداخلة والتي غالبًا ما يشار لها بشجرة DOM أو DOM tree وتشبه بنيتها إلى حد كبير بنية شجرة العائلة التي تضمن الآباء، الأبناء والأشقاء وتلك هي الحال أيضًا بالنسبة للعلاقة بين عقد شجرة DOM. لنرى هذه العلاقات بين عقد شجرة DOM بشكل عملي سنقوم ببناء ملف nodes.html والتي يحوي على أنواع العقد الثلات: عقد النص text، التعليقات comment والعناصر element <!DOCTYPE html> <html> <head> <title>Learning About Nodes</title> </head> <body> <h1>An element node</h1> <!-- a comment node --> A text node. </body> </html> عقدة العنصر html هي العقدة الأب parent node أما عقد العناصر head و body فهي عقد أشقاء وأبناء للقعدة html. أما عقدة body فهي تملك 3 عقد أبناء وهي جميعها عقد أخوة، مع العلم أن نوع العقدة لايؤثر على مستواها. ملاحظة: {.underline} عند العمل مع DOM المولد من قبل html فإن كثرة الفراغات المضافة إلى تعليمات html سيؤدي إلى خلق العديد من عقد النصوص text nodes الفارغة. والتي لاتظهر في تبويبة "Elements" الموجودة في DevTools في chrome. تمييز نوع العقدة كل العقد في المستند document تملك نوع مخصص والتي يمكن الحصول عليها من خلال الخاصية nodeType. قامت شبكة مطوري Mozilla بتحديث قائمتها لـ ثوابت نوع العقدة. وسنذكر هنا قائمة بأكثر أنواع العقد الشائعة وكيفية التعامل معها في هذه المقال نوع العقدة القيمة مثال عقدة العنصر ELEMENT_NODE 1 العنصر < body > عقدة النص TEXT_NODE 3 أي نص ليس جزء من العنصر عقدة التعليق COMMENT_NODE 8 < !--- html comment --- > في تبويبة "Elemnets" الموجودة في أدوات المطوريين قد تلاحظ أنه في حال قمت بالضغط على أي عنصر في DOM وتم تظليله فستلاحظ أنه سيظهر بجانبه القيمة ==$0 ولذلك فإن أحد الطرق السهلة للوصول إلى العنصر النشيط من خلال أدوات التطوير هو كتابة $0. في تبويبة "console" الخاصة بالصفحة nodes.html سنقوم بالضغط على أول عنصر موجود في body وهو h1 الآن سنحصل على نوع العقدة المفعلة حاليًا وذلك من خلال الوصول إليها عبر خاصية nodeType لذلك ندخل التعليمة التالية في console $0.nodeType; وسيكون عندها الخرج هو 1 بالشكل التالي Output 1 وجدنا أن الخرج هو 1 المرتبط بالعنصر المختار h1 والذي يشير إلى نوع ELEMENT_NODE. وبتجربة التعليمة السابقة على نص أو تعليق في ملف HTML فإننا سنحصل على الخرج 3 و 8 بالترتيب. كما يمكننا الحصول على نوع العقدة من دون الحاجة إلى تظليل العنصر في DOM قبل معرفته وذلك من خلال التعليمة document.body.nodeType; حيث سيكون الخرج هو 1 أيضًا. بالإضافة إلى الخاصية nodeType، يمكن استخدام الخاصية nodeValue للحصول على القيمة أو التعليق في عقد النص والتعليق، كما أن الخاصية nodeName تساعدنا في الحصول على اسم الوسم الخاص بالعنصر. تعديل DOM باستخدام الأحداث تعرفنا إلى الآن إلى كيفية تعديل DOM من خلال console ولكن هذا التعديل مؤقت فبمجرد تحديث صفحة الويب ستُلغى هذه التعديلات. في المقالة مدخل إلى DOM استخدمنا console من أجل تعديل لون خلفية الشاشة، الآن يمكن الجمع بين ما تعلمناه سابقًا مع المعلومات في هذه المقالة لبناء زر تفاعلي عندما يتم الضغط عليه. بالعودة إلى ملف index.html سنضيف زر من خلال العنصر button ونضيف له id كما أننا سنضيف رابط جديد لملف جافا سكربت موجود في المجلد js/scripts.js <!DOCTYPE html> <html lang="en"> <head> <title>Learning the DOM</title> </head> <body> <h1>Document Object Model</h1> <button id="changeBackground">Change Background Color</button> <script src="scripts.js"></script> </body> </html> يعرف الحدث event في الجافا سكربت بأنه الفعل الذي يقوم به المستخدم كمثال عندما يقوم المستخدم بتمرير الفأرة فوق عنصر، الضغط على عنصر، أو الضغط على زر معين في لوحة المفاتيح. في هذا المثال نريد للزر الذي بنيناه أن ينتظر ويكون مستعدًا لتلقي الفعل من قبل المستخدم من خلال استخدام event listener للزر. أولًا نقوم ببناء ملف scripts.js ونحفظه في مجلد js ثم سنضيف تعليمات الجافا سكربت التي تقوم بإيجاد العنصر وإسناده إلى متحول let button = document.getElementById('changeBackground'); ثم نضيف التابع addEventListener الذي يعمل على جعل الزر ينتظر حتى يتم الضغط عليه من أجل تنفيذ التابع function الذي يقوم بالفعل في هذا المثال الفعل هو تغيير لون الخلفية. ... button.addEventListener('click', () => { // action will go here }); حيث سنكتب داخل التابع function التعليمة الخاصة بتغيير لون الخلفية إلى لون fuchsia ... document.body.style.backgroundColor = 'fuchsia'; حيث ستكون التعليمات بالترتيب كما يلي let button = document.getElementById('changeBackground'); button.addEventListener('click', () => { document.body.style.backgroundColor = 'fuchsia'; }); وحالما نقوم بحفظ هذا الملف وإعادة تحديث ملف index.html ثم الضغط على زر الموجود في الشاشة فإن الحدث event الذي صممناه لتغير لون الخلفية سوف يعمل مباشرة. ملخص في هذه المقالة قمنا باستذكار المصطلحات التي تساعدنا على فهم وتعديل DOM كما تعرفنا على البنية الشجرية لل DOM وأنواع العقد المستخدمة في إنشائها مثل عناصر html، النصوص، والتعليقات. كما قمنا في النهاية بإنشاء سكربت يسمح للمستخدم بتعديل شكل الموقع من دون الحاجة إلى إدخال التعليمات يدويًا إلى تبويبة console. هذه المقالة هي ترجمة للمقالة Understanding the DOM Tree and Nodes وكاتبتها Tania Rascia
  10. dom

    مقدمة إن نموذج تمثيل المستند ك جسم أو كائن Document Object Model والذي يشار له اختصارا ب DOM يعتبر أحد الطرق الأساسية في بناء مواقع متفاعلة فيما بينها، حيث تُعتبر هذه الطريقة واجهة تسمح للغات البرمجة بمعالجة محتوى المواقع وهيكليتها. أحد اللغات المرتبطة بالـ DOM في المتصفحات هي الجافا سكربت وذلك لأنها لغة برمجة تعمل في مواقع الويب من جهة المستخدم client-side. حيث يمكن الاستفادة من مفاهيم DOM في بناء مواقع ويب متفاعلة مثل إضافة عرض صور للموقع من خلال شاشة عرض بطريقة تدوير الصور، أو إظهار لافتة تنبّه المستخدم إلى وقوعه في خطأ عند محاولته إدخال معلومات ناقصة ضمن الحقول المطلوبة، أو التبديل في قوائم التصفح الخاصة بالموقع ويتم تنفيذ هذه العمليات الثلاث من خلال لغة الجافاسكربت ولكن من خلال معالجة DOM. في هذا المقال سنتعلم ماهو DOM وكيفية التعامل معه من خلال تعريف كائن object هو document والتمييز بين أسطر التعليمات المكتوبة بلغة HTML وبين DOM. ملاحظة: لاتعتبر DOM لغة برمجة كما الحال في لغات البرمجة الاعتيادية كالجافا أو الجافا سكربت... حيث يتم تنفيذ وتطبيق مفاهيمها من خلال لغات برمجة أخرى وهنا سنستخدم لغة الجافا سكربت. المتطلبات الأساسية لفهم كيفية تطبيق مفاهم DOM بشكل فعال وتنفيذه في تطبيقات الويب من المهم أن تكون لديك معرفة سابقة بـ HTML و CSS بالإضافة إلى معرفة القواعد الأساسية حول طريقة كتابة تعليمات الجافا سكربت وهيكليتها. ما هي DOM للبدء ببناء أي موقع ويب مهما كان بسيط سنبدأ أولًا بإنشاء مستند HTML، حيث يقوم برنامج المتصفح الذي يستخدمه الزائر لمشاهدة الموقع بتفسير تعليمات html و CSS وتحويلهم إلى المحتوى، الهيكلية والتصميم الذي نشاهده أمامنا في الصفحة النهائية. بالإضافة لذلك يقوم المتصفح ببناء تمثيل للمستند يُعرف بأنه نموذج كائن المستند Document Object Model ويسمح هذا النموذج للجافا سكربت بالوصول إلى المحتوى المُعبّر عنه كنص text أو كعناصر element ضمن المستند ورؤيتها على شكل أغراض objects. ولسهولة فهم هذا المفهوم الجديد سنتعرف عليه من خلال مثال عملي يبدأ بتطوير موقع بسيط وذلك بإنشاء ملف جديد يسمى index.html ثم حفظه ضمن مجلد المشروع project directory <!DOCTYPE html> <html lang="en"> <head> <title>Learning the DOM</title> </head> <body> <h1>Document Object Model</h1> </body> </html> إن أسطر التعليمات السابقة هي أساسيات HTML لبناء موقع ويب. سنستخدم هنا متصفح Chrome ولكن يمكنك الوصول إلى نفس الخرج من خلال المتصفحات الحديثة الأخرى. أولًا سنقوم بفتح ملف index.html الذي قمنا ببنائه من خلال متصفح chrome عندها سيظهر أمامك صفحة ويب فارغة وتحوي فقط عنوان هو "Document Object Model" ومن خلال النقر بالزر اليميني في أي مكان بالصفحة ستظهر لدينا قائمة نختار منها "Inspect" والتي ستفتح لنا على جانب الصفحة أدوات التطوير، سيظهر لدينا DOM تحت تبويب "Elements" في هذه الحالة نلاحظ أن DOM الذي قمنا بإظهاره مطابق تمامًا لتعليمات html التي كتبناها سابقًا لبناء الموقع، وعند تمرير السهم فوق العنصر سنلاحظ أن مايقابله من صفحة الويب المفتوحة أمامنا ستُظلّل باللون الأزرق، كما أن السهم الموجود على جانب كل عنصر يسمح بتوسيع العناصر المكتوبة بالداخل. كائن المستند Document كائن المستند document هو عبارة عن كائن مُدمج يملك عدد من الخصائص properties والطرق methods التي يمكن استخدامها للوصول إلى الموقع وتعديله. لفهم كيفية عمل DOM يجب أولًا فهم كيف تعمل الكائنات objects في الجافا سكريبت، ويمكن ذلك من خلال الإطلاع على مقالة برمجة الكائنات Objects في جافاسكريبت في أدوات التطوير التي أظهرناها سابقًا بجانب ملف index.html ننتقل إلى التبويبة "Console" ونكتب document داخل console ثم نضغظ زر Enter حيث نلاحظ أن الخرج هو نفسه الخرج الموجود في تبويبة "Elements" document; Output #document <!DOCTYPE html> <html lang="en"> <head> <title>Learning the DOM</title> </head> <body> <h1>Document Object Model</h1> </body> إن كتابة المستند document أو العمل مباشرة في تبويب console ليست أمرًا شائعًا نقوم به خارج debugging ولكنه يساعد في ترسيخ مفهوم كائن المستند document وكيفية تعديله ومعرفه العناصر الأخرى المدرجة ضمنه. ماهو الفرق بين DOM وتعليمات HTML حاليًا في المثال السابق الذي طرحناه تبدو لنا DOM كما لو أنها تعليمات HTML نفسها ولكن يوجد أمثلة يقوم فيها المتصفح بتوليد DOM مختلف عن تعليمات HTML مثل: تعديل DOM من خلال تعليمات الجافا سكربت والتي يتم تنفيذها في جانب المستخدم. قيام المتصفح بتصحيح الأخطاء الموجودة في التعليمات HTML المكتوبة سابقًا بشكل تلقائي. سنقوم الآن باكتشاف آلية تعديل DOM من خلال استخدام الجافا سكربت التي تُطبق في جانب المستخدم. أولًا ندخل في console التعليمة document.body; عندها سيكون الخرج: Output <body> <h1>Document Object Model</h1> </body> سنلاحظ هنا أن document هو عبارة عن كائن أما body فهو خاصية property لهذا الكائن لذلك من للوصول إلى هذه الخاصية نكتب اسم الكائن ثم نقطة ثم الخاصية document.body عندها سيكون الخرج هو جميع عناصر body الموجودة في المستند document. نستطيع من خلال console تغيير بعض الصفات الظاهرة في الكائن body في هذا الموقع فمثلًا يمكن تغيير صفات style الخاصية بالـ body مثل تغيير لون الخلفية ويتم ذلك من خلال التعليمة document.body.style.backgroundColor = 'fuchsia'; بعد كتابة هذه التعليمة وإدخالها سنلحظ تغير لون خلفية الموقع للون الفوشيا: بالانتقال إلى تبويبة "Elements" أو من خلال كتابة تعليمة document.body في console مرة أخرى سنلاحظ أن DOM قد تغير Output <body style="background-color: fuchsia;"> <h1>Document Object Model</h1> </body> ملاحظة: رأينا في المثال السابق عند تغيير لون الخلفية أن خاصية لون الخلفية في CSS والذي يكتب بالشكل background-color تمت كتابته بالشكل backgroundColor في الجافا سكربت وبنفس هذا الترتيب فإن أي خاصية في CSS يمكن كتابتها في الجافا سكربت بطريقة camelCase والتي تعني أن الحرف الأول من الكلمة الأولى صغير بينما الحرف الأول من الكلمة الثانية كبير. عند الضغط بالزر اليميني على الصفحة واختيار "View Page Source" سنلاحظ أن التعليمات الظاهرة لاتحوي على الصفة attribute الجديدة للون الخلفية الذي أضفناه من خلال الجافا سكربت وذلك لأن التعليمات الأساسية الموجودة في جانب الخادم Server التي تم بناء الموقع من خلالها لاتتأثر بأي تعليمات كتبت في جانب المستخدم بلغة الجافا سكربت، ولذلك في حال قمنا بتحديث الصفحة فإن الخاصية التي أضفناها ستُلغى. بالنسبة للحالة الأخرى التي ذكرناها عن اختلاف DOM و تعليمات HTML وهي عندما يتم تصحيح الأخطاء المكتوبة في التعليمات source code آليًا من قبل المتصفح، كمثال شائع فإن الوسم tbody يجب أن يرافق الوسم table دائمًا ولكن غالبًا لايقوم المطورون بتضمين هذه الوسوم بالشكل الصحيح ضمن HTML وعندها يقوم المتصفح تلقائيًا بتصيح هذا الخطأ وإضافة الوسم tbody وبالتالي يتم تغيير DOM. بالإضافة إلى ذلك دائمًا يصحح DOM الوسوم التي لايتم إغلاقها. ملخص عرضنا في هذه المقالة تعريف للـ DOM واستطعنا الوصول إلى كائن المستند document باستخدام الجافا سكربت وconsole من أجل تغيير خصائص تابعة للكائن document، كما تعرفنا على الفرق بين HTML و DOM. أما في المقالة التالية سنقوم باستعراض معلومات مهمة عن HTML وشجرة DOM وكيفية التعامل مع عقدها Nodes وماهي الأنواع الشائعة منها. ترجمة بتصرف للمقال Introduction to DOM وكاتبتها Tania Rascia
  11. تحدثنا في الدرس السابق عن انتقال وتحول العناصر باستخدام CSS3، وفي هذا الدرس سنتحدث عن التّحريك: التحريك Animation تقوم خاصية الانتقالات transition في CSS3 بعمل عظيم من خلال بناء التفاعلات البصرية عند الانتقال من حالة إلى أخرى وهي مثالية من أجل هذا النوع من التّغييرات المبنية على تغيّر وحيد، ولكن عند الحاجة إلى مزيد من التّحكم مثل الحاجة إلى الانتقال بين عدة حالات عندها يتم استخدام التحريك animation. تحديد إطارات العمل الرئيسية Keyframes للبدء بعملية التحريك باستخدام CSS3 يجب أولًا تحديد عدة نقاط أساسية يمر بها العنصر بشكل إجباري، ولتنفيذ ذلك نستخدم القاعدة @keyframes والتي تتضمن اسم التحريك animation name ونقاط التحريك الأساسية وخصائصهم المساهمة في تحريك العنصر. @keyframes slide { 0% { left: 0; top: 0; } 50% { left: 244px; top: 100px; } 100% { left: 488px; top: 0; } } ملاحظة: لابد من استخدام البادئات التي تدعم المتصفحات vendor prefixing مع القاعدة keyframe، كما هو الحال في جميع خصائص الانتقالات والتحريك، حيث سيكون شكل البادئات بالشكل التالي: @-moz-keyframes @-webkit-keyframes @-o-keyframes نلاحظ أنه تمّ تعريف عنصر الحركة animation بالإسم slide وذكر اسمه مباشرة بعد اسم القاعدة keyframes@ كما تم تحديد الإطارات المختلفة لهذا التحريك من خلال تحديد عدة نقاط أساسية ضمن الإطار هي النسب المئوية ابتداءًا بالقيمة 0% إلى القيمة 100% مع ذكر 50% كقيمة وسطية، أما الكلمات المفتاحية From وto فتُستخدم للتبديل بين مكاني الـ %0 و %100 كما نريد. كما أنه يمكن استخدام نقاط متوسطة إلى جانب النقطة الموجودة عند الـ %50. وقد تم إدراج خصائص العنصر الذي نقوم بتحريكه كقائمة داخل كل نقطة من نقاط التحريك كما فعلنا في المثال السابق حيث اعتبرنا أن left و top هي الخصائص. وتتم عملية التحريك animation من خلال تطبيق الانتقال transition بين النقاط الأساسية التي حددنا في الأعلى من خلال النسب المئيوة ولكن لأكثر من مرة (في هذه الحالة نطبق انتقال أول بين 0% و50% ثم آخر إلى 100%) سنقوم بتطبيق التحريك على الكرة الخضراء والتي يتم بناؤها في html: <div class="stage"> <figure class="ball"></figure> </div> والنّتيجة كما في الشّكل التّالي: وسنشرح الآن بالتفصل طريقة إضافة خصائص العناصر للوصول إلى عملية التحريك السابقة. خاصية الاسم Animation Name الآن بعد أن حددنا الإطار الأساسي للتحريك، نحتاج إلى إسناد هذا التّحريك المعرف للعنصر، ولفعل ذلك نستخدم الخّاصية animation-name التي تقوم على تعريف القاعدة keyframe@ كقيمة لهذه الخاصية، مع العلم أن هذا التّعريف يتم على العنصر الذي نريد تطبيق الحركة عليه. .stage:hover .ball { animation-name: slide; } نلاحظ أننا أسندنا الإطار الرّئيسي لعملية التّحريك slide الّذي عرفنّاه في الأعلى إلى كلا الصّفّين المحددين لرسم الكرة stage و ball ولكن تحديد قيمة animation-name فقط ليس كافٍ بل يجب تحديد خاصية animation-duration مع قيمتها لإخبار المتصفح الوقت الذي يحتاجه لإنهاء الحركة animation. مدة التحريك animation duration، تابع التوقيت Timing Function والتأخير Delay عندما نقوم بالتصريح عن الخاصية animation-name لعنصر ما فإن طريقة التّحريك ستكون مشابهة بشكل كبير للانتقال transition، حيث يحتاج لإكمال تعريفه إلى خصائص تحديد الفترة الزّمنية، تابع التوقيت، والتأخر الممكن أن يحصل. لبدء عملية التّحريك نحتاج إلى تعريف الخاصية animation-duration والّتي تشبه خاصية الفترة الزّمنية المستخدمة في الانتقالات transition حيث نحدد هذه الفترة باستخدام الثّانية او أجزاء الثّانية. .stage:hover .ball { animation-name: slide; animation-duration: 2s; } كما يمكن تحديد تابع التّوقيت animation-timing-function وقيمة التّأخير animation-delay على التّتالي. وتشبه هذه الخصائص مثيلاتها من الخصائص المستخدمة في الانتقالات، إن قيمة هذه الخّاصيات تعمل بشكل مشابه لعملها في الانتقالات transitions. .stage:hover .ball { animation-name: slide; animation-duration: 2s; animation-timing-function: ease-in-out; animation-delay: .5s; } في المثال القادم تقوم خاصية التحريك على جعل الكرة ترتد مرة خلال الحركة نحو اليسار، وهذا التصرف يحدث فقط في حال تم تمرير المؤشر فوق stage. أولًا نبين الكرة من خلال التعليمات: <div class="stage"> <figure class="ball"></figure> </div> أما تعليمات التحريك في CSS3 فهي: @keyframes slide { 0% { left: 0; top: 0; } 50% { left: 244px; top: 100px; } 100% { left: 488px; top: 0; } } .stage { height: 150px; position: relative; } .ball { height: 50px; position: absolute; width: 50px; } .stage:hover .ball { animation-name: slide; animation-duration: 2s; animation-timing-function: ease-in-out; animation-delay: .5s; } ونتيجة هذه التعليمات تظهر حركة الكرة بالشكل تخصيص التحريك: يتيح التّحريك إمكانية تخصيص الطّريقة التي يتصرف فيها العنصر، مثلًا، عدد مرات تكرار التّحريك والجّهة التي ينتهي إليها التحريك. تكرار التحريك: افتراضيًا يتم تنفيذ التحريك المعرّف مرة واحدة فقط من بدايته لنهايته، ولجعل هذا التّحريك يعيد نفسه عددًا من المرات نستخدم خاصية animation-iteration-count، حيث أن القيمة المعطاة لهذه الخّاصية ممكن أن تكون عدد صحيح محدد أو infinite والتي تعني استمرار التنفيذ عدد لانهائي من المرات. .stage:hover .ball { animation-name: slide; animation-duration: 2s; animation-timing-function: ease-in-out; animation-delay: .5s; animation-iteration-count: infinite; } نلاحظ أننا وضعنا قيمة التّكرار لانهاية وبالتّالي سيتم التّكرار بشكل دائم. اتجاه التحريك: بالإضافة إلى امكانية تحديد عدد مرات تكرار تحريك العنصر، يمكن أيضًا تحديد اتجاه الحركة الكلي باستخدام خاصية animation-direction والتي تأخذ القيم: Normal: والتي تقوم بتشغيل التحريك كما هو محدد من البداية للنهاية Reverse: وتقوم بتشغيل التحريك عكس المعرف من خلال القاعدة @keyframes أي ستكون البداية من 100% ثم تعمل بالعكس وصولًا إلى نقطة التعريف 0% Alternate: وتقوم بتشغيل التحريك نحو الأمام أي من النقطة 0% إلى النقطة 100% ثم العودة إلى الخلف من النقطة 100% إلى 0% ويمكن تحديد عدد مرات تكرار هذه الطريقة من خلال الخاصية animation-iteration-count التي يمكن أن تكون عدد صحيح من المرّات ويبدأ العد من الرقم 1. ولكن هذه الخاصية ستعمل بالعكس عند تشغيل أي تابع في خاصية animation-timing-function أي في حال استخدمنا ease-in فسيكون اتجاه الحركة من 0% إلى 100% أمّا إذا استخدمنا ease-out فعندها سيكون التحريك من 100% إلى 0%. Alternate-reverse: تجمع بين alternate و reverse حيث يكون اتجاه الحركة عند تنفيذ التحريك هي بالعكس أي من 100% إلى 0% ثم العودة مرة أخرى 100%. .stage:hover .ball { animation-name: slide; animation-duration: 2s; animation-timing-function: ease-in-out; animation-delay: .5s; animation-iteration-count: infinite; animation-direction: alternate; } بناءً على التعليمات السابقة سيتم التحريك من البداية نحو النهاية بحسب قيمة خاصية الاتجاه alternate وسيتم التكرار عدد لانهائي من المرات، أما شكل تابع الحركة فهو ease-in-out حالة تحريك العنصر: إن خاصية animation-play-state تتيح تشغيل وإيقاف التّحريك باستخدام القيم running وpaused بالتتالي، حيث أن طريقة عمل هذه الخاصية أنه عندما نشغل عنصر حركة وهو بحالة ايقاف paused، فإنه سيستمر من الحالة الحالية بدلًا من أن يبدأ من البداية من جديد. في هذا المثال، خاصية animation-play-state ستضبط عند paused، عند تفعيل حالة الحركة لدى الدائرة بالضغط عليها سنلاحظ كيف يتوقف عنصر التحريك حتى نقوم بتحرير المؤشر. .stage:hover .ball { animation-name: slide; animation-duration: 2s; animation-timing-function: ease-in-out; animation-delay: .5s; animation-iteration-count: infinite; animation-direction: alternate; } .stage:active .ball { animation-play-state: paused; } وتظهر النتيجة كما في الشكل: خاصية Fill Mode: تحدد خاصية animation-fill-mode هيئة العنصر قبل وبعد تشغيل التحريك.إن هذه الخاصية تقبل القيم: none أي لا يتم تطبيق أي تغيير على العنصر قبل أو بعد تشغيل التحريك. forwards ستحتفظ بالهيئة المحددة للعنصر بآخر إطار keyframe كان داخله العنصر وبالتالي سنلاحظ أن هذه الحالة تتأثر بقيم خصائص أخرى، هي خاصية اتجاه الحركة animation-direction وعدد مرات التكرار animation-iteration-count. backwards ستطبق الهيئات عند أول إطار رئيسي مباشرة عند تعريف هذه الخاصية قبل بدء تنفيذ التحريك. وهذا يتضمن تطبيق هذا الشكل في أي وقت قد يكون تم ضبطه من خلال خاصية التأخير. مع العلم أن هذه القيمة تتأثر بخاصية الاتجاه animation-direction. both تجمع بين القيمتين forwards و backwards معًا. .stage:hover .ball { animation-name: slide; animation-duration: 2s; animation-timing-function: ease-in-out; animation-delay: .5s; animation-fill-mode: forwards; } .stage:active .ball { animation-play-state: paused; } وسيظهر شكل حركة الدّائرة هكذا: اختزال تعليمات التحريك: لحسن الحظ يمكن تحديد صيغة مختصرة لعناصر التحريك، كما هو في الانتقالات. ويتم ذلك باستخدام الخاصية animation حيث يمكن ترتيب القيم التابعة للخصائص الأخرى داخل الخاصية animation بالشكل التالي: animation-name animation-duration animation-timing-function animation-delay animation-iteration-count animation-direction animation-fill-mode animation-play-state حيث يمكن اختزال التعليمات في الأمثلة السابقة إلى الشكل: .stage:hover .ball { animation: slide 2s ease-in-out .5s infinite alternate; } .stage:active .ball { animation-play-state: paused; } وكما نلاحظ أنه لايتم الفصل بين قيم الخصائص بفاصلة أبدًا. ترجمة-بتصرف- لمقال: Transition & Animation
  12. أهم التّطورات التي ظهرت في CSS3 هي القدرة على نقل العنصر من شكل أو مكان لآخر بالإضافة إلى تحريكها ضمن مجموعة من الخطوات المتسلسلة. منذ عدة سنوات يطالب المبرمجون العاملون في تطوير واجهات الإنترنت بإدخال ميزة القدرة على إدخال تفاعلات وتحريك العناصر من خلال html و CSS من دون الحاجة للدخول في الجافاسكربت أو الفلاش، وقد أصبحت هذه الإمكانية ممكنة الآن في CSS3. طريقة عمل انتقال العناصر transitions هي من خلال تغيير طريقة ظهور أو هيئة العنصر نفسه عند وقوع حدث معين مثل النقر على العنصر أو مرور المؤشر عليه مثلًا أي هو تغير حالة العنصر إلى حالة أخرى، أما طريقة التحريك Animation فهي تغيير مكان العنصر أو هيئته لأكثر من مرة ويتم من خلال اختيار مجموعة من النقاط الممكنة ضمن الإطار الرئيسي للصفحة ويتم تغيير مظهر أو حالة العنصر. الانتقالات Transition: كما ذكرنا سابقًا يتم تنفيذ عملية الانتقال للعنصر من خلال تغيير حالة العنصر، حيث يجب تعريف كل حالة والتصميم style الذي سيُطبق على العنصر في هذه الحالة، وأسهل طريقة لتحديد الحالة التي يقع فيها العنصر الآن والتصميم الذي يجب أن يأخذه من خلال استخدام صفوف اللاحقة pseudo-classes وهي :target - :active - :focus - :hover ويوجد أربع خصائص مرتبطة بالانتقال transition بشكل عام وهي transition-property، transition-duration، transition-timing-function و transition-delay مع ملاحظة أنه يمكن استخدام البعض منها فقط لبناء عملية الانتقال. سنستعرض في المثال التالي طريقة الانتقال الّتي ستتم لمدة ثانية واحدة من خلال تغيير لون خلفية المربع المرسوم عند تمرير المؤشر فوقه: نبني المربع ككتلة div من خلال تعليمة html <div class="box">Box</div> و لتنفيذ الانتقال على المربع المرسوم يتم ذلك من خلال تعليمات CSS فقط وهي: .box { background: #2db34a; transition-property: background; transition-duration: 1s; transition-timing-function: linear; } .box:hover { background: #ff7b29; } حيث يظهر المربع باللون الأخضر وعند تمرير المؤشر فوقه يتغير لون خلفية المربع وتم تحديد ذلك من خلال الخاصية transition-property لمدة ثانية واحدة فقط وتم تخصيص الزمن من خلال الخاصية transition-duration ويمكن معاينة المثال بشكل مباشر من الرابط. ومن الأفضل كتابة التعليمات من خلال إضافة vendor prefixes والتي تعمل على تأمين دعم كافة المتصفحات .box { background: #2db34a; -webkit-transition-property: background; -moz-transition-property: background; -o-transition-property: background; transition-property: background; -webkit-transition-duration: 1s; -moz-transition-duration: 1s; -o-transition-duration: 1s; transition-duration: 1s; -webkit-transition-timing-function: linear; -moz-transition-timing-function: linear; -o-transition-timing-function: linear; transition-timing-function: linear; } .box:hover { background: #ff7b29; } خاصية transition-property تحدد خاصية transition-property خصائص العنصر التي سوف يتم تعديلها تزامنًا مع تنفيذ خصائص الانتقال الأخرى، ولايتم تغييير قيمة أي خاصية أخرى للعنصر غير مذكورة ضمن هذه الخاصية. في المثال السابق تم تغير قيمة background التابعة للعنصر لأنها ذكرت كقيمة للـ transition-property خلال زمن قدرة ثانية واحدة، في حين أن الخصائص الأخرى للعنصر ستبقى محافظة على قيمتها ولن تتأثر بالقيم المعطاة للـ transition-duration وtransition-timing-functionفي حال كنا نريد تغيير قيمة أكثر من خاصية عند تطبيق الانتقال يمكن إدراجها في الخاصية نفسها transition-property مع وضع فواصل فيما بينها. .box { background: #2db34a; border-radius: 6px transition-property: background, border-radius; transition-duration: 1s; transition-timing-function: linear; } .box:hover { background: #ff7b29; border-radius: 50%; } نلاحظ أن الخصائص التي نريد تغييرها عند الانتقال هيَّ background و border-radius وتم تحديد الانتقال بـ .box:hover حيث يتم تنفيذها كما في الّشكل: خصائص العنصر القابلة للانتقال ليست كل خصائص العنصر قابلة للتّغيير وإنما فقط تلك العناصر التي يمكن ملاحظة التّغيير الذي يطرأ عليها مثل الألوان، والحجم. حيث يمكن أن تتبدل من قيمة إلى أخرى. مثلًا لا يمكن تغيير الخاصية display لأنها لاتمتلك نقطة تعريف يمكن ملاحظة التغيير الذي يحصل من خلالها. أهم الخصائص التي يمكن تطبيق الانتقال عليها: background-color background-position border-color border-width border-spacing bottom clip color crop font-size height left letter-spacing line-height margin max-height max-width min-height min-width opacity outline-color outline-offset outline-width padding right text-indent text-shadow top vertical-align visibility width word-spacing z-index خاصية transition-duration يتم تعيين المدة الزمنية التي تتم فيها عملية الانتقال باستخدام خاصية transition-duration وتحديد قيمتها من خلال استخدام وحدات الزمن العامة بما في ذلك الثانية (s) والميلي ثانية (ms) كما يمكن أن تكون القيمة عبارة عن كسور مثل أجزاء من الثانية (0.2s). عندما يُطبق الانتقال على أكثر من خاصية للعنصر يمكن أن يتم تطبيق قيمة فترة زمنية محددة لكل خاصية، أي كما تحتوي خاصية transition-property على أكثر من قيمة يمكن أن تحتوي خاصية transition-duration أيضًا على أكثر من قيمة، وكل خاصية تأخذ المدة الزمنية حسب ترتيب قيم الخصائص (في المثال الذي سنستعرضه ستكون الفترة الزمنية 0.2s تابعة لخاصية background، أما خاصية border-radius فستكون لمدة ثانية واحدة) ويمكن أيضًا أن نُعطي مدة زمنية واحدة لجميع الخصائص المراد تغييرها. .box { background: #2db34a; border-radius: 6px; transition-property: background, border-radius; transition-duration: .2s, 1s; transition-timing-function: linear; } .box:hover { background: #ff7b29; border-radius: 50%; } والنتيجة تكون واضحة كما في الصورة: خاصية سرعة الانتقال Transition Timing تُستخدم خاصية transition-timing-function لضبط سرعة الانتقال. حيث يمكننا تحديد المدة الزمنية من خلال الخاصية transition-duration. ويمكن إعطاء خاصية transition-timing-function عدة قيم هي: liner: عند إعطاء الخاصية المذكورة هذه القيمة فإن الانتقال بسرعة ثابتة خلال المدة الزمنية المحددة. ease-in: تعني هذه القيمة أن الانتقال يبدأ ببطئ ويستمر بالتسارع طول فترة الانتقال. ease-out: تعني أن الانتقال يبدأ بسرعة ويستمر بالتباطؤ طوال فترة الانتقال. ease-in-out: الانتقال يبدأ ببطئ ويستمر بالتسارع حتى يصل إلى الوسط عندها يبدأ بالتباطؤ مرة أخرى حتى انتهاء المدة المحددة للانتقال. إن تابع الانتقال يُحدّد كتابع رياضي لمنحنى مكعب بيزيير، هذه التوابع الزمنية هي تابعة لمنحنى بيزيير ويمكن رسمها من خلال القيم (x1, y1, x2, y2) بالإضافة إلى قيمتَي نقطة البداية ونقطة النّهاية، أو من خلال الخطوات المحددة بشكل مميز، من خلال عدد الخطوات واتجاه الحركة. عندما يمتلك الانتقال عدة خصائص فإنه يتم تحديد عدة توابع لطريقة الانتقال، ففي مثالنا خاصية background كان تابعها هو linear أما خاصية border-radius فإن تابع الانتقال الخاص بها هو ease-in .box { background: #2db34a; border-radius: 6px; transition-property: background, border-radius; transition-duration: .2s, 1s; transition-timing-function: linear, ease-in; } .box:hover { background: #ff7b29; border-radius: 50%; } كما هو واضح في الصورة، أو يمكن رؤية المعاينة المباشرة من هنا. التأخير في الانتقال Transition Delay يمكن أيضًا تعريف خاصية transition-delay بأنها تتحكم بتأخير تحول العنصر، فمن خلالها يتم تحديد الزمن الذي سيتم تأخير تحول العنصر بمقداره، ويحدد بالثواني أو الملي. وكما هو الحال في جميع الخصائص الأخرى يمكن تحديد أكثر من قيمة منفصلة خاصة بالتأخير من خلال الفصل بينها بالفاصلة فقط. .box { background: #2db34a; border-radius: 6px transition-property: background, border-radius; transition-duration: .2s, 1s; transition-timing-function: linear, ease-in; transition-delay: 0s, 1s; } .box:hover { background: #ff7b29; border-radius: 50%; } نلاحظ عند تنفيذ التعليمات السابقة أن لون الخلفية تغير مباشرة بدون أي تأخير زمني، وأن قيمة التأخير الأولى المقابلة للخلفية هي 0 ثانية، بينما شكل الحدود وانحنائها تغير بعد ثانية واحدة. اختزال تعليمات الانتقال من الممكن أن يصبح ملف التعليمات كبيرًا جدًا في حال أضفنا تعليمات الانتقال بشكل فردي، وسيكون الوضع أسوأ لو أضفنا البادئات الخاصة بدعم المتصفحات vendor prefixes. ولحسن الحظ يوجد خاصية الاختزال لخصائص الانتقال، مع الاحتفاظ بالقدرة على احتواء كل الخصائص وقيمها. ويتم ذلك من خلال استخدام الخاصية transition فقط ونكتب فيها قيم الخصائص بالتّرتيب التّالي: transition-property transition-duration transition-timing-function transition-delay ولا يتم وضع فاصلة بين هذه القيم إلا إذا كنت تريد تعريف أكثر من انتقال. فمثلًا يمكن كتابة التعليمات من المثال السابق بشكل مختزل، بحيث نذكر الانتقال الأول وهو تغير لون الخلفية خلال 0.2 جزء من الثانية والحركة تتسارع صعودًا، ثم وضعنا فاصلة لذكر تفاصيل الانتقال الثاني وهو تغيير شكل حدود المربع لتصبح دائرية. .box { background: #2db34a; border-radius: 6px; transition: background .2s linear, border-radius 1s ease-in 1s; } .box:hover { color: #ff7b29; border-radius: 50%; } يوجد العديد من الأمثلة التي تبين طريقة استخدام CSS3 في الانتقالات وإضفاء طابع الحركة على العناصر من دون الحاجة إلى استخدام جافا سكربت، من هذه الأمثلة طريقة إنشاء زر يعطي انطباع الضغط عند النقر عليه. أولًا نبني الزر من خلال html <button>Awesome Button</button> ومن ثم نضيف تعليمات الانتقال على الزر من خلال CSS button { border: 0; background: #0087cc; border-radius: 4px; box-shadow: 0 5px 0 #006599; color: #fff; cursor: pointer; font: inherit; margin: 0; outline: 0; padding: 12px 20px; transition: all .1s linear; } button:active { box-shadow: 0 2px 0 #006599; transform: translateY(3px); } وتظهر المعاينة كمافي الصورة: ويمكن استخدام خاصية التحول لقلب العناصر إلى الاتجاه الآخر أيضًا، مثل قلب صورة إلى الجزء الخلفي منها. أولًا نكتب تعليمات بناء بطاقة الصورة من خلال html <div class="card-container"> <div class="card"> <div class="side"><img src="https://academy.hsoub.com/uploads/monthly_2016_01/SiteLogo-346x108.png.dd3bdd5dfa0e4a7099ebc51f8484032e.png" alt="أكاديمية حسوب"></div> <div class="side back">أكاديمية حسوب</div> </div> </div> ثم نبدأ بكتابة تعليمات تحريك البطاقة وقلبها إلى الجزء الآخر من خلال CSS3 .card-container { height: 108px; perspective: 600; position: relative; width: 346px; background: #444; } .card { height: 100%; position: absolute; transform-style: preserve-3d; transition: all 1s ease-in-out; width: 100%; } .card:hover { transform: rotateY(180deg); } .card .side { backface-visibility: hidden; height: 100%; position: absolute; width: 100%; } .card .back { transform: rotateY(180deg); } حيث تُقلب البطاقة إلى الوجه الآخر عند مرور المؤشر فوقها، ويمكن رؤية المعاينة من الرابط المباشر ترجمة-بتصرف- لمقال: Transition & Animation
  13. مستطيلات داخل مستطيلات هذا هو التصميم المعتمد عند الغالبية في بناء صفحات الويب الخاصة بنا، ولكننا نحاول الآن كسر هذه الصورة النمطية عن أشكال CSS من خلال استخدام أشكال هندسية جديدة، ولكن هذه الأشكال Shapes لا تتفاعل أبدًا مع محتوى الشكل أو مع العناصر الأخرى داخل الصفحة. يتم الآن وضع تخصيصات جديدة للأشكال في CSS لتغيّر من هذا الجمود في الأشكال. ففي منتصف عام 2012 وضعت شركة Adobe هدفًا جديدًا أمامها ألا وهو تزويد مصممي الويب بطريقة جديدة تغير من الطريقة النمطية في انسياب المحتوى داخل وحول الأشكال المعقدة - وهو أمر جديد تمامًا لم نكن قادرين في تطبيقه حتى باستخدام JavaScript. على سبيل المثال، ستلاحظ في التصاميم الجديدة التي تعتمد على الأشكال المتعددة انسياب النص حول الصور الدائرية بشكل سلسل وجميل، ولكن بدون هذه الأشكال سيتوضع النص بشكل مستطيل من دون أي تأثيرات إضافية، وبالتالي نجد أن هذه الطريقة الجديدة في التعامل مع الأشكال ستأخد التصاميم إلى مستوى جديد من التطور. دعم المتصفحات إن المتصفحات التي تدعم الأشكال CSS Shapes Module 1 بالنسبة لنسخة الحاسب حتى نهاية عام 2017 كانت Chrome، Firefox، Opera و (Safari(WebKit، بينما لايقدم متصفح Internet Explorer هذا الدعم. ويختلف الأمر بالنسبة لنسخة المتصفحات الخاصة بالموبايل حيث أن متصفح Safari هو فقط من يدعم خاصية الأشكال. ومع ذلك فقد وصلت تقنية الأشكال في CSS المرحلة الأولى منها إلى أن جميع المتصفحات تتعرف على طريقة كتابة تعريف الأشكال Syntax الخاصة بها وتخصيصها وبالتالي تعتبر برمجة الأشكال مستقرة جدًا ولن يكون الوقت طويلًا حتى يتم تنفيذها بشكل كامل على جميع المتصفحات. إن المرحلة الأولى من تطوير الأشكال في CSS يركز على الخصائص التي تغير من طريقة انسياب المحتوى حول الشكل أي أنها تركز على خاصية shape-outside بالإضافة إلى الخصائص الأخرى المتعلقة بها. ومن المتوقع أن استخدام هذه الميزة الجديدة في الأشكال عند CSS ودمجها مع الميزات الأخرى المتطورة مثل الإخفاء والقص Clipping and Masking، طريقة معالجة العنصر قبل عرضه CSS Filters، و مزج وتركيب العناصر معًا Compositing and Blending سيسمح لنا ببناء الأشكال وتصميمها بشكل احترافي متطور من دون الحاجة إلى المحررات المستخدمة في التصميم كالفوتوشوب و InDesign. سيركّز بناء الأشكال في CSS مستقبلًا على طريقة التفاف المحتوى داخل الشكل نفسه أيضًا. فعلى سبيل المثال، من السهل اليوم بناء المعين باستخدام CSS حيث يتوجب علينا فقط تدوير العنصر بزاوية 24 درجة ثم تدوير المحتوى داخله وبالتالي يعود المحتوى ليتوضع أفقيًا ضمن الصفحة. ولكن المعين لن يكون هو الأب الحاوي container للمحتوى، وبالتالي سيبقى المحتوى مستطيل الشكل. ولحل هذه المشكلة يمكن استخدام الخاصية shape-inside والتي تقوم بجعل المحتوى يأخذ شكل معين داخل التصميم أيضًا. بناء شكل باستخدام CSS يمكن تطبيق شكل على أي عنصر element من خلال استخدام خاصية الأشكال shape property. حيث نمرر خاصية الشكل كتابع shape function يمرر مجموعة من المعاملات parameters التي تعرف الشكل الذي نريد تطبيقه مثل نقطة المركز ونصف القطر إذا كان الشكل دائرة أو مجموعة من النقاط على المحيط إذا كان الشكل مضلعًا. من الأشكال التي يمكن بناؤها باستخدام التوابع التالية ()circle ()ellipse ()inset ()polygon حيث يعرف كل شكل من خلال مجموعة من النقاط. فبعض التوابع تمرر هذه النقاط كمعاملات، وبعضها الآخر يمرر نقطة واحدة ومقدار الإزاحات ولكن في النهاية جميعها ترسم الأشكال من خلال مجموعة من النقاط. حيث سنقوم بتعريف هذه المتحولات الممررة عند بنائنا للأشكال في المثال القادم من خلال هذه التوابع. كما يمكن تعريف الشكل من خلال استخراجه من صوة باستخدام قناة ألفا وذلك عند تمرير الصورة لخاصية الشكل حيث سيقوم المتصفح باستخراج الشكل بالاعتماد على shape-image-threshold. يُعرف أي شكل من خلال البكسلات التي تحوي قيمة ألفا أكبر من القيمة البدائية كما أن الصورة يجب أن تكون متوافقة مع شروط CORS وبالتالي في هذه الحالة إذا لم يتم إظهار الصورة في الصفحة لأي سبب (مثل أن تكون غير موجودة أو محذوفة) عندها ستيم إظهار شكل الإطار الخاص بها. خصائص الشكل shape proprties التي تقبل أن تمرر التوابع التي ذُكرت في الأعلى كقيمة لها هي: shape-outside: تقوم هذه الخاصية بتخصيص طريقة التفاف المحتوى حول الشكل من الخارج. shape-inside: تقوم هذه الخاصية بتخصيص طريقة التفاف المحتوى داخل الشكل. كما يمكن استخدام خاصية shape-outside بالتزامن مع استخدام خاصية shape-margin من أجل إضافة هامش حول الشكل الذي نخصص طريقة انسياب المحتوى حوله وبالتالي بناء فراغ صغير بين المحتوى والشكل. وبنفس الطريقة يمكن استخدام خاصية shape-padding مع خاصية shape-inside من أجل إضافة هامش داخلي بين المحتوى داخل الشكل وحدود الشكل. باستخدام خصائص الشكل وتوابعه يمكن تعريف الشكل كعنصر element من خلال إضافة سطر واحد من تعليمات CSS. .element { shape-outside: circle(); /* content will flow around the circle defined on the element */ } أو من خلال التعليمة التالية حيث ندخل لها رابط الوصول إلى الشكل الذي ستستخرج منه الشكل المطلوب. .element { shape-outside: url(path/to/image-with-shape.png); } ولكن عند تطبيق هذا السطر من تعليمات CSS على الشكل لن يظهر الشكل المطلوب إلا إذا تحقق الشرطين التاليين: يجب أن يكون العنصر element عائمًا floated حيث أنه من الممكن مستقبلًا أن نحدد الأشكال على العناصر غير العائمة ولكن ليس الآن. يجب أن يكون العنصر لديه أبعاد واضحة لأنه سيتم استخدام الارتفاع والعرض لبناء العنصر ضمن نظام الإحداثيات. يتم تعريف الأشكال من خلال مجموعة من النقاط لأنها تملك إحداثيات ديكارتية حيث أن نظام الإحداثيات الديكارتية هو الذي يُعرف من خلاله مواقع العناصر ضمن المتصفح لذلك عند تعريف العنصر element في المثال في الأعلى يتوجب علينا إضافة الارتفاع والعرض بشكل محدد. .element { float: left; height: 10em; width: 15em; shape-outside: circle(); } مع العلم أن إضافة هذه الأبعاد لا تؤثر على طريقة استجابة الشكل نفسه. وبما أن كل شكل يتم تعريفه بواسطة مجموعة من النقاط التي يتم وضعها باستخدام زوج من الإحداثيات فإن تغيير إحداثيات نقطة ما سيؤثر بشكل مباشر على الشكل الذي تم إنشاؤه. فمثلًا يمكننا إنشاء مضلع مسدس الشكل من خلال استخدام تابع ()polygon حيث يتكون الشكل من ست نقاط ولكن عند تغيير إحداثيات إحدى النقاط الأفقية (النقطة الملونة باللون البرتقالي) عندها سيتغير الشكل الناتج إلى شكل آخر وبالتالي سيؤثر على طريقة تدفق المحتوى داخل الشكل وخارجه. المربع المرجعي للأشكال يتم تعريف وبناء أي شكل يُرسم باستخدام CSS داخل ما يسمى بالمربع المرجعي الذي يستعمل لرسم الشكل على العنصر. يمتلك أي عنصر ارتفاع وعرض بالإضافة إلى مربع هامش الذي يحدد المسافة بين العنصر وباقي العناصر في الصفحة margin-box، مربع المحتوى content-box، مربع الهامش الداخلي الذي يحدد المسافة بين المحتوى وحدود الشكل padding-box، مربع الحدود border-box. حيث تُستخدم إحدى هذه المربعات كمرجع لرسم الشكل على العنصر. افتراضيًا يُستخدم مربع الهامش margin-box كنقطة البداية الأساسية لأي عنصر وبالتالي إذا أردنا تطبيق الشكل على العنصر سيتم أولًا إزاحة الشكل إلى الأسفل ثم تمديده من أسفل زاوية الهامش إلى باقي مربع الهامش margin-box. كما يمكن استخدام قيمة مربع أخرى كمرجع بالإضافة إلى مربع الهامش ويتم ذلك من خلال تمرير اسم المربع بجانب تابع الشكل ()circle إلى خاصية الشكل المُستخدمة shape-outside. shape-outside: circle(250px at 50% 50%) padding-box; الكلمة المفتاحية padding-box هي التي خصصت بأن الشكل سيتم تطبيقه على العنصر بالنسبة إلى منطقة الهامش الداخلية للعنصر، أما تابع ()circle فعرّف الشكل الدائري المراد رسمه من خلال ذكر حجمه ومكان توضعه. تعريف الأشكال باستخدام التوابع سنبدأ بمثال عن طريقة التفاف النص حول صورة شخصية لمستخدم دائرية الشكل مثل تلك الصور المستخدمة في الصفحات التعريفية عن الأشخاص حيث ستكون التعليمات في ملف HTML لبناء الصورة وكتابة المحتوى <img src="http://api.randomuser.me/0.3.2/portraits/men/7.jpg" alt="profile image" /> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Harum itaque nam blanditiis eveniet enim eligendi quae adipisci?</p> <p>Assumenda blanditiis voluptas tempore porro quibusdam beatae deleniti quod asperiores sapiente dolorem error! Quo nam quasi soluta reprehenderit laudantium optio ipsam ducimus consequatur enim fuga quibusdam mollitia nesciunt modi.</p> أما ضمن ملف CSS فسنستخدم تابع ()circle من أجل تطبيق الشكل الدائري على الصورة الشخصية التي وضعناها في ملف HTML. سنستخدم من أجل تدوير الصورة الخاصية border-radius حيث لا تمتلك هذه الخاصية أي تأثير على طريقة انسياب المحتوى داخل أو حول الصورة التي نطبق عليها الشكل. وبمعنى آخر لا تمتلك هذه الخاصية التأثير على منطقة المحتوى content area المتواجدة داخل العنصر element الذي قمنا ببنائه ولا على المحتوى الخارجي حول العنصر، وإنما يكون تأثيرها فقط على شكل الحدود الخاصة بالعنصر وخلفيته إن وُجدت. أي سيبقى المحتوى داخل العنصر مستطيل الشكل والمحتوى الموجود خارج العنصر سينظر ويتعامل مع العنصر نفسه كما لوكان مستطيل الشكل. أي أننا نقوم باستخدام الخاصية border-radius من أجل جعل الصورة فقط دائرية الشكل. img { float: left; width: 150px; height: 150px; border-radius: 50%; margin-right: 15px; } تبين الصورة السابقة الشكل الذي سيظهر به المحتوى حول الصور الدائرية في المتصفحات القديمة التي التدعم الأشكال في CSS، ولتغيير هذا الشكل المربع في الانسياب حول الصورة الدائرية سنقوم بتعديل التعليمات بما يتوافق مع الشكل الذي أضفناه من خلال استخدام خصائص الشكل img { float: left; width: 150px; height: 150px; border-radius: 50%; shape-outside: circle(); shape-margin: 15px; } حيث سنرى في صفحة الويب عند استخدام هذه التعليمات شكلًا دائريًا مُطبّقًا على الصورة وسيظهر المحتوى ملتفًا حول الدائرة كما في أول صورة شاهدناها في المثال. الطريقة الأساسية في استخدام تابع الدائرة هي circle() = circle( [<shape-radius>]? [at <position>]? ) إن علامة الاستفهام ? في التعليمة السابقة تشير إلى أن هذه المعاملات الممررة اختيارية ولكن في حال عدم تمريرها سيأخذ التابع القيمة الافتراضية الموجودة في المتصفح وهي رسم الشكل ابتدءًا من مركز العنصر الذي نطبق عليه الشكل. يتم تحديد طول نصف قطر الدائرة بأحد وحدات الطول (البكسل px، النقطة pt…) ويقوم المتصفح برسمه ابتداءً من نقطة المركز وصولًا إلى إحدى أضلاع المستطيل المرجع ويحدد ذلك الخاصية خاصية closest-side: وهي الخاصية الافتراضية وتعني رسم نصف القطر وصولًا إلى أقرب ضلع من أضلاع المستطيل. خاصية furthest-side: وتعني أن المتصفح سيرسم نصف القطر وصولًا إلى أبعد ضلع من أضلاع المستطيل يمكن تحديد هذه الخاصية وتمريرها كمعامل لتابع رسم الدائرة عند خاصية shape-outside shape-outside: circle(farthest-side at 25% 25%); /* defines a circle whose radius is half the length of the longest side, positioned at the point of coordinates 25% 25% on the element’s coordinate system*/ shape-inside: circle(250px at 500px 300px); /* defines a circle whose center is positioned at 500px horizontally and 300px vertically, with a radius of 250px */ يعمل تابع رسم الإهليلج (القطع الناقص) ()ellipse فهو يعمل بنفس الطريقة التي يعمل فيها تابع ()circle وبنفس القيم ماعدا أنه بدلًا من تمرير تابع واحد لنصف القطر يجب تمرير اثنين أحدهما من أجل طول نصف القطر على محور الإحداثيات X والآخر لنصف القطر على محور الإحداثيات Y ellipse() = ellipse( [<shape-radius>{2}]? [at <position>]? ) في حيث أن تابع ()inset يُستخدم لرسم شكل مستطيل مدور الزوايا ويمكن أن نجعل المحتوى ينساب حول هذه الزوايا بشكل دائري أيضًا. ونمرر لتابع ()inset: أربع قيم إزاحة تحدد قيمة إزاحة الحواف بالنسبة لصندوق المرجع نحو داخل العنصر. معامل اختياري وهو قيمة تدوير الزوايا round حيث يتم تحديد القيمة من خلال border-radius بنفس طريقة استخدامها في تدوير الدائرة التي شرحناها سابقًا، ولكن هنا يجب تمرير أربع قيم. inset() = inset( offset{1,4} [round <border-radius>]? ) من خلال التعليمات التالية سنقوم برسم مستطيل دائري الزاويا على عنصر عائم floated element .element { float: left; width: 250px; height: 150px; shape-outside: inset(0px round 100px) border-box; } تابع الأشكال الأخير الذي سنتحدث عنه هو ()polygon الذي يعرف مجموعة معقدة من الأشكال الهندسية من خلال تحديد عدد من النقاط المعرفة بإحداثياتها الديكارتية (point(x,y والتي من خلالها يتحدد موقع النقطة ديكارتيًا على المتصفح. في المثال التالي نشاهد صورة عائمة في يمين الصورة وتغطي ارتفاع كامل مساحة العرض المتاحة لها، ونريد أن نظهر النص الذي على يسارها بشكل انسيابي حول الساعة الرملية الموجودة داخل الصورة ولذلك سنستخدم تابع ()polygon من أجل تعريف هذا الشكل غير المنتظم على الصورة. تعليمات CSS التي ستقوم بهذه العملية هي img.right { float: right; height: 100vh; width: calc(100vh + 100vh/4); shape-outside: polygon(40% 0, 100% 0, 100% 100%, 40% 100%, 45% 60%, 45% 40%); } يمكن تعيين إحداثيات النقط التي تعرف الشكل من خلال وحدات الطول أو من خلال النسب المئوية التي استخدمتها. ستُنتج التعليمات السابقة الشكل الذي شاهدناه في الأعلى حيث أنها لا تؤثر على باقي أجزاء الصورة الموجودة خارج الشكل المرسوم كما نلاحظ، لأن تطبيق شكل العنصر فقط يؤثر على منطقة تدفق المحتوى من دون أن يغيّر أي شيء في الخلفيات والحدود. ومن أجل إظهار شكل المضلع الذي رسمناه فإننا بحاجة إلى قص أجزاء من الصورة خارج الشكل وذلك من خلال استخدام خاصية clip-path الموجودة في نموذج الإخفاء CSS Masking Module. تقوم خاصية clip-path على أخذ نفس شكل التابع المستخدم والقيم الممررة له. وبالتالي إذا استخدامنا نفس الشكل المضلع الذي مررناه للخاصية shape-outside فإن هذه الخاصية ستعمل على قص كل أجزاء الصورة خارج الشكل المحدد img.right { float: right; height: 100vh; width: calc(100vh + 100vh/4); shape-outside: polygon(40% 0, 100% 0, 100% 100%, 40% 100%, 45% 60%, 45% 40%); /* clip the image to the defined shape */ clip-path: polygon(40% 0, 100% 0, 100% 100%, 40% 100%, 45% 60%, 45% 40%); } وستكون النتيجة النهائية لهذه التعليمات كما في صورة في الوقت الحالي يتم دعم خاصية clip-path من خلال إضافة بادئة إليها أولًا، مثلًا ستعمل هذه الخاصية مع Chrome إذا أضفنا لها -webkit-. إن خاصية clip-path هي أفضل إضافة مع خصائص الأشكال الأخرى لأنها تساعد في إظهار الأشكال المُنشأة من خلال قص أي جز من العناصر خارج الشكل. تابع()polygon يأخذ أيضًا خيارًا آخر هو إما nonzero أو evenodd والتي تحدد كيفية التعامل مع المناطق داخل الشكل المضلع التي تتقاطع مع نفسها ولمعرفة المزيد عن هذه الخصائص يمكن الإطلاع على SVG fill-rule تعريف شكل باستخدام صورة لتعريف شكل باستخدام صورة نحتاج إلى صورة من نمط ألفا والتي يمكن للمتصفح أن يحللها. يُعرّف الشكل بأنه مجموعة من البكسلات التي تمتلك قيمة ألفا أكبر من القيمة الحدّية والتي هي افتراضيًا القيمة 0 (أي شفافة تمامًا) أو يمكن تحديد قيمتها صراحة من خلال الخاصية shape-image-threshold.وبالتالي سيتم استخدام أي بكسل ليس شفافًا كجزء من الشكل المُعرّف من قبل الصورة. في مراحل قادمة من تعريف أشكال CSS قد يتم استخدام بيانات الإضاءة من الصورة بدلًا من استخدام بيانات ألفا. وفي حال حدوث هذا فإن خاصية shape-imgae-threshold سيتم توسيعها لتستخدم مع الإضاءة أو ألفا. الآن سنقوم باستخدام الصورة التالية من أجل تعريف شكل على عنصر ومن ثم تحديد طريقة التفاف النص حول الشكل: باستخدام الخاصية shape-outside قيمة()url التي تشير إلى الصورة نستطيع جعل المحتوى ينساب حول العنصر الذي يحمل شكل الورقة. .leaf-shaped-element { float: left; width: 400px; height: 400px; shape-outside: url(leaf.png); shape-margin: 15px; shape-image-threshold: 0.5; background: #009966 url(path/to/background-image.jpg); mask-image: url(leaf.png); } وبالطبع في حال أردنا تطبيق الصورة كخلفية في العنصر فإنه يجب قص الصورة الموجودة خارج الشكل من خلال الخاصية mask-image حيث أنه لايمكن استخدام خاصية clip-path مع الصور صاحبة القيمة ألفا، والنتيجة ستكون بالشكل التالي وفي حال قمنا ببناء أشكال معقدة ونريد تعريفه باستخدام صورة. فإنه يمكن استخدام الصور ذات قناة ألفا من خلال الفوتوشوب بدلًا من تعريفها يدويًا. كما يمكن استخدام الصورة بدلًا من تابع الشكل عندما يكون لديك عدة مناطق عائمة أو مناطق مستبعدة داخل العنصر نفسه ولكنه لايمكن تعريف عدة أشكال في العنصر نفسه ولكن إذا كانت الصورة تحوي على عدة مناطق يقوم المتصفح باستخراج هذه المناطق من الصورة واستخدامهم. تفاعل الأشكال في CSS مع التصميم المُتجاوب هل تستطيع الأشكال المبنية باستخدام CSS تتناسب مع التصميم المتجاوب؟ التخصيص الحالي لخاصية shape-outside تستطيع العمل مع التصميم المتجاوب وذلك لأنه يسمح بتحديد أبعاد العنصر من خلال النسب المئوية أو أحد وحدات الطول، ويمكن تحديد نقاط الشكل الممررة كمعاملات لتابع الشكل. وهذا يعني أن خاصية العنصر shape-outside ستكون متجاوبة responsive بشكل كامل ولكن خاصية shape-inside ليست متجاوبة responsive إلى الآن ولكن سيتم إدخالها في المرحلة الثانية حتى يتم حل بعض الحدود. الأدوات المستخدمة لبناء أشكال CSS إن بناء الأشكال المعقدة بالاعتماد على تابع Shape يعد مهمة شاقة خصوصًا عند بناء العديد من الأشكال المضلعة المعقدة من خلال تمرير الإحداثيات الديكارتية لعدة نقاط من الشكل باستخدام التابع ()polygon ولكن لحسن الحظ فإن Adobe وفرّت العديد من الأدوات التفاعلية التي تقوم بهذا العمل بشكل أسهل. هنا يوجد عدة أدوات تسمح لنا ببناء الأشكال المضلعة من خلال الرسم ثم تتولى الأداة توليد تابع الأشكال الخاص بها. ولكن يوجد بعض المحدوديات في هذه البرامج إذا أردنا رسم الشكل بالاعتماد على صورة محددة لأنه لايوجد طريقة لإدخال الصورة إلى الأدوات ومن ثم إنشاء الشكل من خلالها. الأداة الأكثر تطورًا في هذا المجال لرسم الأشكال التفاعلية المتطورة بناها فريق تطوير الويب التابع لمنصة Adobe وتم وضعها كإضافة لمحرر brackets المجاني الخاص بالشركة. مستقبل الحالات المبعدة من المعالجة في CSS لبناء أي شكل في CSS يجب أن يكون خاضعًا لعدد من المواصفات المحددة، فمثلًا لا يمكن تعريف الخاصيتين shape-inside و shape-outside إلا لأشكال مخصصة ولكن الحالات المقصاة هذه سيتم تعريف خصائص تسمح بالتفاف المحتوى حول العناصر مهما كان شكلها حتى لو لم تكن عائمة في المرحلة التالية من تطوير الأشكال في CSS وسيصبح بالإمكان التفاف النص على كامل الشكل من اتجاهات مختلفة كما في المثال الموضح بالصورة ترجمة –وبتصرّف- للمقال CSS Shapes 101 لصاحبته Sara soueidan
  14. قمنا مؤخرًا بتحديث تصميم صفحة "?Why Should You Attend" التي كانت ماتزال تحتفظ بالتصميم القديم للموقع وقد احتجنا إلى أن نوائمها مع باقي الموقع. وعندما بدأنا العمل عليها قررنا أن نحسّن تصميم هذه الصفحة بأحدث التقنيات المستخدمة في التصميم حاليًا ألا وهي: الأشكال العائمة غير المستطيلة non-rectangular float shapes وميزة الكشف المباشر من قبل المتصفح عن قدرته على تطبيق تعليمات css وهذا مايعرف بـ feature queries. ولكن ليكن واضحًا بأننا لم نقم ببناء هذا المثال المتطور من دون أن يزودنا Mike Pick صاحب Monkey Do بالتصميم حيث أدركنا بأن رؤيته تتوافق بشكل جميل مع ميزات CSS الحديثة وقد كنا سعداء جدًا بالنتائج التي حصلنا عليها من خلال هذه التقنيات الحديثة لذلك أردنا مشاركتها معكم. تصميم الفقاعات styling bubbles هنا يوجد بعض المقتطفات من تصميمنا الأولي (التصميم النهائي لم يتم إنشاؤه كبطاقة صور لذلك لايمكنني عرض الشكل النهائي ولكن هذا الشكل قريب جدًا). مايهمني في هذا المثال هو طريقة استخدام الصور الدائرية، التي كنا نشير إليها باسم "الكُوّة portholes" ولكن بعد التفكير أفضل تسميتها بـ "الفقاعات". عندما بدأت التحضير لتنفيذها من خلال التعليمات البرمجية عدت بالتفكير إلى ما قالته جين سيمون عن الاتجاه الفني الحقيقي في الويب. وبالتحديد كنت أفكر حول الأشكال في CSS وكيف يمكن أن أكون قادرًا على استخدامهم بحيث تكون أسطر الكتابة مرتبة حول محيط الدائرة بشكل انسيابي. تقوم طريقة بناء هذا التنسيق على دمج مجموعة من التقنيات المستخدمة في التعامل مع الأشكال في CSS وهي Ragged Float وفكرتها تقسيم الصورة المنحنية إلى مجموعة من الشرائط مستطيلة الشكل ثم عرضها بالتسلسل لتظهر الشكل النهائي المنحني. و Sliced Sandbags التي تقوم على مبدأ بسيط هو بناء مجموعة من div التي تتوضع فوق الصورة الأساسية وبالتالي هذه الأقسام لاتكون الصورة الحقيقية التي سيتوضع النص حولها وإنما عبارة عن مجموعة من تعليمات php. الآن نمتلك الأشكال العائمة float shapes حيث يمكننا أن نعرّف دائرة أو مضلع ثم نقوم بتوصيف كيفية انسياب النص حول الشكل. أعلم ستقول بأنك لم تسمع أبدًا عن دعم هذه الأشكال في المتصفحات، بالتأكيد هذا صحيح حيث أن المتصفحات التي تدعمها إلى الآن هي WebKit/Blink family—Chrome, Safari, and Opera ولكن ذلك لايشكل عائقًا أمام هذه التقنية لأن المتصفحات التي لاتدعم الأشكال العائمة float shapes ستنفذ الطريقة القديمة boxy float في التعامل مع هذه الأشكال وذلك من خلال عرضها كألبوم صور. من أجل إضافة فقاعة سيكون الشكل الأساسي لتعليمات CSS هو: img.bubble.left { float: left; margin: 0 40px 0 0 ; shape-outside: circle(150px at 130px 130px); } img.bubble.right { float: right; margin: 0 0 0 40px; shape-outside: circle(150px at 170px 130px); } وبالمناسبة هذه الصور الفقاعة هي عبارة عن صور مربعة أبعادها 260px سوف يتم عرضها على سطح المكتب بهذا الحجم ولكن عند تصغير العرض إلى مساحات أصغر سيتم تحجيمها بنسبة 30% لتتناسب مع العرض الجديد. طريقة توضع الشكل لنفهم طريقة إعداد شكل الفقاعة النهائي سنقوم بحساب عدد بكسلات توضع الصورة، أولًا أبعاد الصورة الفقاعة هو 260×260 بكسل ونقوم بإضافة 40 بكسل عند الهامش الأيمن للصورة وبالتالي يتشكل لدينا مربع يحوي الصورة وأبعاده هي 300 بكسل بالعرض و 260 بكسل بالطول وتشغل الصورة الأساسية القسم اليساري من هذا المربع. إذًا وعند القيام بحساب بسيط نجد مركز الدائرة هو عند النقطة 130px 130px ونصف قطر الصورة الدائرية الفقاعة هو 130 بكسل ونمدد المحيط الخارجي للفقاعة 20 بكسل فيصبح نصف قطر الدائرة النهائي هو 150 بكسل. وبنفس الطريقة نبني الفقاعات الموجودة على الجانب الأيمن، حيث تنزاح نقطة المركز إلى 170px 130px لأننا أضفنا 40 بكسل كهامش أيسر للصورة، وستشرح الصورة كيفية توضع مركز كل دائرة من أجل كل صورة سنلاحظ مايلي: الصندوق الأزرق الفاتح يُظهِر العنصر نفسه المعرف بالصف img. المربع البرتقالي الفاتح هو منطقة الهامش الأساسية وعرضها 40 بكسل. الدائرة البنفسجية والتي تمّ تعريفها في تعليمات css بالمعرف shape-outside. بالإمكان ملاحظة كيفية انسياب النص إلى المنطقة البرتقالية حيث يصل إلى أعلى الدائرة البنفسجية وهذا هو تأثير خاصية shape-outside. المناطق خارج هامش الشكل يكون طريقة عرض النص فيها عادي. نلاحظ أن الدائرة البنفسجية تمتد خارج منطقة الهامش وذلك لأنه باستخدام خاصية shape-outside يتم قص الدائرة بحسب الأبعاد المعطاة بغض النظر عن مربع الهامش، لذلك فإنه إذا قمنا بزيادة نصف قطر الدائرة مثلًا 400 بكسل فهذا يعني أن الصورة ستغطي نصف الصفحة مع أن التصميم الأساسي للنص سيبقى حول حواف هامش الصورة العائمة كما لو أنه لايوجد أي شكل على الإطلاق. استجابة الأشكال Being Responsive حتى الآن طريقة التصميم جيدة فالنص ينساب حول الشكل العائم بطريقة سلسلة في متصفحات chrome /safari / opera ويتعامل مع الأشكال كما لو أنها مربع شكل عادي في Firefox /Edge وستبقى هذه طريقة تعامل المتصفحات مع الأشكال جيدة طالما أنه لم يتم تصغيير نافذة المتصفح وتتسبب في التفاف النص بين الفقاعات ولكن بالطبع يمكن حدوث ذلك في هذه الحالة بالنسبة للصور العائمة على اليمين لن يكون الوضع سيئًا على عكس الصور اليسارية التي لن يكون الأمر جميلًا بالنسبة لها. ففي بعض الحالات سيكون الالتفاف مقبولًا كما شاهدنا في الصورة السابقة وذلك في حالة كون النص عبارة عن عدة أسطر، أما في حالة كان النص فقط كلمة أو كلمتين وعند حدوث الالتفاف لهما سيكون شكلهما غير مقبول قليلًا ولذلك من الأفضل تغيير طريقة إعداد الهوامش في النصوص بحيث لايتم الالتفاف حول الفقاعات المرافقة للنص. على سبيل المثال: .complex-content div:nth-child(even):not(:last-child) p { margin-right: 20%; } .complex-content div:nth-child(odd):not(:last-child) p { margin-left: 20%; } أي أنه من كل الكتل div الأبناء زوجية الأرقام (وليست الابن الأخير) نضيف 20% كهامش على المحتوى ونقوم بنفس الأمر من أجل الكتل الأبناء أصحاب الأرقام الفردية. بإضافة هذه التعليمات الجديدة سيظهر شكل النصوص عند تصغيير نافذة المتصفح بشكل جيد في Chrome حيث سيتم التفاف النص على طول الفقاعة وسيتم إدخال النصوص إلى داخل هوامش الصورة بقيم معقولة ولكن في متصفح firefox الذي لايزال يمتلك النظام القديم boxy float في التعامل مع الصور لن يكون هناك التفاف في النص ودفع إلى الداخل وإنما سيتم إظهار شريط التمرير على طول الصفحة ومن جهة أخرى فإن زيادة قيمة الهامش من أجل اصطفاف السطور كما في firefox (الهامش كان بنسبة 33%) لامعنى له في الأشكال العائمة float shape في chrome وذلك لأن النص لن يصل أبداً حتى النصف السفلي من الدوائر. دعم ميزة Querying feature تشغيل ميزة feature query يتم من خلال إضافة سطر supports@ إلى التعليمات حيث نستطيع من خلال هذه الطريقة معرفة المتصفحات التي لاتدعم الأشكال العائمة التي أضفناها فتقوم تعليمة support@ بتعديل الهوامش إلى 33% كما أنها تقلل هذه الهوامش في حال كانت المتصفحات تستطيع التعامل مع هذه الأشكال، ويتم هذا من خلال إضافة تعليمات: .complex-content div:nth-child(even):not(:last-child) p { margin-right: 33%; } .complex-content div:nth-child(odd):not(:last-child) p { margin-left: 33%; } @supports (shape-outside: circle()) { .complex-content div:nth-child(even):not(:last-child) p { margin-right: 20%; } .complex-content div:nth-child(odd):not(:last-child) p { margin-left: 20%; } } وبهذا سيظهر تصميم العناصر بشكل جيد في جميع المتصفحات، بالرغم من وجود بعض الأشياء الصغيرة جدًا التي يمكن إضافتها. ولكن بشكل عام طريقة العرض في جميع الحالات التي تدعم الأشكال العائمة أو لا تبعث على السرور في الفيديو الأول طريقة العرض في متصفح chrome تحميل الفيديو الأول والفيديو الثاني يظهر طريقة العرض في متصفح firefox الذي لايدعم الأشكال العائمة تحميل الفيديو الثاني بالأخير إنها نعمة وجود ميزة مثل feature queries تسمح للمتصفحات مثل firefox و MS Edge الحصول على دعم لإظهار الأشكال العائمة. ومع أنه لايوجد تحسينات قريبة للمتصفحات ولكن يتم العمل بشكل دائم على إدخال التحسينات على CSS نفسها بحيث سيصبح شعارها قريبًا "صمم وانسى"، كما يمكن القول بأن التعديل الأساسي في CSS هو طريقة التعامل مع الأشكال وتحسين عمليه استجابتها من دول وجود الدعم الكافي في المتصفحات لها. ترجمة –وبتصرّف- للمقال A Redesign with CSS Shapes لصاحبه Eric Meyer
  15. class هو تجريد للغرض object أي هو مجموعة الأغراض التي تحمل نفس الصفات العامة ويستخدم this للتأشير على الغرض ونحن في داخله عند البناء بشكل عام لتجنب حالات الغموض وخصوصًا في حالات برمجة الصفوف التي تحوي inner classes للتعرف على المزيد من طريقة عمل التعليمة السابقة بشكل خاص https://www.quora.com/In-Android-coding-what-does-MainActivity-this-do-and-what-are-MainActivity-and-this-separately-class-keyword-etc