اذهب إلى المحتوى

ابراهيم الخضور

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

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

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

كل منشورات العضو ابراهيم الخضور

  1. سنعمل في هذا المقال على تنسيق ملف HTML بسيط كي نطبق عمليًا بعض ميزات اللغة. لا بد أن تمتلك دراية بأساسيات عمل الحاسوب وقد ثبت البرمجيات الأساسية لتطوير الويب قبل متابعة القراءة، كما يُفضل أن تطلع على أساسيات HTML وأيضًا أساسيات التعامل مع الملفات. تحضير ملف HTML سنبدأ العمل انطلاقًا من ملف HTML، لذلك انسخ الشيفرة التالية واحفظها في حاسوبك في ملف جديد باسم index.html: <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Getting started with CSS</title> </head> <body> <h1>I am a level one heading</h1> <p>This is a paragraph of text. In the text is a <span>span element</span> and also a <a href="https://example.com">link</a>.</p> <p>This is the second paragraph. It contains an <em>emphasized</em> element.</p> <ul> <li>Item <span>one</span></li> <li>Item two</li> <li>Item <em>three</em></li> </ul> </body> </html> إضافة تنسيقات CSS إلى الملف ينبغي علينا بدايةً إخبار ملف HTML بوجود بعض قواعد التنسيق التي نريد تطبيقها، وهنالك ثلاث طرق مختلفة لإنجاز الأمر لكننا سنستخدم الآن الطريقة الأكثر تداولًا وفائدةً وهي ربط ملف CSS بملف HTML عبر ترويسته أي ضمن العنصر <head>. انشئ ملفًا في نفس المجلد الذي يضم ملف HTML واحفظه باسم styles.css، إذ يدل الامتداد css. على أنّ الملف هو ملف تنسيق CSS. ضع الشيفرة التالية داخل العنصر <head> في ملف HTML لكي تربط به ملف التنسيق styles.css: <link rel="stylesheet" href="styles.css"> يخبر العنصر <link> المتصفح عن وجود ملف منفصل يضم قواعد تنسيق من خلال استخدام السمة rel ويدله على مكان وجوده من خلال السمة href. يمكنك اختبار نجاح عملية الربط بإضافة القاعدة التالية إلى ملف التنسيق ثم تجريب الشيفرة عبر المحرر. h1 { color: red; } تحفظ التغييرات التي أجريتها على ملفي CSS و HTML ثم حمّل الصفحة من جديد ضمن المتصفح. سيتحوّل لون عنوان المستوى الأول في أعلى الصفحة إلى الأحمر. فإن حدث ذلك يكون عملك صحيحًا، وإن لم يحدث ذلك تحقق من كتابة التعليمات بدقة. بإمكانك متابعة العمل على ملف styles.css على حاسوبك أو استخدام محرر الشيفرة الذي ستراه تاليًا. سيتعامل محرر الشيفرة مع تنسيقات CSS التي نكتبها في اللوحة الأولى كما لو أنها ارتبطت بملف HTML بالطريقة التي شرحناها سابقًا. دورة تطوير واجهات المستخدم ابدأ عملك الحر بتطوير واجهات المواقع والمتاجر الإلكترونية فور انتهائك من الدورة اشترك الآن تنسيق ملف HTML لقد أظهرنا إمكانية استهداف أي عنصر من عناصر HTML وتنسيقه عندما حوّلنا لون العنوان إلى الأحمر. ننجز الأمر باستخدام "محدد عناصر element selector" وهو محدد يستهدف عنصر HTML وفقًا لاسمه. فلكي نستهدف مثلًا عنصر الفقرة النصية، عليك استخدام المحدد p. لاحظ كيف ستحول القاعدة التالية جميع الفقرات النصية إلى اللون الأخضر: p { color: green; } بإمكانك أيضًا استهداف عدة عناصر في الوقت ذاته باستخدام فاصلة "," بين أسمائها. ستحوّل القاعدة التالية عنصر الفقرة النصية وجميع عناصر القائمة إلى اللون الأخضر: p, li { color: green; } حاول أن تطبق ما تعلمته في محرر الشيفرة التالي: تغيير السلوك الافتراضي للعناصر عندما نلقي نظرة على ملف HTML محكم التوصيف حتى لو كان بسيطًا كما في مثالنا، نلاحظ كيف يضيف المتصفح بعض التنسيقات الافتراضية لتسهُل قراءة الصفحة. فالعناوين كبيرة وسميكة والقوائم منسقة على شكل نقاط، ويعود السبب في ذلك إلى التنسيقات الافتراضية التي بُنيت ضمن المتصفح والتي يطبقها على جميع الصفحات. إنّ غياب هذه التنسيقات الأساسية سيجعل الصفحة في حالة فوضى كبيرة ويتوجب علينا حينها تنسيق كل شيء من الصفر. تجدر الإشارة هنا إلى أن جميع المتصفحات الحديثة ستعرض الصفحات وفق نفس التنسيق الافتراضي تقريبًا. لكننا نفضل في بعض الأحيان اختيار تنسيقات مختلفة عن الطريقة التي يتبعها المتصفح، عندها نختار العنصر الذي نريد أن نغيّر تنسيقه ونستخدم قواعد CSS لتغيير مظهره. فإن أردت مثلًا تغيير عرض عناصر القائمة غير المرتبة <ul> كي لا تظهر على شكل نقاط، يمكنك إضافة القاعدة التالية: li { list-style-type: none; } يمكنك الاطلاع على تفاصيل أكثر عن الخاصية list-style-type ضمن موسوعة حسوب وستجد الكثير من الأمثلة التي تعرض جميع القيم الممكنة التي تأخذها هذه الخاصية بدلًا من إزالة قائمة النقاط فقط. إضافة أصناف لقد رأينا حتى اللحظة إمكانية استهداف العنصر باسمه ثم تنسيقه، وتتيح لك هذه الطريقة تنسيق جميع العناصر من النوع ذاته كي تُعرض بالمظهر ذاته. لكنك لن تفعل ذلك في أغلب الأوقات بل تحتاج إلى طريقة لتطبيق بعض القواعد على مجموعة محددة من العناصر دون غيرها. وأكثر الطرق شيوعًا لإنجاز الأمر هي إضافة صنف class إلى عنصر HTML ثم استهداف جميع العناصر التي تضم هذا الصنف. لتجرب الأمر، أضف السمة class إلى العنصر الثاني من القائمة غير المرتبة <ul> لتصبح كالتالي: <ul> <li>Item one</li> <li class="special">Item two</li> <li>Item <em>three</em></li> </ul> يمكنك استهداف جميع العناصر التي تنتمي إلى الصنف special بإنشاء "محدد صنف class selector" يبدأ بنقطة يليه اسم الصنف كالتالي: .special { color: orange; font-weight: bold; } احفظ التعديلات ثم حمل الصفحة من جديد ضمن المتصفح. يمكنك تطبيق الصنف على أية عناصر في الصفحة إن أردتها أن تبدو برتقالية وبخط سميك. حاول أن تضيف الصنف special إلى العنصر <span> داخل الفقرة النصية، ثم حمّل الصفحة من جديد وراقب ما سيحدث. قد تجد أحيانًا قاعدة CSS تضم اسم العنصر يليها اسم الصنف كالتالي: li.special { color: orange; font-weight: bold; } تعني الصياغة السابقة أنّ القاعدة ستستهدف جميع عناصر <li> التي تمتلك الصنف special فقط. وهكذا لن تكون قادرًا على تطبيق هذا التنسيق على العنصر <span> السابق أو أية عناصر أخرى تمتلك الصنف special، وعليك حينها تحديد العناصر التي ستطبق عليها التنسيق على شكل قائمة كالتالي: li.special, span.special { color: orange; font-weight: bold; } وكما قد تتصور، بإمكانك تطبيق بعض القواعد على عناصر مختلفة ولا حاجة لكتابة القواعد مجددًا في كل مرة تحتاجها فيها. لهذا من الأفضل أحيانًا تجاوز العنصر والإشارة إلى صنفه إلا إن أردت تطبيق قواعد خاصة على عنصر بمفرده أو أردت التأكد من أنها لن تُطبق على عناصر أخرى. تنسيق أشياء بناءً على موقعها في الملف قد ترغب أحيانًا بعرض بعض الأجزاء بشكل مختلف وفقًا لموقعها ضمن الصفحة، وستجد عددًا من المحددات التي تساعدك على إنجاز الأمر، لكننا سنلقي نظرة الآن على اثنين من هذه العناصر. لاحظ وجود عنصري <em> في ملف HTML أحدهما داخل فقرة نصية والآخر ضمن عنصر قائمة. ولاختيار عنصر <em> الذي يقع داخل عنصر القائمة <li> يمكنك استخدام محدد يُدعى "محددات العناصر السلسلة descendant combinator" والذي يتكوّن من اسم العنصر الأب يليه فراغ ثم العنصر الابن. أضف القاعدة التالية إلى ملف التنسيق: li em { color: rebeccapurple; } سيختار هذا المحدد أي عنصر <em> داخل العنصر <li>. وهكذا من المفترض أن تجد محتوى <em> الموجود ضمن عنصر القائمة الثالث باللون البنفسجي بينما يبقى العنصر الآخر كما هو. كما قد ترغب أحيانًا بتنسيق فقرة نصية عندما تأتي مباشرة بعد عنوان ويكون لهما نفس المستوى في شجرة HTML. لإنجاز الأمر، ضع الإشارة + (محدد العناصر الأخوة sibling combinator) بين محددي العنصرين: h1 + p { font-size: 200%; } يضم المثال الموجود في محرر الشيفرة التالي القاعدتين السابقتين، حاول أن تضيف قاعدة لتجعل العنصر <span> أحمر اللون إن كان داخل فقرة نصية. ستعرف إن نجحت في الأمر عندما ترى هذا العنصر أحمر اللون في الفقرة النصية ولم يتغير لون نفس العنصر داخل عنصر القائمة الأول. ملاحظة: تمنحك CSS طرق عدة لاستهداف عناصر HTML ولا يمثل ما عرضناه سوى البداية! سنتعرف لاحقًا على تفاصيل هذه المحددات والكثير غيرها في مقالات لاحقة. تنسيق أشياء بناءً على حالتها سنلق نظرة الآن على طريقة تنسيق العناصر وفقًا لحالتها، ومن أوضح الأمثلة على هذه الفكرة تنسيق الروابط التشعبية. لتنسيق الروابط التشعبية نستهدف العنصر <a> الذي يأخذ حالات مختلفة كأن يكون الرابط قد نقُر أو لم يُنقر بعد أو أن يكون مؤشر الفأرة فوقه أو لا، أو تلقى تركيز الدخل عبر لوحة المفاتيح أو أنه نُقر وتجري عملية الانتقال إلى الهدف. بإمكانك أن تغير تنسيق الرابط التشعبي باستخدام CSS لتميز الحالات السابقة. لاحظ كيف تنسق الشيفرة التالية الرابط باللون الزهري إن لم يُنقر بعد وبالأخضر عند زيارة الرابط: a:link { color: pink; } a:visited { color: green; } كما يمكنك تغيير مظهر الرابط عند تمرير مؤشر الفأرة فوقه كان تزيل الخط الذي يظهر تحته افتراضيًا: a:hover { text-decoration: none; } حاول أن تجرب قواعد CSS المختلفة للتحكم بحالة الرابط في محرر الشيفرة التالي: لقد أزلنا في الشيفرة السابقة الخط أسفل الرابط التشعبي عند تمرير مؤشر الفأرة فوقه، كما يمكن إزالته أيضًا في جميع حالات الرابط. لكن ينبغي أن تدرك أنّ الرابط لا بدّ وأن يبدو رابطًا في المواقع الحقيقية، وبالتالي إبقاء الخط تحته أمر مهم جدًا كي يدرك المستخدم وجود جزء يمكن نقره من النص، فهذا هو السلوك المتوقع للرابط. إذًا فقد تجعل بعض تنسيقات CSS صفحتك صعبة القراءة وأقل شمولية (لا يمكن لجميع الزوار الوصول إليها). ملاحظة: سنشير أحيانًا إلى مصطلح سهولة الوصول accessibility أو الشمولية في سلسلة المقالات هذه ويقصد بها ما يجعل الصفحة مفهومة وسهلة الاستخدام لأي كان. فقد يستخدم زائري الصفحة حاسوبًا مع فأرة أو هاتف مزود بشاشة لمس أو قارئ شاشة ينطق محتوى الصفحة أو قد يحتاج إلى حجم أكبر للنص أو قد يتنقل في الموقع باستخدام لوحة مفاتيح فقط. عادة ما يكون التنسيق الافتراضي لملف HTML مناسبًا للجميع، فلا تعقد استخدامه عندما تبدأ تطبيق تنسيقات CSS. الجمع بين المحددات والمجمّعات يمكنك الجمع بين عدة محددات selectors أو مجمّعات combinator معًا. إليك مثالًا: /*<article> الموجود ضمن <p> داخل <span> اختر أي عنصر */ article p span { ... } /*<h1>الذي يأتي بعد <ul> الذي يأتي مباشرة بعد العنصر <p> اختر العنصر */ h1 + ul + p { ... } يمكن الجمع بين عدة أنواع أيضًا: body h1 + p .special { color: yellow; background-color: black; padding: 5px; } تنسق الشيفرة السابقة أي عنصر من الصنف special موجودة ضمن فقرة <p> تأتي مباشرة بعد عنصر <h1> موجود داخل عنصر <body>. لا تقلق إن بدت القاعدة السابقة معقدة، ستعتاد على ذلك حالما تبدأ بكتابة المزيد من قواعد CSS. خلاصة ألقينا نظرة في هذا المقال على عدد من الطرق المستخدمة في تنسيق ملف HTMl مستخدمين CSS. سنطور معارفنا مع تقدمنا في سلسلة المقالات هذه، لكنك تمتلك الآن ما يكفي لتنسيق النصوص وتطبيق قواعد CSS بطرق مختلفة لاستهداف أية عناصر في ملف HTML. سنتابع في مقالنا التالي مع هيكلية CSS. ترجمة -وبتصرف- للمقال Getting started with CSS. اقرأ أيضًا المقال السابق: تعرف على CSS الدليل السريع إلى لغة التنسيق CSS كيفية تنسيق عنصر div في HTML باستخدام CSS
  2. تتيح لك اللغة التوصيفية CSS -اختصارًا للتنسيقات الانسيابية Cascading style sheets- إضفاء مظهر رائع على صفحات الويب، فكيف تفعل ذلك خلف الستار؟ إذ يشرح لك هذا المقال ماهية اللغة من خلال أمثلة بسيطة عن صياغتها، كما يعرّف بعض المصطلحات المتعلقة بهذه اللغة. ننصحك قبل المتابعة في قراءة هذا المقال أن: تطلع على أساسيات استخدام الحاسوب وتصفح الويب. تطلع على المقال تثبيت البرمجيات الأساسية للانطلاق في تطوير الويب وتتفهم طريقة إنشاء وإدارة الملفات كما شرحناها في مقال التعامل مع الملفات. تطلع على أساسيات HTML كما شرحناها ضمن سلسلة المقالات مدخل إلى HTML. ستظهر العناوين بحجم أكبر من النص العادي وتنفصل الفقرات وتباعد بينها فراغات عندما توصَّف نصًا أو محتوًى عمومًا باستخدام لغة HTML، كما ستلوَّن الروابط أيضًا ويظهر خط أسفلها لتمييزها عن النص المحيط، وإنّ ما تراه في الواقع هو التنسيق الافتراضي الذي يطبقه المتصفح، وهو تنسيق بسيط يُطبّق على ملفات HTML لتسهيل قراءتها حتى إذا لم يخصص مؤلف الصفحة أية تنسيقات صراحةً. لن تنبض صفحات ويب بالحيوية إذا اكتفينا بهذه التنسيقات البسيطة، لذلك وجدت CSS لتساعد في التحكم بمظهر عناصر HTML عندما تُعرض على المتصفح، وبالتالي ستقدِّم المحتوى الذي تريد بالمظهر الذي تريد. دورة تطوير واجهات المستخدم ابدأ عملك الحر بتطوير واجهات المواقع والمتاجر الإلكترونية فور انتهائك من الدورة اشترك الآن استخدامات CSS أشرنا سابقًا إلى أنّ CSS هي لغة توصيف للطريقة التي تظهر بها المستندات للمستخدِم، ونقصد بالمستند عادةً أنه ملف نصي جرت هيكلته باستخدام إحدى لغات التوصيف وأكثرها شيوعًا لغة HTML، فقد تصادف لغات توصيف أخرى مثل XML أو SVG. تُعرض هذه المستندات للمستخدِم بتحويلها إلى شكل سهل القراءة بواسطة المتصفحات مثل Firefox أو Chrome أو Edge التي صُممت خصيصًا لهذه الغاية سواء على شاشات الحواسيب أو أجهزة الإسقاط أو الأجهزة الذكية. ملاحظة: يُدعى المتصفح أحيانًا باسم "عميل المستخدِم" ويعني ببساطة أنه برنامج يمثل المستخدِم داخل نظام التشغيل، ولهذا فالمتصفحات هي النوع الرئيسي من العملاء الذي نقصده عندما نتكلم عن CSS، وطبعًا ليس النوع الوحيد، إذ ستجد مثلًا برامج عميلة أخرى مثل تحول ملفات HTML و CSS إلى ملفات PDF لتسهيل طباعتها. يمكنك استخدام CSS لتنسيق أبسط مظاهر النص مثل اللون وحجم خط الكتابة، كما تستخدَم لإنشاء تخطيط مثل تحويل أحد أعمدة النص إلى تخطيط مكوّن من منطقة للمحتوى الرئيسي ومنطقة جانبية تضم معلومات تتعلق بهذا المحتوى، ويمكنك أيضًا استخدام اللغة لإضافة تأثيرات مثل تحريك المحتوى (الرسوم المتحركة). صيغة CSS تُبنى لغة CSS على قواعد تعرّف مجموعة من التنسيقات التي يمكن تطبيقها على عنصر أو مجموعة عناصر محددة موجودة في صفحة HTML، فقد ترغب مثلًا بعرض العنوان الرئيسي للصفحة على أساس نص أحمر كبير، ولتنجز الأمر لاحظ شيفرة CSS البسيطة التالية: h1 { color: red; font-size: 5em; } تبدأ القاعدة بتسمية "مُحدِّد Selector" مهمته تحديد عنصر HTML الذي سيخضع للتنسيق، وهذا العنصر هو <h1> في مثالنا السابق، ويلي اسم المحدد قوسين معقوصين { } نضع ضمنهما مجموعة من التصريحات Declarations التي تأخذ صورة زوج (خاصية: قيمة)، إذ يعرِّف كل زوج خاصيةً لتنسيق العنصر أو العناصر المحددة ثم يسند إليها القيمة المطلوبة، وتأتي دائمًا نقطتين متعامدين بعد الخاصية تليها القيمة، وقد تأخذ الخاصية قيمًا مختلفةً وفقًا لطبيعتها، فقد تأخذ الخاصية color في مثالنا مجموعةً متنوعةً من القيم، كما تأخذ الخاصية font-size أيضًا قيمًا لها وحدات قياس مختلفة. تضم صفحة تنسيقات CSS قواعد كثيرة تأتي واحدة تلو الأخرى مثل: h1 { color: red; font-size: 5em; } p { color: black; } ستتذكر بسرعة بعض القيم وتستخدمها بينما ستضطر إلى البحث عن أخرى. قد تساعدك العديد من المواقع على الإنترنت في إيجاد هذه القيم سريعًا مثل موسوعة حسوب أو شبكة مطورِي موزيللا. ملاحظة: يمكنك العثور على كل خاصيات CSS في توثيق CSS العربي في موسوعة حسوب فاجعله تحت يدك لأنك سترجع له مرارًا وتكرارًا. وحدات CSS قُسّمت اللغة إلى عدة وحدات لتنظيم العدد الكبير من الخصائص التي يمكن استخدامها في تنسيق العناصر، فقد تجد مثلًا أنّ الخصائص المتعلقة بالخلفيات في موسوعة حسوب في وحدة مستقلة وكذلك الإطارات وهكذا. لا تكترث كثيرًا في هذه المرحلة لفهم هيكلية CSS، لكن من السهل إيجاد المعلومات إذا اتبعت مثلًا فكرة إمكانية وجود ما تبحث عنه ضمن الأشياء المشابهة له، وبالتالي في المواصفة نفسها CSS specification، فقد تتوقع على سبيل المثال، وجود الخاصية background-color في وحدة "الخلفيات" والخاصية border-color في وحدة "الإطارات". مواصفات CSS تُعرَّف جميع تقانات الويب مثل جافاسكربت، HTML، CSS، وغيرها ضمن مجلدات ضخمة تدعى بالمواصفات Spesification أو "specs" اختصارًا، والتي تنشرها منظمات تحديد المعايير مثل W3C أو WHATWG أو ECMA أو Khronos. تحدد هذه المواصفات الطريقة الدقيقة التي تُستخدَم فيها هذه التقانات، ولا تختلف CSS عن غيرها في هذا الصدد، فقد طورتها مجموعة من W3C تُدعى "مجموعة عمل CSS" تضم ممثلين عن مصنِّعي المتصفحات وغيرها من الشركات التي تهتم بهذه التقانة بالإضافة إلى خبراء حياديين لا ينتمون إلى أيّ منظمة أو شركة. تطوّر المجموعة خصائص CSS الجديدة أو تحدد مواصفاتها، وقد يكون السبب هو رغبة متصفح محدد بامتلاك إمكانات معينة أو لحاجة المطورين والمصممين إلى توصيف لبعض الميزات أو أن تجد المجموعة نفسها ضرورةً لتحديد متطلبات معينة، كما تطوَّر اللغة باستمرار وتضاف إليها ميزات جديدة، لكن الأمر الأساسي هو عدم تغيير الأمور بطريقة تؤثر على مواقع ويب القديمة، فالمواقع التي بُنيت عام 2000 بتقنيات CSS محدودة لا بدّ أن تبقى قابلةً للاستخدام في متصفحات اليوم. من الطبيعي بوصفك مبتدئًا أن تجد مواصفات CSS مرهقة، فهي مخصصة للمهندسين كي يستخدمونها في دعم ميزات في البرمجيات العميلة للمستخدِم وليس لتُقرأ من قبل مطورِي ويب لفهم CSS، إذ يفضِّل الكثير من المطورِين المحترفين قراءة توثيقات أكثر وضوحًا مثل توثيقات "شبكة مطوري موزيللا" أو توثيقات "موسوعة حسوب" الموجهة للقارئ العربي، لكن لا بدّ أن تعرف أنها موجودة وأن تفهم العلاقة بين خصائص CSS التي تستخدمها وبين دعم المتصفح لها والمواصفات. معلومات عن دعم المتصفحات لميزات CSS عندما تعتمد مجموعة CSS ميزةً جديدةً ستتمكن من استخدامها في صفحاتك حالما يدعمها متصفح، أي حالما تُكتب الشيفرة اللازمة للمتصفح لتحويل التعليمات المتعلقة بهذه الميزة إلى شيء ملموس على الشاشة، وسنطلع أكثر على ذلك في مقال "آلية عمل CSS". لا تدعم معظم المتصفحات الميزات الجديدة في الوقت نفسه عادةً، لهذا ستجد فجوةً في استخدام هذه الميزات بين متصفح وآخر، ولهذا من المهم جدًا أن تتحقق من دعم المتصفحات لميزة قبل استخدامها. نعرض مثلًا في الشكل التالي دعم بعض المتصفحات للخاصيتين font-family و system-ui. الخاتمة كونت الآن فكرة عن ماهية لغة CSS واستخداماتها وأهم خصائصها ومواصفاتها، وأصبحت جاهزًا للانطلاق مع هذه اللغة وتعلمها، لذا انتقل الآن إلى المقال التالي أساسيات استخدام CSS. ترجمة -وبتصرُّف- للمقال What is CSS اقرأ أيضا فهم وإنشاء قواعد CSS الدليل السريع إلى لغة التنسيق CSS معالجة المشاكل الشائعة للتوافق مع المتصفحات في HTML و CSS
  3. لا بد من وضع استراتيجية تسويق واضحة لمختلف الأعمال التجارية التي تنشط على شبكة الإنترنت. إذ تساعدك استراتيجية التسويق الفعالة في الترويج لعلامتك التجارية في هذا المضمار الذي يشهد تنافسيةً عالية. وإن لم تكن تهتم للأمر لكنك مستعد للخوض فيه، فقد وصلت إلى المكان المناسب. قد لا يكون موضوع استراتيجيات الترويج واضحًا بالنسبة إليك بداية، لكنك ستتعلم كل ما يلزم مع تقدمك في قراءة هذا المقال. تستخدم العديد من الشركات المرموقة والعلامات التجارية التي تنشط على الإنترنت استراتيجيات تسويق متعددة، فقد خبرت تلك الشركات فكرة استراتيجيات التسويق، وهي قادرة على إنتاجها. لا تقلق إن كنت مبتدئًا في هذا المجال، لأننا سنبدأ شرحنا بالأساسيات الأكثر أهمية. دعونا إذًا ننطلق بدايةً من وضع التعريفات الأساسية وطريقة استخدام استراتيجية الترويج لعلامة تجارية. ما هي استراتيجية الترويج لعلامة تجارية؟ تُعد استراتيجية الترويج للعلامة التجارية طريقة عمل لإيصال علامتك التجارية إلى جمهورك ببساطة، وستجد الكثير من الأنواع المختلفة للاستراتيجيات التي يتبعها أصحاب تلك العلامات التجارية. تحاول الشركات جهدها في استخدام استراتيجيات فعالة انطلاقًا من تلك التقليدية إلى العصرية منها. وبغض النظر عن مدى جودة منتجات أو خدمات علامة تجارية، لن تحصل على الفائدة المرجوة منها ما لم تدعمها باستراتيجية الترويج الفعّالة والصحيحة. يرغب المستهلكون في شراء حاجاتهم في أيامنا هذه من علامات تجارية موثوقة، لهذا من الضروري جدًا للمنتجين الترويج عن أنفسهم كعلامات تجارية. الأسباب التي تدفعك إلى وضع استراتيجية ترويج للعلامة التجارية لنقل مبدئيًا أن زيادة النمو في العائدات هو الهدف الشامل لاستراتيجيات ترويج العلامات التجارية. لكن هنالك أسباب أخرى تساعدك في تحقيق تلك الغاية، وتشجعك على استخدام استراتيجية في الترويج. فإن كنت لا تزال في شك من أمر استراتيجية الترويج، فعليك الاطلاع على بعض الأسباب التي نطرحها تاليًا عن الحاجة إلى مثل هذه الاستراتيجية. إدراك وجود علامتك التجارية أولى الثمار التي تجنيها من اعتمادك استراتيجية ترويج، هو إدراك السوق لوجود هذه العلامة التجارية. إذ لا تحاول العلامات التجارية في أيامنا هذه تعزيز مواقعها كعلامة تجارية أو تطوير منتجاتها أو خدماتها وحسب، بل تحاول أن تعزز إدراك السوق لوجود منتجاتها وخدماتها، وذلك من خلال استراتيجيات التسويق. وعليه، فإدراك وجود منتجات أو خدمات لعلامة تجارية يُعَد عنصرًا هامًا من عناصر ما يُعرف بمخروط أو قمع المبيعات؛ لهذا لا بد من اعتماد استراتيجية ترويج لعلامتك التجارية إن كنت تريد مزيدًا من الوعي لما تقدمه. اكتساب الموثوقية إن لم تكن هوية علامتك التجارية موثوقةً أو لا توحي بأنها محط للثقة، فلن تستقطب مزيدًا من الزائرين على موقعك، وبالتالي لا مزيد من المبيعات. على هذا الأساس، تُعَد موثوقية ما تعرضه علامتك التجارية مهمةً جدًا، وستعزز هذه الموثوقية بكل تأكيد من خلال استراتيجية تسويق صحيحة لعلامتك التجارية. قاعدة أكبر من العملاء الموالين لعلامتك التجارية يٍعَد هذا سببًا وجيهًا لتطوير استراتيجية تسويق علامتك التجارية، فالعملاء الذين يجدون أن علامتك مصدر للثقة سيتحولون إلى عملاء مناصرين لها، وسترتفع نسبة العملاء الذين يعودون إليك ويصبحون أيضًا ناصحين مخلصين لك. سهولة تحقيق الأهداف أيًا تكن الأهداف الكلية لعلامتك التجارية، قد تتمكن من إنجازها بمساعدة استراتيجية تسويق لها. وبدون استراتيجية واضحة، ستغدو مهمةً طويلةً ومعقدة. خطوات تطوير استراتيجية تسويق لعلامة تجارية بعد أن ألفت مفهوم استراتيجية التسويق لعلامة تجارية وأهميتها، حان الوقت لتبني استراتيجية خاصة بك. لا تقلق إن لم تعرف كيف تبدأ، فما عليك سوى اتباع الخطوات التي سنذكرها تاليًا. ستساعدك هذه الخطوات الخمس في بناء استراتيجية بسيطة لكن فعّالة لتسويق علامتك التجارية. الخطوة الأولى: كوّن هوية لعلامتك التجارية عليك في البداية تكوين هوية لعلامتك التجارية بالطريقة الصحيحة. ابدأ باختيار اسم فريد لها وشعار وغيرها من المتطلبات الإبداعية، كما سيزيد وجود مقولة سهلة التذكر ترتبط بعلامتك إدراك السوق لوجود هذه العلامة. اعصف دماغك حتى تجد اسمًا سهل التذكر يمثّل فعلًا منتجاتك أو خدماتك. الخطوة الثانية: ابحث عن الفئة المستهدفة من العملاء ابحث تاليًا عن الجمهور الذي ترغب في توجيه منتجاتك إليه. إذا لم تكن تعرف تمامًا الفئة التي تستهدفها، فسيكون مصير علامتك الإخفاق. لا بد من معرفة حاجات عملائك واهتماماتهم وأشياء كثيرة أخرى عنهم. الخطوة الثالثة: حسّن منتجك أو خدمتك على ضوء الخطوتين السابقتين بعد تكوين الهوية وتحديد الفئة المستهدفة من العملاء، لا بد من إحداث بعض التغييرات في منتجك أو خدمتك، ولهذا عليك تحسين أو تعديل منتجك وفقًا لرأي جمهورك، فما تفضلّه أنت قد لا يفضلّه جمهورك. كذلك، ينبغي أن تضع دائمًا شعارك وما يبرز علامتك التجارية على منتجاتك. الخطوة الرابعة: اختر قنوات التسويق بعناية تتلخص الخطوة الرابعة قبل الأخيرة في اختيار قنوات التسويق التي تحتاجها لتروّج لعلامتك التجارية. وتضم استراتيجية التسويق الخاصة بعلامتك التجارية العديد من الفئات الفرعية المخصصة لقنوات تسويق مختلفة، لذا قرر إذا ما كنت تريد منصات التواصل الاجتماعي، أو كنت تحتاج إلى موقع ويب خاص بك. هنالك الكثير من الطرق المختلفة التي يمكنك اعتمادها لتسويق علامتك التجارية ويمكنك اعتماد منصات مختلفة للفئات الفرعية المختلفة التي تضمها استراتيجيتك. الخطوة الخامسة: ترسيخ علامتك التجارية يُعَد ترسيخ العلامة التجارية أمرًا آخر شديد الأهمية عندما تعمل على استراتيجية تسويق علامتك التجارية. لا بد من الترويج لمنتجك على أنه منتج فريد، على الرغم من احتمال وجود آلاف المنافسين في مجال تخصصك. لا بد من الترويج له بطريقة تثبّت مكانته وتبرزها للجمهور، كأن تروي قصص نجاح علامتك وتعرض بعض الحالات التي تميّز بها هذا المنتج وغيرها، فهي تقنيات مفيدة في ترسيخ مكانة هذه المنتج. ستزيد استراتيجية تسويق العلامة التجارية عدد العملاء المناصرين لهذه العلامة، وتزيد من إدراك السوق لوجود منتج مميز تقدمه هذه العلامة. وكما أشرنا سابقًا، هناك الكثير من الطرق التي يمكن بواسطتها التسويق لعلامة تجارية، لكن لا بد أن تضم استراتيجية تسويق علامة تجارية هويةً لهذه العلامة وأسلوبًا لتمييز منتجاتها عن المنافسين وقنوات صحيحةً للتسويق ومعلومات دقيقةً عن الجمهور المستهدف. ترجمة -وبتصرف- للمقال Steps To Develop Your Brand Marketing Strategy لصاحبته Mansi Rana. اقرأ أيضًا 10 نصائح للترويج لعلامتك التجارية بشكل فعّال باستخدام محتوى الفيديو كيف تستغل التسويق عبر البريد الإلكتروني لبناء علامة تجارية شخصية استراتيجية الترويج والتأثير الهائل للاعلانات
  4. تُعَد الإضافات Add-On في مستندات جوجل بمثابة التطبيقات الصغيرة التي تتعلق بالمستند وتوسّع قدراته وإمكانياته من أتمتة للمهام المتكررة أو مراقبة الأعمال أو التعامل مع منصة جوجل درايف أو إضافة وظيفة جديدة تخدم التطبيق الكلي. سنحاول في هذا المقال شرح آلية تثبيت واستخدام هذه الإضافات، ثم سنشرح أهم الإضافات التي تسهّل عمل المحررين سواءً في المجال الأكاديمي، أو الصحفي، أو مجال الترجمة. الإضافة Writing Habit لكتّاب المقالات والمترجمين تعمل هذه الإضافة على مراقبة ما تكتبه في مستندك لغايات إحصائية تساعدك في تحسين أسلوب عملك وفهم طريقة عملك وحماستك لنمط معين من الأعمال وتحفّزك على المضي في تحقيق كمية الأعمال التي يجب إنجازها. ويمكنك أن تستشف من المعلومات التي تقدمها هذه الإضافة الكثير، لكن دعونا أولًا نستعرض طريقة تثبيتها. تثبيت Writing Habit انقر على قائمة "الإضافات"، ثم اختر "الإضافات"، وبعدها "الحصول على إضافات" لتستعرض نافذة البحث عن إضافات. عند إكتمال تحميل النافذة، اكتب في صندوق البحث أعلاها اسم الإضافة "Writing Habit" ثم اضغط Enter. انقر على لوحة هذه الإضافة التي تظهر في نافذة البحث لتنتقل إلى نافذة التثبيت ثم انقر الزر "تثبيت". سيطلب بعد ذلك التطبيق إذنك لتثبيت الإضافة، اُنقر على الزر "متابعة" إن أردت إكمال العملية. يفتح المتصفح عند ذلك صفحة التحقق من حسابك على جوجل لإتمام عملية الحصول على موافقتك. بعد منحك الإذن لتطبيق مستندات جوجل لتثبيت الإضافة، ستجري عملية التثبيت وسيبلغك التطبيق بنجاح عملية التثبيت من خلال النافذة التالية: لتشغيل الإضافة، انتقل إلى قائمة "الإضافات" ثم "Writing Habit"، ثم انقر على " ابدأ التتبع strat tracking". تظهر الإضافة على شكل نافذة جانبية على يسار المستند ويكون كل شيء جاهزًا للعمل. العمل مع Writing Habit ضبط الإعدادات: إنّ العمل مع هذه الإضافة بسيط جدًا وسنبدأ بضبط بعض الإعدادات الخاصة بها، لذلك اُنقر على زر "إعدادات Settings" في أعلى نافذة الإضافة لتظهر النافذة التالية: حدد العدد الكلي لكلمات المقال الذي تريد كتابته أو ترجمته في المربع "الإجمالي Total"، وعدد الكلمات التي تريد كتابتها يوميًا في المربع "يوميًا Daily". اختر الزمن المتوقع لإنهاء العمل من خلال مربع التقويم " تاريخ الانتهاء Finish By". اختر طريقة عرض الساعات، فإما أن تكون "كائنًا ليليًا Night OWL" يبدأ يومك الساعة الرابعة صباحًا وينتهي الرابعة صباحًا في اليوم التالي، أو أن تكون إنسانًا طبيعيًا باختيارك الوضع "طبيعي Standard". انقر على مربع التحقق "الوضع الليلي Enable Dark Mode" إن أردت عرض خلفية نافذة الإضافة باللون الأسود. حدّد الفاصل الزمني (بالثانية) لحفظ التغييرات التي أجريتها من خلال الخيار "تكرار الحفظ Save frequency". احفظ الإعدادات من خلال النقر على زر "حفظ Save"، أو عد إلى الإعدادات الافتراضية بالنقر على الزر "مسح البيانات Clear Data". استعراض تقدم العمل: انقر على الزر "قائمة Menu" في أعلى نافذة الإضافة وانتقل إلى الخيار "قليل minimal"، وهو ما يعرض افتراضيًا في نافذة الإضافة. ستجد عدد الكلمات التي أنجزتها في هذا اليوم والنسبة المئوية من حجم العمل الكلي وسرعة ما أنجزته اليوم، إذ يظهر تحت العنوان "إنجاز streak" مستوى الإنجاز اليوم، فلو أنجزت 1500 كلمة وكان هدفك اليومي 1000، فستقدر سرعة إنجازك على الشكل "1.5x"؛ أما بقية الخيارات فهي: متابعة تحقيق الهدف Goal tracking: تضم هذه النافذة المؤشرات التالية: نسبة ما أنجزته حتى اللحظة إلى العمل الكلي Overall، وعدد الكلمات التي ينبغي كتابتها يوميًا حتى تنتهي من عملك في اليوم المقرر. العمل المنجز اليوم "Today". مخطط بياني على شكل أعمدة يوضح مقدار العمل الذي أنجزته موزعًا على ساعات اليوم. مخطط بياني لما أنجزته خلال أسبوع والأيام التي شهدت أفضل إنجاز إضافة إلى الأيام التي عملت خلالها. مخطط بياني لما أنجزته خلال شهر والأيام التي شهدت أفضل إنجاز إضافة إلى الأيام التي عملت خلالها. حالة العمل اليوم Today Status: تضم هذه النافذة المؤشرات التالية: عدد الدقائق الفعلية التي كتبت خلالها "Active" وعدد الكلمات الكلية "words" هذا اليوم وعدد الكلمات في الدقيقة "WPM". مخطط أعمدة يدل على عدد الكلمات التي كتبتها في كل ساعة. جدول يضم جلسات الكتابة Sessions هذا اليوم ويضم ساعة بداية الكتابة وساعة توقفها وعدد الكلمات في هذه الجلسة والوقت المستغرق في كتابتها. سرعة الكتابة الوسطية هذا اليوم Projected writing speed مقدرًا بآلاف الكلمات في الدقيقة. تاريخ العمل History: تضم هذه النافذة المؤشرات التالية: الإنجاز الحالي Current streak وأطول إنجاز Longest streak. العمل الشهري من خلال زر يعطي تفاصيل العمل (كتابة ونسخ ولصق وغيرهم) كل يوم. زر تصدير Export يولّد ملف مايكروسوفت إكسل يضم تفاصيل ما عملته خلال آخر 180 يومًا مقسمًا إلى أشهر وأيام وساعات. جدول بالمستندات التي تتابعها الإضافة. الفائدة من هذه الإضافة من الواضح أنّ هذه الإضافة فعّالة جدًا في تتبع مسار عمل المحرر على مشروع معين وتعطيه صورةً شديدة التفصيل عن أسلوب عمله وفترات نشاطه وكسله، وتعكس رغبته في العمل على هذا المشروع، كما تعكس حالته النفسية أثناء تقدم العمل، وهذا ما يدفعه إلى توزيع الأعمال اللاحقة على ضوء ما أنجزه في المشروع الحالي. الإضافة BibCitaion لمؤلفي ومحرري المقالات الموثّقة تساعد هذه الإضافة الباحثين والأكاديميين وحتى كتاب المقالات الاحترافية في توثيق المصادر التي تشير المقالة أو البحث العلمي إليها. وعلى الرغم من تقديم تطبيق مستندات جوجل الوظيفة نفسها من خلال أداة الاقتباسات الموثّقة، إلا أنّ Bibcitaion أوسع وأشمل بكثير. تثبيت Bibcitaion اتبع نفس الخطوات التي فعلناها سابقًا عند تثبيت Writing Habit وسترى كيف تنضم هذه الإضافة إلى قائمة "الإضافات" بكل بساطة. العمل مع Bibcitaion من قائمة "الإضافات" في مستندات جوجل، انتقل إلى "Bibcitaion Bibliograph & Citation Generator"، ومن ثم إلى "Manage citation" إذا نقرت على زر "اعرض أكثر Show More" سترى الكم الكبير من المصادر التي تساعدك هذه الإضافة المميزة على توثيقها، مثل الكتب Book والمقالات الصحفية Journal Article ومراجعات الكتب Book Review وبراءات الاختراع Patent وغيرها الكثير. تسمح لك هذه الأداة بتوثيق هذه المصادر وفق العديد من التنسيقات العالمية المشهورة مثل MLA و APA، بالإضافة إلى العديد من التنسيقات الخاصة بمجلات محكّمة ودوريات، سواءً باختيارها من القائمة مباشرةً، أو بإدراج معلومات التوثيق من الملفات الخاصة بها (ملفات بلاحقة bib. عادةً). سنجرّب هذه الأداة في توثيق كتاب ومقال صحفي Journal Article، وبمجرد أن تفهم سياق العمل لن يصعب عليك توثيق أي مصدر آخر. توثيق كتاب: انقر على الزر "كتاب Book" لتظهر لك نافذة تسألك إن كنت ترغب بالبحث عنه من خلال عنوانه او اسم المؤلف أو رقم ISBN الخاص به، أو أن تملأ جميع المعلومات بنفسك. بالنسبة لي شخصيًا أفضل البحث من خلال رقم ISBN وهذا ما فعلته وإليك النتيجة: لقد عُثِر على الكتاب المطلوب! وإضافةً إلى ذلك، يمكنك معرفة تفاصيل كثيرةً عن الكتاب بالنقر على زر "أظهر أكثر Show more" أسفل لوحة الكتاب، إذ سيفتح النقر عليها صفحة جوجل بوك google book الخاصة بالكتاب. اُنقر على لوحة الكتاب وستجد أن التوثيق جاهز تمامًا وفق التنسيق المطلوب. بإمكانك تغيير التنسيق بالنقر على القائمة المنسدلة التي تعلو التوثيق واختيار ما تشاء من التنسيقات المتوفرة، أو النقر على أيقونة السهم المجاور لهذه القائمة لرفع ملف التنسيق الخاص بك إن كنت ملزمًا بتنسيق معيّن ومعرّف مسبقًا بأحد اللاحقتين (ris. أو bib.). مرر مؤشر الفأرة على التوثيق لترى خيارات استخدام التوثيق من "النسخ Copy" أو "الإدراج في النص In-text" أو "التحرير Edit" إن أردت تصويب بعض المعلومات المستخلصة تلقائيًا من رقم الكتاب أو إضافة معلومات جديدة إليها. انقر أخيرًا على زر "إضافة التوثيق إلى المستند Add bibiliography to the doc" لإدراجه في مستندك تحت قائمة المراجع. توثيق مقال صحفي: اُنقر على الزر "مقال صحفي Journal Article" لتظهر لك نافذة تسألك عما إذا كنت ترغب بالبحث عنه من خلال عنوانه أو اسم المؤلف أو رقم DOI الخاص به، أو أن تملأ جميع المعلومات بنفسك. سننقر على زر "ملء يدوي Fill manually" هذه المرة. اختر إن كان المقال "فرديًا Individual" أو "عائد لمؤسسة Organization". اكتب اسم المؤلف الأول ثم الأخير، ثم اختصار لقبه مثل ("phD" لحامل شهادة الدكتوراه) أو ("MD" للطبيب). يمكنك إضافة مؤلفين آخرين بالنقر على الزر "إضافة مؤلف للمقال Add article author"، أو إضافة محرر للمقال بالنقر على زر "Add editor". أضف تباعًا تاريخ النشر باليوم والشهر والسنة، ثم عنوان المقال واسم المجلة ثم "رقم المجلد Volume number" و"رقم العدد Issue number". أضف بعد ذلك أرقام الصفحات التي تريد توثيقها (من - إلى)، واسم قاعدة البيانات إن كانت متاحة. تجاوز تاريخ الولوج إلى المقال "Data accessed/viewed" وانتقل لتكتب رقم DOI لها. يمكنك إغفال أية معلومة لا تعرفها طالما أن مربعها لا يحتوي الكلمة "مطلوب Riquired". انقر الزر "تحديث Update" لإتمام العملية. إليك النتيجة: لإدراج التوثيق تابع خطوات الإدراج التي شرحناها سابقًا. الإضافة Highlight Tool للمراجعين والمدققين تقدم هذه الأداة ميزةً رائعةً لتوحيد التخاطب بالطريقة اللونية بين مؤلفي ومدققي ومحرري كتاب بما يخدم العمل التعاوني وينظمه بصورة بسيطة وواضحة. قد يرى البعض أن فكرة التعليقات والاقتراحات المبنية أساسًا في مستندات جوجل تعزز العمل المشترك على مستند وهذا أمر لا شك فيه، لكن تخيل الحالة التي يشترك فيها أربعة أو خمسة كتاب ومحررين على مشروع معين، ثم يبدأ كل مساهم في كتابة تعليق هنا وإسناد مهمة هناك، ثم الرد على تعليق وإسناد العمل أو الرد إلى آخر. هنا ستظهر إشكالية الموضوع وصعوبة المتابعة. الفائدة من هذه الإضافة ما تقدمه أداة Highlight Tool هي فكرة بسيطة مفادها تحديد النقاط الأساسية التي ينبغي مراجعتها في النص، ثم وضع رمز لوني ووصف كتابي لكل نقطة من هذه النقاط بالاتفاق بين جميع المحررين، ثم إنشاء مكتبة تضم تلك النقاط ومشاركتها بين الجميع. فإن رأى مثلًا أحد المراجعين معلومةً خاطئةً في النص وقد اتُفق على تلوين هذا النوع من المشاكل باللون الأحمر، فسيلوّن هذا المراجع المعلومة الخاطئة باللون الأحمر ويتابع العمل. وهكذا تشكل هذه الأداة طريقةً لتوحيد وتنظيم مراجعة مستند. تثبيت الأداة Highlight Tool اتبع نفس الخطوات التي فعلناها سابقًا عند تثبيت Writing Habit وسترى كيف تنضم هذه الإضافة إلى قائمة "الإضافات" بكل بساطة. العمل مع Highlight Tool من قائمة "الإضافات" في مستندات جوجل، انتقل إلى "Highlight Tool" ثم "ابدأ Start" وستظهر النافذة الجانبية للإضافة على الشكل التالي: إنشاء مكتبة جديدة للرموز اللونية: انقر على الزر الأخضر "مكتبة التلوين Highlighter Library" في أعلى نافذة الإضافة لتظهر النافذة التالية: انقر على الزر "مجموعة جديدة New Set" أسفل النافذة لتظهر لوحة أدوات جديدة أعلى النافذة تتيح لك بناء المجموعة المطلوبة. اكتب اسم هذه المجموعة في مربع النص "اسم المجموعة Set name" ضمن لوحة أدوات المجموعة وابدأ العمل على أول عنوان بكتابة وصف للرمز اللوني، ثم اختيار اللون الخاص بهذا الوصف من مربع الألوان إلى جوار العنوان. سأختار هنا اللون الأحمر للدلالة على العبارة "المعلومة المدرجة خاطئة"، لهذا سأكتب هذه العبارة في مربع النص "عنوان Label" واختار اللون الأحمر. بإمكانك أيضًا إزالة هذا العنوان بالنقر على الزر (-) إلى جواره، وستبدو نافذة إنشاء مكتبة كالتالي: انقر على الزر (+) إلى جوار مربع النص الذي يحمل اسم مجموعتك لإضافة عنوان جديد يصف رمزًا لونيًا جديدًا. كرّر هذه العملية لتحدد جميع الرموز اللونية التي قد تحتاجها في تدقيق مستند. انقر على زر "حفظ Save" أسفل النافذة لحفظ المكتبة الجديدة للرموز اللونية. لإنشاء مكتبة جديدة كرر الخطوات السابقة جميعها. ولأجل توضيح الأمر في مقالنا، بنيت المكتبة اللونية التالية: عليك الآن تصدير هذه المكتبة حتى يستخدمها بقية المحررين، لذلك نفِّذ الخطوات التالية: انقر على قائمة "الإضافات" ثم "Highlight Tool"، ثم اختر "تصدير مكتبة Export library". ستظهر لك نافذة لعرض المكتبات المتوفرة للتصدير، وهنا اختر المكتبة المطلوبة أو جميعها إن أردت وانقر الزر "تصدير Export" أسفل النافذة. تخبرك الإضافة عند إنجاز العمل بأن التصدير قد نجح إلى مستند جوجل جديد ويزوّدك بالرابط "document" للوصول إليه. بالنقر على هذا الرابط، يمكنك الاطلاع على ملف المكتبة وتغيير اسمه وحفظه ومشاركته كما تشاء، لكن لا تعدل فيه شيئًا. لاستخدام المكتبة، حدّد النص الذي تريد الإشارة إلى وضعه، ثم انقر على الرمز اللوني الذي تريد. بإمكانك نقل الجمل التي لونتها إلى مستند جديد كي تعالج الإشكال فيها بمعزل عن المستند الأصلي. ولتنفيذ ذلك اتبع الخطوات التالية: في قسم "نقل الجمل الملونة Extract Highlights"، انقر زر "وفق الترتيب اللوني By color" أو "وفق التسلسل By sequence". ستظهر الآن نافذة تطلب إليك تحديد المكان الذي ستنسخ إليه الجمل الملونة، فإما إلى "مستند جديد New Document" أو إلى "المستند الحالي Current Document" أو إلى "عنوان مستند معين Document URL". اختر الوجهة المطلوبة وانقر على "نقل Extract". وسترى نتيجةً رائعةً جدًا! الخلاصة رأينا في هذا المقال كيف توسّع الإضافات عمل تطبيق مستند جوجل وتمنحه وظائفيةً متقدمةً بما يتناسب مع حاجة مستخدميه. وقد حاولنا أن نختار لكم مجموعةً من الإضافات التي تلائم عمل المحررين والمؤلفين أيًا كانت غاياتهم من استخدام مستندات جوجل. وسنوجز عمل هذه الإضافات كالتالي: الإضافة Writing Habit: تقدم إحصائيات واسعةً ودقيقةً عن تقدم العمل في مشروع التأليف أو التحرير الحالي. الإضافة Bibcitaion: تقدم طريقةً متقدمةً وأنيقةً وعصريةً لتوثيق المصادر التي ترد في المقال أو الكتاب. الإضافة Highlight Tool: تقدّم طريقةً لتوصيف أنواع الأخطاء أو الملاحظات التي يجدها المراجع في المستند بطريقة لونية قابلة للنقل إلى بقية المحررين أو المراجعين. وعلى الرغم من وجود إضافات متنوعة ومفيدة أخرى، إلا أن دعمها للغة العربية محدود، وهو ما دفعني للابتعاد عنها، فإن كانت لغتك الإنكليزية جيدة أو تحتاج إلى دعم في كتابة المقالات أو أوراق العمل بالإنجليزية، فستجد الكثير الكثير من الإضافات الرائعة. اقرأ أيضًا إعداد الصفحة وتنسيق الفقرات في مستندات جوجل قائمة الأدوات في مستندات جوجل تنسيق وتوثيق الأوراق الأكاديمية باستخدام مستندات جوجل كيفية المشاركة في كتابة وتعديل مستند باستعمال مستندات جوجل تاريخ النُسخ واستعمال الإضافات في مستندات جوجل
  5. بعد أن تعلمنا أساسيات التعامل مع الجداول في HTML في المقال السابق، نلقي الضوء في هذا المقال على بعض الميزات المتقدمة لجداول HTML مثل الشروحات captions والملخصات summaries وتجميع الصفوف لتشكيل ترويسة أو جسم أو تذييل، كما يمر المقال على مفهوم سهولة الوصول accessibility لذوي المشاكل البصرية إلى محتوى الجداول. إضافة شرح إلى الجدول يمكنك إضافة شروحات إلى الجداول باستخدام العنصر <caption> بعد وضعه تحت وَسم البداية للعنصر <table> مباشرةً: <table> <caption>Dinosaurs in the Jurassic period</caption> ... </table> يُستخدَم الشرح -كما يوحي المثال السابق- لإضافة وصف لمحتوى الجدول، إذ يساعد القراء على تقدير فائدة هذا المحتوى عندما يتصفحون الموقع وخاصة للمستخدِمين المكفوفين، فبدلًا من قراءة خلايا عديدة لمعرفة الغاية من الجدول، فسيعتمد المستخدِم على شرح الجدول ليقرر الاستمرار في القراءة أم لا. ملاحظة: يمكن استخدام السمة summary التي يملكها العنصر <table> لإضافة وصف يمكن لقارئات الشاشة الوصول إليه وقراءته، لكننا نفضِّل استخدام العنصر <caption> بدلًا منها لأنها أُلغيَت في مواصفات HTML5، ولا يمكن رؤيتها من قبل المستخدِمين صحيحِي البصر لأنها لن تعرض على الصفحة. تطبيق: إضافة شرح إلى جدول لنلق نظرةً ثانيةً إلى مثال برنامج التدريس اليومي الذي طرحناه في المقال السابق: عُد إلى البرنامج اليومي لمدرّسة اللغات الذي ستجده على جيت-هاب واحفظ نسخًا منه على حاسوبك الشخصي. أضف شرحًا مناسبًا إلى محتوى الجدول. احفظ التعديلات التي أجريتها ثم حمِّل الملف ضمن المتصفح لترى النتيجة. ملاحظة: يمكن إيجاد الملف بصورته النهائية على جيت-هاب. إضافة هيكلية للجدول باستخدام <thead> و <tfoot> و <tbody> عندما تتعقد هيكلية الجداول نوعًا ما، فلا بدّ من تحديد هذه الهيكلية بصورة أوضح، ولتنفيذ ذلك تُستخدَم العناصر <thead> و <tfoot> و <tbody> التي تسمح لك بتوصيف ترويسة وتذييل وجسم للجدول. لن تحسّن هذه العناصر إمكانية الوصول السهل لمستخدِمي قارئات الشاشة، ولن تحسّن في المظهر المرئي للجدول أيضًا، ولكنها مفيدة جدًا لتخطيط الجدول وتنسيقه، إذ تعمل هذه العناصر على أساس خطافات لإضافة تنسيقات CSS إلى الجداول، ففي حالة الجداول الطويلة التي تمتد على صفحات مثلًا يمكنك تكرار الترويسة والتذييل في كل صفحة تطبعها، كما يمكنك عرض جسم الجدول بأكمله على صفحة واحدة ومن ثم تصفح محتوياته باستخدام شريط تمرير للأعلى والأسفل، ولتفعل ذلك اتبع ما يلي: استخدِم العنصر <thead> لتغليف الجزء الذي يمثّل ترويسة الجدول، وهو عادةً الصف الأول الذي يضم ترويسات جميع الأعمدة لكن ليس دائمًا، فإذا كنت تستخدِم عنصرَي تجميع خلايا العمود <colgroup> و <col>، فضع ترويسة الجدول بعدها مباشرةً. استخدِم العنصر <tfoot> لتغليف الجزء الذي يمثل تذييل الجدول، فقد يكون هذا الجزء هو الصف الأخير من الجدول وقد جمعت فيه قيم الأسطر السابقة مثلًا، وبإمكانك وضع عنصر التذييل في آخر الجدول كما تتوقع أو تحت ترويسة الجدول مباشرةً لأن المتصفح سيصيّره في المكان الصحيح. استخدِم العنصر <tbody> لتغليف بقية الأجزاء، فقد يأتي جسم الجدول تحت الترويسة أو تحت التذييل تبعًا للطريقة التي تنويها في تنظيم الجدول. ملاحظة: إن العنصر <tbody> هو دائمًا جزء من الجدول سواءً صرَّحت عن ذلك أم لا، ولكي تتحقق من ذلك افتح أيًا من أمثلة الجداول التي لا تحتوي على <tbody> ثم تفحص شيفرة باستخدام أدوات مطوري ويب وستجد أنّ المتصفح قد أضاف هذا العنصر تلقائيًا، وقد تتساءل إذًا لِمَ عليّ إضافته أصلًا، والجواب هو أنه سيمنحك تحكمًا أكبر بهيكلية الجدول وتنسيقه. تطبيق: هيكلة جدول لنحاول تجربة العناصر التي تهيكل الجدول: ابدأ بحفظ نسخ عن الملفين spending-record.html وminimal-table.css في مجلد جديد على حاسوبك. افتح ملف HTML في المتصفح وسترى أنّ الجدول واضح مع إمكانية لتحسينه قليلًا، إذ يبدو الصف "SUM" الذي يضم مجموع النفقات في المكان الخاطئ، كما قد تلاحظ بعض التفاصيل المفقودة في الشيفرة. ضع الصف الذي يمثّل الترويسة داخل عنصر <thead> والصف "SUM" ضمن عنصر <tfoot> وبقية الجدول ضمن عنصر <tbody>. احفظ التعديلات وحمّل الملف من جديد، ثم لاحظ كيف انتقل الصف إلى أسفل الجدول. استخدِم السمة colspan لتمتد الخلية "SUM" على الأعمدة الأربعة الأولى كي يظهر المبلغ تحت العمود "Cost". أضف بعض التنسيقات البسيطة إلى الجدول لترى فائدة تطبيق تنسيق CSS، لذا ضع الشيفرة التالية داخل العنصر <style> الموجود داخل العنصر <head> في ملف HTML: tbody { font-size: 95%; font-style: italic; } tfoot { font-weight: bold; } احفظ التعديلات ثم حمّل الملف من جديد وشاهد نتيجة عملك، فإذا لم يكن العنصران <tbody> و <tfoot> في مكانهما الصحيح، لكان عليك كتابة شيفرة تنسيق أكثر تعقيدًا للحصول على النتيجة ذاتها. ستبدو نتيجة العمل كما يلي: ملاحظة: يمكنك الاطلاع على النسخة النهائية من المثال على جيت-هاب. الجداول المتداخلة يمكن وضع جدول ضمن آخر شرط أن تكون هيكلية الجدول مكتملة بما في ذلك العنصر <table>، ولا ننصح عادةً بمثل هذا التداخل لأنه سيعقد من توصيف الجداول ويصعّب وصول قارئات الشاشة إلى محتوياتها، كما يُعَدّ إدراج خلايا أو صفوف أو أعمدة جديدة إلى الجدول المطلوب هو الأجدى في كثير من الحالات، لكنك قد تضطر أحيانًا إلى إدراج جدول ضمن آخر خاصةً إذا أردت إدراج محتوى من مصدر خارجي دون تعقيدات إضافية على سبيل المثال، وتعرض الشيفرة التالية جدولين بسيطين متداخلين: <table id="table1"> <tr> <th>title1</th> <th>title2</th> <th>title3</th> </tr> <tr> <td id="nested"> <table id="table2"> <tr> <td>cell1</td> <td>cell2</td> <td>cell3</td> </tr> </table> </td> <td>cell2</td> <td>cell3</td> </tr> <tr> <td>cell4</td> <td>cell5</td> <td>cell6</td> </tr> </table> ستبدو نتيجة تنفيذ الشيفرة كما يلي: جداول خاصة بذوي المشاكل البصرية لنراجع باختصار استخدامات الجداول، إذ تُعَدّ الجداول أداةً مفيدةً تساعدنا في الوصول السريع إلى البيانات والبحث عن القيم المختلفة فيها، وبنظرة سريعة مثلًا على الجدول الذي نعرضه تاليًا ستتمكن من معرفة عدد الخواتم Rings التي بيعت في جينت Gent شهر 8 عام 2016، إذ نربط بصريًا بين المعلومات الواردة في الجدول مع ترويسة الصف والعمود لنجد المطلوب. table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } Clothes Accessories Trousers Skirts Dresses Bracelets Rings Belgium Antwerp 56 22 43 72 23 Gent 46 18 50 61 15 Brussels 51 27 38 69 28 The Netherlands Amsterdam 89 34 69 85 38 Utrecht 80 12 43 36 19 لكن ما الحل إذا لم تتمكن من رؤية الجدول؟ كيف ستتمكن من قراءته؟ يستخدِم أصحاب المشاكل البصرية قارئات الشاشة التي تقرأ المعلومات الواردة في صفحات الويب، إذ ستعمل هذه التقنية بسهولة عند قراءة نص صرف، لكن يُعَدّ تفسير محتوى جدول أمرًا مختلفًا ويمثِّل تحديًا للمكفوفين، ومع ذلك يمكننا الاستغناء عن الارتباطات البصرية والاستعاضة عنها بارتباطات برمجية عند استخدام العناصر الصحيحة، وسنعرض تاليًا بعض التقنيات التي تسهّل الوصول إلى الجداول قدر المستطاع. ملاحظة: يوجد حوالي 253 مليون شخص كفيف أو يعاني من مشاكل بصرية وفقًا لمنظمة الصحة العالمية WHO في عام 2017. استخدام ترويسات الأعمدة والصفوف تستخدِم قارئات الشاشة الترويسات في الارتباطات البرمجية بينها وبين الخلايا المتعلقة بها، إذ يعرِّف ويفسر ضمّ ترويسات الصفوف والأعمدة بيانات كل خلية وبذلك ستتمكن القارئات من إيصال هذا التفسير إلى المكفوف. استخدام السمة scope تُضاف هذه السمة إلى العنصر <th> -أي ترويسة جدول- لتدل قارئات الشاشة على الخلايا التي تمثلها ترويسة معينة (هل هي ترويسة صف أم عمود مثلًا؟)، وبالعودة إلى مثال التطبيق السابق الذي طرحناه سابقًا، سترى كيف تُميَّز ترويسة العمود لأنها ستبدو على الصورة التالية: <thead> <tr> <th scope="col">Purchase</th> <th scope="col">Location</th> <th scope="col">Date</th> <th scope="col">Evaluation</th> <th scope="col">Cost (€)</th> </tr> </thead> وترويسة كل صف ستبدو أيضًا كما يلي إذا أضفنا ترويسات صفوف بالإضافة إلى ترويسات الأعمدة: <tr> <th scope="row">Haircut</th> <td>Hairdresser</td> <td>12/09</td> <td>Great idea</td> <td>30</td> </tr> تستطيع قارئات الشاشة التعرُّف على هذا التوصيف، وبالتالي ستقرأ كامل الصف أو العمود مباشرةً على سبيل المثال. تأخذ السمة scope أيضًا القيمتين colgroup و rowgroup وتستخدمان لتوصيف الترويسات التي تقع فوق عدة أعمدة أو صفوف، فإذا عدت إلى الجدول السابق "حول مبيعات شهر 8 عام 2016"، فستجد أنّ الخلية "Clothes" هي ترويسة للخلايا "Trousers" و "Skirts" و "Dresses"، وينبغي الإشارة إلى جميع هذه الخلايا على أنها ترويسات (<th>)، ولهذا لا بدّ من أن نزوّد الخلية "Clothes" بالسمة "scope="colgroup، في حين ستأخذ بقية الترويسات التي تقع تحتها مباشرة السمة "scope="col. استخدام السمتين id و headers يمكننا استخدام السمتين id و headers على أساس بديل عن scope في الارتباطات بين الترويسات والخلايا، ويجري الأمر كما يلي: زوّد كل عنصر <th> (عنصر الترويسة) بسِمة id تحمل قيمة فريدة. زوّد كل عنصر <td> (عنصر الخلية) بسِمة headers تضم قائمةً بمعرِّفات جميع الترويسات التي تُعَدّ ترويسةً مباشرةً لهذه الخلية تفصل بينها مسافةً فارغةً. يعطي هذا الأسلوب جدوّل HTML تعريفًا صريحًا لموقع كل خلية بترويسة (أو ترويسات) العمود والصف الذي تشكل جزءًا منه، ولا بدّ من الإشارة إلى ضرورة استخدام ترويسات للصفوف والأعمدة معًا إذا أردت استخدام هذه الطريقة. لاحظ طريقة تطبيق هذا الأسلوب على مثال التطبيق السابق السابق: <thead> <tr> <th id="purchase">Purchase</th> <th id="location">Location</th> <th id="date">Date</th> <th id="evaluation">Evaluation</th> <th id="cost">Cost (€)</th> </tr> </thead> <tbody> <tr> <th id="haircut">Haircut</th> <td headers="location haircut">Hairdresser</td> <td headers="date haircut">12/09</td> <td headers="evaluation haircut">Great idea</td> <td headers="cost haircut">30</td> </tr> ... </tbody> ملاحظة: تُعَدّ هذه الطريقة دقيقةً جدًا في ارتباط خلايا البيانات مع الترويسات، لكنها تتطلب عملًا أكثر لا يحتمل أية أخطاء، ولهذا يُعَدّ استخدام السمة scope كافيًا لمعظم الجداول. تطبيق: استخدام السمتين scope و headers احفظ نسخةً عن الملفين items-sold.html و minimal-table.css في مجلد جديد على جهازك. حاول استخدام السمة scope لتحسين طريقة عرض هذا الجدول. حاول حفظ نسخة أخرى عن الملفين السابقين في مجلد آخر على جهازك ثم استخدم السمتين scope و headers لدعم فكرة سهولة الوصول إلى الجدول. ملاحظة: يمكنك التحقق من صحة عملك بموازنته مع النسخة النهائية باستخدام scope وباستخدام headers على جيت-هاب. خلاصة على الرغم من وجود بعض الأفكار الأخرى التي يمكنك تعلمها عن الجداول في HTML، إلا أننا زودناك بكل ما تحتاجه حاليًا، ولا بدّ الآن من التوجه نحو تعلّم طريقة تنسيق ملفات HTML باستخدام لغة التوصيف CSS. للمزيد من التفاصيل، ارجع إلى توثيق الجداول في موسوعة حسوب. ترجمة -وبتصرُّف- للمقال HTML table advanced features and accessibility. اقرأ أيضًا أساسيات بناء الجداول في HTML كيفية إنشاء جدول أنيق بـ HTML و CSS جداول html ذات رأسية وأعمدة ثابتة باستخدام jQuery الجداول (Tables) في CSS
  6. يقدِّم هذا المقال الأساسيات اللازمة لبناء جداول HTML، إذ يغطي مفاهيم مثل الصفوف والأعمدة والخلايا والترويسات وكيفية ضبط الخلايا لتمتد على عدة أعمدة وصفوف، كما يعلمك طريقة تجميع الخلايا في العمود نفسه لتنسيقها معًا. ما هو الجدول؟ الجدول هو بنية لهيكلة البيانات على صورة صفوف rows وأعمدة column -أي بيانات مجدولة- لتسهيل عملية البحث عن القيم التي تبدي ترابطًا فيما بينها مثل ربط الأشخاص بأعمارهم أو بالأيام التي يعملون فيها أسبوعيًا أو بأيام التدريب في النادي. استُخدِمت الجداول بكثرة ومنذ زمن بعيد، وتقدِّم الصورة التالية دليلنا على ذلك، فهي مستند لعملية إحصاء في الولايات المتحدة يعود إلى عام 1800: وبالتالي ليس غريبًا أن يهتم مخترعو HTML بإيجاد وسائل لهيكلة بيانات الويب على هيئة جداول. كيف يعمل الجدول؟ الغاية من الجدول واضحة، إذ يسهِّل استخدام الجداول تفسير المعلومات بالربط البصري بين ترويسة عمود وترويسة صف، ولنلق نظرةً على الجدول التالي باحثِين عن كوكب غازي ضخم شبيه بالمشتري Jovian له 62 قمرًا، إذ يمكنك إيجاد الكوكب المطلوب بالربط بين العمود الصحيح و الصف الصحيح. عندما تُنجز الجداول في HTML بالصورة الصحيحة، سيسهل على تقنيات الوصول السهل مثل قارئات الشاشة التعامل مع المحتوى الذي تقدِّمه، أي ينبغي أن يُحسِّن استخدام الجداول تجربة الأشخاص ذوي الإعاقة البصرية وسليمي البصر على حد سواء. تنسيق الجداول ألق نظرةً على الجدول السابق نفسه لكن من خلال مثال مباشر على جيت-هاب، ولاحظ أنّ هذا الأخير أسهل قراءةً من الجدول الذي عرضناه في المقال، وذلك لأننا طبقنا أدنى درجات التنسيق عند بنائه موازنةً بنسخة جيت-هاب. لا نخفيك سرًا أنّ الجداول التي تتمتع بمستوى تنسيق CSS رفيع أنها أكثر تأثيرًا في الويب، لهذا لا بد من الاهتمام جيدًا بهيكلة البيانات في جداول HTML بالإضافة إلى التنسيق الجيد، لكننا سنركز في سلسلة المقالات هذه على الجزء المتعلق بلغة HTML فقط، إذ سنزود الجداول في هذه السلسلة بالحد الأدنى من تنسيقات CSS التي تُسهِّل قراءة محتوياتها موازنةً بالجداول غير المنسقة، كما يمكنك تنزيل ملف CSS و قالب HTML الذي يُستخدم لتطبيق التنسيقات من المستودع المخصص على جيت-هاب، إذ سيمنحك هذان الملفان نقطة انطلاق جيدة عند العمل على جداول HTML. ما الحالات التي لا ينبغي فيها استخدام جداول HTML؟ تُستخدَم الجداول لعرض البيانات القابلة للجدولة فهمي مصممة لهذا الغرض، كما يستخدِمها البعض أيضًا في تخطيط صفحات الويب مثل أن يضم أحد الصفوف الترويسة وآخر يضم أعمدة المحتوى الرئيسي وآخر للتذيل، وهذا أمر خاطئ كليًا، وقد استُخدمت هذه الطريقة في تخطيط الصفحات فيما مضى عندما كان دعم تنسيق CSS في المتصفحات المختلفة متفاوتًا وسيئًا، لكنها أقل انتشارًا في أيامنا هذه، إلا أنك قد تراها بين الفينة والأخرى. إذًا نختصر فنقول: يُعَدّ استخدام تخطيط الجداول بدلًا من تقنيات تخطيط CSS فكرةً سيئةً، وإليك الأسباب الرئيسية: يؤثر تخطيط الجدول على سهولة وصول ذوي المشاكل البصرية: تفسِّر قارئات الشاشة التي يستخدمها فاقدِي البصر وسوم العناصر الموجودة في صفحة HTML وتقرأ محتوياتها، وطالما أنّ الجدول ليس الأداة الصحيحة لتخطيط الصفحة وأنّ توصيفه أكثر تعقيدًا من تقنيات تخطيط CSS، فسيربك مستخدِميها ما تنطقه تلك القارئات. ينتج عن الجداول وسومًا مختلطةً: تضم الجداول هيكليةً أعقد من تلك المستخدَمة عادةً في تخطيط الصفحة، وقد يزيد ذلك من صعوبة كتابة الشيفرة وصيانتها وتنقيحها. الجداول غير متجاوبة تلقائيًا: إذا استخدمت العناصر المناسبة مثل <header> أو <article> أو <div> أو <section>، فسيكون عرضها 100% من عرض العنصر الأب تلقائيًا، ويقاس في المقابل عرض الجداول افتراضيًا وفقًا لحجم محتواها، لذلك يحتاج الأمر إلى قياسات أخرى ليعمل تخطيط الصفحة جيدًا على الأجهزة المختلفة. تطبيق: إنشاء جدول تحدثنا طويلًا عن المفهوم النظري وحان الوقت لنبني جدولًا بسيطًا: انسخ الملفين blank-template.html و minimal-table.css إلى حاسوبك وضعهما في مجلد جديد. يُوضع محتوى كل جدول بين وسمَي البداية والنهاية للعنصر <table>، لذلك ضع عنصرَي الجدول داخل جسم ملف HTML. تُعَدّ الخلية أصغر حاوية ضمن الجدول وننشئها باستخدام العنصر <td> اختصارًا لبيانات جدول table data، ولنضف الشيفرة التالية داخل العنصر <table>: <td>Hi, I'm your first cell.</td> انسخ الشيفرة السابقة ثلاث مرات إضافية إذا أردت تكوين صف من أربعة خلايا: <td>Hi, I'm your first cell.</td> <td>I'm your second cell.</td> <td>I'm your third cell.</td> <td>I'm your fourth cell.</td> يمثِّل كل عنصر <td> خليةً واحدةً، وتمثِّل هذه العناصر بمجموعها الصف الأول الذي يكبر بإضافة عناصر جديدة، ولإنهاء الصف الأول والانتقال إلى الصف الثاني نستخدِم العنصر <tr> اختصارًا لصف جدول table row: ضع الخلايا الأربعة التي أنشأتها سابقًا ضمن وسمَي البداية والنهاية للعنصر <tr>: <tr> <td>Hi, I'm your first cell.</td> <td>I'm your second cell.</td> <td>I'm your third cell.</td> <td>I'm your fourth cell.</td> </tr> نكون بهذا الشكل قد أنشأنا الصف الأول، ولا بدّ من تغليف خلايا جديدة <td>ضمن عنصر <tr> جديد: نتيجة العمل لا بدّ أن يظهر الجدول كما يلي: ملاحظة: يمكنك إيجاد المثال السابق بصيغته النهائية ضمن المستودع المخصص له على جيت-هاب. عناصر ترويسة جدول لنركِّز الآن على ترويسات الجدول، وهي خلايا خاصة تلاحظها في بداية الصف أو العمود وتحدد طبيعة البيانات التي يضمها كما في الخليتين Person و Age في مثالنا الأول، ولتوضيح فائدة الترويسات سنلقي نظرةً على هذا المثال: <table> <tr> <td>&nbsp;</td> <td>Knocky</td> <td>Flor</td> <td>Ella</td> <td>Juan</td> </tr> <tr> <td>Breed</td> <td>Jack Russell</td> <td>Poodle</td> <td>Streetdog</td> <td>Cocker Spaniel</td> </tr> <tr> <td>Age</td> <td>16</td> <td>9</td> <td>10</td> <td>5</td> </tr> <tr> <td>Owner</td> <td>Mother-in-law</td> <td>Me</td> <td>Me</td> <td>Sister-in-law</td> </tr> <tr> <td>Eating Habits</td> <td>Eats everyone's leftovers</td> <td>Nibbles at food</td> <td>Hearty eater</td> <td>Will eat till he explodes</td> </tr> </table> يصيّر المتصفح الشيفرة السابقة إلى الجدول التالي: إنّ المشكلة في هذا الجدول هي صعوبة الوصول إلى البيانات المطلوبة كما ينبغي، على الرغم من وضوح الغاية منه، لكن إذا برزت ترويسات الأعمدة والصفوف بصورة ما، فسيتحسن الوضع. تطبيق: ترويسات الجداول سنحاول في هذا التطبيق تحسين الجدول السابق: احفظ نسخةً عن الملفين dogs-table.html و minimal-table.css في مجلد جديد على حاسوبك، إذ يحتوي ملف HTML على المعلومات ذاتها التي يعرضها الجدول السابق. استخدم العنصر <th> -اختصارًا لترويسة جدول table header- لتمييز الخلية الأولى على أنها ترويسة دلاليًا وبصريًا، إذ يعمل هذا العنصر بالطريقة نفسها التي يعمل بها العنصر <td> إلا أنه يعرِّف ترويسةً وليس خليةً عاديةً. انتقل إذًا إلى ملف HTML واستبدل العناصر <th> بكل العناصر <td> التي تحيط بخلايا الترويسة. احفظ التغييرات التي أجريتها على ملف HTML ثم حمّله في المتصفح وستلاحظ كيف ستبدو الخلايا الأولى على أساس ترويسات. ملاحظة: يمكنك الاطلاع على الملف بشكله النهائي على جيت-هاب، كما يمكنك الاطلاع على شيفرته المصدرية. ما هي فوائد الترويسات؟ جاوبنا بالفعل على جزء من هذا السؤال، إذ يسهِّل استخدامها استخلاص المعلومات ويحسِّن مظهر الجدول عمومًا. ملاحظة: تمتلك ترويسات الجداول تنسيقًا افتراضيًا محددًا، إذ تُكتَب بخط ثخين وتتوضَّع في مركز الخلية حتى ولو لم تطبق بنفسك أية تنسيقات، وذلك لتمييزها. تمتلك الترويسات فوائدًا إضافيةً، إذ يسمح لك استخدام السمة scope -التي سنتعرف إليها لاحقًا- بدعم الوصول السهل، وذلك بربط كل ترويسة بكل البيانات التي يضمها الصف نفسه أو العمود، إذ ستتمكن عندها قارئات الشاشة من قراءة كامل الصف أو العمود، وهذا بالطبع أكثر فائدةً. تمكين الخلايا من الامتداد على عدة صفوف أو خلايا قد نرغب أحيانًا في تمديد الخلايا لتشمل عدة صفوف أو أعمدة، ولنأخذ مثلًا الجدول التالي الذي يعرض بعض أسماء الحيوانات، فقد نرغب في بعض الحالات بعرض أسماء الإناث والذكور إلى جانب النوع وقد لا نرغب بذلك، وفي كلتا الحالتين نريد أن يمتد اسم الحيوان على كامل الجدول، وإليك الشيفرة الأولية: <table> <tr> <th>Animals</th> </tr> <tr> <th>Hippopotamus</th> </tr> <tr> <th>Horse</th> <td>Mare</td> </tr> <tr> <td>Stallion</td> </tr> <tr> <th>Crocodile</th> </tr> <tr> <th>Chicken</th> <td>Hen</td> </tr> <tr> <td>Rooster</td> </tr> </table> لكن لا يعطي الخرج النتيجة التي نريدها تمامًا: نريد مثلًا أن يمتد "Hippopotamus" أو "Crocodile" مقدار عمودين، وأن يمتد Horse" و "Chicken" مقدار صفين، إذ تمتلك ترويسة الجدول لهذه الغاية السمتَين colspan و rowspan اللتين تقبلان قيمًا بلا واحدة تدل على عدد الصفوف أو الأعمدة التي نريد أن تمتد الترويسة عليهما، لذا ستسمح القيمة "2"=colspan مثلًا لترويسة عمود أن تمتد بمقدار عمودين، إذًا لنستخدِم هاتين السمتين لتحسين مظهر الجدول السابق: احفظ نسخةً عن الملفين animals-table.html و minimal-table.css في مجلد جديد على حاسوبك، إذ يحتوي ملف HTML على المعلومات ذاتها التي يعرضها الجدول السابق. استخدم السمة colspan لتجعل الخلايا "Animals" و "Hippopotamus" و "Crocodile" تمتد بمقدار عمودَين. استخدم السمة rowspan لتجعل الخلايا "Horse" و "Chicken" تمتد بمقدار صفَّين. احفظ التغيرات التي أجريتها على الملف ثم حمّله في المتصفح لترى النتيجة. ملاحظة: يمكنك إيجاد المثال السابق بصيغته النهائية ضمن المستودع المخصص له على جيت-هاب. تزويد الأعمدة ببعض التنسيقات تقدِّم HTML طريقةً لتنسيق محتوى عمود بأكمله (عناصر <col> وعناصر <colgroup>)، وقد وُجِدت هذه الطريقة لأنّ عملية تنسيق كل عنصر <td> أو <th> على حدة في عمود ستكون مزعجةً وغير فعّالة وسيكون الأمر كذلك عند استخدام محدِّد تنسيق معقَّد مثل nth -child:. ملاحظة: تنسيق العمود بهذه الطريقة مخصص لبعض السمات فقط، وهي:border و background و width و visibility، فإذا احتجت لاستخدام خصائص أخرى، فلا بدّ من تنسيق كل عنصر <td> أو <th> على حدة، أو استخدام محدد تنسيق معقّد مثل nth -child:. لنلق نظرةً على هذا المثال: <table> <tr> <th>Data 1</th> <th style="background-color: yellow">Data 2</th> </tr> <tr> <td>Calcutta</td> <td style="background-color: yellow">Orange</td> </tr> <tr> <td>Robots</td> <td style="background-color: yellow">Jazz</td> </tr> </table> ستكون النتيجة كما يلي: إنّ الطريقة المتبعة في الشيفرة السابقة ليست ملائمةً، إذ علينا كتابة التنسيق في كل خلية من خلال العمود، فمن السهل في هذه الحالة -إن كنا نعمل على مشروع واقعي- استخدام السمة class وتحديد التنسيق المطلوب مرةً واحدةً ضمن ملف تنسيق منفصل، وبدلًا من ذلك يمكننا تحديد معلومات التنسيق هذه مرةً واحدةً فقط ضمن عنصر <col> يقع داخل العنصر <colgroup> الذي يُدرج مباشرةً أسفل وسم بداية الجدول <table>، وإليك الطريقة: <table> <colgroup> <col> <col style="background-color: yellow"> </colgroup> <tr> <th>Data 1</th> <th>Data 2</th> </tr> <tr> <td>Calcutta</td> <td>Orange</td> </tr> <tr> <td>Robots</td> <td>Jazz</td> </tr> </table> عرّفنا عمودين اثنين، الأول فارغ ولا يحتوي على أي تنسيق لكنه ضروري لكي لا يتغير تنسيق العمود الثاني المطبق، وإذا أردت تنسيق كلا العمودَين، فاكتف بعنصر <col> واحد يحمل التنسيق المطلوب شرط استخدام السمة span: <colgroup> <col style="background-color: yellow" span="2"> </colgroup> تأخذ هذه السمة قيمًا على صورة أرقام بلا واحدة مثل سمة colspan وسمة rowspan التي تدل على عدد الأعمدة التي سيطبَّق عليها التنسيق. تطبيق: تنسيق أعمدة ترى في الجدول التالي برنامجًا زمنيًا لمواعيد مدرِّسة لغات، ولدى هذه المدرّسة صفًا جديدًا لتعليم الهولندية Dutch يوم الجمعة بأكمله، وتدرِّس الألمانية German على فترات يومَي الثلاثاء والخميس، وتريد المدرّسة تلوين أعمدة الأيام التي تُدرِّس فيها: أعد إنشاء الجدول السابق متبعًا الخطوات التالية: احفظ نسخةً عن الملف timetable.html في مجلد جديد على حاسوبك، إذ يحتوي ملف HTML على المعلومات ذاتها التي يعرضها الجدول السابق ما عدا معلومات تنسيق الأعمدة. أضف العنصر <colgroup> إلى العنصر <table> وتحت وَسم البداية مباشرةَ، ثم أضف إلى هذا العنصر عناصر <col> لإضافة تنسيقات إلى الأعمدة. لا تغيِّر تنسيق أول عمودين. أضف لونًا إلى خلفية العمود الثالث بضبط قيمة السمة style على ;background-color:#97DB9A. استخدم عرضًا مختلفًا للعمود الرابع بضبط قيمة السمة style على ;width: 42px. أضف لونًا إلى خلفية العمود الخامس بضبط قيمة style على ;background-color:#97DB9A. أضف لونًا مختلفًا وإطارًا إلى العمود السادس لتشير إلى أنه يوم مميز، إذ ستبدأ بتدريس لغة جديدة، ولذلك اضبط قيمة style على ;background-color:#DCC48E; border:4px solid #C1437A إذا وجدت نفسك عالقًا أمام مشكلة ما أو أردت التحقق من عملك، فيمكنك مراجعة النسخة النهائية للمثال على جيت-هاب. خلاصة عرضنا في هذا المقال أساسيات إنشاء جداول في HTML، إذ سنلقي نظرةً في المقال التالي على ميزات أكثر تقدمًا، مع الأخذ بالحسبان سهولة وصول ذوِي الإعاقة البصرية. للمزيد من التفاصيل، ارجع إلى توثيق الجداول في موسوعة حسوب. ترجمة -وبتصرَّف- للمقال HTML table basics. اقرأ أيضًا كيفية إنشاء جدول أنيق بـ HTML و CSS جداول html ذات رأسية وأعمدة ثابتة باستخدام jQuery الجداول (Tables) في CSS
  7. يهدف هذا المقال أساسًا إلى التعريف ببرمجة الروبوتات Robots Programming، ونظرًا لاختلاف النُهج المتبعة في البرمجة وفقًا لطبيعة عمل الروبوت، ستختلف عملية برمجة الروبوت وأساليبها، لهذا سنتحدث باختصار عن الروبوتات وأنواعها ثم الاستخدامات الأكثر شيوعًا لها، ومن ثم نعرّج على أهم لغات البرمجة المستخدمة في برمجة الروبوتات وكيفية البدء بمسار تعلم برمجة الروبوت المناسب لك ونتحدث في مقالنا عن برمجة الروبوتات للأطفال وأشهر اللغات والمنصات التي تساعد على دخول عالم برمجة الروبوتات والتحكم بها. من الآلات إلى الروبوتات فرض الانتقال إلى المجتمعات الصناعية اختراع كم هائل من الآلات التي سهلت على الصناعيين أداء وظائف كثيرة وأدت إلى ظهور ما يعرف بخطوط الإنتاج التي تضم سلسلة من الآلات المتتابعة لتنفيذ بعض أو كل مراحل المنتج وفق آليات ميكانيكية بحتة. لكن في البدايات الأولى كان وجود العامل البشري أساسيًا في قيادة تلك الآلات، إذ تجد فريقًا من العمال يحيط بكل آلة لتشغيلها واقتصر دور الآلة حينها على تسريع العمل وتخفيف المجهود العضلي. مع الوقت ظهرت الحاجة إلى منح تلك الآلات نوعًا من القيادة الآلية دون تدخل البشر وخاصة في الصناعات الخطرة أو عالية الدقة، وقد استغل المهندسون حينها الثورة في عالم الإلكترونيات لإدخال الآلات عصر الإلكترونيات الصناعية. إذ زودت تلك الآلات بتقنيات تجعلها قادرة على العمل والتوقف تلقائيا وفقا لظروف التشغيل وإدخال المؤقتات الزمنية التي تتحسس لأجزاء من الثانية والمفاتيح الإلكترونية التي تعطي أوامر التشغيل والإغلاق إلكترونيًا عن بعد دون الحاجة إلى المفاتيح الميكانيكية التي لا بد من تشغيلها من قبل العامل البشري. أما المرحلة الثانية من التطور فكانت ظهور المتحكمات الصغرية أو الدقيقة Microcontroller والمعالجات الدقيقة Microprocessor ومن ثم الدارات الرقمية التي طورت على أساسها، إذ مكنت هذه الدارات المهندسين من إعداد بيئة تحكم متكاملة لخط إنتاج أو معمل بأكمله. إذ تُبرمج معطيات التحكم بكل آلة باستخدام لغات برمجة مخصصة ثم تنقل هذه البرمجيات إلى لوحات التحكم المبنية على تلك المعالجات لتقود هذه الآلات بدقة. ولم يعد أمر تغيير برنامج العمل مرهقَا ولن يتطلب تغييرات في التجهيزات وإيقاف خطوط الإنتاج طويلًا لضبط المتغيرات الجديدة، بل تُعدّل البرمجيات خارج إطار خط الإنتاج ثم تحمّل إلى لوحات التحكم ليتوقف بعدها الخط لدقائق ثم يباشر العمل وفق البرنامج الجديد، وتُعرف هذه اللوحات بالدارات المنطقية القابلة للبرمجة PLC وتُبرمح لتقوم بعمل معين عندما تتحسس لمتغير ما أو يأتيها أمر ما. عندما تقود هذه الدارات تجهيزة محددة لتقوم بمجموعة خطوات معقدة لإنجاز عمل متكامل دعونا هذه التجهيزة روبوت Robot وتعرف عملية برمجتها ببرمجة الروبوت. وسرعان ما أخذ مجال الروبوتات أو الروبوتيكس Robotics منحنى مستقلا بذاته وبدأت تكنولوجيا بناء الروبوتات بالتطور السريع مدعومة بالتطور التكنولوجي الكبير لتقنيات الحوسبة في مجالي التجهيزات والبرمجيات من جهة والحاجة الملحة لوجود تجهيزات ذكية قادرة على تحسس البيئة المحيطة واتخاذ القرار بنفسها دون الرجوع إلى الإنسان وخاصةً في المجالات العسكرية أو الصناعات الكيميائية والنووية والعمل خارج كوكب الأرض ناهيك عن الرفاهية التي يسعى البشر إلى تحقيقها. فالطائرات المسيرة ومركبات استكشاف أجرام المجموعة الشمسية والروبوتات الطبية والتجهيزات المنزلية الذكية وألعاب الأطفال التفاعلية وغيرها الكثير من الأمثلة هي روبوتات تختلف في مستوى تعقيدها وبرمجتها وذكائها. لمحة عن الروبوتات قلنا أن الروبوتات هي تجهيزات قابلة للبرمجة يمكنها استشعار البيئة المحيطة والقيام برد فعل محدد سلفًا من قبل صانعها أو قادرة على اتخاذ قرار بنفسها محاكية بذلك السلوك البشري. وهنا لا بد من الإشارة إلى أن الروبوتات القادرة على اتخاذ القرارات باستقلالية تعتمد أساسًا على تقنيات الذكاء الصنعي وتعلم الآلة وهذا بدوره مرتبط بتقنيات أكثر تعقيدًا وتطورًا في برمجة الروبوتات. الأجزاء الرئيسية للروبوت يتكون الروبوت بشكل عام من الأقسام الرئيسية التالية: هيكل فيزيائي Physical Structure مخصص يساعد الروبوت على أداء الوظيفة الخاصة به، وهنا يأتي دور التصميم الصحيح في إنجاز الروبوت المطلوب.فالروبوتات التي تستخدم في صناعة السيارات مثلًا لا بد أن تتمتع بتصميم فيزيائي يناسب عملها. منظومة تحكم Control System تضم كل المكوّنات التي تتلقى البيانات من المحيط وتعالجها. تُبرمج منظومة التحكم لتقود المكوّنات الأخرى للروبوت كي تؤدي العمل المطلوب منها، فهي تتصرف كالدماغ البشري بشكل أو بآخر. الحساسات أو المستشعرات Sensors التي تستكشف البيئة المحيطة (مثل مستشعرات الحرارة والحركة والبعد وغيرها) وتنقل المحرضات إلى منظومة التحكم على شكل إشارات إلكترونية تفهمها منظومة التحكم وتدفع الروبوت إلى الاستجابة لها وفقًا لبرمجة الروبوت المحددة. المشغّلات Actuators المسؤولة عن تحريك الروبوت وتتكون عادة من محركات تتلقى الإشارات من منظومة التحكم لتنفيذ الحركة المطلوبة. وتختلف طريقة عمل هذه المشغلات وفقا لعمل الروبوت، فقد تعمل بالكهرباء أو الهواء المضغوط (مشغلات بنيوماتية pneumatic actuators) أو هيدروليكية وغيرها. مصدر للطاقة الكهربائية لتغذية منظومة التحكم وأجهزة التحسس والمشغلات الكهربائية. الطرفيات المخصصة، وهي مكوّنات خارجية خاصة بكل روبوت لتساعده على أداء وظيفته. فقد تحتاج روبوتات المعامل إلى طرفيات قابلة للتغيير لأغراض الطلاء أو القص أو الثقب، كما يحتاج الروبوت الجراحي إلى معدات خاصة به وهكذا. الأنواع الرئيسية للروبوتات نشير عادة بالروبوتات إلى الروبوتات الفيزيائية التي تأتي بمختلف الأشكال والأحجام من 0.2 ميليمتر إلى روبوتات بطول 200 متر. لكن لا بد من الإشارة أيضًا إلى الروبوتات البرمجية التي تنفذ مهام روبوتية كاملة عبر الحاسوب في العالم الافتراضي. يمكن أن نميز بين خمسة أنواع رئيسية من الروبوتات: الروبوتات مسبقة البرمجة Pre-Programmed Robots: وهي روبوتات مصممة لأداء مهام محددة في بيئة فيزيائية محددة مثل الأذرع الروبوتية التي تعمل على خطوط التجميع والتي تنفذ مهامها بسرعة وكفاءة عالية مقارنة بالعامل البشري. وتُعد برمجة الروبوتات في هذه الحالة سهلة نسبيًا الروبوتات الشبيهة بالبشر Humanoid Robots: وهي روبوتات تبدو كالبشر وتحاكي أفعالهم عادة كالمشي والعدو والقفز وحمل الأغراض، وقد تصمم أحيانًا لتبدي تعابير تحاكي تعابير الوجه التي يبديها البشر. روبوتات ذاتية التصرف Autonomous robots: روبوتات تعمل بشكل مستقل عن المشغلين البشريين وتصمم عادة لتنفيذ مهام في بيئات مفتوحة لا تتطلب مراقبة بشرية أو لا يمكن وجود البشر فيها. وتتميز هذه الروبوتات بوجود بنية قادرة على اتخاذ القرار وهي بنية حاسوبية عادة تقدّر كل خطوة بناءً على البيانات التي ولّتها مسبقًا. تتميز برمجة الروبوتات ذاتية التصرف بالتعقيد وتعتمد على خوارزميات الذكاء الصنعي وتعلم الآلة. وكمثال عليها نجد الطائرات المسيرة ذاتية التصرف والروبوتات الطبية وروبوتات الأعمال المنزلية. الروبوتات الخاضعة للتحكم عن بعد Teleoperated robots: يتصل الإنسان بهذه الروبوتات غالبًا عبر شبكة لاسلكية ويتحكم بها عبر واجهة تحكم خاصة. تعمل هذه الروبوتات في بيئات جغرافية ومناخية قاسية جدًا كالغواصات الروبوتية والمسيرات المتعقبة للألغام. روبوتات الواقع المعزز Augmenting robots: وتستخدم لتعزيز القدرات البشرية أو تعويض القدرات التي قد يفقدها. من هذه الروبوتات الأطراف الصناعية الروبوتية والهياكل الخارجية الروبوتية التي تساعد البشر على رفع الأوزان الثقيلة. الروبوتات البرمجية الروبوتات أو البوتات البرمجية Bots هي برامج حاسوبية تنفذ مجموعة من المهام الحاسوبية ذاتيًا ومن أشهرها روبوتات المحادثة الآلية. يتطور هذه النوع من الروبوتات مدعومًا بالتطور البرمجي الحاصل في مجالات الذكاء الصناعي وعلم البيانات وخوارزميات تعلم الآلة، ويتوقع أن يصبح قادرًا على برمجة الروبوتات الفيزيائية والتحكم بها، فهذه الروبوتات هي المنتج النهائي لعلم الذكاء الصنعي. دورة الذكاء الاصطناعي احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة. اشترك الآن كيفية برمجة الروبوت تضم معظم الروبوتات الفيزيائية نوعًا من منظومات التحكم الحاسوبية القابلة للبرمجة. فقد تُبني هذه الأنظمة على أساس المتحكمات الصغرية أو المعالجات الصغرية وتكون مخصصة لنوع محدد من الروبوتات أي تُبنى وفق رؤية المصنع. تُبرمج الروبوتات المخصصة من قبل مُصنِّعها فقط ولا يمكن تعديل برمجياتها ووظائفها إلا من قبل الشركة المصنعة. كما قد تكون منظومات التحكم معممة يمكن استخدامها لتتحكم بأية روبوتات مثل حواسيب أردوينو المصغرة وحواسب راسبيري باي ووحدات الدارات المنطقية القابلة للبرمجة. يمكن لأي شخص استخدام هذه الحواسب سواءً أكان هاويًا في مجال الروبوتات أو حتى خبيرًا لبناء روبوتات بسيطة وصولًا إلى روبوتات عالية التعقيد. تُبرمج منظومات التحكم المخصصة أو المعممة باستخدام لغات برمجة مختلفة نستعرضها في فقرات قادمة. لا تُعد برمجة الروبوت أمرًا بديهيًا ومباشرًا، بل موضعًا شديد الخصوصية يتعلق أولًا وأخيرا بطبيعة الروبوت ومكوّناته والوظائف المطلوبة منه. ولا يعني ذلك أن برمجة الروبوت أمر معقد وصعب المنال، فحتى الأطفال قادرون على تعلم أساسيات برمجته والتحكم به. برمجة الروبوت فيزيائيًا تُخفي هذه الروبوتات الجزء المتعلق بكتابة برامجها عن المستخدم، فلا حاجة فيها لشاشة وواجهة خاصة ولغة برمجة روبوت بل يعتمد كليًا على أجزاء فيزيائية (أزرار، روافع، قطع) تبرمج الروبوت ليقوم بأعمال معينة وفق تتابع ضغط هذه الأزرار أو تتابع تركيب هذه الأجزاء أو القطع مثل ألعاب الروبوت Bee-Bot: مثال آخر عليها Code-a-Pillar التي تُستخدم لتعليم الأطفال من 3-8 سنوات برمجة الروبوت من منطق التتابع ومبدأ الاستجابة للأفعال. برمجة الروبوت بالمهام لا تختلف هذه الروبوتات عن سابقاتها سوى بفصل منظومة التحكم عن المكونات الفيزيائية للروبوت. تشبه هذه الروبوتات في طبيعتها الألعاب التي يجري التحكم بها عن بعد لكن المستخدم قادر على إدارتها وبرمجة سلوكها من خلال أجهزة تحكم عن بعد أو عن طريق تطبيقات للهواتف الذكية يمكن من خلالها وضع تسلسل معين لعدة مهام مبرمجة مسبقًا ومخزنة في ذاكرة الروبوت لإنجاز وظيفة محددة. تُستخدم هذه الروبوتات كروبوتات تعليمية للمبتدئين والأطفال في مراحل عمرية مبكرة (6-13 عامًا) وتعزز تعليميًا مفهومين اثنين: الفصل والتمييز بين العتاد الصلب والبرمجيات. اتقان تقسيم أي وظيفة إلى مهام منفصلة ووضعها ضمن التسلسل المنطقي الصحيح. اكتساب مهارات أساسية في برمجة الروبوت. ولا تقتصر هذه الطريقة في البرمجة على الروبوتات التعليمية بل تتعداها إلى روبوتات في الخدمة الفعلية كالطيران المسير الاستكشافي الذي يشغله أشخاص غير مختصين. إذ يمكن الوصول إلى هذه الروبوتات عن بعد ثم استخدام برنامج حاسوبي أو تطبيق هاتف محمول لوضع مجموعة من المهام المبرمجة مسبقًا وفق تسلسل محدد لإتمام برمجة الروبوت لإنجاز المطلوب. فإن أراد شخص غير مختص برمجة روبوت طائرة مسيرة تُبرمج بالمهام لرفعها مسافة 100 متر ثم فتح الكاميرا والتقاط صورة وفق اتجاه محدد ثم العودة إلى القاعدة وتكرار الأمر 10 مرات يوميًا، ما عليه مثلًا سوى سحب مهمة "التحليق إلى ارتفاع" من قائمة المهام الجاهزة في التطبيق ويضع بعدها "افتح الكاميرا" ثم تعليمة "وجه الكاميرا" ثم "التقط صورة" ثم "انخفض" ويضع كل تلك التعليمات في تعليمة "كرر الخطوات السابقة" ثم يرسل البرنامج إلى الروبوت ليُبرمج! لا داعٍ في هذه الحالة لأن يعرف أي شيء عن طريقة برمجة كل مهمة ولا عن آلية عمل المستشعرات أو المحركات أو الكاميرا أو آلية التوجيه لكنه يضع المهمات في سياقها الصحيح والنتيجة هي برمجة الروبوت بنجاح. برمجة الروبوت بالتعلم والقيادة تأتي هذه الروبوتات مصنّعة بالكامل ومخصصة، فلا يمكن تطويرها أو تعديلها سوى من قبل الشركات المصنعة وعادة ما تقوم بوظيفة أو مجموعة وظائف محددة سلفًا كالأذرع الروبوتية. وتتطلب برمجة الروبوت في هذه الحالة وحدة تحكم وبرمجة Programming pendant منفصلة عن جسم الروبوت تتيح للمشغل اختيار مكوّن محدد من مكوّنات الروبوت ثم إعطائه مهمة معينة كأن يختار محرك الجزء العلوي من الذراع ثم تدويره بزاوية محددة وتخزين الأمر. ينتقل بعدها إلى مكوّن آخر لتنفيذ حركة أو قياس معين ومن ثم تخزين الأمر لينتقل إلى الأمر التالي، وعند الإنتهاء من إملاء مجموعة الأوامر على الروبوت، يُخزّن المشغل هذه الأوامر في برنامج يعطيه اسمًا محددًا ثم يتأكد من تكرار الروبوت لهذا البرنامج بدقة قبل أن يغلق وضع التعليم Teach وينتقل إلى وضع العمل وفقا للبرنامج الجديد وهكذا تنتهي برمجة الروبوت. الروبوتات المبرمجة بلغات برمجة مع ظهور عصر الحواسيب القوية وغير المكلفة، بدأ الميل إلى برمجة الروبوتات باستخدام لغات برمجة الحواسيب أو نسخ مخصصة منها مزودة بمزايا خاصة لتسهيل برمجة الروبوت والتعامل مع مكوّناته. مع ذلك لا زالت الكثير من الشركات تقدم لغات برمجة خاصة لبرمجة روبوتاتها، معظمها لغات وظيفية أو لغات مرئية. يُصطلح عادة على برامج الروبوت المكتوبة بلغات برمجة مخصصة ببرمجة الروبوتات المطفأة Offline-programming، إذ تكتب البرمجيات على منصات غير متصلة بالروبوت الذي يتابع عمله وفقًا لبرنامجه القديم إن كان مبرمجًا مسبقًا. عند انتهاء برمجة الروبوت على تلك المنصات تجري محاكاة عمل البرنامج الجديد والتأكد من عمله. يُوقف الروبوت لدقائق ريثما يُحمّل برنامجه الجديد ثم يتابع العمل وفق هذا البرنامج. لغات برمجة الروبوت تُستخدم مجموعة متنوعة من لغات البرمجة وأطر العمل لبرمجة الروبوتات واختبار هذه البرمجيات ومن ثم نقلها إلى الروبوت. سنتحدث تاليًا عن مفهوم أنظمة تشغيل الروبوت ومن ثم سنلقي نظرة على لغات البرمجة المخصصة لكتابة برمجيات الروبوتات. مفهوم أنظمة تشغيل الروبوت يُشار عادة إلى البرمجيات وبيئات العمل والمنصات وأدوات البرمجة الوسطية التي تتكامل في سبيل برمجة الروبوتات بمنظومة تشغيل الروبوت Robot Operating System. لا ينبغي الخلط بين منظومة تشغيل الروبوت ونظام تشغيل وحدة التحكم OS وبرمجيات وحدة التحكم الثابتة (برمجيات العتاد) firmware. فإن استخدمت مثلًا حاسوب راسبيري باي للتحكم بروبوت، يكون نظام تشغيل لوحة التحكم هو راسبيان غالبا (نسخة من نظام لينكس) أما الأدوات التي تساعدك برمجة روبوتك ونقل البرنامج إليه والتي تعمل على راسبيان فهي منظومة تشغيل الروبوت. يميل أغلب مصنعي الروبوتات ذات الوظائف الثابتة كروبوتات التغليف والتعبئة أو الروبوتات المخصصة للعمل في ظروف استثنائية على كتابة برنامج واحد فعّال وتحميله على الروبوت. تُعرف هذه البرامج بالبرمجيات الثابتة. برمجة الروبوتات باستخدام لغات البرمجة عامة الأغراض تعتمد برمجة الروبوتات وفق هذه الطريقة على لغات برمجة عامة الأغراض مثل لغة بايثون أو لغة جافا ثم تبنى مكتبات خاصة أو مجموعة أدوات باستخدام هذه اللغات لتنفيذ إجرائيات برمجية تتحكم بمكونات الروبوت على صعيد الحركة أو تحسس البيئة المحيطة أو عمليات اتخاذ قرار. يعتمد الأمر في هذه الحالات على منظومة التحكم بالروبوت. فإما أن تُكتب برامج الروبوت بإحدى لغات البرمجة على حاسوب وتختبر ثم تُنقل إلى منظومة تحكم الروبوت مثل معظم الروبوتات الصناعية والروبوتات المبنية على تجهيزات PLC أو تلك المجمعة حول متحكم صغري مثل لوحات أردوينو. أو أن يُحمّل نظام تشغيل متكامل على لوحة التحكم بالروبوت (راسبيان، ويندوز، لينكس،أندرويد،…إلخ.) وكل ما عليك حينها كتابة برنامج الروبوت باستخدام اللغة التي تريد من خلال تثبيت إحدى بيئات العمل المناسبة على نظام التشغيل ومن ثم تجريب البرنامج على الروبوت مباشرة. من أمثلة لوحات التحكم هذه لوحات راسبيري باي ولوحات أدرويد ADROID وحواسب ASUS Tinker Board، وتدعم اللوحات السابقة أنظمة تشغيل مشتقة من نظام لينكس كما تدعم اللوحتين الأخيريتين نظام أندرويد. إليك قائمة بأكثر لغات البرمجة شيوعًا في برمجة الروبوتات سواء لكتابة البرامج الثابتة أو القابلة للبرمجة المباشرة: لغتي C و ++C: يعتمد الكثير من مبرمجي الروبوتات على هاتين اللغتين في كتابة برامجهم لأن الكثير من مكتبات التحكم بالتجهيزات الفيزيائية للروبوت قد طورت باستخدام هاتين اللغتين. تسمح هذه المكتبات بالتفاعل المنخفض المستوى من العتاد الصلب وتؤمن أداءً عاليًا شديد الكفاءة في زمن التنفيذ، كما تدخل في تطوير الكثير من بيئات التشغيل الأكثر شهرة مثل DART وRobotics Library (RL). وعلى الرغم من صعوبة إنجاز بعض الوظائف بهاتين اللغتين مقارنة بلغة بايثون وماتلاب، لكن أداء هاتين اللغتين في زمن التشغيل جعلهما لغتين معياريتين في برمجة الروبوتات حتى مع زيادة شعبية لغة بايثون. بايثون: ازدادت شعبية هذه اللغة لسهولة استخدامها والكم الهائل من المكتبات التي تساعد على برمجة الروبوتات، فلا حاجة لإعادة اختراع العجلة، إذ ستجد تقريبًا كل ما تبحث عنه ضمن تلك المكتبات. كما أن اعتمادها مع ++C كلغة أساسية في نظام تشغيل الروبوتات ROS وكذلك من الحواسيب المخصصة للتحكم بالروبوتات مثل راسبيري باي سيجعلها مرشحة لتكون اللغة الأكثر شعبية في عالم برمجة الروبوتات. جافا: نظرًا لشعبية هذه اللغة بين مطوري الروبوت المختصين بعلوم الحاسوب وجدت هذه اللغة طريقها إلى برمجة الروبوت. جافا أيضًا هي لغة مفسّرة ويعني ذلك أنك لن تّضطر إلى نقلها إلى لغة آلة محددة ومعالج محدد أو لوحة تحكم محددة بل تعمل آلة جافا الافتراضية على تنفيذ التعليمات أثناء التشغيل مما يعني إمكانية كتابة الشيفرة مرة واحدة وتنفيذها على آلات مختلفة. تُعد جافا لغة أساسية في تطوير روبوتات العديد من الشركات مثل IBM. اللغة #C (بيئة NET.): تأتي شعبيتها من عدة أسباب أهمها: اللغة الرئيسية التي تُستخدم في برمجة الروبوتات ضمن بيئة تطوير الروبوتات Microsoft Robotics Developer Studio من مايكروسوفت، فإن أردت العمل على هذه البيئة لا بد من تعلم هذه اللغة. تُستخدم كأساس في محركات الواقع الافتراضي مثل Unity الذي تزداد شعبيته حاليًا. لغة مفسرة وبالتالي يمكن أن يعمل البرنامج ضمن أية آلة أو روبوت. ماتلاب MATLAB: تشتهر هذه اللغة ضمن صفوف مهندسي ومصممي الروبوت وضمن النطاق الأكاديمي لتحليل البيانات وتطوير أنظمة التحكم. تشتهر هذه اللغة بوجود صندوق الأدوات Robotics Toolbox الذي يمكنه المساعدة في تطوير نظام روبوت كامل باستخدام ماتلاب فقط. لغتي LISP و PROLOG: يُتوقع ازدياد شعبية هاتين اللغتين مستقبلًا مع زيادة الاهتمام بتطبيق تقنيات الذكاء الصنعي، فهاتين اللغتين من أكثر اللغات استعمالًا في خوارزميات الذكاء الصنعي وستجدان طريقًا للدخول إلى عالم برمجة الروبوتات. إذ استخدمت بيئة تطوير الروبوتات ROS لغة LISP في كتابة بعض أجزائها، واستخدمت IBM لغة PROLOG كجزء من الأدوات البرمجية في مشروع IBM's Watson AI. لغة سكراتش Scratch: سكراتش هي لغة برمجة مرئية صُممت لتعليم البرمجة للمبتدئين وتستهدف الفئة العمرية بين 8 و 16 عامًا ويزداد الاعتماد على هذه اللغة في نوادي الروبوت والمدارس التقنية. تتكون اللغة من من مجموعة من الكتل البرمجية التي تُسحب وترتب وفق تسلسل معين لإنجاز البرنامج المطلوب. تدعم سكراتش مجموعة من المكتبات التي يمكنها التحكم بالطرفيات التي تتصل بحاسوب راسبيري باي مما زاد في شعبيتها. بإمكانك الاطلاع ضمن أكاديمية حسوب على كتابة برامج تتحسس للوسط الخارجي باستخدام راسبيري باي ولغتي بايثون وسكراتش دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن برمجة الروبوتات بلغات برمجة مخصصة تظهر الحاجة إلى لغات برمجة مخصصة للروبوتات Robots Programming Languages واختصارًا RPL كنتيجة للتنوع الكبير في الآلات الروبوتية والروبوتات واختلاف وظائفها. إذ تظهر في عالم الصناعة مفاهيم لا بد من أخذها بعين الاعتبار عند برمجة الروبوت مثل التحكم بالقوى المطبقة force control وأداء المهام على التوازي Parallelism. لهذا السبب طوّرت لغات برمجة تمتلك إمكانيات لغات برمجة الحواسيب إلا أنها مخصصة للروبوتات نذكر منها: لغة VAL: طورتها شركة Unimation لبرمجة بروبوتاتها وتستخدم شركة Stäubli حاليا النسخة VAL 3 منها. لغات HELP و RAIL و Karel: وهي لغات مخصصة من الجيل الثاني لبرمجة الروبوتات، وقد أظهرت ميزات متطورة للتحكم بالحركة والتعامل مع واجهات الحساسات والمشغلات والتخاطب البيني ضمن الروبوت وأبدت بعض ملامح الذكاء الصنعي. لغة RAPID: التي تستخدمها شركة ABB لغة KRL: وهي اختصار لعبارة (Kuka Robot Language)وتستخدمها شركة Kuka لغة URScript: وتستخدمها شركة Universal Robots. مسارات تعلم برمجة الروبوتات والروبوتكس ما الغاية من مشروع الروبوت الذي أريد أن أنجزه؟ ما هي مدى معرفتي بالإلكترونيات؟ ما مدى معرفتي بالبرمجة عمومًا؟ هل أنا مهتم حقا ببناء هذا المشروع من الصفر أم أبحث عن حلول سريعة؟ إن الإجابة على الأسئلة السابقة أمر ضروري قبل العمل على أي مشروع يتعلق ببرمجة الروبوتات واستثمارها. فإن كنت هاويًا فضوليًا للمعرفة عليك أن تسلك مسارًا محددًا وإن كنت هاويًا تسعى إلى الاحتراف والعمل في مجال الروبوتات فهي حكاية أخرى وإن كنت ذو خلفية في علوم الحاسوب أو الهندسة يختلف مسار العمل وإن كنت رجل أعمال أو مستشارًا تقنيًا لمؤسسة صناعية يختلف الأمر كذلك. برمجة الروبوت للأطفال علينا أولًا أن نعزز مفاهيم هامة للطفل مثل السبب والنتيجة والتسلسل الصحيح لإنجاز المهام وذلك في سن مبكرة من 3-6 سنوات. يمكننا بداية الاستفادة من الدمى الروبوتية التي تبرمج فيزيائيا لحث الطفل على التعلم مثل الدمى التي تتحرك وتدور وتصدر أصواتًا وفق تسلسل محدد مسبقًا يتعلمه الطفل بالضغط المتتالي على مجموعة أزرار على الدمية. كما يمكن بناء مهاراته في التصميم الفيزيائي من خلال ألعاب فك وتركيب المكعبات الملونة لبناء هياكل متوازنة ومتناظرة التصميم. في سن أكبر من 6-10 سنوات يمكنه العمل على روبوتات مبرمجة بالمهام لإدراك مفهوم البرنامج بشكل منفصل عن الآلة وتنمية الرغبة في التفكير بطريقة عمل هذه المهام. كما يمكن أيضا تلقين الطفل أساسيات الدوائر الكهربائية واستخدام البطاريات وتوصيلها مع أضواء ومحركات صغيرة وحثه على بناء هياكل معدنية أو بلاستيكية عبر ألعاب الفك والتركيب المكونة من قطع متنوعة وعجلات ومحركات وبراغي ومفكات. إن أبدى الطفل بعد عمر الثامنة براعة ورغبة يمكن أن تبدأ بتعليمه البرمجة من خلال لغة سكراتش وبعد أن يتجاوز العاشرة يمكن العمل على روبوتات جاهزة تبرمج بلغة برمجة مثل سكراتش وينقل البرنامج إليها من الحاسوب لتنفيذ مهام معينة. سيبدي الطفل أو اليافع بعد ذلك رغبته في الاطلاع والتعلم أكثر وسيدرك بنفسه طريق البحث وتعلم ما يلزمه وسيخط مساره في مجال برمجة الروبوتات والروبوتيكس عمومًا بنفسه. على الرغم من ذلك يمكنك إرشاده إلى تعلم لغات برمجة جديدة مثل بايثون والعمل على أنظمة تشغيل مثل لينكس وأندرويد ليألفها ثم العمل مع لوحات تحكم حاسوبية مثل راسبيري باي ليتحكم بعناصر فيزيائية كالأضواء والمصوّتات وتحريك اللوحة على هيكل ذو عجلات وموازنتها واستشعار البيئة المحيطة. بإمكانك الاطلاع ضمن أكاديمية حسوب على كتابة برامج تتحسس للوسط الخارجي باستخدام راسبيري باي ولغتي بايثون وسكراتش. وهكذا ستضع الناشئة على الطريق الصحيح للانطلاق في عالم الروبوتات أكاديميًا وتقنيًا. برمجة الروبوت للمبرمجين والمهندسين إن كنت مبرمجًا فأنت جاهز للانطلاق. قد ترغب في تعزيز معلوماتك في مجال الإلكترونيات وخاصة الحساسات والمحركات ومصادر تغذيتها واستثمارها إن كنت ترغب في تصميم روبوت بنفسك. أما إن كنت ترغب في تشغيل روبوت موجود قابل للبرمجة فأنت في الواقع لا تحتاج شيئًا سوى اختيار لغة البرمجة والبحث عن مكتبات جاهزة لتنفيذ ما تريده. ستظهر المشكلة إن أردت أن يقوم الروبوت بوظيفة واضطررت إلى برمجة هذه الوظيفة بنفسك للمرة الأولى (احتمال ضعيف) عندها عليك العودة إلى المراجع الأكاديمية وتعزيز قدراتك في الفيزياء علم التحريك والإلكترونيات! أما إن كنت مهندسًا كهربائيًا أو مهندس إلكترونيات، عليك فقط تعزيز قدراتك البرمجية التي يُفترض أن تكون موجودة وراجع معلوماتك عن الإلكترونيات ومصادر التغذية وستكون جاهزًا لبرمجة أي روبوت واستثماره وحتى تصميم وبناء روبوت من الصفر. اختر المنصة التي تريد العمل عليها واطلع على أفكار ومشاريع الآخرين وانطلق! تبقى التوجيهات نفسها للمهندسين غير الاختصاصيين والتقنيين. تعلم لغة برمجة مستخدمة في برمجة الروبوت وافهم مكوّنات الروبوت الذي تنوي العمل عليه وبرمجته أيًا كانت الأسباب التي دفعتك لذلك. استعن بالإنترنت ومنتديات الروبوتيكس التي تهتم بنفس الروبوت الذي تستهدفه واجتهد قليلًا ولن يذهب عناؤك سدى. لكن غالب الظن أنك في مصنع ما وأوكلت إليك مهمة قيادة روبوت أو برمجته لسبب أو لآخر، اطلع على كتيب التشغيل وافهم تعليمات تشغيل الروبوت على وضع التعلم فغالبًا ما تُبرمج هذه الروبوتات بالتعلم حاول أن تبحث عن مقاطع فيديو تشرح آلية برمجته وخذ بعض الوقت في التجربة (لا تبالغ كثيرًا في التجريب فهذه الروبوتات مكلفة!) وسترى أن الأمر سينجح. برمجة الروبوت للهواة والفضوليين إن كنت طفًلا أو يافعًا أو مبرمجًا أو مهندسًا فقد أرشدناك في فقرات سابقة إلى الطريق الصحيح. أما إن كنت شغوفًا بالتعلم ورأيت أو قرأت محتوى يتحدث عن علم الروبوت ووقع الأمر في نفسك موقعًا حسنًا ورغبت في تجربة الأمر، فاسمح لي أن أهنئك وأشجعك! ابدأ مسيرتك بتعلم أساسيات التحكم الكهربائي التقليدي فهي من ناحية مهنة محترمة ومن ناحية أخرى ستكون مدخلك إلى التحكم الرقمي. ستجد الكثير من المعاهد التعليمية التقنية؛ سجل في أحدها فهذا أسرع من متابعة قناة على اليوتيوب وعملي أكثر. في الوقت ذاته إن لم تكن متقنًا للعمل على الحاسوب (أعي تماما أن الحاسوب قد أهمل من قبل الكثيرين لصالح الهواتف الذكية) فحاول أن تألف التعامل معه، تعلم على الأقل نظامي ويندوز ولينكس فهذا أساسي. عندما تنهي أساسيات التحكم التقليدي انتقل إلى التحكم الرقمي باستخدام وحدات PLC (ستجد أنها الخطوة التالية في مسار التعلم في معهدك إن سجلت في أحدها). يعتمد الكثير من الروبوتات على وحدات PLC كمنظومات تحكم، وستتعرف خلال هذه الفترة على مفهوم البرمجة والبرامج، إذ تُبرمج تلك الوحدات من خلال لغات برمجة مرئية بالمهام. لن يكون الأمر صعبًا، فقط اجتهد قليلًا ولا تستسلم. خلال هذه الفترة تعمق قليلًا في أحد توزيعات نظام التشغيل لينكس وستجني ثمار هذا الجهد بالتأكيد. حتى هذه اللحظة ستكون قد اكتسبت المهارات التالية: إدراك مفهوم التحكم الصناعي. إدراك مفهوم البرنامج وكتابة برمجيات بسيطة للتحكم بالتجهيزات الكهربائية. التعامل مع عناصر كهربائية وإلكترونية عديدة خلال مسار الدورتين السابقتين وسترى أنك تحتاجها في عالم الروبوت. العمل على الحاسوب وبأكثر من نظام تشغيل. قد يلبي طموحك هذا الحد، فقد تتمكن إن اجتهدت من تحريك مركبة وتفادي العقبات واستشعار المحيط لكن قد يكون روبوتك كبير الحجم ومحدود الإمكانات لأن أغلب التجهيزات التي تعلمتها كبيرة الحجم من الحساسات إلى المحركات إلى لوحات التحكم والتغذية، لم لا! فالروبوتات الكبيرة لها جاذبيتها! إن أردت أن تصل إلى روبوت أصغر حجمًا أو ينفذ مهام أعقد وأكثر دقة فعليك متابعة مسيرتك التعليمية. تعلم العمل على الحواسب المصغرة أو حواسب اللوحة الواحدة مثل أردوينو وراسبيري باي من خلال دورات تعليمية في معاهد أو على منصات إلكترونية، فلن يعيقك الآن أي شيء لفهم واستيعاب كل ما يقال. ابدأ بتعلم لغة برمجة وأنصحك بتعلم البرمجة بلغة بايثون على الرغم من أنّ لغة سكراتش تمثل مرحلة انتقالية بين لغات برمجة PLC ولغات برمجة الحواسيب. الخيار لك على أية حال. ستجني خلال هذه الفترة ثمار تعلمك لأنظمة تشغيل الحواسب وعندما تنهيها ستكون ملمًا ببرمجة الحواسيب وبأساسيات الإلكترونيات المتعلقة بالروبوت. انظر دورة تطوير التطبيقات بلغة بايثون التي تقدمها أكاديمية حسوب لتعليم البرمجة بلغة بايثون بلغة عربية ودون الحاجة إلى خبرة مسبقة: أنت مستعد الآن، ابنِ روبوتك الخاص باستخدام حاسوب مصغر مستفيدًا من تجارب الهواة الآخرين. حاول أن تتواصل معهم من خلال المنتديات وأن تعزز قدراتك بالاطلاع المستمر. تتوفر أيضا صناديق أدوات مخصصة لبناء روبوتات متنوعة تختلف في تعقيدها، يمكنك اقتناء واحدة فهي تزودك بالكثير من التجهيزات والقطع لبناء هيكل الروبوت وبرمجته. هؤلاء الذين يريدون تغيير العالم! لا شيء سيقف في طريقك سوى ذاتك. ثق بنفسك وبإمكاناتك ووسع مداركك في كل المجالات فعلم الروبوت لا يقتصر على علم واحد فهو مزيج من كل العلوم. لا تستسلم ولا تردد أبدًا عبارة "لا أعرف كيف أفعل ذلك". كن مبرمجًا محترفًا ومهندسًا محترفًا في عملك وتخيل واحلم كما لو كنت طفلًا. ستسمع كثيرًا عبارة "لا تعد اختراع العجلة"، قد ترى أن هذا صحيح، لكن إن كان عليك إعادة اختراع العجلة فافعل! رحلة سعيدة إن قررت الانطلاق في عالم الروبوتات! وإن احتجت إلى أي مساعدة، أضف سؤالك في قسم التعليقات أو في قسم الأسئلة والأجوبة. اقرأ أيضًا ما هو حاسوب راسبيري باي Raspberry Pi؟ ما هي لوحة أردوينو Arduino؟ تصميم لعبة السلك والحلقة باستخدام برنامج سكراتش وحاسوب راسبيري باي أسهل لغات البرمجة تعلم البرمجة فوائد البرمجة
  8. سنتحدث في هذا المقال عن مفهوم الصور المتجاوبة responsive images التي تُعرَض عرضًا يلائم كل الأجهزة مع اختلاف قياس شاشة العرض ودقتها والميزات الأخرى المتعلقة بذلك، كما سنبحث في الأدوات التي تقدمها HTML لإنجاز العمل، ويساعدك ذلك في تحسين الأداء على مختلف الأجهزة، فهي جزء من مفهوم التصميم المتجاوب الذي يُعَدّ أحد مواضيع تنسيقات CSS الذي لا بدّ من الاطلاع عليه في مرحلة ما. لا بدّ قبل المتابعة في قراءة هذا المقال أن تكون على دراية بأساسيات HTML وطريقة إدراج الصور الساكنة في صفحات ويب. لماذا تستخدم الصور المتجاوبة؟ سنعرض حالةً نموذجيةً لإيصال الفكرة، فقد يحتوي موقع ويب نمطي على صورة تمثِّل خلفية ترويسة صفحاته، بالإضافة إلى صور في الأقسام الأخرى، ومن المحتمل امتداد صورة الترويسة لتشغل عرض الترويسة بأكمله، في حين تُدرَج بقية الصور ضمن أعمدة المحتوى، وإليك مثالًا كما يلي: سيعمل الموقع جيدًا ضمن الشاشات الواسعة كما في الحواسب المحمولة أو المكتبية، وبإمكانك متابعة هذا المثال مباشرةً ضمن مخزن جيت-هاب الخاص به، كما يمكنك الاطلاع على شيفرته المصدرية. ضُبط عرض جسم المحتوى ليكون 1200 بكسل، إذ سيبقى هذا العرض كما هو في نوافذ العرض viewports الأكبر، كما سيضبط جسم المحتوى نفسه في منتصف المساحة المخصصة له؛ أما في نوافذ العرض الأضيق، فسيشغل المحتوى 100% من العرض الذي تتيحه نافذة العرض. ضُبط موقع صورة الترويسة لينطبق مركزها على مركز الترويسة بصرف النظر عن عرض الترويسة، فإذا عُرض الموقع على شاشة ضيقة، فسيُرى من الصورة تفاصيلها المهمة -أي الأشخاص- وستختفي بقية التفاصيل من الطرفين؛ أما الارتفاع، فسيبقى 200 بكسل. ضُبطت الصورة الموجودة في عمود المحتوى بحيث يتقلص حجمها تلقائيًا لتبقى دائمًا ضمن العمود إذا قلّ عرضه بدلًا من تجاوز حدوده. مع ذلك، ستظهر المشاكل عندما تحاول عرض الموقع على الأجهزة ذات الشاشات الضيقة، إذ تظهر الترويسة في الصورة التالية جيدًا، لكنها ستمتد في أجهزة الهاتف المحمول لتشغل مقدارًا أكبر من ارتفاع الشاشة، كما ستصعب رؤية الأشخاص في أول صور المحتوى وهي بهذا الحجم. يمكن تحسين الوضع بعرض نسخة مجتزأة من الصورة تضم التفاصيل المهمة فقط عندما تُعرَض الصورة ضمن شاشة ضيقة، كما يمكن استخدام صورة مجتزأة أخرى تناسب الشاشات متوسطة العرض مثل شاشات الأجهزة اللوحية، في حين تظهر المشكلة عندما تحاول تقديم تلك الصورة المجتزأة بالطريقة التي ذكرناها في مختلف التخطيطات، وتُعرف هذه المشكلة بمشكلة الرؤية الفنية art direction problem. بالإضافة إلى ذلك، لا حاجة إلى إدراج صور بهذا الحجم في الصفحة إذا كانت ستُعرَض على شاشة هاتف محمول، كما ستبدو الصور النقطية في المقابل خشنة إذا عُرضت على شاشات أوسع، فالصور النقطية هي شبكة من البكسلات الطولانية والعرضية، وتُدعى هذه المشكلة بمشكلة تبدّل الدقة resolution switching problem. ليس ضروريًا أيضًا عرض الصور الكبيرة على شاشات أصغر حجمًا، فقد يسبب ذلك هدرًا في الحجم المتاح لتراسل البيانات وخاصةً لمستخدِمي الهواتف المحمولة الذين لا يرغبون بالطبع في تنزيل صورة كبيرة مخصصة لمستخدِمي الحواسب المكتبية مثلًا إذا وجدت صور تناسب أجهزتهم، لذا لا بدّ من استخدام صور بدقات مختلفة على أساس حل مثالي، ثم يختار المتصفح الصورة ذات الدقة الأنسب وفقًا لأبعاد الشاشة التي تعرضها، ولنزيد الطين بلِّة، سنجد أنّ بعض الأجهزة تتمتع بشاشات عالية الدقة وتحتاج إلى صور أكبر حجمًا كي تُعرض جيدًا، إذًا نحن أمام المشكلة السابقة نفسها لكنها في سياق مختلف. قد تعتقد بأن حل المشكلة سيكون استخدام الصور الشعاعية، وهي بالفعل حل إلى حد ما، فهي صغيرة الحجم و قابلة لتغيير الحجم دون تشوهات ويجدر بك استخدامها إذا أمكن ذلك، لكن هذه الصور غير مناسبة لكل الحالات، إذ يمكن استخدام هذه الصور لعرض الرسومات البسيطة والأنماط وعنصر الواجهات وغيرها، لكن من الصعب إنشاء صورة شعاعية بتفاصيل عالية الدقة مثل التي تجدها مثلًا في الصور الشخصية، إذ تبقى الصور النقطية مثل JPEG أكثر ملائمةً في حالات مثل هذه. لم تظهر هذه المشاكل في بدايات الويب، ففي بداية تسعينات القرن الماضي لم تعمل المتصفحات سوى على الحواسب المكتبية والمحمولة، ولم يكن على مطورِي المتصفحات حينها التفكير بإيجاد حلول لهذه المشاكل، فقد استخدمت تكنولوجيا الصور المتجاوبة مؤخرًا لحل المشاكل التي قدّمنا لها وذلك باستخدام عدة ملفات للصورة نفسها لكن بأحجام مختلفة على المتصفح لحل مشكلة تبدّل الدقة، أو صور مختلفة ملائمة للمساحات المختلفة لحل مشكلة الرؤية الفنية. سنناقش في هذا المقال بعض الميزات التي تدعمها معظم المتصفحات الحديثة المستخدَمة في الحواسب وأجهزة الهاتف المحمول بما في ذلك مايكروسوفت إيدج لكن دون مايكروسوفت إكسبلورر وهي <srcset> و <sizes> و <picture>. كيفية إنشاء صور متجاوبة؟ سنلقي نظرةً أقرب في هذا القسم على المشكلتين اللتين تحدثنا عنهما سابقًا وسنعرض بعض الحلول باستخدام صور HTML المتجاوبة مركِّزين على العنصر <img>، وسنستعمل صور HTML ونترك موضوع صور CSS إلى وقت لاحق على الرغم من أنّ صورة الترويسة في المثال السابق هي للديكور فقط وأنها أدرجت باستخدام خلفيات CSS، إذ يعدّ البعض CSS أكثر قدرة على إدارة الصور المتجاوبة من HTML. تبدل الدقة والأحجام المختلفة ما هي المشكلة التي تواجهنا عند تبدّل الدقة؟ نريد إظهار محتوى الصورة نفسه لكن بحجم أكبر أو أصغر وفقًا للجهاز الذي يعرضها، ونجد هذه الحالة في الصورة الموجودة في المحتوى الرئيسي للموقع النمطي الذي عرضناه سابقًا، إذ يتيح لك العنصر <img> الإشارة إلى ملف مصدري واحد: <img src="elva-fairy-800w.jpg" alt="Elva dressed as a fairy"> سنستخدِم السمتَين الجديدتَين <srcset> و <sizes> في تزويد هذا العنصر بصور مصدرية إضافية مع تلميحات تساعد المتصفح في انتقاء المناسبة منها، ويمكنك الاطلاع مباشرةً على هذا الأسلوب في المثال الموجود على جيت-هاب، كما يمكنك الاطلاع على الشيفرة المصدرية. <img srcset="elva-fairy-480w.jpg 480w, elva-fairy-800w.jpg 800w" sizes="(max-width: 600px) 480px, 800px" src="elva-fairy-800w.jpg" alt="Elva dressed as a fairy"> قد تبدو السمتان <srcset> و <sizes> معقدتين، لكن فهمهما ليس بهذه الصعوبة إذا كتبتهما وفقًا للتنسيق الذي اتبعناه في الشيفرة، وذلك بجعل كل قيمة تأخذها السمة في سطر مستقل، إذ تحتوي كل قيمة على قائمة من الخيارات تفصل بينها فاصلةً ,، ويتكون كل قسم من أقسام هذه القائمة من ثلاثة أجزاء، ولنلق نظرةً على محتوى كل قسم: السمة srcset يعرِّف مجموعةً من الصور التي نريد من المتصفح اختيار الأنسب منها وحجم كل صورة، كما يفصل بين معلومات كل مجموعة بفاصلة، وتضم كل مجموعة المعلومات التالية: اسم ملف صورة مثل elva-fairy-480w.jpg. فراغ. العرض الأصلي للصورة بالبكسل (480w، ولاحظ استخدام الواحدة w وليس البكسل px)، كما إن الحجم الأصلي للصورة هو الحجم الحقيقي الذي تجده عندما تفحص ملف الصورة على حاسوبك، إذ يمكنك مثلًا اختيار الصورة في ماك أو إس ثم الضغط على المفتاحين Cmd+I لعرض معلومات الصورة على الشاشة. السمة sizes يعرِّف مجموعةً من الشروط لعرض الوسائط مثل عرض الشاشة، ويشير إلى أفضل حجم للصورة يمكن اختياره عندما يتحقق أحد شروط العرض، ويتكون كل شرط مما يلي: طبيعة الشرط (max-width:600px): يصف بصورة مبسطة إحدى الحالات التي يمكن أن تكون عليها الشاشة، إذ نقول في هذه الحالة أنّ اتساع نافذة العرض هو 600 بكسل أو أقل. فراغ. عرض المقر slot الذي تشغله الصورة عندما يتحقق الشرط (480px). ملاحظة: بدلًا من تحديد عرض المقر التي ستشغلها الصورة بمقياس مطلق (تحديده بدقة 480px)، يمكنك تحديده بالنسبة إلى نافذة العرض viewport المستخدَمة (50vw مثلًا) لكن ليس على أساس نسبة مئوية، وربما قد لاحظت أنّ عرض المقر الأخير لا يرتبط بشرط على الوسائط لأنه سيكون الخيار الافتراضي عندما لا يتحقق أيّ من هذه الشروط. وبالتالي، ما سيفعله المتصفح عند استخدام هذه السمات هو ما يلي: النظر إلى عرض الجهاز. يتحقق من قائمة شروط الوسائط الموجودة في السمة sizes ليجد أول شرط محقق. ينظر إلى قياسات مقر العرض الذي مُرر إلى استعلام إيجاد الصورة المناسبة. يحمّل الصورة التي يُشار إليها من قائمة عناصر السمة srcset والتي تتطابق أبعادها مع أبعاد المقر الذي ستُعرَض ضمنه، وإذا لم تكن هناك صورة مطابقة، فأول صورة أكثر اتساعًا من قياس المقر المُختار. إذا حمَّل متصفح نافذة عرضه المتاحة هي 480px صفحة الويب، فسيكون الشرط (max-width: 600px) محققًا، وبالتالي سيختار المتصفح المقر الذي اتساعه 480px، وسيحمل أيضًا الصورة elva-fairy-480w.jpg لأن قياسها هو الأقرب لقياس المقر، كما إنّ حجم الصورة التي عرضها 800 بكسل هو 128 كيلو بايت، بينما حجم الصورة التي عرضها 480 بكسل هو 63 كيلو بايت ومقدار التوفير سيكون 65 كيلو بايت، وتخيل الآن وجود عدة صورة في الصفحة، كم سيوفر ذلك على مستخدِمي الهاتف المحمول من ناحية حجم بيانات التراسل. ملاحظة: إذا كنت تختبر هذا الأسلوب على متصفح حاسوب مكتبي وأخفق في تحميل الصور الأقل عرضًا عندما تضبط نافذة العرض على أقل عرض ممكن، فيمكنك حينها إلقاء نظرة على قيمة نافذة العرض باستخدام طرفية جافاسكربت بكتابة الشيفرة التالية: document.querySelector('html').clientWidth ملاحظة: تختلف المتصفحات في القيم الدنيا لاتساع نافذة العرض، فلا يمكنك تصغيرها إلى ما دونها، وقد تكون هذه القيم أكثر اتساعًا مما تتوقع، ولكي تختبر ذلك عبر متصفح هاتف محمول، فيمكنك استخدام أدوات مثل الصفحة about:debugging في متصفح فايرفوكس لتفحص الصفحة التي تُحمَّل على الهاتف المحمول باستخدام أدوات مطورِي الويب الخاصة بمتصفحات الحواسب المكتبية، ولتميز الصور التي حمّلها المتصفح عند تحميل الصفحة استخدم أدوات مطورِي ويب مثل نافذة مراقب الشبكة Network Monitor. ستتجاهل المتصفحات القديمة التي لا تدعم الميزات السابقة كل ذلك وتتوجه مباشرةً إلى السمة src لتحمل الصورة التي تشير إليها قيمة هذه السمة. ملاحظة: ستجد داخل العنصر <head> في شيفرة المثال السابق السطر التالي: <meta name="viewport" content="width=device-width"> ملاحظة: تجبر هذه الشيفرة متصفحات الهواتف المحمولة على استخدام نافذة العرض الحقيقية لها لتحميل صفحات ويب، فقد لا تطلعك بعض المتصفحات على الاتساع الحقيقي لنافذة عرضها، و ستحمِّل صفحات تحتاج إلى نافذة عرض أوسع ثم تقلصها، وهذا أمر سلبي من منظور الصور المتجاوبة أو التصميم المتجاوب. تبدل الدقة: الحجم نفسه ودقة مختلفة قد تحاول دعم قيم مختلفة للدقة لتناسب أجهزة متنوعة، لكن النتيجة على الشاشة هي الصورة نفسها بأبعادها الحقيقية، إذ يمكنك في هذه الحالة دفع المتصفح إلى اختيار الصورة ذات الدقة الأنسب باستخدام العنصر srcset مع التوجيه x- دون استخدام العنصر sizes، كما يمكنك الاطلاع مباشرةً على هذا الأسلوب في المثال الموجود على جيت-هاب، كما يمكنك الاطلاع على الشيفرة المصدرية: <img srcset="elva-fairy-320w.jpg, elva-fairy-480w.jpg 1.5x, elva-fairy-640w.jpg 2x" src="elva-fairy-640w.jpg" alt="Elva dressed as a fairy"> إليك نتيجة التنفيذ: طبّقنا في هذا المثال شيفرة CSS التالية على الصورة كي يصبح اتساع الصورة 320 بكسل على الشاشة، وتُدعى بكسلات CSS أيضًا: img { width: 320px; } لا حاجة في هذه الحالة إلى استخدام السمة sizes، إذ سيحدِّد المتصفح دقة العرض ويستخدِم أفضل صورة تشير إليها السمة srcset، فإذا كان للجهاز الذي يُحمّل الصفحة حدًا معياريًا أو حدًا أدنى لدقة العرض، ومثَّل كل بكسل من بكسلات الجهاز بكسل CSS واحد، فسيحمّل المتصفح الصورة elva-fairy-320w.jpg مطبِّقًا القيمة 1x للتوجيه ولا حاجة لذكر هذه الحالة؛ أما إذا كانت دقة الجهاز أعلى بحيث يمثل كل بكسلين من الجهاز بكسل CSS واحد على سبيل المثال، فسيحمّل المتصفح الصورة elva-fairy-640w.jpg، ولاحظ أنّ حجم الصورة التي عرضها 640 بكسل هو 93 كيلو بايت، بينما حجم الصورة التي عرضها 320 بكسل هو فقط 39 كيلو بايت. مشكلة الرؤية الفنية قلنا بأن هذه المشكلة ستظهر عندما نحاول تغيير الصورة لتلائم مختلف القياسات المستخدَمة لعرضها، ولنفرض مثلًا أنّ صفحة الويب ستعرض صورةً كبيرةً لمنظر طبيعي وسطه شخص على متصفح حاسوب مكتبي، وعندما تُعرض الصورة نفسها على متصفح هاتف محمول، فستتقلص هذه الصورة لتجعل الشخص الموجود في الصورة صغيرًا جدًا ومن الصعب رؤيته، فمن الأفضل إذًا في هذه الحالة اختيار صورة أصغر تركِّز على الشخص، ويسمح لنا العنصر <picture> بإنجاز هذا النوع من الحلول. نلاحظ وجود صورة تحتاج بشدة إلى رؤية فنية مختلفة بالعودة إلى المثال الأصلي غير المتجاوب. <img src="elva-800w.jpg" alt="Chris standing up holding his daughter Elva"> لنصلح الأمر باستخدام العنصر <picture> الذي يمثّل غلافًا لعدة عناصر <source> تؤمِّن مصادر مختلفةً يختار المتصفح أنسبها ويليها العنصر <img>، كما ستبدو شيفرة المثال المتجاوب كما يلي: <picture> <source media="(max-width: 799px)" srcset="elva-480w-close-portrait.jpg"> <source media="(min-width: 800px)" srcset="elva-800w.jpg"> <img src="elva-800w.jpg" alt="Chris standing up holding his daughter Elva"> </picture> تحتوي العناصر <source> على السمة media التي تحوي بدورها شروطًا على الوسائط، كما في المثال الأول الذي يشرح السمة srcset، إذ تُعَدّ هذه الشروط اختبارات لاختيار الصورة التي ستعرض، بحيث ستُعرض أول صورة يعيد شرطها القيمة الصحيحة True، فإذا كان اتساع نافذة العرض 799 بكسل في الشيفرة السابقة أو أقل، فسيتحقق الشرط داخل العنصر <source> الأول وستعرض الصورة التي يشير إليها، بينما إذا كان الاتساع هو 800 بكسل أو أكثر، فسيختار المتصفح العنصر الثاني. تحتوي السمة srcset على المسار الذي يقود إلى الصورة، ويمكن لأيّ عنصر <source> امتلاك هذه السمة التي تحتوي على مراجع إلى عدة صور، كما يمتلك هذا العنصر السمة sizes أيضًا، وبالتالي ستتمكن من تقديم عدة صور للمتصفح من خلال العنصر <picture> مع إمكانية تزويده بقيم مختلفة لدقة كل صورة، وفي واقع الأمر لن تضطر إلى استخدام هذا الحل إلا قليلًا. وفي شتى الحالات، لا بدّ من وجود العنصر <img> مع سمتَيه src و alt قبل وسم النهاية <picture/> مباشرةً، وإلا فلن تعرض أية صورة، كما يمثِّل هذا العنصر الحالة الافتراضية التي سيعرضها المتصفح إذا لم يتحقق أيّ شرط من شروط الوسائط إذ تستطيع حذف العنصر <source> الثاني فهو موجود على أساس خطة بديلة في حال لم يدعم المتصفح العنصر <picture>. تسمح لك هذه الشيفرة بعرض الصورة المناسبة على كلتا الشاشتين العريضة والضيقة، كما تعرض الصور التالية: ملاحظة: ينبغي عدم استخدام السمة media إلا في الحالات التي تتطلب رؤية فنية، ولا تضع شروطًا على الوسائط ضمن السمة sizes عندما تستخدِم السمة media. لماذا لا نستخدم CSS وجافاسكربت لحل المشكلة؟ سيحمِّل المتصفح الصور عندما يبدأ بتحميل الصفحة قبل أن تبدأ عملية تحميل وتفسير ملفات تنسيق CSS وملفات جافاسكربت، ولهذه الآلية فوائدها في تقليل زمن تحميل الصفحة، لكنها ستؤثر سلبًا على الصور المتجاوبة التي تستخدِم حلولًا مثل srcset التي تضم قائمةً بمجموعة من الصور، فلن تستطيع مثلًا تحميل العنصر <img> ثم التحقق من أبعاد نافذة العرض مستخدمًا جافاسكربت وتغيّر بعدها الصورة المصدرية إلى النسخة الأصغر آليًا إذا كانت هي الأنسب، وذلك لأنّ المتصفح سينهي تحميل الصورة الأصلية قبل انتهاء هذه العملية وستكون قد حملّت أيضًا الصورة الأصغر وهذا هو الأسواء من وجهة نظر الصور المتجاوبة. استخدام التنسيقات الحديثة للصور تعطي بعض التنسيقات الجديدة للصور مثل WebP و AVIF حجمًا أصغر وجودةً عاليةً في الوقت ذاته، كما زاد دعم المتصفحات لها بصورة واسعة في هذه الفترة. يمكنك توفير الدعم للمتصفحات الأقدم باستخدام العنصر <picture> عن طريق تزويد المتصفح بنوع الصورة من خلال السمة type وسيرفض مباشرةً الأنواع أو التنسيقات غير المدعومة: <picture> <source type="image/svg+xml" srcset="pyramid.svg"> <source type="image/webp" srcset="pyramid.webp"> <img src="pyramid.png" alt="regular pyramid built from four equilateral triangles"> </picture> انتبه إلى النقاط التالية: لا تستخدِم السمة media إلا في حال احتجت إلى حل مشكلة الرؤية الفنية. لا يمكنك الإشارة في العنصر <source> إلا إلى صورة من النوع نفسه الذي تحدده السمة type. استخدِم قائمةً تفصل بينها "," عند استخدام srcset و sizes. تطبيق: العمل على صور متجاوبة خاصة بك نطلب إليك في هذا التمرين استخدام رؤية فنية مناسبة لتجهِّز صورتين إحداهما عريضة والأخرى ضيقة باستخدام العنصر <picture> بالإضافة إلى إمكانية تبديل الدقة مستخدمًا السمة srcset: اكتب شيفرة HTML المناسبة للتمرين، كما يمكنك استخدام الملف not-responsive.html على أساس بداية. اعثر على صورة جميلة واسعة مع بعض التفاصيل المميزة، ثم اصنع منها نسخةً مناسبةً للويب باستخدام أيّ محرر صور تراه مناسبًا، واقتطع منها جزءًا يركِّز على تلك التفاصيل المميزة واصنع منه صورةً أخرى (اجعل عرضها 480 بكسل فهو مناسب للتمرين). استخدم العنصر <picture> لإنجاز مبدِّل الصور. أنشئ ملفات صور مختلفة الأبعاد لكنها تعرض الصورة ذاتها. استخدم السمتَين srcset و size لإنجاز مبدّل دقة إما لتقديم صور بالأبعاد نفسها لكن بدقة مختلفة أو بأبعاد مختلفة لتناسب نوافذ عرض مختلفة. خلاصة ناقشنا في هذا المقال نقطتين هامتين عن الصور المتجاوبة: الرؤية الفنية: وهي مشكلة تظهر عندما تريد استخدام صور مجتزأة تركِّز على تفاصيل محددة من صورة أصلية لتلائم التخطيطات المختلفة لصفحة مثل عرض صورة واسعة لمنظر طبيعي على متصفح حاسوب مكتبي ثم اجتزاء صورة أصغر منها تركز على تفاصيل مميزة لعرضها ضمن متصفحات الهواتف المحمولة، ورأينا كيف يُستخدَم العنصر <picture> في حل المشكلة. تبدّل الدقة: وهي مشكلة تظهر عندما تحتاج إلى صور ضيقة كي تعرضها ضمن شاشات ضيقة دون الحاجة إلى صور واسعة مثل تلك التي تعرض على الشاشات الواسعة مثل شاشات الحواسب المكتبية، كما تظهر عندما تريد استخدام صور بالأبعاد ذاتها لكن بدقة مختلفة لكي تعرض عرضًا مناسبًا على الشاشات عالية أو منخفضة الدقة، كما يمكنك استخدام الرسوم الشعاعية مثل SVG أو استخدام السمَتين srcset و size. ترجمة -وبتصرُّف- للمقال Responsive images. اقرأ أيضًا إعداد صور متجاوبة الصور في تصميم صفحات الويب المتجاوب مدخل إلى التصميم المتجاوب والتصميم المتكيف أساسيات تصميم الويب المتجاوب
  9. أهمية البرمجة تنبثق من العصر الذي نعيش فيه حاليًا وهو عصر ما بعد الثورة الرقمية بكل تفاصيله، إذ دخلت الحواسب والأجهزة الذكية والروبوتات جميع مناحي حياتنا من التواصل إلى التسوق إلى التعلم إلى تحليل البيانات وصولًا إلى الروبوتات المقادة عن بعد مثل الروبوتات الجراحية ومسابر الفضاء، وذلك يتطلب عملًا حثيثًا يضطلع به مئات الآلاف من التقنيين والمبرمجين لتأمين الوسيلة الأفضل للتخاطب بين البشر وهذه التجهيزات حتى تؤدي وظيفتها بالطريقة الأفضل والأكثر أمانًا لتبقى التقنيات الرقمية على قدر كبير من الموثوقية. تتجلى أهمية البرمجة في بناء واجهات التخاطب والتحكم بتلك التجهيزات الرقمية وإدارة منظوماتها سواء في الموقع الفيزيائي نفسه أو عن بعد بالاستفادة من تقنيات الويب والاتصال مع قواعد البيانات وتحليلها واستخلاص الرؤى واتخاذ القرارات. ما هي البرمجة؟ البرمجة عمومًا هي تلقين المتلقي ما ينبغي فعله لأداء وظيفة ما وفق نهج يفهمه. فإن عوّدت نفسك على الاستيقاظ باكرًا في نفس التوقيت لتشرب قهوتك الصباحية سيجوز لنا القول أنك برمجت نفسك على هذا الأمر، وعندما تضبط منبهك للاستيقاظ على الساعة الخامسة فستكون قد برمجت منبهك لأداء هذه المهمة. وينسحب الأمر إلى مجال الآلات لتصبح عملية تلقينها كيفية التصرف هي المقصود غالبًا بعبارة برمجة، سواء أكانت هذه الآلات حواسيب أو الهواتف الذكية أو روبوتات. فالبرمجة وفقًا للمصطلح التقني الشائع هي كتابة مجموعة متسلسلة من التعليمات التي تفهمها الآلة لتؤدي عملًا بسيطًا أو مجموعة معقدة من الأعمال باستخدام لغة تفهمها الآلة. تُدعى تلك اللغات التي تفهمها الآلة بلغات البرمجة Programming Languages، ونظرًا لأهمية البرمجة في تشغيل واستثمار الحواسيب والتجهيزات القابلة للبرمجة، تتعدد لغات البرمجة المستخدمة وفقًا للغاية من البرامج وطبيعة الجهاز الذي يُبرمج، لهذا تجد كمًا كبيرًا من لغات البرمجة العامة والمخصصة لإنجاز البرامج المطلوبة بأفضل أداء. تُستخدم الحواسب عادة في كتابة البرمجيات ثم تُنقل إلى التجهيزات التي تحتاجها مثل الحواسب الأخرى أو الهواتف أو الروبوتات وغيرها من التجهيزات الذكية. ما هي أهمية البرمجة؟ تتجلى أهمية البرمجة في تطبيقاتها المتنوعة في شتى المجالات التقنية، إذ تشكل العمود الفقري لكل أشكال التطور المبني على تفاعل الإنسان والآلة الحالي والمستقبلي. من أهم المجالات التي تعتمد على البرمجيات نجد: توطين البيانات وإدارتها. التعليم الإلكتروني والتعلم عن بعد. التجارة الإلكترونية وريادة الأعمال. بناء الأنظمة الخبيرة وأنظمة اتخاذ القرار. التصميم الرسومي ومعالجة الصور. تحليل اللغات الطبيعية. برمجة الروبوتات. أهمية البرمجة في توطين البيانات وإدارتها يُقصد بتوطين البيانات تخزين وتأمين البيانات المحلية الوطنية أو القومية لجميع أشكال الإحصائيات الإنتاجية والديموغرافية لأغراض التوثيق والتحليل وبناء استراتيجيات الدولة المستقبلية على أساس علمي ومنهجي صحيح. تأتي أهمية البرمجة في مجال توطين البيانات في بناء برمجيات قادرة على تخزين وتصنيف وتحليل مختلف أنواع الإحصائيات الحيوية إضافة إلى تطوير برمجيات تحليل بيانات موثوقة قادرة على توفير رؤية دقيقة لما سيؤول إليه الوضع في المستقبل بناء على نتائج التحليل. صحيح أن سوق البرمجيات العالمي مكتظ بكافة أنواع البرمجيات لتنفيذ أية مهمة تقريبًا، إلا أن الاعتبارات الأمنية للحكومات تجعل من أولى الأولويات وخاصة في الدول المتقدمة تقنيًا بناء برمجيات خاصة بها لإدارة مختلف شؤونها، وهذا ما نراه في الواقع عالميًا. أهمية البرمجة في مجال التعليم الإلكتروني والتعلم عن بعد فرض الواقع التقني الجديد تغييرًا جذريًا في مختلف المفاهيم ومنها مفهوم التعلم، إذ مهد تطور الحاسوب والتقنيات المرتبطة به إلى ظهور منصات تعلم إلكترونية لا يُضطر فيها المتعلم إلى الإنتقال إلى المدرسة أو المعهد أو الجامعة لمتابعة دراسته في مواعيد محدد وضمن خطة دراسية صارمة، بل يتابع دراسته من خلال الاتصال مع منصة التعليم الإلكتروني من خلال شبكة الإنترنت أو الشبكات المحلية وفق البرنامج الزمني والأوقات التي تناسبه مما يعطي حرية كبيرة في التحصيل العلمي دون التعارض مع أي عمل آخر يمتهنه المتعلم. لقد سهّلت البرمجة وجود مثل هذه المنصات، فكتابة برمجيات متخصصة في إدارة كل متعلم لبرنامجه الزمني وإدارة المنصة لشؤون متعلميها الدراسية والمالية بفعالية وإدارة طريقة التواصل بين المتعلم والمعلّم ومصادر التعليم. إن نجاح المنصات التعليمية الأكثر شهرة على الصعيد العربي والعالمي مثل أكاديمية حسوب تأتي من التقنيات البرمجية التي تستخدمها لتسهيل التواصل مع المتعلمين وتقديم المعلومة بأفضل شكل ممكن. أهمية البرمجة في مجال التجارة الإلكترونية وريادة الأعمال لا شك بأن المتاجر الإلكترونية على الرغم من أنها في مرحلة النمو حاليًا في المنطقة العربية من أكثر مجالات العمل جذبًا للراغبين في الانطلاق في عالم التجارة الإلكترونية نظرًا لمرونتها العالية وسهولة إدارتها. وتظهر أهمية البرمجة في هذا المجال في تصميم وبناء متاجر إلكترونية جذابة يسهل للعملاء استخدامها وتستخدم في هذا الصدد تقنيات الويب المختلفة التي تعتمد أساسًا على شبكة الإنترنت. إضافة إلى المتاجر الإلكترونية، تظهر أهمية البرمجة في تصميم وبرمجة مواقع الويب أو إنشاء المواقع الإلكترونية التي تقدم دفعًا كبيرًا للأعمال الناشئة وتروّج لعلاماتها التجارية. إن اختيار اللغة البرمجية المناسبة والتقنيات المناسبة لتحقيق المطلوب من الموقع أمر حيوي جدًا في نجاحه واستمراريته. أهمية البرمجيات في مجال اتخاذ القرار والأنظمة الخبيرة إن اتخاذ قرار معين يتطلب دراسة مختلف الجوانب التي تحيط بظروف اتخاذه والغاية منه. لكن عندما تكون القرارات متعلقة بعدد كبير من العوامل متفاوتة التأثير سيجعل المسألة برمتها أكثر تعقيدًا وحساسية. هنا تأتي دور البرمجة في وضع تنفيذ خوارزميات قادرة على التعامل مع مختلف العوامل المؤثرة في اتخاذ قرار معين بطريقة تشابه طريقة تفكير العقل البشري لكن بسرعة أكبر بكثير وبالاستناد إلى قواعد معرفية ضخمة جدًا قد لا تمتلكها منظومة اتخاذ القرار البشرية مهما كان كم الخبرة التي يحملها أعضاء هذه المنظومة. تُدعى هذه المنظومات البرمجية بالأنظمة الخبيرة وتظهر أهمية البرمجة في هذا المجال في تأمين القواعد المعرفية اللازمة عن طريق برمجيات تحليل البيانات وبناء محرّكات تحليل منطق اتخاذ القرار. أهمية البرمجة في التصميم الرسومي ومعالجة الصور تُعد التصميمات الفنية والصور المتحركة والتحريك السينمائي من أهم التقنيات المستخدمة في الترويج الإعلاني والتسويق وصناعة السينما. ولا يخفى على أحد الدور الكبير الذي لعبته الحواسيب في تطوير هذه الصناعة التي بلغت مستويات رائعة جدًا من الواقعية. ولم يكن لهذا الصناعة الإزدهار لولا تقنيات البرمجة الذي وظفت علوم الرياضيات والفيزياء والحركة في وضع خوارزميات غاية في الدقة للفصل بين الصور على شكل طبقات متراكمة لإنتاج صورة كلية وتحريك هذه الصور وفق أنماط مختلفة لإنتاج المشهد السينمائي المطلوب. تظهر أهمية البرمجة في وضع خوارزميات الحركة ومعالجة الصور موضع التنفيذ من قبل الحواسب وبناء برامج تعتمد على هذه الخوارزميات لإنتاج العمل الفني المطلوب. أهمية البرمجة في مجال تحليل اللغات الطبيعية يُقصد بتحليل اللغات الطبيعية العملية التي يحلل فيها الحاسوب أو الأجهزة الذكية كلام البشر ويفهم محتواه وينفذ الأوامر مباشرة. فعندما تنقر على أيقونة الأوامر الصوتية في جوجل وتقول "Hi Google" يستعد البرنامج لفهم كلامك والبحث عن مضمونه، وهذا تطور رائع جدًا في مضمار التواصل مع الآلة. ولم تكن هذه التقنية لتزدهر لولا البرمجيات وخوارزميات التعلم التي تقف خلفها والتي تسعى الشركات إلى تطويرها باستمرار لدعم تحليل وفهم مختلف اللغات المحكية وتحويلها إلى نصوص مكتوبة أو لتسهيل التعامل مع الآلات عمومًا. أهمية البرمجة في مجال الروبوتات لا يخفى على أحد الدور الكبير الذي تلعبه الروبوتات في كافة المجالات من التجهيزات المنزلية الذكية إلى المصانع المقادة بالروبوتات إلى الطيران المسير إلى التحكم بمركبات روبوتية في الفضاء الخارجي. تعتمد برمجة الروبوتات أساسًا على لغات برمجة عامة الأغراض مثل بايثون أو جافا ثم تبنى مكتبات خاصة أو مجموعة أدوات باستخدام هذه اللغات لتنفيذ إجرائيات برمجية تتحكم بمكونات الروبوت على صعيد الحركة أو تحسس البيئة المحيطة أو عمليات اتخاذ قرار. وكلما كان البرنامج أفضل من ناحية الشيفرة أي من ناحية إنجاز مهامه بأبسط وأدق شكل كلما كان تجاوب الروبوت متناسبًا مع طبيعة العمل الذي يوكل إليه. وهنا تظهر أهمية البرمجة في جعل برمجيات الروبوتات مثالية. فوائد البرمجة تتغير نظرتك إلى أهمية البرمجة وفوائدها وفقًا لموقعك منها، فلأصحاب المشاريع التي تنتج البرمجيات رؤاهم ولأصحاب المشاريع التي تستخدم البرمجيات رؤاهم وللمبرمجين أنفسهم رؤاهم الخاصة. لهذا سنحاول أن نتحدث بداية عن فوائد البرمجة عمومًا ثم تفصيل هذه الفوائد وفقًا للتوجهات المختلفة للمهتمين. من أهم فوائد البرمجة نجد: البرمجيات صناعة متقدمة ومزدهرة ويزداد سوقها اتساعًا ومنافسةً فهي مجال مناسب للاستثمار. تساعد على تطوير الأعمال على كافة الأصعدة من خلال إيجاد منظومات إدارة وتحليل قوّية وفعّالة. إيجاد الحلول التقنية للكثير من المشاكل الصناعية والاقتصادية من خلال تقنيات الذكاء الصنعي دون المخاطرة بتطبيق حلول غير مضمونة. يزداد الطلب كثيرًا على المبرمجين من مختلف الاختصاصات، مما يجعل البرمجة مهنة مميزة على صعيد تطوير المسار المهني للمهتمين كما أن مردودها المهني مرتفع نسبيًا. فائدة البرمجة لشركات إنتاج البرمجيات إن البرمجيات صناعة رائجة جدًا ومهمة جدًا لتطوير شتى المناحي التقنية، لذلك تُعنى الشركات المنتجة للبرمجيات بتطوير منتجاتها وفق منهجيات واضحة وصارمة لضمان نجاحها. لهذا فإن الفائدة الرئيسية للبرمجة وفقًا لوجهة نظر الشركات المنتجة قائم على ركيزتين اثنتين: الأولى الحصول دومًا على منتجات فريد ومطلوبة لضمان التفوق في مجالات محددة، والثاني بناء منتج قابل للاستمرار والمنافسة في حال وجود منافسين وهذه هي الحال دومًا في أية صناعة. وهكذا تجد مايكروسوفت وآبل مثلًا مهتمتين بتطوير لغات برمجية تخدم الهدفين السابقين، بينما تهتم شركات اخرى مثل أدوبي بإنتاج برمجيات التصميم بمساعدة الكمبيوتر وهكذا. فائدة البرمجة لأصحاب المشاريع يعتمد أصحاب المشاريع على البرمجة لعدة غايات أساسية: إدارة وتنظيم الأعمال الإدارية والمالية من خلال تطوير برمجيات مخصصة أو شراء حزم برمجية جاهزة. تصميم وبناء مواقع ويب مخصصة للترويج عن الشركة أو العمل، وتُعد هذه المواقع الواجهة الرئيسية للشركة في العالم الافتراضي. إنجاز منظومات دعم القرار بالاعتماد على الأنظمة الخبيرة. إيجاد الحلول للكثير من المشاكل التقنية والصناعية في المصانع والمؤسسات الإنتاجية، من خلال تطوير أو استخدام برمجيات لمحاكاة الحلول المفترضة والتأكد من صحتها وسلامتها قبل تطبيقها الفعلي وبالتالي تخفيف الأعباء المادية وتجنب توقف العمل في حال فشل الحل. فائدة البرمجة لمن يرغبون في تطوير أنفسهم البرمجة مجال عمل واسع ومتشعب ومن أكثر مجالات العمل ازدهارًا على المدى البعيد وأكثرها طلبًا وأجورها مرتفعة نسبيًا. تساعد على الفهم الأعمق للآلات وطبيعة عملها وكيفية التعامل معها. تساعد البرمجة في تطوير المسيرة المهنية للعاملين في مختلف المجالات لأننا نعيش عصر التكنولوجيا والآلات الذكية. تقوّي القدرات التحليلية وتنمي الإبداع عند المتعلم. تزيد من من مهارة المتعلم في التفكير المنطقي وإيجاد الحلول للمشاكل التي يواجهها بطريقة منهجية. أهم لغات البرمجة تتنوع لغات البرمجة وتختلف تبعًا لتطور المسارات التي تحتاج إلى البرمجة واختلافها، إذ تجد لغات مخصصة لبرمجة وتحليل البيانات ولغات مخصصة لتطوير مواقع وتطبيقات الويب وأخرى لتطوير تطبيقات الهواتف الذكية وأخرى لبرمجة الروبوتات، كما ستجد لغات برمجة عامة الأغراض تصلح تقريبًا لبرمجة أية وظائف على مختلف الأجهزة القابلة للبرمجة. نستعرض تاليًا أهم لغات البرمجة وفقًا لمجالات الاستخدام. لغة Java لغة جافا Java هي لغة عامة الأغراض تُستخدم لبرمجة مختلف أنواع التطبيقات. تُعد هذه اللغة من أكثر اللغات انتشارًا وقد بُنيت خصيصًا لتكون عالية المحمولية، أي يمكن أن تكتب شيفرة لتنفيذ مهمة ما مرة واحدة، ثم تنقل هذه الشيفرة إلى أية تجهيزات قادرة على تفسيرها وتنفيذها سواء كانت حواسب أو هواتف ذكية. لغة بايثون Python يُصنف الكثيرون لغة بايثون على أنها أكثر لغات العالم سهولة من ناحية كتابة الشيفرة والاستخدام ولا يعود سبب انتشارها الكبير إلى سهولتها وحسب، بل للكم الهائل من مكتبات الشيفرة الجاهزة التي تساعد المبرمج على إنجاز مختلف أنواع الوظائف بكل سهولة ويسر. تُعد بايثون لغة عامة الأغراض وتُستخدم في كتابة تطبيقات سطح المكتب وتطبيقات الويب وبرمجة الروبوتات والأنظمة المدمجة. دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن لغة ++C لغة C++‎ هي لغة عامة الأغراض يعتمد عليها الكثير من مبرمجي التطبيقات ومطوري لغات البرمجة والأنظمة المدمجة في كتابة برامجهم، فهي لغة قوية تسمح بالتفاعل المنخفض المستوى من العتاد الصلب وتؤمن أداءً عاليًا شديد الكفاءة في زمن التنفيذ. وعلى الرغم من صعوبة إنجاز بعض الوظائف بهذه اللغة موازنة بلغة بايثون مثلًا، لكن أداءها المضمون في زمن التشغيل جعلها من أكثر اللغات استخدامًا. لغة جافاسكربت JavaScript لغة جافاسكربت هي لغة برمجة قوية وخفيفة تستخدم لبناء تطبيقات ومواقع الويب بالكامل، كما يمكن استخدامها ضمن بعض إطارات العمل لكتابة تطبيقات كاملة لسطح المكتب. دورة تطوير التطبيقات باستخدام لغة JavaScript تعلم البرمجة بلغة جافا سكريبت انطلاقًا من أبسط المفاهيم وحتى بناء تطبيقات حقيقية. اشترك الآن لغة روبي Ruby لغة روبي هي لغة عامة الأغراض حديثة الانتشار تركز على البساطة والإنتاجية، وقد اكتسبت شهرتها من أناقتها ووضوح صياغتها وقوتها. إذ يمكنك أخذ فكرة أولية عن الوظيفة التي تفعلها شيفرة روبي دون أن يكون لك خلفية مسبقة عن هذه اللغة وقد استفادت هذه اللغة من الكثير من المزايا المتقدمة للغات اخرى مثل Perl و Smaltalk. تعدُّ روبي لغةً برمجيةً عامة الغرض وتستخدم في بناء البرامج المكتبية، وتطبيقات الويب، وخوادم الويب، وأدوات النظام، وفي قواعد البيانات وغيرها. لغة PHP صُممت لغة PHP لبناء مواقع وتطبيقات ويب عالية الأداء والكفاءة. وتعتبر بلا منازع من أشهر اللغات التي تُستخدم في تطوير مواقع الويب. إذ تستخدم في بناء صفحات الويب وتوليد المحتوى الديناميكي للمواقع والوصول إلى الملفات الموجودة على الخوادم والعمل عليها والوصول إلى قواعد البيانات والتعامل معها والتحكم بسماحيات وصول المستخدم وتشفير البيانات. ومع وجود كل هذه الميزات يعدها الكثيرون لغةً سهلة التعلم. دورة تطوير تطبيقات الويب باستخدام لغة PHP احترف تطوير النظم الخلفية وتطبيقات الويب من الألف إلى الياء دون الحاجة لخبرة برمجية مسبقة اشترك الآن لغة كوتلن Kotlin لغة كوتلن هي لغة قوية حديثة العهد نسبيًا طورتها جوجل مستفيدة من لغتي جافا وجافاسكربت. صُممت اللغة أساسًا لتطوير تطبيقات ويب وتطبيقات نظام التشغيل Android الذي يُستخدم في الهواتف الذكية، كما بدأت مؤخرًا بدعم كتابة تطبيقات مخصصة لأنظمة تشغيل مدمجة أخرى مثل IOS. لغة سويفت Swift لغة سويفت هي لغة من تصميم شركة آبل استبدلت بها اللغة الرسمية لها Objective C التي تفتقد الكثير من ميزات اللغات الحديثة. ظهرت هذه اللغة عام 2014 وكانت مخصصة لكتابة برامج خاصة بأنظمة تشغيل آبل مثل نظام التشغيل IOS و macOS لكنها بدأت بعد ذلك بدعم أنظمة تشغيل أخرى مثل لينكس في أواخر 2015 ثم ويندوز مؤخرًا ابتداء من نسختها 5.3 عام 2020. خاتمة لقد ولجنا بلا شك عصر الآلات الذكية وسيبدأ قريبًا (إن لم تكن قد بدأ بالفعل) تدريس لغات البرمجة كغيرها من اللغات الطبيعية في المناهج الدراسية وفي مراحل مبكرة. لأن اعتمادنا على الآلات يزداد بشدة ومن الضروري تعلم أساسيات التخاطب معها لضمان استثمارها بالشكل الأمثل، فأهمية البرمجة حاليًا تماثل أهمية أي لغة أجنبية نتعلمها. لهذا آثرنا في هذا المقال عرض أهمية البرمجة ومجالات استخدامها وفق منحىً اطلاعي وليس تعلميًا حتى يكوّن المهتمون بالبرمجة فكرة عامة عن مفهوم البرمجة وتطبيقاتها وفوائدها سواءً كانوا من أصحاب المشاريع أو الراغبين في تعلم البرمجة لتطوير مسيرتهم المهنية. اقرأ أيضًا تعلم البرمجة دليلك الشامل إلى لغات البرمجة دليك الشامل إلى: لغات برمجة الألعاب تعلم PHP
  10. تمتلك الرسوم الشعاعية vector graphics أهميةً واضحةً في حالات عديدة، إذ تتميز بحجم منخفض وقدرة كبيرة على تغيير أبعادها دون أن تتشوه عند تصغيرها أو تكبيرها، ونستعرض في مقالنا كيفية إضافة رسوميات مثل هذه إلى صفحات الويب وخاصةً صور SVG. لا بدّ قبل متابعة القراءة أن تكون على دراية بأساسيات HTML وبكيفية إدراج الصور في صفحاتك. ملاحظة: لا نهدف في هذا المقال إلى تعليمك إنشاء رسوميات SVG، وإنما فقط كيفية إدراجها في صفحات الويب. ما هي الرسوميات الشعاعية؟ ستتعامل خلال مسيرتك في عالم الويب مع نوعين من الصور: الصور النقطية Raster images: تعرَّف على صورة شبكة من البكسلات، إذ يحتوي ملف الصورة النقطية على معلومات تعرض موقع كل بكسل في الصورة واللون الدقيق لكل بكسل، ومن أكثر تنسيقات الصور النقطية انتشارًا على الويب هي بِت-ماب bmp. و png. و jpg. و gif.. الصور الشعاعية vector images: تُعرَّف باستخدام خوارزمية، إذ يحتوي ملف الصورة الشعاعية على تعريفات للشكل والمسار اللذين يستخدِمهما الحاسوب في عرض الصورة بالطريقة الصحيحة عندما يصيرها على الشاشة، ومن أمثلتها صور SVG التي تساعدك في إنشاء رسوم شعاعية مناسبة جدًا للاستخدام في الويب. حتى تدرك الفرق بين النوعين سنعرض عليك مثالًا يمكنك استعراضه مباشرة ضمن مستودع جيت-هاب المخصص، إذ يعرض الملف صورتان متطابقتان تمامًا لنجمتين حمراوين يحيط بهما ظل أسود إلى جوار بعضهما، وتكون الصورة اليمينية بتنسيق SVG واليسارية بتنسيق PNG، كما سيبدو الفرق واضحًا عندما تكبر الصورة في الصفحة، إذ تتشوه صورة PNG لأنها تحتوي على معلومات تحدِّد موقع كل بكسل بدقة كما تحدد لونه، فعندما تكبر هذه الصورة سيزداد حجم كل بكسل ليملأ حجمًا أكبر على الشاشة وستبدو الصورة مشوشةً، في حين لا يتغير شكل صورة SVG، لأن تغير أبعاد الصورة لن يؤثر في مظهرها نظرًا لاستخدام هذه الصور خوارزمية لعرض الشكل حتى بعد تضاعف حجمه. ملاحظة: إن كلتا الصورتين السابقتين في الواقع من نوع PNG (طريقة عرضهما في المقال)، إذ تمثِّل الصورة اليسارية صورةً نقطيةً، في حين تمثِّل الصورة اليمينية صورةً شعاعيةً. يُعَدّ حجم ملفات الصور الشعاعية أقل من مقابلاتها النقطية، لأنها ستحمل فقط عدة خوارزميات بدلًا من معلومات عن كل بكسل بمفرده. ما هي صور SVG؟ تُعَدّ SVG لغةً وصفيةً مبنيةً على أساس اللغة XML، وتشابه من حيث المبدأ لغة HTML، ما عدا أنها تضم عناصر مختلفة لتعريف الأشكال التي تريد عرضها في صفحتك والتأثيرات التي تريد تطبيقها على هذه الأشكال، فهي إذًا لغة لتوصيف الرسوم وليس المحتوى، وستجد عناصر إنشاء الأشكال البسيطة مثل الدائرة <circle> والمستطيل <rect> على أساس مثال عن أبسط العناصر، كما ستجد عناصر تعطي ميزات أكثر تقدمًا مثل <feColorMatrix> لتحويل الألوان باستخدام مصفوفة التحويل و <animate> لتحريك أجزاء من الرسم الشعاعي و <mask> لتطبيق قناع فوق صورتك. ستعطيك الشيفرة التالية على سبيل المثال صورةً لدائرة ومستطيل: <svg version="1.1" baseProfile="full" width="300" height="200" xmlns="http://www.w3.org/2000/svg"> <rect width="100%" height="100%" fill="black" /> <circle cx="150" cy="100" r="90" fill="blue" /> </svg> وستكون النتيجة كما يلي: قد يراودك شعور أنّ كتابة شيفرات الصور الشعاعية هين كما في المثال السابق، وبالطبع يمكنك كتابة شيفرات أمثلة بسيطة مثل تلك، ولكن سرعان ما ستجد صعوبةً بالغةً عند تصميم الصور الأعقد، إذ يستخدِم معظم المصممين برامج تحرير خاصة لإنشاء صور مثل هذه مثل "إنكسكيب Inkscape" و "إليستريتور illustrator"، كما تساعدك هذه البرمجيات في إنشاء رسومات متنوعة باستخدام أدوات متنوعة للرسم وإنشاء أشكال تقريبية للصور مثل ميزة "تتبع الخريطة النقطية Trace Bitmap" التي يتيحها برنامج "إنك سكيب". تمتلك صور SVG ميزات متقدمةً إضافةً إلى ما ذكرناه سابقًا: يمكن الوصول إلى النصوص ضمن صور SVG والتعامل معها، ولهذا الأمر فوائده فيما يتعلق بالسيو SEO. يمكن أن تنقل جزءًا من صورة إلى أخرى لأنها مكوّنات مستقلة على هيئة عناصر، وبإمكانك أيضًا تنسيقها باستخدام CSS وكتابة شيفرة للتحكم بها باستخدام جافاسكربت. لماذا سيفضِّل المطورون إذًا استخدام الصور النقطية على صور SVG؟ السبب هو بعض المساوئ: يمكن أن تتعقد شيفرتها بسرعة، مما يعني أنّ حجمها قد يزداد، وقد يتطلب تصييرها زمنًا أطول من قِبَل المتصفح. قد يكون إنشاء هذه الصورة أصعب من الصور النقطية تبعًا لطبيعة الصور التي تنوي إنشاءها. لا تدعم المتصفحات القديمة هذه الصور، وبالتالي لن تكون مناسبةً إذا قررت دعم تلك المتصفحات مثل النسخ القديمة لإنترنت إكسبلورر فهي مدعومة ابتداءً من النسخة IE9. يرى البعض أنّ الرسوميات النقطية أفضل عند إنشاء صور معقدة عالية الدقة مثل الصور الفوتوغرافية، وذلك للأسباب التي شرحناها آنفًا. إضافة صور SVG إلى صفحات الويب سنعرض في هذا القسم الطرق المختلفة التي يمكن اتباعها لإضافة صور SVG الشعاعية إلى صفحة الويب. الطريقة السريعة: استخدام العنصر يُنفَّذ الأمر بالإشارة إلى ملف الصورة الشعاعية من خلال السمة src، كما تحتاج إلى ضبط قيمتي الطول والعرض من خلال السمتَين width و height وخاصةً إذا لم ترث صورة SVG نسبة البُعدين aspect ratio، لذا راجع مقال إضافة الصور في صفحات HTML إذا لم تفعل ذلك من قبل. <img src="equilateral.svg" alt="triangle with all three sides equal" height="87" width="100" /> الحسنات: طريقة سريعة، صياغة قواعدية مألوفة، ويمكن تزويدها ببديل نصي من خلال السمة alt. يمكن تحويلها إلى رابط بسهولة عن طريق وضعها ضمن العنصر <a>. يمكن أن يخزِّن المتصفح ملف SVG تخزينًا مؤقتًا، مما يزيد من سرعة تحميل الصفحة إذا أردت تحميلها مجددًا في المستقبل. السيئات: لا يمكن التلاعب بالصورة عبر جافاسكربت. ينبغي استخدام قواعد CSS سطرية inline CSS ضمن شيفرة ملف SVG لتتمكن من التحكم بمحتوى الصورة، إذ لن تؤثر ملفات التنسيق الخارجية التي يستدعيها ملف SVG. لا يمكن إعادة تنسيق الصورة باستخدام أصناف CSS المجردة مثل :focus. استكشاف الأخطاء ودعم المتصفحات عند استخدامك متصفحات لا تدعم SVG مثل IE8 ومتصفحات نظام أندرويد بنسخة تحت 2.3، فيمكنك الإشارة إلى صورة PNG أو JPG عبر السمة src ومن ثم استخدم السمة srcset التي تميزها المتصفحات الحديثة فقط للإشارة إلى صورة SVG، وفي هذه الحالة لن تشغل صورة SVG سوى المتصفحات الحديثة التي تدعمها وستشغل المتصفحات الأقدم الصورة النقطية: <img src="equilateral.png" alt="triangle with equal sides" srcset="equilateral.svg"> يمكنك استخدام صورة SVG على أساس خلفية من خلال تنسيقات CSS، إذ ستجد في الشيفرة التالية أنّ المتصفحات القديمة ستتعامل فقط مع الصورة النقطية التي تفهمها، في حين ستميز الحديثة صورة SVG: background: url("fallback.png") no-repeat center; background-image: url("image.svg"); background-size: contain; لا يمكن كذلك التلاعب بالخلفية SVG التي ندرجها من خلال تنسيقات CSS باستخدام جافاسكربت، وتخضع للقيود نفسها التي يخضع لها استخدام قواعد CSS مع صور SVG التي يدرجها العنصر <img>. كيفية إدراج صور SVG ضمن صفحات HTML يمكنك فتح ملف SVG باستخدام محرر نصي، كما يمكن نسخ شيفرته ولصقها في ملف HTML، إذ تُدعى هذه العملية بالإدراج المباشر SVG inline أو inlining SVG، بحيث تُضاف هذه الشيفرة إلى HTML بين وسمَي البداية والنهاية للعنصر <svg>، وإليك مثالًا بسيطًا كما يلي: <svg width="300" height="200"> <rect width="100%" height="100%" fill="green" /> </svg> الحسنات: تقلل هذه العملية من طلبات HTTP، وبالتالي قد تقلل من زمن تحميل الصفحة. يمكنك استخدام السمة class والسمة id مع العنصر <svg>، كما يمكن تنسيقه باستخدام CSS ضمن العنصر ذاته أو في أيّ مكان تضع فيه قواعد تنسيق CSS الخاصة بملف HTML، وعمومًا يمكنك استخدام أي سمة وصفية للعنصر SVG على أساس خاصية من خواص CSS. يُعَدّ الإدراج المباشر لشيفرة SVG الطريقة الوحيدة التي تتيح لك استخدام قواعد CSS التفاعلية مثل الأصناف المجردة مثل :focus والرسوم المتحركة ضمن صور SVG، حتى ضمن صفحات التنسيق النمطية. يمكنك توصيف شيفرة SVG على أساس رابط بتغليفها داخل العنصر <a>. السيئات: تُعَدّ هذه الطريقة مناسبةً فقط إذا استخدمت SVG في مكان واحد، فالتكرار قد يسبب استخدامًا كثيرًا للموارد. سيزيد استخدام شيفرة SVG زائدة من حجم ملف HTML. لا يمكن للمتصفحات تخزين شيفرة SVG المباشرة كما تخزن شيفرة SVG المدرَجة ضمن الصور النمطية، وبالتالي لن تزيد سرعة تحميل الصفحات التي حُمِّلت سابقًا في هذه الحالة. بإمكانك تزويد صورة SVG بمحتوى بديل باستخدام العنصر <foriegnObject>، لكن ستنزل المتصفحات التي تدعم SVG أيضًا الصور البديلة أو الاحتياطية، ولهذا عليك التفكير مليًا في ضرورة دعم المتصفحات القديمة أو لا. إدراج صور SVG باستخدام العنصر <iframe> يمكنك أيضًا فتح صور SVG في متصفحك، وبالتالي يمكنك إدراج هذه الصورة في صفحتك باستخدام <iframe> على نحو مماثل تمامًا لما فعلناه في مقال آليات إدراج المحتوى والوسائط المتعددة في صفحة HTML، وإليك مراجعة سريعة: <iframe src="triangle.svg" width="500" height="500" sandbox> <img src="triangle.png" alt="Triangle with three unequal sides" /> </iframe> وبالطبع فهي ليست الطريقة الأفضل لإدراج SVG. السيئات: يملك لعنصر <iframe> آليةً لعرض محتوى بديل في حال حدثت مشكلة في عرضه، لكن ستعرض المتصفحات هذا المحتوى فقط إذا لم تدعم العنصر <iframe> بالكامل. إذا لم يكن لصفحتك وملف SVG أصلًا مشتركًا، فلن تتمكن من استخدام جافاسكربت في صفحتك الرئيسية للتلاعب بهذا الملف. تطبيق: العمل مع SVG سنحاول في هذا التطبيق العمل على بعض رسوميات SVG على سبيل التسلية، فلقد وضعنا في قسم المدخلات Input ضمن محرر الشيفرة في الأسفل بعض الأمثلة لتبدأ، كما يمكنك الاطلاع على عناصر SVG أخرى لتعمل عليها بالعودة إلى مرجع MDN إلى عناصر SVG، إذًا فهذا القسم لشحذ مهاراتك في البحث مع قليل من التسلية. إذا ارتكبت خطأً، فيمكنك مسح ما كتبته بالنقر على زر إعادة الضبط Reset. خلاصة قدمنا لك في هذا المقال جولةً سريعةً على الرسوم الشعاعية وملفات SVG، كما بيّنا فائدتها وكيفية إدراج صور SVG في صفحات الويب، فلم يكن القصد من هذا المقال تزويدك بدليل لتعلم SVG، وإنما لتعريفك بهذه التقنية إذا صادفتها لاحقًا خلال رحلتك في تطوير الويب، فلا تقلق إذًا إذا لم تجد نفسك خبيرًا بعد. سنناقش في آخر مقالات هذه السلسلة مفهوم الصور المتجاوبة responsive images بشيء من التفصيل، وسنلقي نظرةً على أدوات HTML التي تتيح لك إدراج صور تُعرَض جيدًا على مختلف الأجهزة. ترجمة -وبتصرُّف- للمقال Adding vector graphics to the web. اقرأ أيضًا نصائح لإنشاء وتصدير رسومات SVG للويب إضافة الصور في صفحة HTML كيفية إنشاء أشكال بسيطة باستخدام SVG
  11. لا بد وأنك قد امتلكت فكرةً واضحةً عن مفهوم إدراج المحتوى في صفحات الويب بما في ذلك إدراج الصور والفيديو والصوتيات، إذ سنتخذ في هذا المقال خطوات جانبيةً لنلقي نظرةً على العناصر <object> و <embed> و <iframe> التي تسمح بإدراج محتويات متنوعة في صفحات الويب، إذ يُستخدَم <iframe> في إدراج صفحات الويب أخرى؛ أما العنصران الباقيان فيسمحان لك بإدراج ملفات PDF و SVG وحتى ملفات فلاش التي تُعَدّ في نهاياتها ومع ذلك قد تراها. لا بد قبل البدء بقراءة هذا المقال أن تكون على دراية بأساسيات عمل الحاسوب وطريقة تثبيت البرمجيات الأساسية ودراية بطريقة عمل الملفات، ولا بد أيضًا من الدراية بأساسيات اللغة HTML كما عرضناها في مقال تعرَّف على HTML، وطريقة إدراج الصور في صفحة HTML وطريقة إدراج المحتوى البصري والسمعي في صفحة HTML. لمحة موجزة عن تاريخ إدراج المحتوى زادت فيما مضى شعبية استخدام الإطارات frames لإنشاء مواقع الويب، وهي أجزاء صغيرة من موقع ويب مخزنة ضمن صفحات HTML مستقلة، وقد أدرجت هذه الأجزاء في ملف رئيسي دُعي مجمِّع الإطارات frameset يعطيك إمكانية التحكُّم بالمكان الذي يشغله كل إطار بصورة مشابهة نوعًا ما لتحديد أبعاد الصفوف والأعمدة في جدول، وقد عُدّ هذا الأسلوب الأكثر جمالًا في بداية تسعينات القرن الماضي، كما لوحظ أن تقسيم الصفحة إلى أجزاء أصغر سيزيد من سرعة تحميلها وخاصةً مع سرعات الاتصال المنخفضة في تلك الفترة، ولكن مع ذلك، فقد عانى هذه الأسلوب من مشاكل عدة ألغت معها إيجابية السرعة، فلم تَعُد تراها في أيامنا. ازدادت شعبية تقنية الإضافات plug-ins بعد فترة -أي أواخر التسعينات وبداية الألفية الجديدة- مثل جافا أبليت Java Applets وفلاش، مما يسمح لمطورِي الويب من إدراج محتوى غني في صفحات الويب مثل مقاطع الفيديو والرسوم المتحركة التي لم تكن متاحةً باستخدام HTML وحدها، وقد أدرجت هذه التقنيات باستخدام عناصر مثل <object> والعنصر الأقل استخدامًا <embed>، ولقد كانا شديدي الفائدة حينها، لكن سرعان ما خبى تألقهما لأسباب عديدة يتعلق بعضها بسهولة الوصول والأمان وحجم الملفات، وقد توقفت معظم المتصفحات الحديثة في أيامنا عن دعم هذه الإضافات مثل تقنية فلاش. ظهر أخيرًا العنصر <iframe> مع طرق أخرى لإدراج المحتوى مثل <canvas> و <video> الذي يتيح إمكانية إدراج صفحة ويب بأكملها ضمن أخرى كما لو كانت صورةً أو أيّ عنصر آخر، وتستخدَم كثيرًا حاليًا. لننتقل بعد هذا الدرس التاريخي لنرى كيف نستخدِم بعض هذه العناصر. تطبيق: الاستخدامات التقليدية لإدراج العناصر سنقفز مباشرةً في هذا المقال إلى التطبيق لكي نعطيك فكرةً واقعيةً عن فائدة تقنيات إدراج المحتوى في صفحات ويب، إذ يشتهر موقع اليوتيوب كثيرًا في عالم الويب، لكن لا يعلم الكثيرون عن إمكانيات المشاركة التي يتيحها، فلنلق نظرةً إذًا على الطريقة التي يسمح فيها اليوتيوب بإدراج فيديو في أيّ صفحة باستخدام العنصر <iframe>. انتقل إلى موقع اليوتيوب واختر الفيديو الذي تريد. ستجد تحت الفيديو زر مشاركة Share، انقر عليه لتعرض خيارات المشاركة. اختر زر إدراج Embed وستشاهد شيفرة تضم العنصر <iframe>. انسخ هذه الشيفرة والصقها في الصندوق Input لمحرر الشيفرة الذي تراه في الأسفل وراقب ما يحدث في منطقة الخرج output. حاول إدراج خريطة من خرائط جوجل لتكسب نقاطًا إضافيةً: انتقل إلى جوجل ماب وانتق خريطةً ما. انقر على أيقونة القائمة الرئيسية -أي أيقونة الخطوط المتلاحقة- في الزاوية العليا اليسارية من واجهة المستخدِم. اختر مشاركة أو إدراج خريطة Share Embed map. اختر إدراج خريطة Embed map والذي سيزوّدك بشيفرة تضم العنصر <iframe>، انسخها. الصقها في الصندوق Input لمحرر الشيفرة الذي تراه في الأسفل وراقب ما يحدث في منطقة الخرج output. إذ ارتكبت خطأً، فيمكنك مسح ما كتبته بالنقر على زر إعادة الضبط Reset، في حين إذا وجدت نفسك تائهًا كليًا، فانقر على زر أظهر الحل Show solution لترى الحل الصحيح. العنصر iframe بالتفصيل لقد كان الأمر مسليًا وسهلًا حتى الآن، وقد صُمم العنصر <iframe> ليسمح بإدراج صفحات ويب خارجية ضمن صفحة الويب التي تعمل عليها، كما تقدِّم هذه التقنية فائدةً عظيمةً لإضافة محتوى مصدره طرف ثالث في الصفحة لا يمكنك التحكّم به مباشرةً ولا تريد في الوقت نفسه إدراج نسخة خاصة بك من هذا المحتوى مثل الفيديوهات التي تقدمها مزودات الفيديو على الإنترنت مثل يوتيوب أو أنظمة إدراج التعليقات مثل Disque أو الخرائط أو لوحات الإعلانات وغيرها، وما سنعمل عليه في مقالنا هو استخدام العنصر <iframe> في إدراج محتوى مثل هذا في صفحات ويب. لكن، هنالك بعض الهواجس الأمنية فيما يتعلق باستخدام <iframe> كما سنناقش لاحقًا، لكن هذا لايعني ألا تستخدِمه في مواقعك، وإنما لدفعك إلى القليل من التفكير المتأني، ولنتفحّص الشيفرة التالية بشيء من التفصيل، فلنقل أنك تريد إدراج "قائمة مصطلحات شبكة مطوري موزيللا MDN glossary"، إذ يمكنك محاولة العمل على الشيفرة التالية: <head> <style> iframe { border: none } </style> </head> <body> <iframe src="https://developer.mozilla.org/en-US/docs/Glossary" width="100%" height="500" allowfullscreen sandbox> <p> <a href="/en-US/docs/Glossary"> Fallback link for browsers that don't support iframes </a> </p> </iframe> </body> يضم المثال السابق الأساسيات الضرورية لاستخدام <iframe>: border: none: إذا استخدمت هذه السمة، فسيُعرَض العنصر <iframe> دون إطار محيط به، وإلا فسيعرض المتصفح افتراضيًا إطارًا حوله. allowfullscreen: إذا استخدمت هذه السمة، فسيُعرَض العنصر <iframe> داخل شاشة كاملة، وذلك باستخدام الواجهة البرمجية التي تدير العرض على شاشة كاملة Fullscreen API. src: تشابه نظيرتها في العنصر <video> وتضم مسارًا يدل على عنوان URL الخاص بالمحتوى المُدرج. height و width: وهما سمتان تحددان طول وعرض المنطقة التي تعرض محتوى العنصر <iframe>. المحتوى الاحتياطي Fallback content: كما هو الحال مع العنصر <video>، إذ يمكنك إدراج محتوى احتياطي بين وسمَي البداية والنهاية للعنصر <iframe> سيظهر في حال لم يدعم المتصفح هذا العنصر، وقد زودنا المستخدِم في حالات مثل هذه برابط مباشر إلى الصفحة التي لم يتمكن المتصفح من إدراجها، لكن من غير المحتمل أن تصادف متصفحًا حديثًا لا يدعم <iframe>. sandbox: تعمل هذه السمة في متصفحات أكثر حداثةً مثل إنترنت إكسبلورر 10 موازنةً بغيرها من ميزات <iframe>، كما تتطلب إعدادات أمان متشددة أكثر، وسنتحدث عن ذلك في القسم التالي. ملاحظة: لتزيد من سرعة تحميل الصفحة يمكن إسناد قيمة السمة src باستخدام شيفرة جافاسكربت عند انتهاء تحميل المحتوى الرئيسي للصفحة، وبهذه الطريقة يمكن استخدام صفحتك مباشرةً ويقل زمن تحميلها الرسمي وهو معيار سيو SEO هام. اعتبارات أمنية لا نتوقع منك أن تفهم جيدًا جميع هذه الاعتبارات الأمنية من المرة الأولى التي تتعرف عليها، وإنما نريدك الانتباه إليها وأن يكون لديك مرجع لتعود إليه عندما تزداد خبراتك وتقرر استخدام العنصر <iframe> في أعمالك، ولا حاجة للخوف طبعًا من استخدام هذا العنصر وإنما لمزيد من الانتباه. لقد تعلم صانعِي المتصفحات ومطورِي الويب من تجاربهم المريرة أنّ النوافذ الضمنية المتمثلة بالعنصر <iframe> هي هدف شائع -أو ضحية هجوم Attack vectom بالتسمية الرسمية- لأشخاص سيئون على الويب والذين يطلَق عليهم اسم مختطفين hackers أو مخترقين crackers، وذلك كي يهاجموك من خلالها عندما يحاولون تعديل صفحتك بطريقة غير سليمة أو أن يخدعوا آخرين لفعل أمور لا يريدون فعلها مثل إفشاء معلومات حساسة مثل اسم المستخدِم أو كلمة السر، ولهذا السبب يحاول المهندسون المختصون ومطورو المتصفحات ابتكار آليات حماية متنوعة لجعل استخدام العنصر <iframe> أكثر أمانًا وإيجاد طرق عملية مثلى يجدر اتباعها وسنغطي بعضًا منها في هذا القسم. ملاحظة: يعد الاختطاف بالنقر هجومًا شائعًا يستهدف النوافذ الضمنية، إذ يُدرِج المخترق نافذةً غير مرئية في صفحتك أو يدرج صفحتك ضمن موقعه المشبوه، ثم يستخدِم ذلك في لفت انتباه المستخدِمين، وهي طريقة شائعة في تضليلهم أو لسرقة بياناتهم الحساسة. سنقدم لك مثالًا سريعًا: حاول تحميل المثال الذي عرضناه في الأعلى في متصفحك، إذ يمكنك إيجاده ضمن المستودع المخصص له على جيت -هاب، كما يمكنك أيضًا الاطلاع على الشيفرة المصدرية، ما سيحدث أنه بدلًا من أن يعرض لك الصفحة المطلوبة وهي "قائمة مصطلحات MDN"‘ فقد تشاهد رسالة من قبيل "لا يمكن فتح هذه الصفحة I can't open this page"، فإذا انتقلت إلى "الطرفية Console" ضمن أدوات مطورِي الويب المدمجة مع المتصفح، فستجد رسالةً تفسِّر لك سبب ما حدث، إذ سيكون نص هذه الرسالة في فايرفوكس مشابهًا للتالي: (إنّ فتح المورد "https://developer.mozilla.org/en-US/docs/Glossary" في إطار مرفوض لأن التوجيه "X-Frame-Options" قد ضُبط على القيمة DENY)، ويعود السبب إلى أنّ المطورين الذين بنوا "شبكة مطور موزيللا MDN" قد أضافوا إعدادًا إلى الخادم الذي يستضيف MDN يمنع إدراج هذه الصفحات ضمن نوافذ ضمنية <iframe>، ويُعَدّ الأمر منطقيًا، فمن غير الوارد إدراج صفحة كاملة من MDN ضمن صفحات أخرى ما لم تحاول وضعها في موقعك الخاص ثم تدعي ملكيتك لها، أو أن تحاول سرقة بيانات حساسة من خلال هجوم الاختطاف بالنقر، وكلاهما أمر سيء، وبالإضافة إلى ذلك، سيكلِّف هذا الفعل موزيللا مبالغ طائلة عندما يتخطى استهلاك الموقع عرض حزمة التراسل المعتمدة. لا تدرج ما ليس ضروريا قد يبدو معقولًا في بعض الحالات إدراج محتوى مصدره طرف آخر مثل فيديوهات يوتيوب أو خرائط جوجل، لكن وفِّر على نفسك الكثير من المشاكل ولا تدرج أيّ محتوى مصدره طرف ثالث ما لم يكن ذلك ضروريًا جدًا، وتذكَّر دائمًا قاعدة أمان أساسية في ويب تقول: "لا شيء يُدعى زيادةً في الحرص، تحقق مجددًا حتى لو فعلت ذلك، وإذا تحقق أحدهم، فافترض أنك في خطر حتى يثبت العكس". إضافة إلى عامل الأمان، لا بد من الانتباه إلى مواضيع الخصائص، إذ يتمتع المحتوى بمعظمه بحقوق نشر على الإنترنت أو خارجها حتى غير المتوقع منها مثل معظم صور ويكيميديا، لذلك لا تعرض محتوًى على صفحتك ما لم تمتلكه أو حصلت على إذن مكتوب وجلي، فعقوبات مخالفة حقوق النشر شديدة، ونقولها مجددًا: "لا شيء يُدعى زيادةً في الحرص"، فإذا كان المحتوى مرخصًا، فالتزم بشروط الرخصة، فمحتوى أكاديمية حسوب مثلًا مرخَّص بموجب الرخصة CC-BY-NC-SA، ويعني ذلك أنه عليك الإشارة إلى أكاديمية حسوب بصورة مناسبة عند اقتباس أيّ محتوى منها حتى لو أجريت تعديلات مهمة عليه واستعمال المحتوى بشكل غير تجاري. استخدم بروتوكول HTTPS يُعَدّ نسخةً مشفرةً من بروتوكول HTTP، وينبغي استخدامه ما أمكن في موقعك: يقلل استخدام HTTPS فرصة التلاعب بالمحتوى عن بعد أثناء نقله. يمنع HTTPS المحتوى الذي تُدرجه من الوصول إلى محتوى الصفحة الأساسية وبالعكس. يمكِّن بروتوكول HTTPS موقعك من طلب تثبيت شهادات أمان خاصة، إذ تقدِم الكثير من مزودات الاستضافة استخدام HTTPS دون الحاجة إلى أية إعدادات من طرفك لتثبيت شهادة أمان في مكانها، وإذا أردت أيضًا إعداد خدمات HTTPS بنفسك على موقعك، فستجد أدوات وإرشادات تقدِّمها منظمة Let's Encrypt لإنشاء وتثبيت الشهادات المطلوبة تلقائيًا مع دعم مدمج لمعظم خوادم الويب الأكثر استخدامًا مثل Apache و Nginx وغيرها، وقد صممت تلك الأدوات لتجعل العملية سهلة قدر الإمكان، فلا سبب يمنع استخدامها أو غيرها من وسائل دعم HTTPS على موقعك. ملاحظة: تدعم خدمة GitHub pages بروتوكول HTTPS افتراضيًا، فهي منتسبة إذًا لاستضافة المحتوى، فإذا كنت تستخدِم مزود استضافة آخر ولست واثقًا من دعمه للبروتوكول، فاسأله عن هذه التفاصيل. استخدم السمة sandbox دائما عليك منح المحتوى الذي تُدرجه في موقعك أقل مستوى من السماحيات التي تمّكنه من إنجاز عمله دون إعطاء فرصة للمخترقين بإحداث ضرر في موقعك، وينطبق الأمر أيضًا على المحتوى الخاص بك، كما تُدعى أي حاوية للشيفرات يمكن أن تُستخدَم فيها تلك الشيفرات بصورة ملائمة أو لأغراض اختبارية دون أن تسبب أذىً لبقية الشيفرة مصادفةً أو عمدًا بصندوق معزول أو محمي sandbox. يمكن لأيّ محتوى لا يقيده صندوق عزل فعل الكثير على صفحاتك مثل تنفيذ شيفرات جافاسكربت أو إرسال استمارات أو عرض نوافذ منبثقة، لذلك لا بد من استخدام كل التقييدات المتاحة باستخدام السمة sandbox دون أية معاملات -أي قيم- كما عرضناها في مثالنا السابق. لكن إذا كان الأمر ضروريًا، فتستطيع إضافة الأذونات واحدًا تلو الآخر داخل قيمة السمة ""=sandbox وسنتحدث عن هذه الأذونات تاليًا، كما تجدر الإشارة إلى عدم استخدام الخيارين allow-scripts و allow-same-origin معًا، فقد يلتف المحتوى الذي تُدرجه على الخيار allow-same-origin الذي يمنع المواقع من تنفيذ الشيفرة، ثم تُستخدم جافاسكربت في إلغاء عمل السمة sandbox كلها. ملاحظة: لا يمكن لصندوق عزل تأمين الحماية من المخترقين إذا خدعوا المستخدِمين ودفعوهم لزيارة مواقع مشبوهة مباشرةً خارج العنصر <iframe>، فإذا كان هناك احتمال أن يكون المحتوى الذي تريد إدراجه مشبوهًا مثل المحتوى الذي ينتج عن تفاعل مستخدِمين، فحاول تقديمه لمتابعيك من نطاق domain مختلف عن نطاق موقعك. تهيئة توجيهات سياسة أمن المحتوى CSP تؤمن سياسة أمن المحتوى مجموعةً من ترويسات بروتوكول HTTP وهي بيانات وصفية تُرسل مع صفحات ويب عندما يعيدها الخادم مصممة لتحسين مستوى أمان صفحات HTML؛ أما فيما يتعلق باستخدام العنصر <iframe>، فبإمكانك تهيئة الخادم ليعيد ترويسة تضم خيار ملائم للتوجيه X-Frame-Options، إذ سيمنع ذلك مواقع الويب الأخرى من إدراج المحتوى الخاص بك في صفحاتها والذي قد يمكِّن هجمات الاختطاف بالنقر وغيرها من الهجمات، وهذا ما فعله تمامًا مطورو MDN كما رأينا سابقًا. العنصران object و embed لهذين العنصرين وظيفتين مختلفتين عن العنصر <iframe>، فهما أداتان عامتان لإدراج محتوى خارجي مثل ملفات PDF، ومع ذلك لن تستخدِم هذين العنصرين كثيرًا، إذ يستحسن إذا أردت مثلًا عرض ملف PDF أن تربطه بصفحتك بدلًا من إدراجه. استُخدِم هذان العنصران تاريخيًا لإدراج محتوى تتعامل معه إضافات المتصفح plug-ins مثل أدوبي فلاش، لكنها تقنية في طريقها إلى الزوال حاليًا ولا تدعمها المتصفحات الحديثة، لكن إذا رأيت نفسك مضطرًا إلى إدراج إضافة، فإليك الحد الأدنى من المعلومات اللازمة لإنجاز الأمر: table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } <embed> <object> عنوان URL للمحتوى الذي ستدرجه src data النوع الدقيق لملف الوسائط الذي تدرجه type type طول وعرض بكسلات CSS الصندوق الذي تتحكم به الإضافة height ، width height ، width الأسماء والقيم التي تستخدمها الإضافة على أساس معامِلات استخدم عند الحاجة سمات بهذه الأسماء أو القيم استخدم عنصر <parm> مفرد ضمن العنصر <object> محتوى HTML مستقل على أساس خطة بديلة عند عدم توافر المورد غير مدعوم (العنصر <noembed> ملغي) يمكن إضافتها داخل العنصر <object> بعد <parm> لنلق نظرةً على مثال يستخدِم العنصر <object> لإدراج ملف PDF في صفحة ويب، كما يمكنك أيضًا تجريب المثال مباشرة والاطلاع على الشيفرة المصدرية: <object data="mypdf.pdf" type="application/pdf" width="800" height="1200"> <p>You don't have a PDF plugin, but you can <a href="mypdf.pdf">download the PDF file. </a> </p> </object> تُعَدّ ملفات PDF خطوةً انتقاليةً هامةً بين الورقيات والعالم الرقمي، لكنها تعرض الكثير من التحديات المتعلقة بسهولة الوصول، وقد يعاني البعض من قراءتها ضمن شاشات صغيرة، ومازالت هذه الملفات شعبيةً ضمن بعض حلقات المستخدِمين، مع ذلك، يفضَّل ربطها بصفحاتك ليتمكن هؤلاء من تنزيلها أو قراءتها في صفحة مستقلة بدلًا من إدراجها في صفحتك. خلاصة حاولنا في مقالنا هذا تقديم مفهوم إدراج محتوى في صفحاتك بطريقة مبسطة، لأنّ موضوعًا مثل هذا قد يتعقّد بسرعة إذا خضنا فيه بطريقة غير ملائمة، ولهذا اعتمدنا أسلوبًا مألوفًا يبدو متعلقًا مباشرةً بمضمون سلسلة المقالات، ومع ذلك يحاول شرح بعض الميزات المتقدمة المتعلقة بالتقنيات المستخدَمة، كما ننصحك في البداية بعدم إدراج محتوى خارج نطاق المحتوى الذي يقدمه طرف ثالث مثل الفيديوهات والخرائط، وسرعان ما ستبدأ البحث عن استخدامات أخرى لعناصر الإدراج عندما تصبح أكثر تمرّسًا. ستجد الكثير من التقنيات المتعلقة بإدراج محتوى خارجي بالإضافة إلى ما ناقشناه، فقد رأينا بعضها في المقالات السابقة مثل العناصر <video> و <audio> و <img>، كما ستجد غيرها الكثير مثل <canvas> الذي يُستخدَم مع جافاسكربت لتوليد رسوميات ثنائية وثلاثية الأبعاد والعنصر <SVG> الذي يستخدَم في إدراج الرسوميات الشعاعية، وهذا هو موضوعنا في المقال القادم. لمزيد من التفاصيل حول عناصر HTML، ارجع إلى توثيق لغة HTML العربي في موسوعة حسوب. ترجمة -وبتصرُّف- للمقال From object to iframe - other embedding technologies. اقرأ أيضًا مكونات الويب: عناصر HTML المخصصة وقوالبها إضافة محتوى سمعي ومرئي في صفحة HTML إضافة الصور في صفحة HTML
  12. سنتحدث في هذا المقال عن مفهوم جهة انسياب المحارف Directionality عند الكتابة، وهو أمر على درجة كبيرة من الأهمية لمؤلفي المقالات والكتب التي تتطلب استخدام لغتين أو إدخال شيفرات برمجية أو عبارات رياضية مخصصة؛ لكننا سنوجّه هذا المقال لمستخدمي تطبيق مستندات جوجل لأنه لا يقدم دعمًا واضحًا لهذه النقاط على غرار بقية المحررات النصية المتقدمة مثل مايكروسوفت وورد الذي لن يلاحظ مستخدموه الكثير من المشاكل التي نستعرضها في مقالنا. قد تبدو المفاهيم مربكةً للوهلة الأولى، لكن ما إن تألفها حتى تسهّل عليك الأمور وتوفر وقتك وجهدك في تصويب أخطاء قد لا تدرك وجودها إلّا بعد فوات الآوان. مفهوم جهة انسياب المحارف يشير مصطلح "جهة انسياب المحارف" إلى الاتجاه الذي تتحرك وفقه محارف النص من حرف إلى آخر. ولأن معظم قراء هذه المقالة من المؤلفين الذين يكتبون باللغة العربية مع لغة لاتينية أو أكثر، فسنلقي الضوء على ثلاث اتجاهات لانسياب الأحرف هي: يمين-إلى-يسار Right-to-Left واختصارًا RTL: من أبرز الأمثلة عليها اللغة العربية. يسار-إلى-يمين Left-to-Right واختصارًا LTR: ومن أبرز الأمثلة عليها اللغات اللاتينية مثل الإنجليزية. محايدة Neutral: لا تتعلق بجهة محددة بل تتبع سياق الأحرف مثل علامات الترقيم. جهة انسياب المحارف للمحارف جهة انسياب أصيلة لا يمكن أن تتغير تلقائيًا، فمحارف اللغة العربية مثلًا هي محارف من اليمين إلى اليسار حصرًا، ويأتي كل حرف على يسار الآخر، بينما تتجه محارف اللغات اللاتينية دائمًا من اليسار إلى اليمين، في حين يأتي كل حرف على يمين الآخر؛ أما المحارف المحايدة، فتأخذ اتجاه المحارف المحيطة بها، فإن كانت محاطةً بمحرفين لهما نفس جهة الانسياب، ستأخذ جهة انسيابهما، بينما إن كانا من جهتي انسياب مختلفتين، فستأخذ جهة انسياب الفقرة. جهة انسياب الفقرات الفقرة هي كتلة متواصلة من المحارف تنتقل وفق اتجاه محدد وتنتهي بمحرف السطر الجديد. تدعم محررات النصوص ثنائية الاتجاه، مثل مستندات جوجل الكتابة من اليمين إلى اليسار وبالعكس، لاحظ لقطة الشاشة التالية: لا تمتلك الفقرات جهة انسياب مخصصة لها، بل يفرض عليها محرر النصوص اتجاهًا واحدًا، فإما أن تكون من اليمين إلى اليسار RTL، أو من اليسار إلى اليمين LTR، ولا يمكن أن تكون محايدة الاتجاه. فلو فرضنا وجود العبارة التالية "العربية English فارسى"، فإنها ستُعرض بنفس الترتيب (ابتداءً من الهامش نحو الداخل)، سواءً كان الاتجاه RTL أو LTR، وذلك لأن كلمتي "العربية" و"فارسى" لهما محارف ذات اتجاه أصلي RTL، أما "English" فاتجاهها الأصلي هو LTR، وطالما لا وجود لاتجاه موحد لكلمات الفقرة، فستخضع العبارة إلى تنسيق الفقرة. حل مشاكل انسيابية الأحرف في مستندات جوجل أدخلت إلى محارف Unicode مجموعة محارف لا تُطبع على الشاشة وتستخدم للتحكم بانسيابية الأحرف، حيث تُستخدم هذه المحارف بفعالية لحل مشكلة التغير الخاطئ لاتجاه الأحرف عند تبديل لغة الكتابة من يمينية إلى يسارية والعكس. سنتعرف بداية على هذه المحارف، وننتقل بعد ذلك إلى كيفية استخدامها في تطبيق مستندات جوجل لحل بعض مشاكل انسيابية الأحرف. محارف التحكم باتجاه انسياب المحارف المحرف LRM: يقرأ بالشكل (علامة الانسياب نحو اليمين)، ويستخدم لضبط اتجاه نص ينساب نحو اليمين ضمن فقرة تنساب إلى اليسار. تظهر المشكلة عندما تنتهي الجملة التي تنساب إلى اليمين بمحرف محايد كإشارة التعجب. في هذه الحالة إن لم يأتي بعده محرف LTR أي محرف يعزز الاتجاه نحو اليمين (حرف لاتيني مثلًا)، فسيأخذ المحرف المحايد تنسيق الفقرة ويكون RTL، وينكسر الانسياب نحو اليمين ويعود للانسياب نحو اليسار. تُعطى شيفرة Unicode للمحرف بالشكل "U+200E". المحرف RLM: وله عمل معاكس للمحرف السابق، إذ يعطي علامةً لمجرى الكتابة بالتحول نحو اليسار، وتُعطى شيفرة Unicode للمحرف بالشكل"U+200F". المحرفين RLE و LRE: يقرآن بالشكل (إدراج كتلة تنساب إلى اليمين LRE أو اليسار RLE)، وتستخدمان كما يُستخدم المحرفان السابقان، إلا أنهما يستعملان عادةً في المحررات التي لا يوجد فيها ضبط لاتجاه الفقرات، وينتهي تأثير هذا المحرف عند انتهاء الفقرة أو ظهور المحرف المعاكس أو المحرف PDF (اختصار للمصطلح Pop Directional formatting أنهِ تنسيق الانسياب) الذي ينهي عمل أي من المحرفين السابقين. استخدام محارف التحكم باتجاه الكتابة في تطبيق مستندات جوجل لإدراج محارف التحكم في مستندات جوجل، نفذ الخطوات التالية: من "إدراج" انتقل إلى "الرموز الخاصة" لتظهر لك النافذة التالية: في مربع البحث اكتب العبارة "right-to-left" أو "left-to-right" لإيجاد هذه المحارف وستكون نتيجة البحث كالتالي: انقر على الرمز الذي تشاء استخدامه وسيدرج تمامًا حيث يكون مؤشر الكتابة. تمرين تطبيقي (1): حوّل العبارة المكتوبة بالشكل "مرحبًا Hello!" إلى شكلها الصحيح "مرحبًا !Hello". الحل: أدرج الرمز LRM بعد كتابة إشارة التعجب لتخبر مجرى الكتابة أن يعامل المحرف المحايد "!" وكأنه محرف LTR. تمرين تطبيقي (2): حول العبارة ( means careful "!انتبه" The Arabic word) إلى شكلها الصحيح ("means careful "انتبه!" The Arabic word). الحل: أدرج الرمز RLM بعد كتابة إشارة التعجب لتخبر مجرى الكتابة أن يعامل المحرف المحايد "!" وكأنه محرف RTL. تمرين تطبيقي (3): يريد مبرمج إدراج تعليق ضمن شيفرته يشرح باللغة العربية نوع المتغيّر الذي سيعرّفه كالتالي: // التصريح عن المتغيّر Counter على أنه من النوع int int Counter =0; طبعًا كتابة هذا الشرح غير كافٍ، لذا فهلّا ساعدته في حل المشكلة من خلال محارف توجيه الانسياب؟ الحل: انتقل بكل بساطة إلى اليمين واختر اللغة العربية، وسيكون اتجاه الفقرة عندها من اليمين إلى اليسار. اكتب الجملة السابقة تمامًا كما هي وأنهها بالمحرفين (//) كالتالي: "التصريح عن المتغير counter على أنه من النوع int //". افتح بعذ ذلك لوحة المحارف الخاصة وأدرج المحرف RLE في بداية الجملة. لن تلاحظ في البداية أي شيء، لكن بمجرد تغيير اتجاه الفقرة لتصبح من اليسار إلى اليمين ستجد أنك حللت المشكلة! تمرين تطبيقي (4): واجهت شخصيًا عند ترجمة أحد الكتب التي تتحدث عن استخدام التعابير النمطية في جافاسكربت (وهي سلسلة من الرموز تساعدك في البحث عما تريده في نص أو صفحة) أوقاتًا عصيبةً جدًا في كتابة هذه السلاسل ضمن جمل اللغة العربية، علمًا أنني استخدمت تطبيق مستندات جوجل. كان ذلك قبل أن أتعرف على محارف التحكم بالانسياب، لهذا أطلب مساعدتكم في كتابة الجملة الواردة في الصورة التالية: الحل: اكتب الجملة العربية أولًا بالطريقة الاعتيادية، ثم ضع النقطتين المتعامدتين وأدرج بعدها المحرف LRM وستلاحظ أنك ستكتب سلسلة الرموز بعد ذلك بالشكل الصحيح! تهيئة اختصارات سريعة لمحارف جهة الإنسياب في تطبيق مستندات جوجل ستساعدك تلك المحارف كثيرًا، وأشجعك على استخدامها كي لا يضيع وقتك سدى في إيجاد طريقة لترتيب المحارف المختلطة، والتي قد لا تجدها أبدًا! لكن من الواضح أنّ عملية الانتقال إلى نافذة "المحارف الخاصة" ثم البحث عن محارف التحكم باتجاه انسياب المحارف ثم إدراجها في كل مرة أمر أشد إزعاجًا كما يرى البعض. فما الحل إذًا؟ يقدِّم تطبيق مستند جوجل طريقةً لاستبدال سلسلة من المحارف عندما ترد تباعًا بمحرف آخر، إذ يستبدل التطبيق مجموعة المحارف (c) مثلًا بالرمز ©. تُسهّل هذه الميزة حل مشكلتنا كثيرًا، إذ سنخص لكل محرف من محارف الانسياب السابقة سلسلةً من المحارف التي يستبدلها تطبيق مستندات جوجل عند ورودها بمحرف التحكم المطلوب، ونكون قد أنجزنا الأمر. المشكلة أنه لا توجد في تطبيق مستندات جوجل طريقة لنسخ هذه المفردات لذلك سنحتال على الأمر قليلًا! إليك قائمةٌ برموز Unicode الخاصة بكل محرف من محارف التحكم السابقة: table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } المحرف رمز Unocode RLM U+200F LRM U+200E RLE U+202B LRE U+202A اتبع الآن الخطوات التالية: انتقل إلى موقع unicode-explorer الذي يمكنك من نسخ تلك المحارف إن كنت تعرف رمزها. كل ما عليك فعله أن تكتب العنوان التالي "/https://unicode-explorer.com/c" وتضيف رمز المحرف في نهايته (بلا +U)، أي عندما تنتقل إلى العنوان "https://unicode-explorer.com/c/202f" ستصل إلى صفحة المحرف RLM، وعندها انقر على الزر "Copy" لنسخ محرف التحكم. بإمكانك أيضًا استخدام الجدول المعروض في لقطة الشاشة هذه لنسخ محارف التحكم، وكل ما عليك فعله هو أن تنقر الزر "Select"، ثم الضغط على الاختصار Ctrl+C لنسخه: عد إلى تطبيق مستندات جوجل وانتقل إلى "أدوات"، ثم "إعدادات مفضلة" لتظهر لك نافذة الإعدادات. انقر بعد ذلك على النافذة الفرعية "بدائل"  .   سأستبدل المحرفين << عند كتابتهما تلقائيًا بمحرف التحكم RLM، لذلك سأكتب في نافذة "البدائل" الرمز << في المربع "استبدال"، ثم ألصق رمز المحرف RLM الذي نسخته سابقًا في المربع "بـ". عند نجاح الأمر، سيظهر سطر جديد في نافذة "البدائل" يضم الاختصار الذي أنشأته. كرر العملية باختيار الرموز التي تناسبك لكي تستبدل بقية محارف التحكم بانسياب الأحرف. وأقترح أن تستخدم >> للمحرف LRM و]] للمحرف RLE و[[ للمحرف LRE، كما يمكن استخدام الرمز LRM مثلًا للمحرف RLM وهكذا. بهذه الشكل ستكون قادرًا على إدراج محرف التحكم المطلوب بمجرد كتابة سلسلة الرموز التي اخترتها، إذ سيستبدل التطبيق هذه السلسلة بمحرف التحكم تلقائيًا، مما يوفر عليك الوقت والجهد. خلاصة تحدثنا في هذا المقال عن مفهوم جهة انسياب المحارف ورأينا ما يلي: بعض محررات النصوص مثل مستندات جوجل قادر على التعامل إلى حد ما مع الاتجاهات الثنائية يمين إلى يسار والعكس. لبعض المحارف اتجاه أصلي وبعضها الآخر محايد يأخذ اتجاهه من اتجاه المحارف التي تحيط به، مثل علامات الترقيم. تُظهر العناصر المحايدة بعض المشاكل إن لم تتمكن من تحديد اتجاه انسياب النص وتتبع اتجاه انسياب الفقرة. تُستخدم عناصر التحكم باتجاه انسياب المحارف RLM و LRM و LRE و RLE في توجيه النصوص مختلفة الاتجاهات إن كتبت في نفس الفقرة، لكن بطريقة غير صحيحة، وهي محارف غير مطبوعة ولا تظهر على الشاشة. يمكن إدراج هذه العناصر في مستندات جوجل من نافذة "العناصر الخاصة" أو يمكن تخصيص سلسلة محارف خاصة تُستبدل عند ورودها تباعًا بمحرف التحكم من خلال نافذة "بدائل" ضمن نافذة "الإعدادات المفضلة". اقرأ أيضًا مقدمة إلى تطبيق مستندات جوجل Google Docs قائمة الأدوات في مستندات جوجل المشاركة وتوزيع المهام على مستندات جوجل تنسيق وتوثيق الأوراق الأكاديمية باستخدام مستندات جوجل
  13. سنناقش في هذا المقال الموجه خصيصًا للطلاب والباحثين، كيفية استخدام تطبيق مستندات جوجل لكتابة أوراق بحثية وأكاديمية خطوةً خطوة. ربما تمتلك فكرةً عن معظم الأفكار والأدوات التي سنستخدمها في مقالنا، لكننا سنعتمد معايير التنسيق المستخدمة عالميًا في كتابة الأوراق، مثل تنسيق "جمعية اللغات المعاصرة Modern Language Association" واختصارًا MLA وتنسيق "جمعية علم النفس الأمريكية American Psychological Assossiation" واختصارًا APA. هنالك الكثير من التنسيقات الأخرى لكتابة المقالات والأوراق البحثية، لكننا سنكتفي بهذين التنسيقين لأنهما الأكثر شيوعًا من جهة، ولأنهما مدعومان من قِبل مستندات جوجل من جهة أخرى. إنشاء مستند جديد سنفترض في هذه الفقرة أنك تمتلك حسابًا على جوجل، وتألف طريقة الوصول إلى تطبيق مستندات جوجل من خلال جوجل درايف Google Drive أو موقع مستندات جوجل مباشرةً. من خلال موقع مستندات غوغل: النقر على "أيقونة (+)" يمين أسفل الصفحة. بكتابة العبارة "docs.new" في شريط عنوان المتصفح الذي تستخدمه، ثم الضغط على المفتاح Enter. من خلال جوجل درايف: انقر على قائمة "جديد" على يمين الشاشة، ثم النقر على خيار "مستندات Google". التنسيق العام تُطبق مجموعة من التنسيقات العامة المشتركة بين MLA و APA، وهي كالتالي: خط الكتابة وحجمه تفضل جميع التنسيقات السابقة أن يكون الخط مقروءًا دون مبالغة، وأن تكون عائلة الخط المستخدم مألوفةً للقارئ ومريحةً دون وجود ديكورات خاصة، ولهذا وجدتُ أن جميع تلك التنسيقات تدعم الخط "Times New Roman" أو "Arial" على أن يكون حجمه 12. ولتنفيذ الأمر: انقر على القائمة المنسدلة "الخط" في شريط الأدوات أعلى الصفحة، ثم تنقّل بين الخطوط حتى تصل إلى "Times New Roman". انقر على القائمة المنسدلة "حجم الخط" إلى جوار القائمة السابقة، واختر حجم الخط "12". تحديد هوامش الصفحة وقياسها يحدّد التنسيقان السابقان عرض جميع الهوامش (أعلى وأسفل ويمين ويسار) لتكون 1 إنش (2.54 ستنيمتر). عادةً ما يضبط تطبيق مستندات جوجل عرض الهوامش على 1 إنش، لكن لا بد من التأكد من ذلك: انقر على القائمة "ملف"، ثم انتقل إلى إعداد الصفحة. تحقق في نافذة إعداد الصفحة أن جميع الهوامش هي 2.54 سم أو 1 إنش. تفضّل التنسيقات السابقة أن يكون قياس الصفحة من النوع "رسالة 11x8.5 إنش"، حيث يطابق هذا القياس في مستندات جوجل الخيار "21.6x27.9 Lettre سم" ضمن القائمة المنسدلة "مقاس الورق" في نفس النافذة. يمكنك بالطبع اختيار القياس الذي يريده المشرف على الورقة البحثية أو الجهة المسؤولة عن طباعتها. تحديد التباعد بين الأسطر وإزالة الفراغات في بداية الفقرات ينبغي ضبط التباعد بين الأسطر ليكون بمقدار "2" أو ما يسمى بالفراغ المضاعف double-spaced. بين جميع الأسطر والعناوين الرئيسية والفرعية والفقرات والترويسة وقائمة المراجع، أي باختصار، في كل مكان ضمن الورقة البحثية. وقد تجد إغراءً في ترك المزيد من الأسطر الفارغة بين العناوين الرئيسية وباقي الفقرات، لكن هذا الأمر مرفوض في جميع التنسيقات. لضبط التباعد في مستندات جوجل تحتاج إلى فعل الآتي: انقر على قائمة "تنسيق" في شريط الأدوات. انتقل إلى القائمة الفرعية "تباعد الأسطر والفقرات"، ثم اختر "مزدوج". تأكد من إزالة أية فراغات قد تكون قبل الفقرة، وذلك من خلال الخيار "إضافة مسافة قبل الفقرة" الذي سيكون بالصيغة "إزالة مسافة قبل الفقرة" إن كانت هناك مسافة فارغة. إزاحة الفقرات يتطلب التنسيقان السابقان إزاحة أولى كلمات الفقرة النصية بمقدار نصف إنش أو 1.27 سنتيمتر عن الهامش الأيمن أو الأيسر، وذلك وفقًا لجهة كتابة الورقة البحثية (يمين إلى يسار) أو (يسار إلى يمين). ولتطبيق الإزاحة في مستندات جوجل نحتاج إلى الآتي: إن لم يكن شريط قياس المسافات أعلى الصفحة موجودً، فأظهره عن طريق النقر على القائمة المنسدلة "عرض"، ثم الانتقال إلى خيار "عرض أداة قياس المسافات". حرك مزلاج أداة القياس نحو اليسار أو اليمين (حسب اتجاه الكتابة) مقدار 1.27 سنتيمتر. إن لم تتمكن من ضبط هذه القيمة عن طريق المزلاج، يكفي أن تضع مؤشر الفأرة في بداية السطر الأول للفقرة، ثم تضغط المفتاح Tab. تنسيق MLA إليك الآن النقاط الخاصة بتنسيق MLA الخاص بكتابة الأبحاث. عنوان الورقة وصفحة العنوان يضم عنوان الورقة وفق تنسيق MLA الرسمي المعلومات التالية: اسم المؤلف: ويكتب بداية اسم الأستاذ المشرف. اسم ورقم المادة أو الصف. تاريخ وجوب التسليم. لتنفيذ ذلك في مستندات جوجل: انقر على أول سطر في الصفحة المنسقة تنسيقًا عامًا كما أشرنا سابقًا، واحرص أن يكون مؤشر الكتابة عند الهامش الأيمن أو الأيسر وفقًا للغة الكتابة. اكتب اسم المؤلف (اسمك كاملًا إن كان البحث لك، ويفضل كتابة الاسم الأول يليه فراغ، ثم أول حرف من الاسم الأوسط، ثم نقطة، ثم الكنية). انتقل إلى السطر الثاني، ثم اكتب اسم المشرف العلمي. انتقل إلى السطر الثالث واكتب اسم المادة أو الصف ورقمه إن كان متاحًا. انتقل إلى السطر الرابع واكتب تاريخ وجوب التسليم على شكل "يوم (رقم)، ثم فراغ، ثم الشهر (كتابة)، ثم العام (رقم)". احرص أن يكون التباعد مزدوجًا بين الأسطر السابقة وسطر العنوان انتقل إلى السطر التالي لكتابة عنوان الورقة وانقر على زر "محاذاة" واختر "محاذاة للوسط"، أو اضغط المفاتيح Ctrl+Shift+E. اكتب العنوان بالخط العادي قياس 12 دون تسميك أو إمالة أو علامات اقتباس إلا إن احتوى عنوانك على عنوان آخر، واحرص أن يكون الحرف الأول من الكلمة مكتوبًا بشكله الكبير ما عدا أحرف الجر وأحرف الوصل، وذلك إن كانت الأطروحة باللغة الإنجليزية. الترويسة المكررة وأرقام الصفحات ينبغي أن تضم الترويسة وفق هذا التنسيق الاسم الأخير للمؤلف، يليه رقم الصفحة عند الهامش الأيسر تمامًا في الأبحاث المكتوبة بالعربية، وعند الهامش الأيمن في الأبحاث المكتوبة بالإنكليزية. واحرص أن يكون حجم الخط من النوع "Times New Roman" أو "Arial". لتنفيذ ذلك باستخدام تطبيق مستندات جوجل: انقر على القائمة المنسدلة "إدراج"، ثم أرقام الصفحات، ثم إدراج خيار الترقيم المتزايد ابتداءً من صفحة العنوان ذات الرقم "1". بمجرد النقر على الخيار، ستظهر لك ترويسة الصفحة وقد أدرج رقم الصفحة تلقائيًا بجوار الهامش. انقر خلف رقم الصفحة ثم ضع فراغًا، وانقر الزر "تعيين اتجاه النص من اليسار إلى اليمين" في شريط الأدوات لتتمكن من كتابة اسمك قبل رقم الصفحة. انقر في أي مكان آخر من الصفحة لإغلاق الترويسة. ملخص البحث والفقرات يلي عنوان البحث مباشرةً: انتقل إلى السطر التالي بعد العنوان. اختر "محاذاة للوسط" واكتب عبارة "ملخص" على أن تكون بخط سميك. اضبط المحاذاة إلى اليمين مجددًا، ثم اكتب الملخص وفق معايير كتابة الفقرات، مع الحرص على إزاحة الجملة الأولى. افصل الملخص وصفحة العنوان عن بقية البحث بإضافة فاصل صفحات بالنقر على القائمة "إدراج" واختيار "فاصل"، ثم اختيار "فاصل صفحة". ويمكنك أيضًا الضغط على المفاتيح Ctrl+E. يوضح الشكل التالي طريقة تنسيق ملخص البحث، ثم الطريقتين الأكثر اتباعًا في تحديد مستويات العناوين في MLA، لذلك لا ننصحك باستخدام تنسيقات مستندات جوجل نفسها. الصور والجداول لإدراج جدول وفق توجيهات MLA: اكتب عنوان الجدول قبل إدراجه على الشكل "الجدول.1" لأول جدول في الورقة ثم تتابع تصاعديًا في الأرقام حتى ترقم جميع الجداول، ولا تنسى التباعد المزدوج مع السطر التالي. اكتب عنوان الجدول أو ما يتحدث عنه الجدول باختصار إن لم تشر إلى ذلك في جسم المقال، ثم أدرج الجدول. لإدراج جدول: اختر "إدراج" ثم "جدول"، بعدها حدد أبعاد الجدول من الشكل التفاعلي الظاهر. اجعل الجدول بالتنسيق الذي تريد. حافظ على التباعد المزدوج بين الجدول والسطر الذي يليه. حدد مصدر الجدول وفق تنسيق MLA إن كان مأخوذًا من مصدر معين بعد الجدول مباشرةً. إن أردت إدراج أية ملاحظة عن معلومة معينة وردت في وصف الجدول، فانتقل إلى النقطة التي تريد التنويه إلى الملاحظة فيها، ثم أشر إلى وجود الملاحظة بوضع حروف بدلًا من الأرقام على شكل نص مرتفع، كما في لقطة الشاشة التالية: أما لإدراج صورة، فهنالك أسلوبان وفق MLA: الأسلوب الأول: التقديم عبر النص. التقديم للصورة ثم إنهائه بعبارة (شكل.1) أو ((fig.1) في الإنكليزية) وزيادة هذا الرقم تصاعديًا ابتداءً من أول صورة تدرجها. إدراج الصورة في أقرب مناسبة للتقديم السابق، كالتالي باختيار القائمة "إدراج" ثم "صورة" ثم اختر موقع وجود الصورة. أضف مصدر الصورة وفق MLA مباشرةً بعد الصورة، مسبوقًا بالعبارة "شكل.1." ثم المصدر مع المحافظة على التباعد المزدوج بين الصورة والمصدر. الأسلوب الثاني: الشرح مع المصدر. أدرج الصورة. اكتب بعدها مباشرةً عبارة "شكل.1."، يليه وصف الصورة بين علامتي اقتباس مزدوجتين، يليه نقطة، ثم مصدر الصورة. انظر إلى لقطة الشاشة التالية: إدراج الاقتباسات الموثقة وهي العملية الأكثر أهميةً في إعداد الأوراق البحثية، وتشير إلى الأبحاث الأخرى والمراجع التي اقتُبست منها بعض المعلومات حرفيًا أو أشير إليها. يقدم تطبيق مستندات جوجل طريقةً سريعةً وفعّالةً في إدخال معلومات التوثيق citations سواءً يدويًا، أو بالبحث عنها عبر الإنترنت وإيجاد التفاصيل المطلوبة عن مصدر التوثيق، ثم تنسيقه بإحدى طرق التنسيق. لإدراج اقتباس موثّق وفق MLA الإصدار 8 عبر مستندات جوجل، اتبع الخطوات التالية: من القائمة "أدوات" اختر "الاقتباسات الموثَّقة" لتظهر لك النافذة الجانبية "الاقتباسات الموثَّقة" إلى يسار الصفحة. اختر من القائمة المنسدلة أعلى النافذة نوع التنسيق "MLA (الإصدار رقم 8)". انقر على رابط "إضافة مصدر التوثيق" لتظهر لك صفحة جديدة في هذه النافذة. اختر نوع المصدر من القائمة المنسدلة التي تحمي الاسم "نوع المصدر"، سواءً كتاب أم مقال في مجلة علمية أو أي مصدر تريد إدراجه، وتختلف الخطوات التالية وفقًا للمصدر الذي اخترته: في حال كتاب: بإمكانك التوثيق يدويًا من خلال النقر على رابط التوثيق يديويًا في الأسفل إن كنت تعلم جميع تفاصيل ما تريد توثيقه، أو يمكنك لتوفير الوقت البحث عن معلومات الكتاب من خلال رقم ISBN الخاص به. وسواءٌ اخترت التوثيق اليدوي أو الآلي عبر ISBN، ستتوسع النافذة لتعرض تفاصيل الكتاب التي عليك ملؤها، كما قد تلاحظ أن حقولًا كثيرة ملئت مسبقًا إن استخدمت البحث التلقائي. تذكر أنه عليك نقر زر "متابعة" أسفل النافذة عند انتهاء البحث الآلي أو "التوثيق يدويًا" إن لم تكن المعلومات كافية، كما ينبغي أن نلفت انتباهك إلى بعض الحقول التي لا يزال عليك ملؤها حتى لو استخدمت البحث الآلي مثل أرقام الصفحات ورقم العدد ورقم المجلد. في حال مقال علمي أو صحفي: عليك هنا ملء كل الحقول بنفسك، خاصةً تلك التي تحمل علامة (*) التي يتوجب عليك ملؤها. في حال موقع إلكتروني: يمكنك اختيار "التوثيق يدويًا" أو كتابة عنوان الموقع لكي يبحث لك التطبيق عن المعلومات المتوفرة عنه. عند الإنتهاء من إضافة جميع المعلومات، انقر على زر "إضافة مصدر الاقتباس". يمثل الشكل التالي مراحل إعداد اقتباس موثق باستخدام مستندات جوجل. لإضافة التوثيق الذي أعددته وفق تنسيق MLA إلى مستندك اتبع الخطوات التالية: انقر في نهاية الجملة التي تريد توثيقها. اختر من نافذة "الاقتباسات الموثّقة" المصدر الصحيح لهذه الجملة، ثم اختر الأمر "اقتباس" الذي يظهر بمجرد تمرير مؤشر الفأرة فوق المصدر. تظهر علامة الاقتباس وفق تنسيق MLA إلى جوار الجملة المطلوبة، وكل ما عليك الآن هو استبدال المحرف (#) في علامة الاقتباس برقم الصفحة التي أخذ منها الاقتباس أو إهمالها إن لم تكن ضرورية. يمكنك أيضًا تعديل أو حذف الاقتباس بالنقر على زر النقاط الثلاث المتعامدة بجوار "اقتباس" واختيار الأمر المطلوب. عليك أخيرًا إضافة قائمة كاملة بجميع المراجع التي وثّقتها في عملك في آخر الورقة العلمية ووفق تنسيق MLA، لذلك عندما تفرغ من كتابة نص الورقة ويحين وقت كتابة المراجع نفّذ العملية كما يلي: إدراج فاصل صفحات بالنقر على "إدراج" ثم "فاصل" ثم "فاصل صفحة" انقر في السطر الأول للصفحة الجديدة ثم انتقل إلى نافذة "الاقتباسات الموثَّقة" وانقر على زر "إدراج الأعمال الموثّقة" لتحصل على المطلوب. تنسيق APA إليك الآن النقاط الخاصة بتنسيق APA الخاص بكتابة الأبحاث. عنوان الورقة وصفحة العنوان تضم صفحة العنوان وفق تنسيق APA الرسمي المعلومات التالية وفق نفس الترتيب: عنوان المقال: في وسط الصفحة مصاغًا بأبسط وأوضح طريقة ممكنة دون أي حشو أو اختصارات على بعد 3 إلى 4 أسطر عن قمة الصفحة. يكتب بخط سميك وبنفس حجم خط باقي المقاطع مع الانتباه إلى كتابة أحرف أوائل الكلمات بأحرف كبيرة (للغة الإنجليزية) أسماء المؤلفين: في وسط الصفحة تحت العنوان مباشرة وتخضع لقاعدة التباعد المزدوج، تُكتب بالخط العادي دون تثخين وعلى التتالي إن كان هناك أكثر من مؤلف. الجامعة والقسم: كما هو حال تنسيق أسماء المؤلفين. المشرف العلمي: كما هو حال تنسيق المؤلفين. تاريخ وجوب التسليم: كما هو حال تنسيق المؤلفين ويكتب وفق أسلوب (شهر يوم سنة). راجع تنسيق MLA لمعرفة طريقة تطبيق التنسيقات السابقة باستخدام تطبيق مستندات جوجل. الترويسة المكررة وأرقام الصفحات ينبغي أن تضم الترويسة وفق هذا التنسيق عنوان البحث على أن لا يزيد عن 50 محرفًا، وإن كان أطول، فلا بد من اقتراح نسخة قصيرة منه. يأتي رقم الصفحة عند الهامش الأيسر تمامًا في الأبحاث المكتوبة بالعربية، وعند الهامش الأيمن في الأبحاث المكتوبة بالإنجليزية؛ بينما يأتي العنوان ملاصقًا للهامش المقابل. احرص هنا على أن يكون العنوان بحجم الخط من النوع "Times New Roman" أو "Arial" وأن يكون مكتوبًا بأحرف كبيرة. ولتنفيذ ذلك باستخدام تطبيق مستندات جوجل، راجع ما فعلنا سابقًا عند تنسيق ترويسة MLA. ملخص البحث والفقرات يأتي الملخص في تنسيق APA في صفحة مستقلة تلي صفحة العنوان، وتبدأ بالكلمة "ملخص" بخط سميك وبنفس نوع وحجم خط بقية الفقرات. يأتي بعد ذلك نص الملخص دون إزاحة لأول سطر أو لأي سطر آخر، وينبغي المحافظة على قاعدة التباعد المزدوج. في نهاية الملخص وفي نفس الصفحة، انتقل إلى سطر جديد وطبّق إزاحةً مقدارها 1.27 سم، ثم اكتب عبارة "كلمات مفتاحية" بخط مائل، تليها نقطتان متعامدتان ثم فراغ، واسرد بعدها الكلمات المفتاحية في مقالك تفصل بينها الفاصلة ",". ولتنفيذ ذلك باستخدام تطبيق مستندات جوجل، راجع ما فعلنا سابقًا في تنسيق MLA. أما فيما يخص مستوى العناوين، يوضح الشكل التالي طريقة تنسيقها وفق APA باستخدام مستندات جوجل: الصور والجداول إليك أهم قواعد APA في تنسيق الجداول: ابدأ من الهامش الأيمن واكتب كلمة "الجدول"، يليها فراغ، ثم رقم الجدول الذي يتزايد تصاعديًا بخط سميك. اكتب في السطر التالي عنوان الجدول بخط عادي مائل. أدرج الجدول وأظهر الخطوط الأفقية فقط. ولتنفّذ ذلك في مستندات جوجل: علّم الجدول بأكمله وانقر على المثلث الصغير الذي يظهر في الزاوية العليا اليسارية من الجدول، فتظهر لك قائمة من ثلاثة أسطر. انقر على أول عناصر السطر الثالث، ثم انقر على أيقونة "عرض الحدود" التي قد تكون مختفية ضمن الأيقونة "مزيد". اختر القيمة 0 ليختفي العمود الأيمن. كرر نفس الخطوات، لكن اختر هذه المرة ثاني عناصر السطر الثالث وتابع. كرر نفس الخطوات واختر أخيرًا العنصر الثالث من العمود الثالث وتابع فتحصل على المطلوب. أدرج كلمة "ملاحظة" قبل أي ملاحظة ستكتبها تحت الجدول مباشرةً، واكتبها بخط مائل. بالنسبة للصور اتبع خطوات تنسيق الجدول تمامًا، لكن ينبغي أن تكون الصور في المقالات العلمية باللونين الأبيض والأسود فقط. إدراج الاقتباسات الموثقة اتبع نفس الخطوات التي ذكرناها في تنسيق الاقتباسات الموثَّقة وفق MLA، لكن اختر التنسيق "APA (الإصدار رقم 7)" خلاصة تعلمنا في هذا المقال طريقة كتابة ورقة أكاديمية علمية بطريقة احترافية مستخدمين تطبيق مستندات جوجل وذلك وفق التنسيقان APA و MLA وعرضنا الكثير من لقطات الشاشة التي توضح بالأرقام خطوات تنفيذ بعض العمليات. إليك أهم النقاط التي عملنا عليها: تنسيق صفحة العنوان. تنسيق الملخص. تنسيق الفقرات وترويسات الفقرات. التعامل مع مستويات العناوين. إدراج الصور والجداول. إدراج الاقتباسات الموثَّقة. اقرأ أيضًا مقدمة إلى تطبيق مستندات جوجل Google Docs قائمة الأدوات في مستندات جوجل المشاركة وتوزيع المهام على مستندات جوجل
  14. حان الوقت لإدراج مشغلات الفيديو والصوتيات ضمن صفحات HTML بعد أن امتلكنا بعض الخبرة في إدراج الصور ضمن صفحات الويب، إذ سيشرح مقالنا هذا الأمر باستخدام العنصرين <video> و <audio> ثم سيلقي الضوء في النهاية على طريقة إضافة الشروحات أو الكلام الوارد في هذه المقاطع إلى صفحات ويب. لا بدّ قبل البدء بهذا المقال أن تكون على دراية بأساسيات عمل الحاسوب وطريقة تثبيت البرمجيات الأساسية ودراية بطريقة عمل الملفات، ولا بد أيضًا من الدراية بأساسيات اللغة HTML كما عرضناها في مقال تعرَّف على HTML، وطريقة إضافة الصور في صفحة HTML. مقاطع الصوت والفيديو في الويب أراد مطورو الويب استخدام المقاطع الصوتية والفيديوهات منذ بداية هذا القرن -القرن الحادي والعشرين- مع ازدياد سرعة الاتصال بالإنترنت وزيادة عرض حزم التراسل بشكل كاف لدعم أنواع من مقاطع الفيديو (ملفات الفيديو أكبر حجمًا من النصوص والصور)، ولم تتمكن التقنيات الأساسية للويب مثل HTML في البدايات من إدراج الفيديوهات ومقاطع الصوت في صفحات الويب، لذلك ظهرت تقنيات مبنية على الإضافات plug-in مثل تقنية الفلاش Flash وبعدها سيلفرلايت Silverlight وكلاهما قد خرج من الخدمة الآن وقد تصدَّرتا الواجهة في عرض هذا النوع من المحتوى، كما عملت هذه التقنيات جيدًا لكن المشاكل سرعان ما واجتها مثل عدم توافق عملها مع ميزات HTML و CSS إضافة إلى مشاكل أمنية ومشاكل في سهولة الوصول. كان استخدام الحلول الأصلية هو السبيل الأساسي إذا جرى ذلك بالصورة الصحيحة، ولم يتأخر الأمر كثيرًا مع ظهور مواصفات HTML5 التي أضافت ميزات إدراج الفيديو والصوت من خلال العنصرين <video> و <audio> وبعض الواجهات البرمجية بلغة جافاسكربت للتحكم بهما، ولن نتحدث عن جافاسكربت في المقال وإنما فقط العمليات الأساسية التي يمكن إنجازها باستخدام HTML، كما لن نتعلم في مقالنا كيفية إنتاج ملفات الفيديو والصوت فالأمر يتطلب مجموعة مختلفة من المهارات، ولهذا سنستخدِم مجموعةً من ملفات الوسائط المتعددة وشيفرة تدريبية لتجرب العمل عليها إذا لم تستطع التعامل مع ما لديك. ملاحظة: عليك أن تعرف قبل البدء هنا بوجود العديد من مزودِي خدمة الفيديو على الإنترنت online video providers -أو OVP اختصارًا- مثل اليوتيوب وديلي موشن وفيميو، بالإضافة إلى مزودِي خدمات الصوت مثل ساوند كلاود، إذ تقدِّم لك هذه الشركات طريقةً سهلةً لاستضافة وتشغيل الفيديوهات دون أن تلق بالًا لاستنزاف حجم حزمة التراسل، كما تزوّدك هذه الشركات بشيفرة جاهزة لإدراج مقاطع الفيديو أو الصوت في صفحاتك، فإذا قررت استخدام هذا المسار، فيمكنك الابتعاد عن الكثير من النقاط الصعبة التي نناقشها في هذا المقال، إذ سنناقش هذا النوع من الخدمات لاحقًا. العنصر <video> يتيح لك هذا العنصر إدراج الفيديوهات بكل سهولة، وإليك مثالًا بسيطًا: <video src="rabbit320.webm" controls> <p>Your browser doesn't support HTML5 video. Here is a <a href="rabbit320.webm">link to the video</a> instead.</p> </video> تجدر ملاحظة الميزات التالية: src: سمة مشابهة تمامًا لتلك الموجودة في العنصر <img>، وتضم المسار إلى مقطع الفيديو الذي تريد إدراجه. controls: ينبغي أن يكون المستخدِم قادرًا على التحكم بتشغيل وإيقاف الفيديو، لذلك قد تستخدِم هذه السمة لإضافة الواجهة الخاصة بالمتصفح للتحكم بالفيديو، أو أن تبني الواجهة الخاصة بك باستخدام واجهة جافاسكربت برمجية مناسبة، ولا بدّ من ضم الواجهة آلية لبدء وإيقاف تشغيل المقطع على الأقل، وكذلك ضبط مستوى الصوت. الفقرة النصية داخل العنصر <video>: وُتدعى الخطة البديلة fallback، إذ يُعرَض محتواها إذا لم يدعم المتصفح المستخدَم العنصر <video>، مما يسمح بوجود بديل للمتصفحات القديمة، ولا يوجد أسلوب محدَّد لتنظيم الخطة البديلة، فقد زودنا المستخدِم في حالتنا برابط إلى ملف الفيديو لكي يتمكن من الوصول إليه بصرف النظر عن المتصفح الذي يستخدِمه. سيبدو مقطع الفيديو بعد إدراجه مشابهًا للصورة التالية: بإمكانك أيضًا تجريب الصفحة مباشرةً، وألق نظرةً على الشيفرة المصدرية. استخدام تنسيقات مصدرية مختلفة لتحسين التوافقية ربما قد لاحظت وجود مشكلة في المثال السابق إذا حاولت الدخول إلى الرابط المباشر للفيديو من متصفح قديم مثل إنترنت إكسبلورر أو نسخة قديمة من سفاري، إذ لن يعمل الفيديو لأن المتصفحات المختلفة تدعم مقاطع فيديو ومقاطع صوتية بتنسيقات مختلفة، ومن الممكن لحسن الحظ الالتفاف على المشكلة بطرق عدة. محتوى ملف الوسائط لنلق نظرةً سريعةً أولًا على بعض المصطلحات، إذ تُدعى التنسيقات مثل MP3 و MP4 و WebM بالتنسيقات الحاوية أو المُغلِّفة container formats، كما تعرِّف هذه الحاويات هيكليةً لتخزين مسارات الفيديو أو الصوت بالإضافة إلى البيانات الوصفية التي تشرح هذه الوسائط والمرمزات التي تستخدَم في ترميز الأقنية وما شابه. يضم الملف WebM فيديو مكوَّن من مسار فيديو رئيسي وآخر بزاوية مختلفة بالإضافة إلى مسارات صوتية باللغتين الإنجليزية والإسبانية بالإضافة إلى مسار صوتي تعليقي يمكن تصوّره من خلال المخطط التالي، كما يحتوي الملف أيضًا على مسارات كتابية تضم شروحات عن الفيلم وترجمة باللغة الإسبانية وشروحات باللغة الإنجليزية لمسار التعليقات. تحمل مسارات الفيديو والمسارات الصوتية داخل الحاوية بيانات بالتنسيق الذي يوافق المرمّز المستخدَم في ترميز هذه الوسائط، إذ تستخدَم عدة تنسيقات لتخزين مسارات الصوت لتتماشى مع مسارات الفيديو، كما يرمّز كل مسار صوتي باستخدام مرمِّز صوتي وترمّز مسارات الفيديو باستخدام مرمِّز فيديو، كما تدعم المتصفحات المختلفة تنسيقات فيديو مختلفة وتنسيق حاويات مختلف مثل MP3 و MP4 و WebM والتي تحتوي بدورها على تنسيقات مختلفة للفيديو والصوتيات، فعلى سبيل المثال: تحزِم حاوية WebM تقليديًا مسارات الصوت بتنسيق Vorbis أو Opus مع مسارات فيديو بتنسيق VP8 و VP9، كما تدعم كل المتصفحات الحديثة هذه التنسيقات، لكنها قد لا تعمل مع المتصفحات القديمة. تحزِم حاوية MP4 عادةً مسارات الصوت بتنسيق MP3 و AAC مع مسار فيديو بتنسيق H.246، كما تدعم كل المتصفحات الحديثة هذه التنسيقات كما يدعمها إنترنت إكسبلورر. تميل حاويات Ogg إلى حزم مسارات الصوت بتنسيق Vorbis مع مسار فيديو بتنسيق Theora، كما تدعم هذه الحاوية متصفحات فايرفوكس وكروم، لكن تُستبدل بحاويات WebM ذات الجودة الأعلى. تظهر بعض الحالات الخاصة التي تُخزّن فيها بيانات المرمز الصوتي لبعض المسارات الصوتية دون حاوية أو ضمن حاوية بسيطة. مثل المرمزات FLAC التي تُخزّن البيانات عادةً في ملفات بامتداد FLAC. وهي مجرد مسارات خام لبيانات هذا المرمز. توجد حالة أخرى وهي ملفات MP3 دائمة الشعبية وهي المسار أو الطبقة الصوتية الثالثة لتنسيق MPEG-1 مخزّنةً ضمن حاوية MPEG أو MPEG-2، وتظهر أهمية الأمر عندما نعرف أن معظم المتصفحات لا تدعم ملفات الوسائط بتنسيق MPEG من خلال العنصرين <video> و <audio>، لكنها قد تستمر بدعم MP3 نظرًا لشعبيته. تميل مشغلات الصوت إلى تشغيل المسارات الصوتية مثل MP3 و Ogg مباشرةً دون الحاجة إلى الحاويات في هذه الحالة. دعم ملفات الوسائط المتعددة في المتصفحات ملاحظة: ما الذي خلق مشكلة التوافق؟ الجواب هو وجود تنسيقات شعبية مثل MP3 و MP4/H.246 التي تتمتع بأداء ممتاز لكنها خاضعة لبراءات الاختراع التي تغطي بعضًا من التكنولوجيا التي تستند إليها أو كلها، فقد غطت براءة الاختراع MP3 حتى عام 2017 وستغطِّي H.246 حتى 2027 على الأقل، ونظرًا لوجود براءات الاختراع، فلا بد من دفع المتصفحات ضرائب كبيرة إذا أرادت إضافة دعم لهذه المرمزات، بالإضافة إلى ذلك، يفضِّل الكثيرون تحاشي القيود على البرمجيات واستخدام التنسيقات مفتوحة المصدر، ولهذه الأسباب قد يضطر المطورون إلى دعم تنسيقات مختلفة للوصول إلى جمهور أوسع. وجدت المرمزات التي أشرنا إليها سابقًا لضغط الفيديو والصوتيات ضمن ملفات قابلة للإدارة، نظرًا لكون بيانات الفيديو والبيانات الصوتية الخام كبيرة الحجم، في حين يدعم كل متصفح صنفًا من المرمّزات مثل Vorbis أو H.264 التي تستخدَم في تحويل الفيديوهات والصوتيات المضغوطة إلى بيانات ثنائية وبالعكس، كما يملك كل مرمز إيجابياته وسلبياته وتملك كل حاوية إيجابياتها وسلبياتها أيضًا، مما سيؤثِّر على قرارك في الاختيار. ستغدو الأمور أكثر تعقيدًا نظرًا لدعم المتصفحات لتنسيقات مختلفة من ملفات الحاويات، إضافةً إلى اختلافها في دعم المرمزات، ولكي تزيد من فرصة تشغيل معظم المتصفحات لتطبيقك أو موقعك، فمن الأفضل تقديم ملفات الوسائط المتعددة بتنسيقات عدة، إذ لن يعرض المتصفح ملف الوسائط المتعددة إذا لم يدعم تنسيقه، ولكي تتأكد من أنّ ملفات الوسائط التي تستخدِمها ستعمل عبر أيّ مجموعة من المتصفحات أو المنصات أو الأجهزة التي ترغب في الوصول إليها، فاختر أفضل مجموعة من المرمزات والحاويات، ولكنها مَهمة معقدة بالفعل. ملاحظة: تعرَّف على طريقة اختيار حاوية الوسائط المناسبة بالاطلاع على المقال تنسيقات حاويات الوسائط المتعددة، وطريقة اختيار المرمز الصوتي المناسب في مقال دليلك إلى مرمزات الملفات الصوتية في الويب، وطريقة اختيار مرمز الفيديو المناسب في مقال دليلك إلى مرمزات الفيديو في الويب. لا بد أن تأخذ في حسبانك أنّ متصفحات الأجهزة المحمولة قد تدعم تنسيقات إضافية غير مدعومة من المتصفحات نفسها على الأجهزة المكتبية والعكس صحيح، فقد تصمَّم بعض متصفحات الأجهزة المحمولة والمكتبية لإيقاف تحميل الوسائط المتعددة (لكل الوسائط أو لبعض الأنواع التي لا يمكنها التعامل معها داخليًا)، أي أنّ البرمجيات التي ثبتها قد تؤثر جزئيًا على دعم الوسائط المتعددة. إذًا كيف سننجز الأمر؟ لنلق نظرةً على المثال التالي، ويمكنك تجريبه مباشرةً أيضًا: <video controls> <source src="rabbit320.mp4" type="video/mp4"> <source src="rabbit320.webm" type="video/webm"> <p>Your browser doesn't support HTML5 video. Here is a <a href="rabbit320.mp4">link to the video</a> instead.</p> </video> لقد أخرجنا في الشيفرة السابقة السمة src من العنصر <video> ووضعناها داخل عنصر مستقل هو <source> الذي يشير إلى موارد خاصة به، إذ سينتقل المتصفح في هذه الحالة إلى مجموعة العناصر <source> ويشغِّل المورد في أول عنصر يمتلك مرمزه، وبوجود الحاويتين WebM و MP4 ستضمن تشغيل ملفك على معظم المنصات والمتصفحات الموجودة حاليًا. يمتلك كل عنصر <source> السمة type وهي اختيارية لكننا ننصح باستخدامها، كما تتضمن هذه السمة نوع الوسائط المتعددة MIME للملف الذي يحدده العنصر <source>، إذ يستخدِمها المتصفح لتجاوز الفيديوهات التي لا يفهمها، فإذا لم تُحدد قيمة لهذه السمة، فسيحمِّل المتصفح كل ملف ويحاول تشغيله حتى يجد الملف الذي يعمل، وهذا ما يستغرق وقتًا ويسبب هدرًا في استخدام الموارد. ملاحظة: عُد إلى مقال دليلك إلى أنواع ملفات الوسائط وتنسيقاتها لاختيار أنسب مرمز وأنسب حاوية لاستخداماتك ولتتعرف أيضًا على أنواع الوسائط المتعددة MIME التي تعرِّف كل حاوية. ميزات أخرى هنالك عدد من الميزات التي يمكنك إضافتها عند عرض فيديوهات HTML، وإليك مثالًا كما يلي: <video controls width="400" height="400" autoplay loop muted preload="auto" poster="poster.png"> <source src="rabbit320.mp4" type="video/mp4"> <source src="rabbit320.webm" type="video/webm"> <p>Your browser doesn't support HTML video. Here is a <a href="rabbit320.mp4">link to the video</a> instead.</p> </video> ستبدو نتيجة تنفيذ هذه الشيفرة للمستخدِم كما يلي: لنلق نظرةً على الميزات الجديدة: width و height: يمكنك التحكم بأبعاد الفيديو باستخدام هاتين السمتَين أو من خلال تنسيقات CSS، إذ يحافظ الفيديو في كلتا الحالتين على النسبة الأصلية للعرض إلى الطول أو كما تسمى النسبة الباعية أو نسبة البُعدَين aspect ratio، فإذا لم تتحقق هذه النسبة عندما تضبط بنفسك الطول والعرض، فسيغطي الفيديو الصفحة أفقيًا وتُملأ المنطقة غير المشغولة بالفيديو بخلفية ملونة مستمرة. autoplay: تسبب تشغيل الفيديو أو الصوت مباشرةً، في حين لا تزال بقية المحتوى قيد التحميل، كما لا ننصحك باستخدام هذه السمة للفيديو أو ملفات الصوت ضمن موقعك لأنه أمر مزعج لكثير من المستخدِمين. loop: تسبب تشغيل الفيديو او الصوت عند انتهائه، وقد يكون الأمر مزعجًا، لذا لا تستخدِمه إلا إذا كان ذلك ضروريًا. muted: تشغّل مقطع الفيديو افتراضيًا بدون صوت. poster: تضم عنوان URL للصورة التي تُعرَض قبل تشغيل الفيديو، وهي معدَّة للاستخدام مع الشاشات التمهيدية splash screen أو الشاشات الدعائية. preload: تستخدَم للتخزين المؤقت للملفات الضخمة، كما تأخذ أحد القيم الثلاث التالية: "none" وتعني لا تخزين مؤقت، أو "auto" وتعني ضرورة التخزين المؤقت للملف، أو "metadata" لتخزين البيانات الوصفية فقط للملف. يمكنك تجريب المثال السابق مباشرةً على جيت-هاب، كما يمكنك الاطلاع على الشيفرة المصدرية أيضًا، ولاحظ عدم استخدام السمة autoplay في النسخة المباشرة، إذ لن ترى صورة البداية إذا بدأ الفيديو قبل انتهاء تحميل الصفحة. العنصر <audio> يشابه العنصر <[video](https://wiki.hsoub.com/HTML/video)> مع بعض الاختلافات كما سنوضح لاحقًا، وإليك مثالًا عن استخدام العنصر: <audio controls> <source src="viper.mp3" type="audio/mp3"> <source src="viper.ogg" type="audio/ogg"> <p>Your browser doesn't support HTML5 audio. Here is a <a href="viper.mp3">link to the audio</a> instead.</p> </audio> تمثل الصورة التالية نتيجة تنفيذ الشيفرة: ملاحظة: يمكنك تشغيل المثال السابق مباشرةً عبر جيت-هاب، كما يمكنك الاطلاع على الشيفرة المصدرية لمشغل الصوت. يحتل العنصر مساحةً أقل من المساحة التي يحتلها مشغل الفيديو نظرًا لعدم وجود مكوّن مرئي، فكل ما تحتاجه هو عرض مجموعة التحكم لتشغيل المقطع الصوتي، وتتلخص بقية الاختلافات عن عنصر الفيديو بما يلي: لا يدعم العنصر <audio> السمتين width و height لعدم وجود مكوّن مرئي، وبالتالي لا حاجة إلى تحديد طول أو عرض. لا يدعم أيضًا السمة poster. ما عدا ذلك، يدعم العنصر <audio> جميع الميزات التي يدعمها العنصر <video>. عرض المسارات النصية لفيديو سنتحدث الآن عن مفهوم متقدِّم قليلًا ومن المهم معرفته، إذ لا يرغب الكثير من المستخدِمين بسماع محتوى الفيديو أو المقطع الصوتي على الأقل في أوقات معينة، فمثلًا: قد يعاني البعض من مشاكل سمعية مثل الطرش أو صعوبة السمع، وبالتالي لن يسمع الصوت بوضوح وقد لا يسمعه إطلاقًا. قد لا يسمع آخرون الصوت لكونهم في بيئات كثيرة الضجيج مثل مقهى مزدحم عند عرض مباراة رياضية. في البيئات التي يُمنع فيها إصدار الأصوات كما في المكتبات أو عندما يحاول أحدهم النوم إلى جوارك، إذ سيكون وجود الشروحات أو كلمات المقطع أمرًا مفيدًا. قد يرغب الناس الذين لا يتحدثون لغة الفيديو بوجود كلمات المقطع مكتوبةً أو مترجمةً لفهم ما يدور فيه. من الجيد في حالات مثل هذه وجود نص يعرض ما يُقال في الفيديو أو المقطع الصوتي، وهذا ما يتيحه العنصر <video> في HTML، ولإنجاز الأمر، سنستخدِم ملفًا بتنسيق WebVTT والعنصر <track>. يُعَدّ WebVTT هو تنسيق لكتابة ملفات نصية تضم إضافةً إلى المقاطع النصية بيانات وصفية مثل توقيت عرض كل عبارة نصية ومعلومات محدودة عن موقع وتنسيق تلك العبارات النصية. تُدعى العبارات النصية بالأنساق Cues وتُصنَّف وفق أنواع مختلفة تخدم غايات مختلفة: الترجمات Subtitles: وذلك للمقاطع التي تتحدث بلغات مختلفة لا يستطيع فهمها من لا يجيد اللغة. شروحات (تعليقات) captions: نصوص تتزامن مع حوار أو وصف لأصوات ملفتة لكي تساعد من لديه صعوبة في السمع على فهم ما يجري. شروحات متزامنة timed descriptions: نصوص يلفظها مشغل الوسائط المتعددة لتصف مشاهد لذوي المشاكل البصرية أو فاقدي البصر. سيبدو ملف WebVTT نمطيًا كما يلي: WEBVTT 1 00:00:22.230 --> 00:00:24.606 This is the first subtitle. 2 00:00:30.739 --> 00:00:34.074 This is the second. ... لكي تعرض محتويات هذا الملف مع مشغِّلات الوسائط التي تقدمها HTML سنحتاج إلى: حفظ الملف بالامتداد vtt.. ربط الصفحة بملف vtt. مستخدِمًا العنصر <track> الذي ينبغي وضعه ضمن العنصر <video> أو العنصر <audio> لكن بعد جميع العناصر <source>. استخدام السمة kind للعنصر <track> لتحديد نوع الأنساق إذا كانت "subtitles" أو "captions" أو "descriptions". استخدام السمة srclang لإخبار المتصفح عن اللغة التي يجب أن يعرض بها الأنساق، واستخدام السمة label لمساعدة المستخدِم في تحديد اللغة التي يبحث عنها. إليك مثالًا: <video controls> <source src="example.mp4" type="video/mp4"> <source src="example.webm" type="video/webm"> <track kind="subtitles" src="subtitles_es.vtt" srclang="es" label="Spanish"> </video> ستكون النتيجة مقطع فيديو مع الترجمة كما يلي: ملاحظة: تساعد المسارات النصية في تحسين محركات البحث كونها متعطشة للعبارات النصية، إذ تسمح المسارات النصية لمحركات البحث بربط النتيجة بلحظة معينة في الفيديو. تطبيق: إدراج مقطع الفيديو أو الصوت الخاص بك نشجعك في هذا التمرين على الخروج وتسجيل مقطع فيديو أو مقطع صوتي لما يجري في محيطك، إذ يمكنك ببساطة استخدام هاتفك المحمول لسهولة استخدامه في التصوير وفي نقل المقاطع المسجلة إلى الحاسوب، وقد تضطر إلى تحويل الملفات المسجلة لتصبح حاويات MP4 أو WebM في حال سجلت مقطع فيديو، و MP3 أو Ogg في حال تسجيلك لمقطع صوتي، كما ستجد الكثير من البرمجيات التي تساعدك في إنجاز الأمر دون عناء كبير مثل Miro Video Converter و Audacity. إذا لم تشأ أن تسجِّل بنفسك، فلك كامل الحرية في اختيار عينات الفيديو والصوت التي جهزناها لهذا التمرين، كما يمكنك استخدام شيفرتنا المصدرية على أساس مرجع. نطلب منك: حفظ ملفات الفيديو والصوت في مجلد جديد على حاسوبك إنشاء ملف HTML جديد في المجلد نفسه باسم index.html. أضف العنصرين <video> و <audio> إلى الصفحة واجعلهما يعرضان أدوات التحكم الافتراضية للمتصفح. أضف السمة source إلى كل منهما لكي تجد المتصفحات التنسيق الصوتي الذي تدعمه، ولا بد في هذه الحالة من استخدام السمة type أيضًا. أضف صورة بداية poster إلى العنصر <video> كي تعرض قبل تشغيل المقطع. لكي تضع لنفسك نقاطًا إضافيةً، حاول الاطلاع أكثر على المسارات النصية وإضافة بعض الشروحات إلى الفيديو الخاص بك. خلاصة نكون هكذا قد وصلنا إلى نهاية المقال وقد اطلعنا على الأسلوب الدلالي في إدراج مقاطع الفيديو والمقاطع الصوتية في صفحتك، وسنتابع في المقال التالي موضوع إدراج محتوى من ويب ضمن صفحاتك باستخدام تقنيات مثل العنصر <iframe>. ترجمة -وبتصرُّف- للمقال Video and audio content. اقرأ أيضًا إعداد صور متجاوبة إضافة الصور في صفحات HTML إضافة مقاطع الفيديو عبر العنصر <video> في HTML5
  15. كانت الويب في بداياتها مجرد نصوص عدَّها الكثيرون مملةً، ولحسن الحظ لم يتأخر استخدام الصور -وغيرها من المحتوى الأكثر إمتاعًا- كثيرًا ضمن صفحات الويب، كما هناك الكثير من أنواع الوسائط المتعددة التي يمكن التفكير بها، لكن من المنطقي أن نبدأ مع العنصر المتواضع <img> الذي يُستخدَم في إدراج صور بسيطة في الصفحة، إذ سنطلع في هذا المقال على طريقة استخدام العنصر بتفاصيل أكثر بما في ذلك الأساسيات وطريقة إدراج شرح للصورة باستخدام العنصر <figure> وكيفية ربطه بصور الخلفية في CSS. لا بدّ قبل البدء بقراءة هذا المقال أن تكون على دراية بأساسيات عمل الحاسوب وطريقة تثبيت البرمجيات الأساسية ودراية بطريقة عمل الملفات، كما لا بدّ أيضًا من الدراية بأساسيات اللغة HTML كما عرضناها في مقال تعرَّف على HTML. كيف تضع صورة في صفحة ويب نستخدِم العنصر <img> لإدراج صورة في موقع ويب، إذ يُعَدّ هذا العنصر فارغًا، أي لا يضم أيّ محتوى نصي أو وسم نهاية، كما يتطلب على الأقل سمةً واحدةً لكي يعمل بصورة صحيحة وهي src وتُقرأ source، كما تحتوي قيمة هذه السمة على المسار الذي يدل على موقع الصورة التي تريد إدراجها في الصفحة والذي قد يكون عنوان URL نسبيًا relative أو مطلقًا absolute ومشابهةً تمامًا لقيمة السمة href الخاصة بالعنصر <a>. ملاحظة: عليك الاطلاع على مقال أساسيات عنوان URL وأنواعه وخاصة فقرة "عناوين URL المطلقة والنسبية" لتتذكَّر مفهومَي العنوان المطلق والعنوان النسبي قبل المتابعة. إذا كان اسم صورتك dinosaur.jpg مثلًا وتتواجد في المجلد نفسه الذي يوجد فيه ملف HTML، فستدرِج صورتك كما يلي: <img src="dinosaur.jpg"> أما إذا كانت الصورة في مجلد فرعي يُدعى images يتواجد في المجلد نفسه الذي يوجد فيه ملف HTML، فستدرِج صورتك كما يلي: <img src="images/dinosaur.jpg"> وهكذا. ملاحظة: تقرأ محركات البحث أسماء ملفات الصور ويؤثر ذلك من ناحية تحسين محركات البحث سيو SEO، لهذا السبب لا بدّ من تسمية الصور بأسماء وصفية معبرة، فالاسم dinosaur.jpg أفضل بالتأكيد من image835.png. بإمكانك إدراج الصورة أيضًا باستخدام عنوان URL مطلق، وإليك مثالًا كما يلي: <img src="https://www.example.com/images/dinosaur.jpg"> لكن هذا الأمر عديم الجدوى في حالتنا وسيدفع المتصفح إلى مزيد من العمل للبحث عن عنوان آي بي IP لخادم دي إن إس DNS من جديد، فما تفعله على الدوام تقريبًا هو وضع الصور المستخدَمة في الموقع على الخادم نفسه الذي يستضيف صفحات HTML. تحذير: تمتلك معظم الصور حقوق نشر، فلا تعرض صورةً على موقع ما لم: تكن إحدى صورك. تحصل على تصريح خطِّي من مالكها. تكن على يقين تام بأنها صورة تندرِج في نطاق الصور العامة. يُعَدّ انتهاك حقوق النشر أمرًا غير قانوني وغير أخلاقي، وعليك ألا تضع قيمة لسمة src تشير إلى صورة على موقع شخص آخر لم تحصل منه على إذن رسمي، إذ تُدعى هذه العملية بالربط الساخن hotlinking، ولا بدّ من الإشارة إلى أنّ العملية ستسبب سرقة حزمة تبادل البيانات المخصصة لموقع هذا الشخص وهذا أيضًا غير قانوني، كما تبطئ هذه الروابط صفحتك وتحرمك من التحكم بما سيعرض عليها، فلن تعرف إذا أُزيلت هذه الصورة أو عُدّلت بصورة قد تسبب لك حرجًا. ستعطي الشيفرة السابقة النتيجة التالية: ملاحظة: يُشار أحيانًا إلى العنصرين <img> و <video> بأنهما عنصران قابلان للاستبدال Replaced elements، لأن ما يُعرِّف محتواهما وحجمهما هي موارد خارجية مثل ملف صورة أو ملف فيديو، وليس من قِبَل محتواهما مباشرةً. ملاحظة: يمكنك الاطلاع على المثال بصيغته النهائية ضمن المستودع المخصص له على جيت-هاب، كما يمكنك الاطلاع على الشيفرة المصدرية أيضًا. أنشئ موقع احترافي لأعمالك وعزّز حضورك الرقمي صمم موقع إلكتروني فريد وجذاب لعملائك في دقائق دون خبرة برمجية باستخدام سنديان منشئ المواقع العربي أطلق موقعك الآن النص البديل ما سنتحدث عنه الآن هو السمة alt، ومن المفترض أن تكون قيمة هذه السمة نصًا يُظهر وصفًا للصورة وذلك لاستخدامه في الحالات التي لا تُعرض فيها، أو الحالات التي تستغرق فيها الصورة وقتًا طويلًا حتى يصيّرها المتصفح نظرًا لبطء الاتصال بالإنترنت، كما يمكن مثلًا تعديل الشيفرة التي عرضناها سابقًا لتصبح كما يلي: <img src="images/dinosaur.jpg" alt="The head and torso of a dinosaur skeleton; it has a large head with long sharp teeth"> أسهل الطرق التي يمكن من خلالها اختبار ظهور النص البديل هو كتابة اسم ملف الصورة بصورة خاطئة، فإذا كتبنا اسم الصورة في الشيفرة السابقة ليكون dinosooooor.jpg، فلن يعرضها المتصفح وإنما سيعرض النص البديل كما يلي: لماذا إذًا ستحتاج إلى النص البديل؟ إليك بعض الأسباب: إذا كان المستخدِم ذو إعاقة بصرية ويستخدم قارئات الشاشة لقراءة محتوى الصفحات، فسيصف النص البديل الصورة لهذا المستخدِم وبالتالي سيكون أكثر فائدةً منها. ارتكاب خطأ في كتابة اسم الملف أو في مساره. قد لا يدعم المتصفح نوع الصورة، فلا تزال المتصفحات النصية مستعملة فقط مثل Lynx والذي سيعرِض في هذه الحالة وصف الصورة. قد ترغب ربما بتزويد محركات البحث ببعض المعلومات عن صورتك، إذ يمكن لمحركات البحث استخدام النص البديل في مطابقة معيار البحث. قد يوقف المستخدِم عرض الصور على متصفحه عمدًا لتقليل استهلاك البيانات وخاصةً عند استخدام بيانات الهواتف المحمولة أو في الدول التي يكون فيها نطاق التراسل محدودًا أو مكلفًا. ما الذي ينبغي كتابته ضمن السمة alt؟ يعتمد الأمر على سبب استخدامك للصورة، أو بمعنى آخر ما الذي ستخسره إذا لم تُعرض الصورة؟: ديكور للصفحة: عليك استعمال صور الخلفية في CSS لأغراض الديكور، لكن إذا كان عليك استخدام HTML، فأضف السمة فارغةً " "=alt، فإذا لم تكن الصورة جزءًا من المحتوى، فلن تهدر قارئات الشاشة الوقت في نطقها. محتوى ضمن الصفحة: إذا قدَّمت الصورة معلومات هامةً، فعليك إضافة المعلومات نفسها لكن بإيجاز إلى السمة alt أو إلى النص الرئيسي للصفحة كي يراه الجميع وهذا هو الحل الأفضل، ولا تكتب نصًا بديلًا مكررًا عن المحتوى الرئيسي فالأمر مزعج بالنسبة للمستخدِم صحيح البصر، فإذا كانت الصورة مشروحةً شرحًا وافيًا ضمن النص الرئيسي، فأضف سمة النص البديل فارغةً " "=alt. رابط إلى محتوى آخر: إذا وضعت صورةً ضمن العنصر <a> لكي تحوِّل الصورة إلى رابط، فلا بدّ أن تزوِّد الرابط بنص واضح، وفي هذه الحالة يمكنك كتابته ضمن العنصر <a> أو على أساس قيمة للسمة alt العائدة للعنصر <img> أيًا ما يخدم حالتك. محتوى نصي: لا يجب وضع نص ضمن صورة، فإذا احتجت لأن تعرض ظلًا لعنوانك الرئيسي مثلًا، فاستخدم تنسيقات CSS بدلًا من وضع النص ضمن الصورة؛ أما إذا كنت مضطرًا كثيرًا، فضع النص داخل السمة alt. إنّ الغاية الأساسية هي تقديم تجربة يمكن الاستفادة منها، حتى إذا لم تكن الصور ظاهرةً، وبهذا تضمن وصول المحتوى الذي تقدمه إلى جميع المستخدِمين، وحاول منع متصفحك من عرض الصور وانظر كيف ستبدو الصفحة؟ إذ ستدرك عندها أهمية النص البديل في حالات مثل هذه. أبعاد الصورة يمكن استخدام السمتَين width و height لتحديد بُعدَي الصورة أي العرض والارتفاع، كما بإمكانك معرفة هذين البعدين بطرق عديدة، إذ يمكنك مثلًا الضغط على المفتاحين I+Cmd في ماك لعرض المعلومات عن ملف الصورة، وبالعودة إلى مثالنا السابق يمكن كتابة التالي: <img src="images/dinosaur.jpg" alt="The head and torso of a dinosaur skeleton; it has a large head with long sharp teeth" width="400" height="341"> لن يغير ذلك كثيرًا في طريقة عرض الصورة ضمن الظروف الاعتيادية، لكن إذا لم تظهر الصورة عندما ينتقل المستخدِم إلى الصفحة التي تعرضها ولم تحمل هذه الصورة بعد على سبيل المثال، فستجد أنّ المتصفح سيترك فراغًا له أبعاد محددة لكي تُعرَض الصورة ضمنه: سيفيدك هذا الأمر في تحميل الصفحة بسرعة وأكثر سلاسةً. لكن عليك تفادي تغيير أبعاد الصورة مستخدمًا سمات HTML، فإذا كبّرت حجم الصورة كثيرًا، فسينتهي الأمر بإظهار الصورة خشنة وضبابية، وإذا صغّرتها كثيرًا، فستهدِر حجم تراسل البيانات في تنزيل صورة لا تلبي حاجات المستخدِم، كما قد تبدو الصورة مشوهةً إذا لم تحافظ على تناسق الأبعاد، لذلك عليك استخدام محرر صور لضبط الأبعاد ضبطًا صحيحًا قبل إدراجها في صفحة الويب. ملاحظة: إذا كنت مضطرًا لتغيير أبعاد الصورة، فعليك استخدام تنسيق CSS كبديل عن تغيير الأبعاد من خلال العنصر <img> مباشر. عناوين الصور بإمكانك إضافة عناوين إلى صورك باستخدام السمة title لإضافة معلومات دعم إضافية عند الحاجة، وبالعودة إلى مثالنا السابق يمكننا كتابة ما يلي: <img src="images/dinosaur.jpg" alt="The head and torso of a dinosaur skeleton; it has a large head with long sharp teeth" width="400" height="341" title="A T-Rex on display in the Manchester University Museum"> سيعرِض ذلك نص العنوان الذي اخترته عندما تمرر مؤشر الفأرة فوق الصورة. لا يُعَدّ استخدام السمة title مع ذلك محبّذًا، فله مشاكل عدة تتعلق بالوصول السهل وتتمركز حول حقيقة أنّ دعم قارئات الشاشة لها غير متوقع، ولن تعرضها معظم المتصفحات ما لم تمرر مؤشر الفأرة فوقها، لذا من الأفضل إدراج معلومات داعمة مثل هذه ضمن نص المقالة الرئيسية في الصفحة بدلًا من ربطها بالصورة. تطبيق: إدراج صورة حان دورك في تجربة الأمر، إذ سيحاول هذا التمرين تحفيزك على تجربة إدراج صورة، لهذا سيزودك بالعنصر <img> وعليك إدراج الصورة الموجودة على العنوان: https://raw.githubusercontent.com/mdn/learning-area/master/html/multimedia-and-embedding/images-in-html/dinosaur_small.jpg تذكَّر أننا أشرنا سابقًا إلى عدم قانونية إدراج الصور عبر روابط ساخنة، لكننا سنتغاضى هذه المرة عن الأمر كونه تمرين تدريبي. حاول إذا استطعت أيضًا: إضافة نص بديل، ثم التحقق من أنه يعمل بكتابة عنوان URL للصورة بشكل خاطئ. ضبط الارتفاع height والعرض width الصحيحَين للصورة (تلميح: أبعاد الصورة 200 بكسل عرضًا و 171 ارتفاعًا)، ثم اختبار قيم مختلفة للطول والعرض لرؤية التأثيرات. وضْع عنوان title للصورة. إذا ارتكبت خطأً، فيمكنك مسح ما كتبته بالنقر على زر إعادة الضبط Reset، في حين إذا وجدت نفسك تائهًا كليًا، فانقر على زر أظهر الحل Show solution لترى الحل الصحيح. استخدام عنصري الشكل <figure> والشرح <figcaption> مع الصور هنالك طرق عدة لإضافة شروحات إلى الصورة، وإليك مثالًا كما يلي: <div class="figure"> <img src="images/dinosaur.jpg" alt="The head and torso of a dinosaur skeleton; it has a large head with long sharp teeth" width="400" height="341"> <p>A T-Rex on display in the Manchester University Museum.</p> </div> تُعَدّ هذه الطريقة مقبولةً باستخدام العنصر <div>، فهي تعرض المحتوى الذي تحتاجه بطريقة يمكن تنسيقها باستخدام CSS، لكن المشكلة هنا هي عدم وجود ربط دلالي semantical بين الصورة وشرحها مما سيسبب المشاكل عند استخدام قارئات الشاشة، فعند وجود 50 صورة مثلًا مع شروحها، فلا يمكن تمييز أيّ شرح لأيّ صورة. الطريقة الأفضل هي استخدام العنصرين <figure> و <figcaption>، وهما عنصران خاصَّان بالنسخة 5 من HTML، فقد ظهرت هذه العناصر خصيصًا لهذا الغرض، أي لإيجاد حاضنة دلالية للأشكال تربط جليًا بين الصورة وشرحها، ويمكن كتابة المثال السابق على سبيل المثال كما يلي: <figure> <img src="images/dinosaur.jpg" alt="The head and torso of a dinosaur skeleton; it has a large head with long sharp teeth" width="400" height="341"> <figcaption>A T-Rex on display in the Manchester University Museum.</figcaption> </figure> يخبر العنصر <figcaption> المتصفح والتقنيات المساعدة أنّ الشرح الذي يضمَّه سيكون مخصصًا للصورة التي يضمها العنصر <figure>. ملاحظة: لكل من النص البديل alt والشروحات captions دورًا مختلفًا من وجهة نظر الوصول السهل. إذ يستفيد جميع المستخدِمين من الشروحات بمن فيهم من يراها، لكن تقتصر رؤية محتوى السمة alt التي تؤدي المهمة ذاتها على الحالة التي لا تُعرض فيها الصورة، لهذا يجب ألا يتشابه محتوى الشرح ومحتوى النص البديل لأنّ كلاهما سيظهر عند غياب الصورة، لذا حاول أن تمنع متصفحك من عرض الصور وراقب ما سيحدث. لا حاجة ليكون الشكل -أي العنصر <figure>- صورةً حصرًا، فهو حاوية مستقلة يمكن أن: تعبِّر عما تعنيه بطريقة مختصرة وسهلة الفهم. تتواجد في أماكن عدة ضمن تسلسل العناصر السطرية في الصفحات. تزوّد الصفحة بمعلومات أساسية تدعم النص الرئيسي لها. قد يضم الشكل عدة صور أو شذرات (مقاطع) من الشيفرة أو ملفات فيديو أو ملفات صوتية أو جداول أو غير ذلك. تطبيق: إنشاء شكل سنأخذ في هذا التمرين الشيفرة الجاهزة للتمرين التطبيقي السابق ونحولها إلى شكل: غلِّف الشيفرة داخل العنصر <figure>. انسخ النص الموجود داخل السمة title وضَعه داخل العنصر <figcaption> تحت الصورة واحذف بعد ذلك السمة title. إذا ارتكبت خطأً، فيمكنك مسح ما كتبته بالنقر على زر إعادة الضبط Reset، في حين إذا وجدت نفسك تائهًا كليًا، فانقر على زر أظهر الحل Show solution لترى الحل الصحيح. إدراج صور للخلفية باستخدام CSS يمكنك أيضًا استخدام CSS في إدراج الصور ضمن صفحات الويب وشيفرة جافاسكربت أيضًا، لكنه موضوع مختلف، إذ تُستخدّم الخاصية background-image وغيرها من خواص الخلفية background-image التي تبدأ بالكلمة *-background للتحكم في موضع خلفية الصفحة، فإذا أردت مثلًا وضع الخلفية لكل فقرة نصية، فكل ما عليك فعله هو كتابة ما يلي: p { background-image: url("images/dinosaur.jpg"); } قد يكون ضبط هذه الصورة التي استخدِمت على أساس خلفية أسهل من ضبط الصور التي تُدرجها HTML، فلماذا نزعج أنفسنا بها إذًا؟ لأن هذه الصور لأغراض الديكور والتزيين فقط كما أشرنا سابقًا، فإذا أردت استخدامها لتحسِّن مظهر صفحتك، فلا بأس بذلك، لكن تذكَّر أنها لا تملك أية دلالات ولا يمكن أن تحمل نصًا بديلًا ولا تراها قارئات الشاشة، وهنا تظهر أهمية صور HTML. باختصار: إذا كانت للصورة دلالةً تتعلق بالمحتوى الذي تقدِّمه، فعليك استخدام صور HTML، في حين عليك استخدام صور CSS إذا أردتها لتحسين مظهر الصفحة فقط. خلاصة هذا كل ما لدينا الآن، فقد غطينا موضوع الصورة وشروحاتها بالتفصيل، وسنتقدم خطوةً أخرى في مقالنا التالي من جزئية HTML من هذه السلسلة لنتعلم كيف نستخدِم HTML في إدراج مقاطع الفيديو والمقاطع الصوتية في صفحات الويب. ترجمة -وبتصرُّف- للمقال Images in HTML. اقرأ أيضًا المقال السابق: تنقيح شيفرة HTML إعداد صور متجاوبة إضافة مقاطع الفيديو عبر العنصر <video> في HTML5
  16. تُعَدّ كتابة شيفرة HTML أمرًا جليًا، لكن ما الحل عندما تواجهك بعض المشاكل ولم تستطع اكتشاف موطن الخطأ فيها؟ لذا سنقدِّم لك في هذا المقال شرحًا عن بعض الأدوات التي يمكنها مساعدتك لإيجاد الخطأ وإصلاحه. عليك قبل البدء في قراءة هذا المقال الاطلاع على أساسيات HTML التي أوردناها في مقال تعرَّف على HTML وكذلك هيكلة النصوص في HTML وطريقة إنشاء الروابط التشعبية في HTML. تنقيح الشيفرة ليس أمرا صعبا تبقى الأمور عند كتابة شيفرة واضحةً وسلسةً حتى يقع خطأ ما، فقد ارتكبت خطأً ما فجاةً، ولم تَعُد تعمل الشيفرة إطلاقًا أو لم تَعُد تعمل بالطريقة المطلوبة، ويُظهِر الشكل التالي مثالًا عن خطأ يظهر عند ترجمة برنامج بسيط مكتوب بلغة رست Rust. يسهل فهم الخطأ الذي يظهر في الصورة وهو نص بين مزدوجتين غير مغلقتين unterminated double quote string، فإذا نظرت إلى قائمة الأخطاء، فسترى الشيفرة التالية: println!(hello, world!"); يفتقد فيها النص !hello, world إلى إشارة تنصيص البداية، لكن ستتعقد هذه الرسائل وسيصعب فهمها عندما يكبر البرنامج، إذ ستبدو الحالات البسيطة مخيفةً لشخص لا يعرف شيئًا عن لغة Rust. مع ذلك، فإنّ تنقيح البرامج ليس أمرًا صعبًا، والمفتاح الأساسي لإتقان الأمر في أيّ لغة برمجة هو استيعاب اللغة وأدوات التنقيح المساعدة. تنقيح اللغة HTML ليست عملية تنقيح HTML بتعقيد تنقيح Rust، فلن تُترجم إلى صورة أخرى قبل أن يفسرها المتصفح ويعرض النتائج، فهي لغة مفسَّرة interpreted وليست مصرَّفة compiled، كما تُعَدّ عناصر HTML أوضح من ناحية الصيغة والكتابة بصورة أو بأخرى موازنةً بلغة برمجة حقيقية مثل Rust أو جافاسكربت أو بايثون، كما أنّ أسلوب التفسير المتساهل الذي تستخدِمه المتصفحات موازنةً ببقية لغات البرمجة له ميزات إيجابية وأخرى سلبية. شيفرة متساهلة ماذا نعني بالمتساهل permissive إذًا؟ ما يحصل عادةً عند ارتكاب أخطاء في الشيفرة هو ظهور نوعَين من رسائل الخطأ: أخطاء الصياغة syntax error: تنتج عن أخطاء في كتابة التعليمات أو علامات الوصول في الشيفرة مما يتسبب في إيقاف تنفيذ البرنامج كما في الخطأ الذي عرضناه في برنامج Rust السابق (نسيان إشارة تنصيص)، إذ تُعَدّ هذه الأخطاء سهلة الحل إذا كنت ملمًا بقواعد اللغة المستخدَمة وما تعنيه رسائل الخطأ. الأخطاء المنطقية logical errors: لا تقع عند حدوث أخطاء صياغة، لكن عندما لا تعطيك الشيفرة النتيجة المطلوبة، بمعنى ان البرنامج يعمل بطريقة غير صحيحة، فهذه الأخطاء صعبة الحل نظرًا لعدم وجود رسالة خطأ توجهك إلى مصدره. لن تجد في HTML أخطاءً في الصياغة لأن المتصفح يتساهل في تفسيرها، أي ستعرض الصفحة نتيجة تفسير الشيفرة حتى بوجود هذه الأخطاء، إذ تُبنى المتصفحات على قواعد تتيح لها أن تقرِّر طريقة تفسير الشيفرة غير الصحيحة لكي تعرضها حتى بصورة تخالف التوقعات، ويُعَدّ هذا الأمر مشكلةً أيضًا. ملاحظة: يعود تفسير HTML بصورة متساهلة إلى بدايات الويب، إذ اتخذ قرار عندها بأنّ إيصال المحتوى إلى المستخدِم أكثر أهميةً من صحة الصياغة، وربما لم تغدو الويب بهذه الشعبية الآن إذا كانت أكثر تشددًا في بداياتها. تطبيق: دراسة شيفرة متساهلة حان الوقت لدراسة الطبيعة المتساهلة التي تُفسَّر بها شيفرة HTML. نزّل أولًا ملفات مثال التنقيح التجريبي واحفظها على جهازك، إذ كُتِب المثال ليضم قصدًا أخطاءً لكي نستكشفها، ونقول عندها بأنّ شيفرة HTML سيئة التوصيف badly-formed على نقيض أن تكون حسنة التوصيف well-formed. افتح الملف بواسطة المتصفح، إذ ستبدو لك النتيجة كما يلي: ستلاحظ مباشرةً أنّ المشهد ليس جيدًا، فلنلق نظرةً إذًا على الشيفرة لنرى إذا كان بإمكاننا فعل شيء ما. <h1>HTML debugging examples</h1> <p>What causes errors in HTML? <ul> <li>Unclosed elements: If an element is <strong>not closed properly, then its effect can spread to areas you didn't intend <li>Badly nested elements: Nesting elements properly is also very important for code behaving correctly. <strong>strong <em>strong emphasized?</strong> what is this?</em> <li>Unclosed attributes: Another common source of HTML problems. Let's look at an example: <a href="https://www.mozilla.org/>link to Mozilla homepage</a> </ul> لنبحث عن المشاكل: لا توجد وسوم نهاية لعنصر الفقرة <p> ولعناصر القائمة <li>، لكن بالنظر إلى النتيجة، سنجد أنها لم تؤثر سلبًا على تصيير الشيفرة، وذلك لأنّ المتصفح سيستدِل بسهولة على نهاية كل عنصر وبداية الآخر. لا يوجد وسم نهاية لأول عنصر <strong>، وهذا قد يسبب مشكلةً لأنه من الصعب أن يقرِّر المتصفح متى سينتهي تأثير هذا العنصر، ولاحظ أنّ بقية النص قد كُتب بصورة بارزة. لاحظ التوصيف السيئ للقسم التالي للشيفرة، إذ لا يمكن أبدًا توقُّع كيف سيعرض المتصفح هذه الشيفرة نظرًا لمشكلة التداخل السابقة. <strong>strong <em>strong emphasized?</strong> what is this?</em> تفتقد قيمة الصفة href للرابط في نهايتها إلى إشارة تنصيص مزدوجة، وهذا ما يبدو أنه المسبب الأكبر للمشكلة فلم يُصيّر الرابط أبدًا. لنلق نظرةً الآن على الشيفرة التي صيّرها المتصفح مقابل الشيفرة المصدرية، إذ سنستخدم أدوات مطورِي ويب المدمجة في المتصفح. سترى الشيفرة التي صيّرها المتصفح ضمن فاحص DOM: دعونا نتفحَّص الشيفرة الموجودة في نافذة فاحص DOM لنرى كيفية تعامل المتصفح مع الأخطاء، وقد راجعنا الشيفرة باستخدام فايرفوكس، وينبغي أن يكون الأمر مماثلًا في جميع المتصفحات الحديثة: أضاف المتصفح وسوم نهاية للفقرات ولعناصر القائمة. من غير الواضح أين سينتهي أول عنصر <strong>، لذلك غلَّف المتصفح كل كتلة نصية منفصلة ضمن عنصر <strong> منفصل حتى نهاية المستند. حلّ المتصفح مشكلة التداخل الخاطئ للعناصر كما يلي: <strong>strong <em>strong emphasized?</em> </strong> <em> what is this?</em> أما الرابط الذي تنقصه إشارة تنصيص مزدوجة، فقد حذفه المتصفح بالكامل، وأصبح آخر عنصر في قائمة العناصر كما يلي: <li> <strong>Unclosed attributes: Another common source of HTML problems. Let's look at an example: </strong> </li> التحقق من شيفرة HTML لا بد أنك أدركت الآن أهمية التوصيف الحسن لملف HTML، فكيف ستفعل ذلك؟ من السهل في مثال بسيط مثل الذي أوردناه في الأعلى أن تبحث ضمن سطور الشيفرة لتقصي الأخطاء، لكن توقع كيف سيكون الأمر في ملف ضخم؟ إنّ أفضل استراتيجية قد تبدأ بها هي استخدام خدمة التحقق من شيفرة HTML المعروفة باسم Markup Validation Service التي ابتكرتها وتصونها منظمة W3C المسؤولة عن تعريف مواصفات HTML و CSS وغيرها من تقنيات الويب، إذ تُعَدّ هذه الخدمة تطبيق ويب يقبل ملف HTML في الدخل ثم يتفحص محتواه ثم يعطيك تقريرًا بما هو خاطئ: يمكنك استخدام عنوان ملف HTML الذي تريد التحقق منه أو رفعه أو تنسخ وتلصق الشيفرة مباشرةً. تطبيق: التحقق من ملف HTML لنحاول استخدام الملف البسيط الذي ناقشناه سابقًا: حمِّل خدمة التحقق من التوصيف في المتصفح إذا لم تكن قد فعلت ذلك. انتقل إلى نافذة التحقق من دخل مباشر Validate by Direct Input. انسخ شيفرة الملف بالكامل ثم الصقها في مربع النص الكبير الظاهر في النافذة. انقر الزر "تحقق Check". من المفترض أن تظهر قائمة من الأخطاء بالإضافة إلى معلومات أخرى. تفسير رسائل الخطأ عادةً ما تكون رسائل الخطأ مهمةً، لكنها ليست كذلك في بعض الأحيان، وبالطبع ستتمكن مع الممارسة من فهم وتفسير الأخطاء لإصلاح الشيفرة، فلنلق نظرةً إذًا على رسائل الخطأ التي ظهرت لنفهم معناها، ولاحظ أنّ كل رسالة تحمل رقم السطر والعمود الذي ارتكب الخطأ فيه لمساعدتك في تحديد موقعه. Consider adding a `lang` attribute to the `html` start tag to declare the language of this document أي فكّر في استخدام الصفة lang ضمن وسم بداية العنصر html لتحديد لغة المستند: هذه ليست رسالة خطأ وإنما تنبيه، إذ تشير التوصيات إلى ضرورة تحديد لغة الصفحة دائمًا وهذا ما ترشدك إليه هذه الرسالة. End tag `li` implied, but there were open elements أي وسم النهاية للعنصر li موجود لكن هناك وسوم بداية لعناصر (وردت مرتين): تشير الرسالة إلى عنصر مفتوح ورد وسم بداية له وينبغي إغلاقه، إذ وجدت الخدمة وسم النهاية لكنه ليس في المكان الصحيح، ويشير رقمَي السطر والعمود إلى السطر الأول الذي يلي المكان الذي ينبغي وجود وسم النهاية فيه، وهذا دليل واضح لفهم الخطأ الحاصل. Unclosed element `strong` أي عنصر strong غير مغلق: الرسالة واضحة، ويشير رقمَي السطر والعمود مباشرةً إلى مكان الخطأ. End tag `strong` violates nesting rules أي يخالف وسم نهاية العنصر strong قواعد التداخل: تشير الرسالة إلى تداخل خاطئ للعناصر، ويشير رقمَي السطر والعمود مباشرةً إلى موقع المشكلة. End of file reached when inside an attribute value. Ignoring tag أي الوصول إلى نهاية الملف ولانزال ضمن قيمة سمة. إهمال الوسم: هذه الرسالة مشفّرة نوعًا ما، وتشير إلى وجود سمة في مكان ما لم تكتب قيمتها بصورة صحيحة وربما قرب نهاية الملف لأن نهايته ظهرت داخل قيمة السمة، وطالما أنّ المتصفح لم يصيّر الرابط، فسيدلنا ذلك على مصدر الخطأ. End of file seen and there were open elements أي الوصول إلى نهاية الملف مع وجود عناصر مفتوحة: هذه الرسالة غامضة، لكنها تشير مبدئيًا إلى وجود عناصر مفتوحة لابد من إغلاقها بطريقة صحيحة، إذ يشير رقم السطر إلى السطور الأخيرة في الملف، كما تأتي الرسالة مع سطر من الشيفرة يشير إلى مثال عن عنصر مفتوح: example: <a href="https://www.mozilla.org/>link to Mozilla homepage</a> ↩ </ul>↩ </body>↩</html> ملاحظة: قد ينتج عن سمة لم تغلق بإشارة تنصيص عنصر مفتوح لأنّ بقية المستند ستفسر على أنها محتوى لقيمة السمة. Unclosed element `ul` أي عنصر ul غير مغلق: لن تقدِّم لك هذه الرسالة فائدةً كبيرةً لأن العنصر مغلق بصورة صحيحة، لكن سبب رسالة الخطأ هو إشارة التنصيص المفقودة. لا تقلق إذا لم تتمكن من فهم الأخطاء التي تشير إليها الرسائل، وحاول إصلاح بعض الأخطاء في كل مرة ثم أعد تقييم الشيفرة لتقف على الأخطاء التي بقيت، فقد يؤدي إصلاح خطأ واحد ظهر في البداية إلى التخلص من بقية الأخطاء، وستجد أنّ عدة أخطاء قد تنتج أحيانًا عن مشكلة واحدة. ستعرف أنك أصلحت جميع الأخطاء عندما تشاهد الشريط التالي في خرج الخدمة: خلاصة قدمنا في المقال لمحةً عن تنقيح شيفرة HTML والتي ستمنحك بعض الخبرة التي تساعدك عندما تبدأ تعلُّم تنقيح شيفرة CSS وجافاسكربت وغيرها من الشيفرات التي ستقابلها خلال مسيرتك المهنية، وبهذا المقال نصل إلى نهاية سلسلة المقالات التي تُمهِّد للغة HTML. ترجمة -وبتصرُّف- للمقال Debugging HTML. اقرأ أيضًا تنقيح أخطاء شيفرة جافاسكربت في Chrome اختبار الوحدات وأدوات تنقيح الشيفرات وتصحيح الأخطاء في Cpp
  17. تعرّفنا في المقال السابق على طريقة إنشاء مستند جوجل، وأنشأنا مستندًا جديدًا أسميناه "نصائح عن تبادل الأفكار في مستندات جوجل"؛ كما تعرفنا على طريقة مشاركة المستند مع مساهمين آخرين وكيفية تحديد الصفة التي يساهم بها كل منهم والتي قد تكون "محررًِّا" أو "معلقًا" أو "عارضًا". وشاركنا في نهاية المطاف المستند مع ثلاثة مساهمين افتراضيين الأول بصفة محرر، والثاني بصفة معلّق، والثالث بصفة عارض. سنتابع الآن شرح الأفكار المتعلقة بمشاركة المستندات من خلال نافذة المستند التي تظهر عند كل مساهم على حدة وطرق التواصل بين المساهمين وتنظيم العمل. إن كنت مستعدًا سنبدأ. قبل البدء ربما من الأفضل في مثالنا أن تنتحل أنت شخصية المساهمين الثلاث كما فعلت أنا، أي أن تكون لك عدة حسابات على غوغل أو غيرها ثم تستخدم الحساب الأول لإنشاء المستند وستكون المالك، بعدها شارك المستند مع حسابك الثاني بصفة محرر ومع الثالث بصفة معلّق. يُنصح في هذه الحالة بتثبيت متصفحين مختلفين جوجل كروم وفايرفوكس مثلًا، ثم سجّل دخولك من خلال المتصفحين إلى حساب مختلف، الأول كمالك والثاني كمساهم آخر (محرر مثلًا)، وهكذا. وبهذه الطريقة ستتفهم صلاحياتك وطريقة العمل المشترك من وجهتي نظر مختلفتين. العمل المتزامن لمحرر (أو مالك) مع محرر آخر في مستندات جوجل نقصد بالعمل المتزامن وجود أكثر من مساهم في نفس الوقت للعمل على المستند، وهذه ميزة في غاية الأهمية لمناقشة تفاصيل نهائية أو توضيح نقاط أساسية قبل البدء بالعمل تتطلب وجود المساهمين معًا. نفترض في هذا المثال أن المساهم "address@website.com" هو المحرر الرئيسي، والمساهم "address@mysite.com" هو المحرر المساعد، أي كلاهما يساهمان في مستندنا بصفة محرر. إليك لقطة الشاشة التي تظهر عند دخول المحرر المساعد إلى المستند: لربما لاحظت التغيرات التالية في المستند سواءً في منطقة التحرير أو في منطقة أشرطة التحكم أبرزها كما تظهر في لقطة الشاشة السابقة هي: المنطقة 1: يظهر فيها مؤشر على رأسه اسم المساهم الذي يضع مؤشر الفأرة في هذا الموضع من نسخته من المستند (على جهازه)، وهكذا يمكنك متابعة ما يجريه من تغييرات على نسخته مباشرة على نسختك. المنطقة 2: تعرض لك أيقونات المساهمين الموجودين حاليًا إضافة إلى أيقونة المحادثة المباشرة إلى جوارها، وهذا ما سنتحدث عنه لاحقًا. المنطقة 3: تُعرض عند فتح المستند قائمة بالعناوين الرئيسية والفرعية التي يتضمنها، وذلك لأغراض التنقل السريع نحو النقاط المستهدفة إن كانت محددةً مسبقًا من قِبل المالك أو المحرر الرئيسي. يمكن إخفاء الملخص بالنقر على السهم المجاور لكلمة "ملخص". ستظهر في نافذة متصفح المحرر الرئيسي نفس التغيرات السابقة تمامًا، أي ظهور موقع مؤشر فأرة المحرر الآخر وأيقونته في شريط التحكم. التلميح المباشر في المكان ضمن مستندات جوجل لنفترض أن المحرر الرئيسي يريد توجيه المحرر المساعد إلى التوسع في كتابة بعض الفقرات، ولكي يدل المحرر المساعد الذي يُفترض به إنجاز الأمر على المكان المحدد بدقة، سينقر إلى جوار الفقرة المطلوبة ويكتب مثلًا "ما رأيك في التوسع هنا". في نفس اللحظة سيرى المحرر المساعد ما هو مطلوب وأين سينفذه وهذا ما يختصر الكثير من الكلام والنقاش. يمكن استخدام هذا الأسلوب أيضًا في حال أراد أن يعلم أحد المساهمين المساهم الآخر طريقة تطبيق تنسيق معين أو إدراج أي شيء في المستند، ولهذا تُعَد هذه الميزة ممتازةً في هذا المضمار. استخدام المحادثة المباشرة في مستندات جوجل تُعَد هذه الميزة أكثر أناقةً في التواصل بين المساهمين الذين يعملون في نفس الوقت على المستند، إذ تُناقش الآراء ويجري تبادل الأفكار في إطار منفصل عن نص المستند. لبدء المحادثة المباشرة، انقر على زر "إظهار المحادثة" الملاصق لأيقونات المساهمين المتواجدين حاليًا في شريط الأدوات أعلى المستند. لتظهر لك النافذة الجانبية التالية: تعمل هذه المحادثة الكتابية كأي واجهة محادثة تقليدية، إذ يوجد أسفل النافذة مربع يحمل النص الافتراضي "اكتب المحادثة هنا"، ويمكنك كتابة الرسالة التي تريد ثم اضغط المفتاح Enter لإرسالها. استخدام جوجل ميت لعقد اجتماع حول مستند جوجل يُتاح هذا الخيار للمساهمين إن كان لا بد من مناقشة محتوى المستند على أصعدة مختلفة تتطلب خيارات أخرى، مثل مشاركة شاشات الحواسب التي يعملون عليها لتوضيح أمور تتعلق مثلًا باستخدام التطبيق نفسه ولا تتعلق بالمحتوى. في هذه الحالة يمكن جدولة اجتماع ليكون في يوم محدد وتدعو إليه من تريد من المساهمين وتحدد إن كان الاجتماع سيستخدم جوجل ميت Google Meet أم لا. سنحاول تنفيذ الأمر خطوةً بخطوة: انقر على أيقونة التقويم التي تقع أعلى القائمة الجانبية اليسارية لتطبيق مستندات جوجل، ثم انقر أيقونة القائمة (النقط الثلاث المتعامدة) واختر "جدول المواعيد"، بعدها انقر على زر "إنشاء حدث". تظهر لك نافذة إنشاء الحدث التي يتصدرها عنوان المستند كعنوان للحدث بإمكانك تغييره طبعًا. انقر على تحديد موعد لتستخدم تقويم غوغل الذي يُعرض لتحديد الشهر واليوم والساعة، ثم انقر على أيقونة توسيع في اللوحة التي تقع أسفل التقويم لاستئناف عملية التخطيط للاجتماع. انقر على "إضافة مدعوّين" لتختارهم من القائمة المنسدلة أو بكتابة بريدهم الإلكتروني، ثم حدد أذونات المدعويين. تشير اللوحة التي تلي أن الاجتماع سيكون باستخدام جوجل ميت. يمكن بعد ذلك إضافة وصف الاجتماع وسببه وستلاحظ وجود المستند مرفقًا مع الوصف. اختر اللون الذي يميزك كمؤسس للاجتماع (إن أردت)، ثم حدد ما إن كنت متواجدًا فيه. اختر طبيعة عرض هذا الحدث إن كان خاص أو علني (خاص لن يعرف به سوى من دُعي إليه). اختر فترة إرسال إشعار أو تنبيه باقتراب موعد الاجتماع. انقر الزر "حفظ"، ثم حدد إن كنت تريد إبلاغ المدعويين عن طريق البريد الإلكتروني أم لا. يُبلَّغ المدعوون إلى الاجتماع من خلال البريد الإلكتروني وعبر نسختهم من المستند، وذلك عند النقر على أيقونة جوجل مِيت في شريط ادوات المستند، كما يجري تذكيرهم بالاجتماع قبل الموعد المحدد بفترة زمنية هي نفسها التي حددها مؤسس الاجتماع عندما أنشأه؛ أما بالنسبة لتفاصيل عملية الاجتماع ومشاركة الشاشات وعقد مؤتمر فيديو، فهي مماثلة تمامًا لإنشاء مكالمة فيديو من خلال جوجل مييت، ولسنا بصدد شرحها هنا. العمل غير المباشر على مستندات جوجل باستخدام التعليقات والاقتراحات قد لا يكون وجود مساهمين في نفس الوقت لمناقشة موضوع معين خاص بمحتوى المستند أمرًا ضروريًا في معظم الأحيان، كما قد يكتفي كل طرف بإنجاز ما يوكل إليه من مهام مع الإشارة إلى ما أنجزه، أو الاستفسار عن نقطة معينة، أو لفت الانتباه إلى وجود مشكلة ما من خلال ما يُعرف بالتعليق أو اقتراح استبدال محتوى بآخر، وهذا ما يُدعى اقتراحًا. التعليقات في مستندات جوجل وهي طريقة يشير فيها أحد المساهمين إلى مشكلة أو موضوع معين في مكان معين من المستند ويسند أمر متابعته إلى مساهم معين. بالعودة إلى مثالنا التطبيقي "نصائح عن تبادل الأفكار في مستندات جوجل" كما في لقطة الشاشة السابقة، سيلاحظ المحرر المساعد مثلًا تكرار الفقرة الأولى والثانية ويرغب في الاستفهام عن الموضوع قبل أن يعدّل أي شيء. بإمكانه في هذه الحالة تعليم عنوان الفقرة الأولى ثم النقر على أيقونة التعليق ضمن قائمة خيارات الأوضاع الجانبية لتظهر النافذة التالية: اكتب نص التعليق ثم انقر بعد ذلك المفتاح Enter لتنتقل إلى سطر جديد. ابدأ السطر الجديد بالمحرف @ يليه البريد الإلكتروني للمحرر الرئيسي، وعند كتابة عنوان بريد صحيح أو اسم موجود في قائمة الاتصال، سيظهر في نافذة التعليق مربع تحقق من أنك تريد فعلًا إسناد المهمة إلى المساهم الذي ذكرته في التعليق. فعّل مربع التحقق ثم انقر على الزر "تعيين" أسفل النافذة. سيرى المساهم الرئيسي التعليق في موضعه الذي حدده المحرر المساعد عندما يفتح المستند ويلاحظ أن مهمة الإجابة عن الإستفسار منوطة به وحده، كما سيتلقى في نفس الوقت بريدًا إلكترونيًا يلفت انتباهه إلى ما جرى مؤخرًا ضمن المستند كما توضح لقطتي الشاشة المتجاورتين التاليتين: يمكن للمحرر الرئيسي أن يحل الأمر مباشرةً وينهي المناقشة عن طريق النقر على الأيقونة التي تحمل إشارة "صحيح" أعلى نافذة التعليق (ستظهر العبارة "وضع علامة كتم وإخفاء المناقشة" عند وصول مؤشر الفأرة إليها)، وقد يعيد الأمر إلى صاحب التعليق الأساسي أو أي مساهم آخر لتوجيهه إلى طريقة حل الموضوع، وذلك بالنقر على نافذة التعليق، ثم كتابة المطلوب والإشارة بالمحرف @ إلى المساهم المطلوب كما في الشكل التالي: الاقتراحات والوجوه التعبيرية في مستندات جوجل قد يرى أحد المحررين طريقةً أفضل في صياغة محتوى معين أو إدراج محتوى آخر أكثر ملاءمةً لموضوع المستند، لكنه يرغب في استشارة فريق العمل، ولهذا يتيح تطبيق مستندات جوجل وضعًا يُسمى وضع الاقتراح يمكن تفعيله بأن تنقر على قائمة الأوضاع الجانبية التي تظهر على يمين المستند أو من خلال أشرطة الأدوات أعلى المستند: لنفترض أن أحد المحررين وجد أنّ استبدال عبارة "بشكل أسرع" بالكلمة "بسرعة" أمر أفضل من ناحية صياغة النص وأراد أن يقترح الأمر على محرر محدد. عليه في هذه الحالة اختيار وضع الاقتراح، ثم تحديد المكان الذي وردت فيه العبارة وكتابة الكلمة "بسرعة" ليلاحظ أنها تزيح العبارة السابقة التي تظهر مشطوبة دون أن تزيلها ويحيط إطار أخضر بمكانها. تظهر في نفس الوقت نافذة الاقتراح على يسار المستند توضح ما يُقترح استبداله (أو على اليمين إذا كان بالنسخة العربية)، يمكنك عندها ترك النافذة كما هي لتظهر عند كل المساهمين أو الإشارة إلى مساهم محدد من خلال بريده الإلكتروني بعد المحرف @: يمكن عندها للمساهم الآخر قبول الاقتراح بالنقر على أيقونة "موافق" أعلى نافذة الاقتراح أو رفضه بالنقر على أيقونة "مرفوض". لكن إن أردت أن تلفت انتباه المساهمين إلى عبارة أو صورة أو أي محتوى دون أن تكتب شيئًا، فقط أن تشير إلى هذا الموضوع بأسلوب ظريف ومريح لا يحمل معاني ضغط العمل، بإمكانك النقر على أيقونة الوجوه التعبيرية ضمن قائمة الأوضاع الجانبية واختيار الوجه التعبير الذي تريد وسيراه الجميع أينما وضعته. العمل على مستند جوجل بصفة معلق أو عارض في الواقع، لن تتغيّر الأمور كثيرًا من ناحية التفاصيل التي تحدثنا عنها سابقًا، إلا أن المعلّق لن يكون قادرًا على تغيير أي عبارة بل أن يقترح تغييرها فقط، ويجري التغيير أو الاستبدال في حال وافق محرر على هذا الاقتراح، كما يُتاح للمعلق بطبيعة الحال ترك تعليقات وحضور اجتماعات أو تحضيرها واستخدام المحادثة المباشرة. ستجرد المساهم عند دخوله إلى المستند بصفة "عارض" من كل إمكانيات التحرير وإضافة التعليقات أو الاقتراحات ويبدو شكل نافذة التطبيق كالتالي: لكن يمكن أن يطلب العارض من مالك المستند حصرًا إذنًا بالتعديل بالنقر على زر "طلب الإذن بالتعديل" أو مشاركة آخرين بالنقر على زر "مشاركة". خلاصة تعرفنا في هذا المقال على الطرق التي يمكن بها للمساهمين التواصل لإكمال العمل على مستند جوجل. ورأينا أن هنالك حالات يُضطر فيها المساهمون إلى العمل المتزامن (في الوقت ذاته)، لذلك تحدثنا عن التلميح المباشر في المكان بمجرد تعليم العبارة النصية أو استخدام المحادثة الكتابية المباشرة، كما تحدثنا عن أهمية تحديد اجتماع عام من خلال جوجل مييت لمناقشة مواضيع شتى تتعلق بالمستند أو بمستندات جوجل أو غيرها من النقاط التي تتطلب اجتماعًا موسّعًا. أشرنا أيضًا إلى إمكانيات مستندات جوجل في دعم التعاون غير المباشر من خلال التعليقات والاقتراحات وطريقة إسناد المهام إلى المساهمين المختلفين من خلال لقطات شاشة متنوعة وشرح موسع لمعظم الأفكار المتعلقة بهذا الشكل من التعاون. اقرأ أيضًا المشاركة وتوزيع المهام على مستندات جوجل الجداول في تطبيق مستندات جوجل - إنشاء سيرة ذاتية. إعداد الصفحة وتنسيق الفقرات في مستندات جوجل إدراج المعادلات والكائنات الرسوميَّة في تطبيق مستندات جوجل
  18. تقدِّم HTML إضافةً إلى العناصر التي تحدد أجزاءً مستقلةً في الصفحة -مثل الفقرات النصية والصور- عددًا من العناصر الكتلية Block elements لتحدد مناطق كاملة ضمن صفحة ويب مثل ترويسة الصفحة Header أو قوائم التنقل Navigation menu أو أعمدة المحتوى الأساسي main content columns، إذ سيبحث هذا المقال في كيفية التخطيط لوضع هيكلية بسيطة لموقع ويب وكتابة شيفرة HTML المناسبة. عليك قبل البدء في قراءة هذا المقال الاطلاع على أساسيات HTML التي أوردناها في مقال تعرَّف على HTML وكذلك هيكلة النصوص في HTML ومقال طريقة إنشاء الروابط التشعبية. الأقسام الأساسية لصفحة ويب تبدو صفحات ويب مختلفةً عن بعضها لكنها تميل لأن تتشارك مكونات أو أقسامًا معياريةً، ما لم تعرض فيديو على كامل الشاشة أو لعبة ما أو أن تكون جزءًا من مشروع فني أو أن تكون سيئة الهيكلة: الترويسة Header: يظهر عادة على أساس شريط واسع أعلى الصفحة يضم عنوانًا عريضًا وشعارًا، كما يبقى هذا القسم نفسه في كل صفحات الموقع على الأغلب. شريط التنقل Navigation bar: يرتبط بالأقسام الرئيسية للموقع ويُعرض على هيئة أزرار أو روابط أو نوافذ ضمنية tabs، كما يبقى عادةً محتوى هذا الشريط كما هو في جميع صفحات الموقع تمامًا مثل محتوى الترويسة، فعدم وجود هذا الشريط في صفحة ما سيربك المستخدِم ويدفعه إلى ترك الموقع، كما يعتقد الكثير من المصممين أنّ أشرطة القوائم لا بد وأن تكون جزءًا من الترويسة، لكنها تبقى مجرد رؤية غير ملزمة، لأن البعض يجادل أيضًا في أفضلية وجودهما مستقلين من منظور سهولة الوصول أو الشمولية accessibility، إذ يمكن لقارئات الشاشة أن تقرأهما بصورة أفضل في حال كانا منفصلين. المحتوى الرئيسي main content: يُعَدّ منطقةَ واسعةَ منتصف الصفحة تتضمن المحتوى الخاص بصفحة الويب، مثل الفيديو الذي تريد مشاهدته أو القصة التي تريد قراءتها أو الخرائط التي تريد رؤيتها أو عناوين الأخبار أو غير ذلك، وهذا القسم بالتحديد سيختلف بكل تأكيد من صفحة لأخرى. الشريط الجانبي sidebar: يضم بعض المعلومات على أطراف الصفحة مثل الروابط أو الاقتباسات أو الإعلانات وغيرها، يتعلق محتوى هذا القسم بما يرد في المحتوى الرئيسي عادةً، فقد تجد في الشريط الجانبي لمحةً عن صاحب الصفحة أو روابط إلى مواضيع مشابهة، كما قد تجد أحيانًا بعض العناصر الاحتياطية مثل قوائم تنقل إضافية. تذييل الصفحة footer: شريط يمتد أسفل الصفحة ويضم عادة رسومات دقيقة وملاحظات عن حقوق النشر أو معلومات التواصل، ويُستخدَم هذا القسم لإدراج معلومات عامة كما هو حال الترويسة، لكن هذه المعلومات ليست حيويةً أو مكملة لما يعرضه الموقع، كما يُستخدَم التذييل أحيانًا لأغراض تحسين محركات البحث Search Engine Optimization، إذ يعرض أحيانًا روابط سريعة للوصول إلى محتوى ذو شعبية كبيرة على الويب. قد يُبنى هيكل موقع ويب نموذجي مشابه لما تعرضه الصورة التالية: استخدام HTML في هيكلة المحتوى لا يُعَدّ النموذج في الصورة السابقة جميلًا لكنه جيد في توضيح تخطيط موقع ويب نموذجي، إذ تحتوي بعض المواقع على أعمدة أكثر وبعضها يبدو أعقد، لكن الفكرة واضحة، كما تستطيع من خلال تنسيقات CSS استخدام أية عناصر تريد لتغلف بها أيّ قسم ليبدو بالصورة الذي تريدها، لكن وكما تحدثنا سابقًا لا بد من احترام دلالات عناصر HTML واستخدام العنصر الصحيح للغاية الصحيحة. لا يخبرك ما تراه على الصفحة كل شيء، إذ تُستخدَم الخطوط والألوان للفت انتباه المستخدِم إلى الأقسام الأكثر فائدةً في الصفحة مثل قوائم التنقل والروابط المتعلقة بها، لكن ماذا لو أردنا الانتباه إلى الأشخاص ذوي المشاكل البصرية؟ فلن يهتم هؤلاء باللون الزهري أو بالخط الكبير. ملاحظة: يُقدَّر عدد المصابين بعمى الألوان من الذكور بحدود 8% ومن النساء بحدود 0.5%، أي رجل من بين 12 وامرأة من بين 200، في حين ويمثِّل المصابون بالعمى وأصحاب المشاكل البصرية الجادة نسبة 4-5% من تعداد البشر، وقد كان عددهم قرابة 285 مليون عالميًا عام 2012 عندما كان تعداد سكان الأرض سبعة مليارات. تستطيع من خلال HTML توصيف أقسام المحتوى وفقًا لوظيفتها، فبإمكانك استخدام عناصر تعكس وظيفة هذا القسم من المحتوى بشكل واضح لا لبس فيه، وبالتالي ستتمكن التكنولوجيا المساعدِة مثل قارئات الشاشة من تمييز هذه العناصر والمساعدة في إنجاز مهام مختلفة مثل إيجاد العناوين أو قوائم التنقل أو المحتوى الرئيسي، وكما أشرنا في مقالات سابقة، هنالك عواقب عديدة لعدم استخدام هيكلية صحيحة للعناصر أو إهمال المعنى الدلالي لها. تقدِّم لك HTML مجموعةَ من العناصر لإنجاز هيكلية دلالية تعكس وظيفة كل قسم منها: الترويسة header: يمثلها العنصر <header>. شريط التنقل navigation bar: يمثله العنصر <nav>. المحتوى الرئيسي main content: يمثله العنصر <main>، بالإضافة إلى العديد من العناصر التي تمثل أقسامًا فرعيةً للمحتوى الرئيسي مثل <section> و <div>. الشريط الجانبي sidebar: يمثله العنصر <aside>. التذييل footer: يمثله العنصر <footer>. تطبيق: فهم شيفرة الصفحة النموذجية السابقة تمثِّل الشيفرة التالية شيفرة الموقع النموذجي الذي عرضنا صورته سابقًا، ويمكنك تنزيلها أيضًا من المستودع المخصص للمثال على جيت-هب GitHub، كما نطلب منك إلقاء نظرة على الشيفرة ومن ثم العودة إلى القائمة في الأسفل لتحديد أجزاء الشيفرة وما تمثله من أقسام. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>My page title</title> <link href="https://fonts.googleapis.com/css?family=Open+Sans+Condensed:300|Sonsie+One" rel="stylesheet" type="text/css"> <link rel="stylesheet" href="style.css"> <!--الدلالية في النسخ HTML5 الأسطر الثلاث التالية هي طريقة لتعمل عناصر الأقدم من متصفح إنترنت إكسبلورر--> <!--[if lt IE 9]> <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js"></script> <![endif]--> </head> <body> <!-- الترويسة الرئيسية التي ستستخدم في جميع صفحات الموقع --> <header> <h1>Header</h1> </header> <nav> <ul> <li><a href="#">Home</a></li> <li><a href="#">Our team</a></li> <li><a href="#">Projects</a></li> <li><a href="#">Contact</a></li> </ul> <!-- استمارة بحث وهو أسلوب غير مباشر للتنقل عبر الموقع --> <form> <input type="search" name="q" placeholder="Search query"> <input type="submit" value="Go!"> </form> </nav> <!-- المحتوى الرئيسي للموقع --> <main> <!-- يتضمن مقالة--> <article> <h2>Article heading</h2> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Donec a diam lectus. Set sit amet ipsum mauris. Maecenas congue ligula as quam viverra nec consectetur ant hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur.</p> <h3>Subsection</h3> <p>Donec ut librero sed accu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor.</p> <p>Pelientesque auctor nisi id magna consequat sagittis. Curabitur dapibus, enim sit amet elit pharetra tincidunt feugiat nist imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros.</p> <h3>Another subsection</h3> <p>Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum soclis natoque penatibus et manis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est.</p> <p>Vivamus fermentum semper porta. Nunc diam velit, adipscing ut tristique vitae sagittis vel odio. Maecenas convallis ullamcorper ultricied. Curabitur ornare, ligula semper consectetur sagittis, nisi diam iaculis velit, is fringille sem nunc vet mi.</p> </article> <!-- يمكن للمحتوى الجانبي أن يتداخل مع المحتوى الرئيسي --> <aside> <h2>Related</h2> <ul> <li><a href="#">Oh I do like to be beside the seaside</a></li> <li><a href="#">Oh I do like to be beside the sea</a></li> <li><a href="#">Although in the North of England</a></li> <li><a href="#">It never stops raining</a></li> <li><a href="#">Oh well...</a></li> </ul> </aside> </main> <!-- التذييل الرئيسي الذي سيستخدم في جميع صفحات الموقع --> <footer> <p>©Copyright 2050 by nobody. All rights reversed.</p> </footer> </body> </html> خذ وقتك في الاطلاع على الشيفرة وفهمها، كما ستساعدك التعليقات التي أدرجناها ضمن الشيفرة في ذلك، ولا نطلب منك فعل أيّ شيء آخر، لأنّ المفتاح الأساسي لفهم مخطط صفحة أو ملف هو كتابة هيكل HTML ومن ثم إبرازه عبر تنسيق CSS. تفاصيل عناصر تخطيط HTML من الجيد فهم المعنى العام لجميع العناصر التي تعرّف الأقسام المختلفة في صفحة HTML، وهذا ما ستعمل عليه لاحقًا عندما تكتسب خبرةَ أكبر في تطوير الويب، لكن ما عليك استيعابه الآن هي التعريفات التالية: العنصر <main>: يستخدَم لإدراج المحتوى الفريد للصفحة، وينبغي استخدام هذا العنصر مرةً واحدةً في الصفحة، كما يوضَع مباشرةً ضمن العنصر <body>، ولا يجب أن يتداخل هذا العنصر مع عناصر أخرى. العنصر <article>: يغلِّف كتلًا من المحتوى المترابط القائم بذاته والذي لا يتعلق بمحتوى بقية الأقسام مثل المنشورات المستقلة. العنصر <section>: يشابه العنصر <article> لكنه يستخدَم لتحضير جزء من الصفحة ليقدّم وظيفةً بمفرده مثل خريطة صغيرة أو مجموعة من العناوين أو الملخصات لمقالات أو ليقدم موضوعًا محددًا، كما يُعَدّ استخدام عناصر العناوين في بداية كل عنصر <section> ممارسةً جيدةً، وتجدر الملاحظة أنه بالإمكان تجزئة محتوى العنصر <article> إلى أقسام مختلفة وتجزئة القسم <section> إلى مقالات مختلفة وفقًا للسياق. العنصر <aside>: يضم محتوًى لا يتعلق مباشرةً بالمحتوى الرئيسي للصفحة لكنه يزوِّد الصفحة بمعلومات إضافية مثل مدخلات مختصرة أو سيرة ذاتية للمؤلف أو روابط متعلقة بالمحتوى وغيرها. العنصر <header>: يضم محتوًى تعريفيًا للصفحة، فإذا كان هذا العنصر ابنًا للعنصر <body>، فسيمثل الترويسة العامة للصفحة، في حيم إذا كان ابنًا للعنصر <section>، فسيمثل ترويسةً خاصةً بهذا القسم، وحاول عدم الخلط بين الترويسة وعنوان صفحة والعناوين الأخرى. العنصر <nav>: يؤمن وظيفة التنقل الأساسية في الصفحة، ولا يضم روابط ثانوية وما شابهها. العنصر <footer>: يضم محتوًى مناسبًا لتذييل الصفحة. يمكنك الاطلاع على كل عنصر من العناصر السابقة بالعودة إلى توثيق HTML العربي في موسوعة حسوب. عناصر غير دلالية قد لا تجد في بعض الأحيان عنصرًا دلاليًا مناسبًا لتغليف محتوى معيّن، وقد ترغب في أحيان أخرى ضم مجموعة من العناصر مع بعضها لكي تتعامل معها على أساس كيان واحد عند استخدام CSS أو جافاسكربت، إذ تقدِّم HTML في حالات مثل هذه العنصر <div> الذي يُفضَّل استخدامه مع السمة class لكي تعطيه تسميةً محددةً فيسهل استهدافه، كما يُعَدّ العنصر <span> عنصرًا سطريًا غير دلالي ينبغي استخدامه فقط إذا لم تجد عنصرًا دلاليًا مناسبًا لتغليف المحتوى المطلوب أو لم ترد إعطاء معنى محدد لهذا المحتوى، وإليك مثالًا كما يلي: <p>، دخل الملك إلى غرفته مترنحًا في الواحدة ليلًا لم يساعده الدواء كثيرًا وجر نفسه عبر الباب <span class="editor-note">[ملاحظة للمحرر: في هذه اللحظة ينبغي تخفيض الأضواء]</span>.</p> يُفترَض في هذه الحالة أن تقدِّم ملاحظة المحرر الموسومة بالصنف "editor-note" توجيهات إضافيةً للقارئ بما أنها لا تحمل أي دلالة إضافية، ويمكن حل هذا الموضوع ببساطة باستخدام CSS أيضًا، وذلك بإزاحة الملاحظة قليلًا عن النص الرئيسي. يُعَدّ العنصر <div> عنصرًا كتليًا غير دلالي ينبغي استخدامه فقط إذا لم تجد عنصرًا دلاليًا كتليًا مناسبًا لتغليف المحتوى المطلوب أو لم ترد إعطاء معنى محدد لهذا المحتوى، فتخيل على سبيل المثال قائمة تسوّق يمكنك طلبها في أيّ وقت من موقع تجاري: <div class="shopping-cart"> <h2>Shopping cart</h2> <ul> <li> <p><a href=""><strong>Silver earrings</strong></a>: $99.95.</p> <img src="../products/3333-0985/thumb.png" alt="Silver earrings"> </li> <li> ... </li> </ul> <p>Total cost: $237.89</p> </div> لا تُعَدّ هذه القائمة محتوىً جانبيًا <aside> في الواقع، فقد لا تتعلق بالضرورة بالمحتوى الرئيسي للصفحة وتريدها أن تظهر في أيّ مكان ضمن الموقع، كما ليس من المجدي استخدام العنصر <section> لكونها لا تمثل جزءًا من المحتوى الرئيسي، وبالتالي يكون استخدام العنصر <div> في هذه الحالة مناسبًا، كما استخدمنا معه عنوانًا لمساعدة قارئات الشاشة على إيجاده. تنبيه: يُعَدّ استخدام العنصر <div> مريحًا وسهلًا للغاية، فهو لا يحمل أيّ معنى دلالي، وإنما يُجمّع شيفرة HTML ضمن كيان واحد، ولكن انتبه إلى استخدامها فقط عندما لا تجد عنصرًا دلاليًا مناسبًا، وحاول التقليل من استخدامه إلى الحد الأدنى كي لا تعاني في صيانة الموقع لاحقًا. بادئات الأسطر الجديدة والخطوط الأفقية يوجد عنصران ستستخدمهما في مناسبات عدة ولا بد من الاطلاع عليهما: عنصر الانتقال إلى سطر جديد <br>: يدرِج هذا العنصر سطرًا جديدًا ضمن فقرة نصية، ويُعَدّ هذا العنصر الأسلوب الوحيد لتقسيم كتلة نصية أو بنية محددة إلى سلسلة من الأسطر القصيرة مثل حالة عنوان بريدي أو قصيدة شعرية، وإليك مثالًا كما يلي: <p>There once was a man named O'Dell<br> Who loved to write HTML<br> But his structure was bad, his semantics were sad<br> and his markup didn't read very well.</p> سيُصيَّر أو يُعرَض النص في الفقرات على صورة خط طويل دون استخدام العنصر <br>، أما بوجوده، فسيصيِّر المتصفح الفقرة النصية على الصورة التالية: عنصر إدراج سطر أفقي <hr>: يُنشئ العنصر خطًا أفقيًا في الصفحة لكي يعرض تغيرًا في سمة النص مثل تغير الموضوع أو المشهد، وإليك مثالًا كما يلي: <p>Ron was backed into a corner by the marauding netherbeasts. Scared, but determined to protect his friends, he raised his wand and prepared to do battle, hoping that his distress call had made it through.</p> <hr> <p>Meanwhile, Harry was sitting at home, staring at his royalty statement and pondering when the next spin off series would come out, when an enchanted distress letter flew through his window and landed in his lap. He read it hazily and sighed; "better get back to work then", he mused.</p> إذ سيصيَّر كما يلي: التخطيط لموقع ويب بسيط ستكون الخطوة المنطقية التالية حالما تنتهي من وضع خطط هيكلة موقعك البسيط هي العمل على تحديد المحتوى الذي يضمه الموقع وما الصفحات التي تحتاجها وكيف سترتبها وتربطها ببعضها لكي تقدِّم لمستخدِميك أفضل تجربة، وتُدعى هذه الخطوة بمعمارية المعلومات، وقد يحتاج الأمر في المواقع الضخمة والمعقدة إلى الكثير من التخطيط، لكن لموقع بسيط مثل الذي نشير إليه في هذا المقال، فالعملية أسهل وأكثر متعة. تذكّر أنه لديك مجموعة من العناصر التي ستتكرر في معظم -إن لم يكن في كل- الصفحات مثل قوائم التنقل والمحتوى الموجود في تذييل الصفحة، فمن الجيد مثلًا -إذا كان موقعك لإدارة عمل ما- وضع معلومات للتواصل في تذييل جميع الصفحات، ولهذا ينبغي عليك تدوين ما الذي تريده أن يكون مشتركًا في صفحاتك. ارسم لوحة بسيطة لما تتوقع أن يكون عليه هيكل الصفحة (فقد تريدها أن تبدو مماثلة للموقع النموذجي الذي عرضناه في الأعلى). دوّن ما سيكون عليه كل جزء. فكِّر الآن بما تريده من المحتوى غير المشترك بين الصفحات لتضعه في موقعك، ودوِّن ذلك في قائمة. حاول تصنيف عناصر المحتوى الذي تختاره في مجموعات لكي تتشكل لديك أفكار عن المجموعات التي يمكن أن تقع في الصفحات نفسها. حاول بعد ذلك وضع خريطة للموقع على هيئة فقاعات لكل صفحة ثم صل بخطوط بينها لتوضيح طبيعة التواصل بينها، إذ ستكون الصفحة الرئيسية في المنتصف وسترتبط بمعظم الصفحات، وينبغي أن ترتبط كل الصفحات في المواقع البسيطة بالصفحة الرئيسية مع وجود بعض الاستثناءات، كما عليك أيضًا تدوين بعض الملاحظات عن كيفية عرض الأشياء، وهذا أمر مشابه لتقنية تُدعى تصنيف البطاقات. تطبيق: أنشئ خريطة موقع خاصة بك حاول إنشاء خريطة لموقعك، ما الذي تريده من موقعك؟ ملاحظة: احفظ عملك في مكان مناسب، فقد تحتاج إليه لاحقًا. خلاصة من المفترض أنك كوَّنت صورةً واضحةً عن هيكلة صفحة أو موقع ويب، وسنناقش في المقال التالي كيف ننقح الأخطاء في HTML. ترجمة -وبتصرُّف- للمقال Document and website structure. اقرأ أيضا تعلم لغة HTML هيكلة وتوزيع محتوى صفحات الويب تصميم هيكل صفحة الويب والعناصر الأساسية في مجال تجربة المستخدم الفرق بين صفحة الويب وموقع الويب وخادم الويب ومحرك البحث
  19. نلقي نظرةً في هذا المقال على الطرق المتبعة في نقل التغييرات التي تجري أثناء جلسة العمل إلى قاعدة البيانات العلاقية وتخزينها، إضافةً إلى ربط الجداول ببعضها من خلال المفاتيح الخارجية foreign keys. ملفات التهجير Migrations لنتابع في توسعة تطبيق الملاحظات الذي عملنا عليه في كل من مقال العمل مع قواعد بيانات علاقية باستخدام Sequelize والمقال السابق، إذ سننجز آليةً تمكّن بعض المستخدمين الذي يمتلكون ميزةً إدارية admin status من إيقاف نشاط مستخدمين آخرين ومنعهم من تسجيل الدخول أو إنشاء ملاحظات جديدة. نحتاج لإنجاز الأمر إلى حقل يأخذ قيمًا منطقيةً في جدول قاعدة البيانات يشير إلى المستخدمين ذوي الامتيازات الإدارية وأخرى تدل على حالة المستخدم إن كان نشاطه معلقًا أم لا. يمكننا العمل وفق الآلية السابقة بتعديل النموذج الذي يعرّف الجدول والاعتماد على مكتبة Sequelize في مزامنة التغييرات مع قاعدة البيانات من خلال أسطر الشيفرة التالية في الملف "models/index.js": const Note = require('./note') const User = require('./user') Note.belongsTo(User) User.hasMany(Note) Note.sync({ alter: true }) User.sync({ alter: true }) module.exports = { Note, User } ولكن يُعد هذا الأسلوب (تغيير النموذج كلما أردنا توسعة التطبيق) غير منطقي على المدى الطويل، لهذا سنزيل الأسطر التي تدعم مزامنة التغييرات ونستخدم أسلوبًا أكثر قوةً وهو ملفات التهجير migrations التي تقدمها Sequelize وغيرها من المكتبات. ملف التهجير migration عمليًا هو ملف جافاسكربت JavaScript وحيد يصف التعديلات التي تطرأ على قاعدة البيانات، وينشأ عن تغير وحيد أو مجموعة تغيرات معًا. تحتفظ Sequelize بسجلات عن التهجيرات التي أجريت، أي التغيرات التي طرأت وجرى نقلها ومزامنتها مع قاعدة البيانات، إذ تبقى Sequelize مطلّعةً على التغيرات التي طرأت على مخطط قاعدة البيانات ولم تُطبّق بعد عند إنشاء تهجير جديد، ويمكن بهذه الطريقة التحكم بطريقة تطبيق التغييرات من خلال شيفرة البرنامج المخزنة في منظومة التحكم بالإصدار version control. لننشئ أولًا ملف تهجير يهيئ قاعدة البيانات. إليك شيفرة هذا الملف: const { DataTypes } = require('sequelize') module.exports = { up: async ({ context: queryInterface }) => { await queryInterface.createTable('notes', { id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, content: { type: DataTypes.TEXT, allowNull: false }, important: { type: DataTypes.BOOLEAN }, date: { type: DataTypes.DATE }, }) await queryInterface.createTable('users', { id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, username: { type: DataTypes.STRING, unique: true, allowNull: false }, name: { type: DataTypes.STRING, allowNull: false }, }) await queryInterface.addColumn('notes', 'user_id', { type: DataTypes.INTEGER, allowNull: false, references: { model: 'users', key: 'id' }, }) }, down: async ({ context: queryInterface }) => { await queryInterface.dropTable('notes') await queryInterface.dropTable('users') }, } يعرِّف الملف الدالتين up و down؛ إذ تحدّد الأولى كيفية تعديل قاعدة البيانات عندما يجري تهجير؛ وتحدد الثانية آلية التراجع إن كان هناك مبررٌ لذلك. يتضمن ملف التهجير ثلاثة خيارات؛ إذ يُنشئ الخيار الأول الجدول notes؛ بينما ينشئ الثاني الجدول users؛ ويضيف الخيار الثالث مفتاحًا خارجيًا إلى الجدول notes يشير إلى مُنشئ الملاحظة، وتُحدَّد التغيرات في المخطط باستدعاء توابع الكائن queryInterface. و على نقيض النماذج، من الضروري أن تتذكر دائمًا كتابة أسماء الأعمدة والجداول بأسلوب الأفعى snake case عند تعريف ملف تهجير: await queryInterface.addColumn('notes', 'user_id', { type: DataTypes.INTEGER, allowNull: false, references: { model: 'users', key: 'id' }, }) أي ستكتب أسماء الجداول والأعمدة كما تظهر تمامًا في قاعدة البيانات، بينما تُكتب في النماذج بالأسلوب التقليدي للمكتبة Sequelize وذلك عبر استخدام أسلوب سنام الجمل camel Case. خزّن شيفرة ملف التهجير في ملف يحمل الاسم "migrations/20211209_00_initialize_notes_and_users.js". لا بُد أن تُسمى ملفات التهجير أبجديًا عند إنشائها كي تكون التغيرات القديمة قبل الجديدة، وقد تبدأ اسم الملف بتاريخ وتسلسل هذا الملف وهذه طريقةٌ جيدة. يمكننا تشغيل ملف التهجير انطلاقًا من سطر الأوامر باستخدام أداة سطر الأوامر الخاصة بالمكتبة Sequelize، لكننا قررنا تنفيذ التهجيرات يديويًا انطلاقًا من شيفرة البرنامج باستخدام المكتبة Umzug، لهذا سنثبت هذه المكتبة: npm install umzug لنعدّل الملف "util/db.js" الذي يتكفل بمعالجة الاتصال مع قاعدة البيانات: const Sequelize = require('sequelize') const { DATABASE_URL } = require('./config') const { Umzug, SequelizeStorage } = require('umzug') const sequelize = new Sequelize(DATABASE_URL, { dialectOptions: { ssl: { require: true, rejectUnauthorized: false } }, }); const runMigrations = async () => { const migrator = new Umzug({ migrations: { glob: 'migrations/*.js', }, storage: new SequelizeStorage({ sequelize, tableName: 'migrations' }), context: sequelize.getQueryInterface(), logger: console, }) const migrations = await migrator.up() console.log('Migrations up to date', { files: migrations.map((mig) => mig.name), }) } const connectToDatabase = async () => { try { await sequelize.authenticate() // highlight-start await runMigrations() // highlight-end console.log('connected to the database') } catch (err) { console.log('failed to connect to the database') console.log(err) return process.exit(1) } return null } module.exports = { connectToDatabase, sequelize } تُنفّذ الدالة runMigrations ملف التهجير في كل مرة يؤسِّس فيها التطبيق اتصالًا مع قاعدة بيانات عند تشغيله، كما تراقب مكتبة Sequelize التهجيرات المُكتملة، فإذا لم يكن هناك تهجيرات جديدة، فلن ينفع تشغيل الدالة runMigrations في أي شيء. لنبدأ كل شيء من جديد، ونزيل كل الجداول الموجودة من التطبيق: username => drop table notes; username => drop table users; username => \d Did not find any relations. عند تشغيل التطبيق، تظهر رسالةً على الطرفية تحيطك علمًا بوضع التهجيرات: INSERT INTO "migrations" ("name") VALUES ($1) RETURNING "name"; Migrations up to date { files: [ '20211209_00_initialize_notes_and_users.js' ] } database connected إذا أعدنا تشغيل التطبيق، سيُظهر السجل أن التهجير لم يتكرر، وسيبدو مخطط قاعدة بيانات التطبيق على النحو التالي: username=> \d List of relations Schema | Name | Type | Owner --------+--------------+----------+---------------- public | migrations | table | username public | notes | table | username public | notes_id_seq | sequence | username public | users | table | username public | users_id_seq | sequence | username وهكذا نرى أن Sequelize قد أنشأت جدولًا للتهجيرات يسمح بتتبع ما نُفِّذ منها، ويبدو هذا الجدول على النحو التالي: username=> select * from migrations; name ------------------------------------------- 20211209_00_initialize_notes_and_users.js دعونا نضيف الآن بعض المستخدمين إلى قاعدة البيانات، إضافةً إلى مجموعة من الملاحظات، وبعدها نكون جاهزين لتوسعة التطبيق. يمكنك الحصول على شيفرة التطبيق بوضعه الحالي من المستودع المخصص له على GitHub ضمن الفرع part13-6. مستخدم بصلاحيات مدير وتعطيل مستخدم آخر علينا بدايةً إضافة حقلين يقبلان قيمًا منطقية إلى الجدول users، هما: admin: ويحدد فيما لو كان المستخدم مديرًا أم لا. disabled: ويحدد فيما لو أُوقف نشاط هذا المستخدم أم لا. لننشئ أيضًا ملف تهجير يُعدّل قاعدة بيانات في الملف "migrations/20211209_01_admin_and_disabled_to_users.js": const { DataTypes } = require('sequelize') module.exports = { up: async ({ context: queryInterface }) => { await queryInterface.addColumn('users', 'admin', { type: DataTypes.BOOLEAN, default: false }) await queryInterface.addColumn('users', 'disabled', { type: DataTypes.BOOLEAN, default: false }) }, down: async ({ context: queryInterface }) => { await queryInterface.removeColumn('users', 'admin') await queryInterface.removeColumn('users', 'disabled') }, } عدّل النموذج بما يتوافق مع الجدول "users": User.init({ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, username: { type: DataTypes.STRING, unique: true, allowNull: false }, name: { type: DataTypes.STRING, allowNull: false }, admin: { type: DataTypes.BOOLEAN, defaultValue: false }, disabled: { type: DataTypes.BOOLEAN, defaultValue: false }, }, { sequelize, underscored: true, timestamps: false, modelName: 'user' }) يتغير المخطط كما نريد، عندما تُنفَّذ شيفرة ملف التهجير عند إعادة تشغيل التطبيق: username-> \d users Table "public.users" Column | Type | Collation | Nullable | Default ----------+------------------------+-----------+----------+----------------------------------- id | integer | | not null | nextval('users_id_seq'::regclass) username | character varying(255) | | not null | name | character varying(255) | | not null | admin | boolean | | | disabled | boolean | | | Indexes: "users_pkey" PRIMARY KEY, btree (id) "users_username_key" UNIQUE CONSTRAINT, btree (username) Referenced by: TABLE "notes" CONSTRAINT "notes_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) لنوسّع الآن المتحكمات على النحو التالي، بحيث نمنع المستخدم من تسجيل الدخول في حال كانت قيمة الحقل disabled هي true: loginRouter.post('/', async (request, response) => { const body = request.body const user = await User.findOne({ where: { username: body.username } }) const passwordCorrect = body.password === 'secret' if (!(user && passwordCorrect)) { return response.status(401).json({ error: 'invalid username or password' }) } if (user.disabled) { return response.status(401).json({ error: 'account disabled, please contact admin' }) } const userForToken = { username: user.username, id: user.id, } const token = jwt.sign(userForToken, process.env.SECRET) response .status(200) .send({ token, username: user.username, name: user.name }) }) دعونا نوقف نشاط المستخدم "jakousa" بالاستعانة بمعرّفه الفريد ID: username => update users set disabled=true where id=3; UPDATE 1 username => update users set admin=true where id=1; UPDATE 1 username => select * from users; id | username | name | admin | disabled ----+----------+------------------+-------+---------- 2 | lynx | Kalle Ilves | | 3 | jakousa | Jami Kousa | f | t 1 | mluukkai | Matti Luukkainen | t | تأكد من نجاح الأمر بفشل تسجيل دخوله: لننشئ تاليًا وجهةً تسمح للمدير بتغيير حالة حساب مستخدم آخر: const isAdmin = async (req, res, next) => { const user = await User.findByPk(req.decodedToken.id) if (!user.admin) { return res.status(401).json({ error: 'operation not allowed' }) } next() } router.put('/:username', tokenExtractor, isAdmin, async (req, res) => { const user = await User.findOne({ where: { username: req.params.username } }) if (user) { user.disabled = req.body.disabled await user.save() res.json(user) } else { res.status(404).end() } }) تُستخدم في هذه الشيفرة أداتان وسطيتان، تُدعى الأولى tokenExtractor وتماثل الأداة التي استخدمناها لإنشاء الوجهة route الخاصة بإنشاء ملاحظة، إذ تضع مفتاح الاستيثاق الذي فُكّ تشفيره في الحقل decodedToken من كائن الطلب؛ بينما تتحقق الأداة الثانية isAdmin فيما لوكان المستخدم ذا صلاحيات إدارية، فإن لم يكن كذلك يُضبط رمز حالة الطلب على القيمة 401 وتُعاد رسالة خطأ مناسبة. لاحظ كيف رُبطت الأداتين إلى الوجهة، إذ تُنفَّذ كلٌ منهما قبل تنفيذ معالج الوجهة الفعلي، ويمكنك ربط العدد الذي تريده من الأدوات الوسيطة إلى طلبٍ ما. تُنقل الأداة الوسطية tokenExtractor إلى الملف "util/middleware.js" كونها تُستخدم في عدّة مواضع: const jwt = require('jsonwebtoken') const { SECRET } = require('./config.js') const tokenExtractor = (req, res, next) => { const authorization = req.get('authorization') if (authorization && authorization.toLowerCase().startsWith('bearer ')) { try { req.decodedToken = jwt.verify(authorization.substring(7), SECRET) } catch{ return res.status(401).json({ error: 'token invalid' }) } } else { return res.status(401).json({ error: 'token missing' }) } next() } module.exports = { tokenExtractor } يمكن للمدير الآن السماح للمستخدم "jakousa" بمعاودة نشاطه عن طريق إرسال طلب من النوع PUT إلى الوجهة "api/users/jakousa/" إذ يأتي الطلب مع البيانات التالية: { "disabled": false } وكما رأينا في نهاية القسم 4، تحمل هذه الطريقة في إيقاف نشاط المستخدمين بعض المشاكل، إذ يجري التحقق من وضع المستخدم عند تسجيل الدخول، وإن امتلك المستخدم مفتاح استيثاق صحيح في لحظة إيقاف نشاطه، فقد يتمكن من متابعة استخدام مفتاحه لعدم وجود فترة زمنية لصلاحيته، ولن يجري التحقق من حالته عند إضافة ملاحظة جديدة. قبل أن نتابع، سنكتب سكربت npm يسمح لنا بالتراجع عن آخر تهجير، فقد لا ينجح كل شيء في المرة الأولى بالضرورة عند تطوير ملفات التهجير. لنعدّل الملف "util/db.js" على النحو التالي: const Sequelize = require('sequelize') const { DATABASE_URL } = require('./config') const { Umzug, SequelizeStorage } = require('umzug') const sequelize = new Sequelize(DATABASE_URL, { dialectOptions: { ssl: { require: true, rejectUnauthorized: false } }, }); const connectToDatabase = async () => { try { await sequelize.authenticate() await runMigrations() console.log('connected to the database') } catch (err) { console.log('failed to connect to the database') return process.exit(1) } return null } const migrationConf = { migrations: { glob: 'migrations/*.js', }, storage: new SequelizeStorage({ sequelize, tableName: 'migrations' }), context: sequelize.getQueryInterface(), logger: console, } const runMigrations = async () => { const migrator = new Umzug(migrationConf) const migrations = await migrator.up() console.log('Migrations up to date', { files: migrations.map((mig) => mig.name), }) } const rollbackMigration = async () => { await sequelize.authenticate() const migrator = new Umzug(migrationConf) await migrator.down() } module.exports = { connectToDatabase, sequelize, rollbackMigration } // highlight-line لننشئ الملف "util/rollback.js" الذي يتيح لسكربت npm تنفيذ دالة التراجع عن التهجير: const { rollbackMigration } = require('./db') rollbackMigration() وسيكون السكربت على النحو التالي: { "scripts": { "dev": "nodemon index.js", "migration:down": "node util/rollback.js" }, } وهكذا سنتمكن من التراجع عن آخر تهجير بتنفيذ الأمر npm run migration:down من خلال سطر الأوامر. تُنفّذ ملفات التهجير تلقائيًا عندما يُشغَّل البرنامج، لكن قد يكون من الأنسب في مرحلة التطوير تعطيل التنفيذ التلقائي للتهجير وتنفيذه يدويًا من خلال سطر الأوامر. يمكنك الحصول على شيفرة التطبيق بوضعه الحالي من المستودع المخصص له على GitHub ضمن الفرع part13-7. التمرينان 13.7 و 13.8 حاول إنجاز التمارين التالية: التمرين 13.7 احذف كل الجداول من قاعدة بيانات تطبيقك ثم أنشئ ملف تهجير ليهيئ قاعدة البيانات. أضف البصمتان الزمنيتان "created_at" و "updated_at" إلى كلا الجدولين، وتذكر أن عليك إضافتها في ملف التهجير بنفسك. ملاحظة: تأكد من إزالة التعليمتين ()User.sync و ()Blog.sync اللتين تزامنان مخطط النموذج من شيفرتك وإلا سيخفق التهجير. ملاحظة: إذا كان عليك حذف الجداول باستخدام سطر الأوامر، أي إذا لم تكن تنوي التراجع عن الحذف بالتراجع عن آخر عملية تهجير، فلا بُد حينها من حذف محتوى جدول التهجير migrations إذا أردت من برنامجك تنفيذ التهجير مجددًا. التمرين 13.18 وسّع تطبيقك (مستخدمًا ملف تهجير) لكي يكون للمدونة سمةً تدل على سنة كتابتها، أي حقلٌ بالاسم يأخذ قيمًا صحيحة تبدأ من 1991 ولا يزيد عن العام الحالي. تأكد من ظهور رسالة خطأ مناسبة إن أُدخلت قيمة غير صحيحة للسنة. علاقات متعدد-إلى-متعدد many-to-many بين الجداول سنتابع توسعة التطبيق كي يُضاف كل مستخدم إلى فريقٍ أو أكثر، وطالما يمكن لأي عدد من المستخدمين الانضمام إلى فريق وكذلك يمكن لأي مستخدم الانضمام إلى أي عدد من الفرقاء، فإننا أمام علاقة متعدد-إلى-متعدد many-to-many والتي تنُفّذ تقليديًا في قواعد البيانات من خلال جدول الاتصال connection table. لنكتب الآن الشيفرة التي يحتاجها الجدول teams إضافةً إلى جدول الاتصال. سيبدو ملف التهجير أولًا على النحو التالي: const { DataTypes } = require('sequelize') module.exports = { up: async ({ context: queryInterface }) => { await queryInterface.createTable('teams', { id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, name: { type: DataTypes.TEXT, allowNull: false, unique: true }, }) await queryInterface.createTable('memberships', { id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, user_id: { type: DataTypes.INTEGER, allowNull: false, references: { model: 'users', key: 'id' }, }, team_id: { type: DataTypes.INTEGER, allowNull: false, references: { model: 'teams', key: 'id' }, }, }) }, down: async ({ context: queryInterface }) => { await queryInterface.dropTable('teams') await queryInterface.dropTable('memberships') }, } تتضمن النماذج نفس شيفرة ملفات التهجير تقريبًا، وإليك شيفرة نموذج الجدول team في الملف "models/team.js": const { Model, DataTypes } = require('sequelize') const { sequelize } = require('../util/db') class Team extends Model {} Team.init({ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, name: { type: DataTypes.TEXT, allowNull: false, unique: true }, }, { sequelize, underscored: true, timestamps: false, modelName: 'team' }) module.exports = Team وهذه هي شيفرة نموذج جدول الاتصال الموجودة في الملف "models/membership.js": const { Model, DataTypes } = require('sequelize') const { sequelize } = require('../util/db') class Membership extends Model {} Membership.init({ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, user_id: { type: DataTypes.INTEGER, allowNull: false, references: { model: 'users', key: 'id' }, }, team_id: { type: DataTypes.INTEGER, allowNull: false, references: { model: 'teams', key: 'id' }, }, }, { sequelize, underscored: true, timestamps: false, modelName: 'membership' }) module.exports = Membership منحنا جدول الاتصال اسمًا يصف طبيعة محتوياته membership، لكن قد لا تجد بالضرورة اسمًا يصف هذا الجدول، ولهذا يمكنك تسميته بأسماء الجداول التي يربطها تفصل بينها شرطة سفلية، مثل "user_teams" الذي يصلح أيضًا لحالتنا هذه. أجرينا إضافةً صغيرةً على الملف "models/index.js" لربط الفرقاء والمستخدمين على مستوى الشيفرة باستخدام التابع belongsToMany: const Note = require('./note') const User = require('./user') const Team = require('./team') const Membership = require('./membership') Note.belongsTo(User) User.hasMany(Note) User.belongsToMany(Team, { through: Membership }) Team.belongsToMany(User, { through: Membership }) module.exports = { Note, User, Team, Membership } لاحظ الفَرق بين ملف التهجير لجدول الاتصال ونموذجه عند تعريف مفتاح خارجي، إذ تُعرَّف الحقول باستخدام أسلوب الأفعى في ملف التهجير: await queryInterface.createTable('memberships', { // ... user_id: { type: DataTypes.INTEGER, allowNull: false, references: { model: 'users', key: 'id' }, }, team_id: { type: DataTypes.INTEGER, allowNull: false, references: { model: 'teams', key: 'id' }, } }) بينما تُعرَّف نفس الحقول في النموذج باستخدام أسلوب سنام الجمل: Membership.init({ // ... userId: { type: DataTypes.INTEGER, allowNull: false, references: { model: 'users', key: 'id' }, }, teamId: { type: DataTypes.INTEGER, allowNull: false, references: { model: 'teams', key: 'id' }, }, // ... }) لننشئ الآن بعض الفُرقاء وبعض الأعضاء باستخدام الطرفية: insert into teams (name) values ('toska'); insert into teams (name) values ('mosa climbers'); insert into memberships (user_id, team_id) values (1, 1); insert into memberships (user_id, team_id) values (1, 2); insert into memberships (user_id, team_id) values (2, 1); insert into memberships (user_id, team_id) values (3, 2); تُضاف معلومات أعضاء الفرقاء بعد ذلك إلى الوجهة التي تعيد كل المستخدمين: router.get('/', async (req, res) => { const users = await User.findAll({ include: [ { model: Note, attributes: { exclude: ['userId'] } }, { model: Team, attributes: ['name', 'id'], } ] }) res.json(users) }) لاحظ أن الاستعلام الذي طُبع على شاشة الطرفية يجمع ثلاثة جداول. هذا الحل جيدٌ فعلًا، لكن ما تزال هنالك ثغرة، إذ تأتي نتيجة الاستعلام مع جميع سمات الصف المطلوب من جدول الاتصال، علمًا أننا لا نحتاجها كلها. وبقراءة توثيق Sequelize جيدًا ستجد الحل: router.get('/', async (req, res) => { const users = await User.findAll({ include: [ { model: Note, attributes: { exclude: ['userId'] } }, { model: Team, attributes: ['name', 'id'], through: { attributes: [] } } ] }) res.json(users) }) يمكنك الحصول على شيفرة التطبيق بوضعه الحالي من المستودع المخصص له على GitHub ضمن الفرع part13-8. فكرة عن خاصيات كائن نموذج Sequelize تظهر خاصيات نموذجنا من خلال الأسطر التالية: User.hasMany(Note) Note.belongsTo(User) User.belongsToMany(Team, { through: Membership }) Team.belongsToMany(User, { through: Membership }) يمكّن ذلك Sequelize من إنشاء استعلامات تستخلص مثلًا كل ملاحظات المستخدمين أو كل أعضاء فريق، وبفضل تلك التعريفات نستطيع أيضًا الوصول مباشرةً إلى ملاحظات مستخدم مثلًا من خلال الشيفرة؛ ففي الشيفرة التالية مثلًا نحاول البحث عن مستخدم معرّفه المميز id=1، ثم نطبع الملاحظات المرتبطة به: const user = await User.findByPk(1, { include: { model: Note } }) user.notes.forEach(note => { console.log(note.content) }) يربط التعريف: User.hasMany(Note) الخاصية notes إلى الكائن user الذي يمنح وصولًا إلى الملاحظات التي أنشأها المستخدم، ويربط التعريف: User.belongsToMany(Team, { through: Membership }) الخاصية teams إلى الكائن user، الذي يمكن استخدامه في الشيفرة: const user = await User.findByPk(1, { include: { model: team } }) user.teams.forEach(team => { console.log(team.name) }) لنفترض أننا نريد إعادة كائن JSON من وجهة route تقود إلى مستخدم واحد يتضمن اسم المستخدم وعدد الملاحظات التي أنشأها. يمكننا تجربة الطريقة التالية: router.get('/:id', async (req, res) => { const user = await User.findByPk(req.params.id, { include: { model: Note } } ) if (user) { user.note_count = user.notes.length delete user.notes res.json(user) } else { res.status(404).end() } }) حاولنا إضافة الحقل noteCount في الكائن الذي أعادته Sequelize وإزالة الحقل notes منه، لكن ما يحدث أن هذه الطريقة لن تنفع لأن الكائن الذي تعيده Sequelize ليس كائنًا عاديًا تعمل فيه الحقول الإضافية كما نريد؛ ولذلك فإن الحل الأفضل لحالتنا هو إنشاء كائن جديد كليًا بناءً على البيانات المستخلصة من قاعدة البيانات: router.get('/:id', async (req, res) => { const user = await User.findByPk(req.params.id, { include: { model: Note } } ) if (user) { res.json({ username: user.username, name: user.name, note_count: user.notes.length }) } else { res.status(404).end() } }) نظرة ثانية إلى العلاقات متعدد-إلى-متعدد many-to-many سنختبر علاقة متعدد-إلى-متعدد أخرى في التطبيق؛ إذ ترتبط كل ملاحظة بالمستخدم الذي أنشأها من خلال مفتاح خارجي، ونريد الآن أن يدعم التطبيق ربط الملاحظة بمستخدمين آخرين، وربط المستخدم بعدد غير محدد من الملاحظات التي أنشأها آخرون، والفكرة هنا أن هذه الملاحظات هي تلك التي أشار المستخدم على أنها تهمّه. لننشئ جدول الاتصال user_notes في هذه الحالة، وسيكون ملف التهجير بسيطًا: const { DataTypes } = require('sequelize') module.exports = { up: async ({ context: queryInterface }) => { await queryInterface.createTable('user_notes', { id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, user_id: { type: DataTypes.INTEGER, allowNull: false, references: { model: 'users', key: 'id' }, }, note_id: { type: DataTypes.INTEGER, allowNull: false, references: { model: 'notes', key: 'id' }, }, }) }, down: async ({ context: queryInterface }) => { await queryInterface.dropTable('user_notes') }, } كما لن تجد أفكارًا جديدةً أيضًا في شيفرة النموذج: const { Model, DataTypes } = require('sequelize') const { sequelize } = require('../util/db') class UserNotes extends Model {} UserNotes.init({ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, userId: { type: DataTypes.INTEGER, allowNull: false, references: { model: 'users', key: 'id' }, }, noteId: { type: DataTypes.INTEGER, allowNull: false, references: { model: 'notes', key: 'id' }, }, }, { sequelize, underscored: true, timestamps: false, modelName: 'user_notes' }) module.exports = UserNotes يضم الملف "models/index.js" بعض التغيرات ليصبح على النحو التالي: const Note = require('./note') const User = require('./user') const Team = require('./team') const Membership = require('./membership') const UserNotes = require('./user_notes') Note.belongsTo(User) User.hasMany(Note) User.belongsToMany(Team, { through: Membership }) Team.belongsToMany(User, { through: Membership }) User.belongsToMany(Note, { through: UserNotes, as: 'marked_notes' }) Note.belongsToMany(User, { through: UserNotes, as: 'users_marked' }) module.exports = { Note, User, Team, Membership, UserNotes } يُستخدم التعريف belongsToMany مجددًا، إذ يربط الآن المستخدمين إلى الملاحظات عن طريق النموذج UserNotes المتعلق بجدول الاتصال، لكننا سنستخدم هذه المرة اسمًا بديلًا alias للسمات المُنشأة باستخدام الكلمة المحجوزة as، إذ سيتداخل overlap الاسم الافتراضي ("notes" المستخدم) مع معناه السابق وهو الملاحظات المُضافة من قِبل المستخدم. سنوسِّع الوجهة التي تقود إلى مستخدم وحيد لتعيد الفُرقاء التي ينتمي إليها المستخدم، وملاحظاتهم، والملاحظات الأخرى التي حددها المستخدم: router.get('/:id', async (req, res) => { const user = await User.findByPk(req.params.id, { attributes: { exclude: [''] } , include:[{ model: Note, attributes: { exclude: ['userId'] } }, { model: Note, as: 'marked_notes', attributes: { exclude: ['userId']}, through: { attributes: [] } }, { model: Team, attributes: ['name', 'id'], through: { attributes: [] } }, ] }) if (user) { res.json(user) } else { res.status(404).end() } }) لا بُد من استخدام الاسم البديل الذي عرّفناه من خلال السمة as خلال السياق. سننشئ بعض البيانات الاختبارية في قاعدة البيانات لاختبار الميزة: insert into user_notes (user_id, note_id) values (1, 4); insert into user_notes (user_id, note_id) values (1, 5); ستكون النتيجة النهائية على النحو التالي: لكن ماذا لو أردنا أن نضمّن معلومات تتعلق بمؤلف الملاحظة إلى الملاحظات التي يحددها مستخدم؟ يُنفَّذ الأمر بإضافة التعليمة include إلى الملاحظات المحدّدة من قبل المستخدم: router.get('/:id', async (req, res) => { const user = await User.findByPk(req.params.id, { attributes: { exclude: [''] } , include:[{ model: Note, attributes: { exclude: ['userId'] } }, { model: Note, as: 'marked_notes', attributes: { exclude: ['userId']}, through: { attributes: [] }, include: { model: User, attributes: ['name'] } }, { model: Team, attributes: ['name', 'id'], through: { attributes: [] } }, ] }) if (user) { res.json(user) } else { res.status(404).end() } }) ها هي النتيجة النهائية كما نتوقع: يمكنك الحصول على شيفرة التطبيق بوضعه الحالي من المستودع المخصص له على GitHub ضمن الفرع part13-9. التمرينات 13.19 - 13.23 حاول إنجاز التمرينات التالية. التمرين 13.19 اِمنح المستخدمين القدرة على إضافة مدوّنات إلى قائمة قراءة reading list. وعند إضافتها إلى القائمة يجب أن تكون حالتها "غير مقروءة unreaded"، ويمكن لاحقًا تعليم المدوّنة على أنها "مقروءة". نفّذ فكرة قائمة القراءة مستخدمًا جدول اتصال، وعدّل قاعدة البيانات من خلال ملف تهجير. لا يهم في هذا التمرين إضافة مدونات إلى القائمة وعرضها بنجاح أكثر من استخدام فكرة الولوج المباشر إلى قاعدة البيانات. التمرين 13.20 أضف الآن طريقةً كي يدعم التطبيق قوائم القراءة. تُضاف المدوّنة إلى قائمة القراءة من خلال الطلب HTTP POST إلى الوجهة "api/readinglists/"، ويُرفق مع الطلب المدوّنة ومعرّف المستخدم: { "blogId": 10, "userId": 3 } عدّل الوجهة "GET /api/users/:id" لإعادة قائمة المدوّنات إضافة إلى معلومات المستخدم بالتنسيق التالي: { name: "Matti Luukkainen", username: "mluukkai@iki.fi", readings: [ { id: 3, url: "https://google.com", title: "Clean React", author: "Dan Abramov", likes: 34, year: null, }, { id: 4, url: "https://google.com", title: "Clean Code", author: "Bob Martin", likes: 5, year: null, } ] } حتى هذه اللحظة، لا حاجة لإظهار إن كانت المدوّنة مقروءةً أم لا. التمرين 13.21 عدّل الوجهة التي تصل إلى مستخدم وحيد لكي تعرض فيما إذا كانت كل مدوّنة في قائمة القراءة مقروءةً أم لا، إضافةً إلى المُعرَّف المميز "id" للصف المقابل في جدول الاتصال. يمكن عرض المعلومات وفق التنسيق الآتي مثلًا: { name: "Matti Luukkainen", username: "mluukkai@iki.fi", readings: [ { id: 3, url: "https://google.com", title: "Clean React", author: "Dan Abramov", likes: 34, year: null, readinglists: [ { read: false, id: 2 } ] }, { id: 4, url: "https://google.com", title: "Clean Code", author: "Bob Martin", likes: 5, year: null, readinglists: [ { read: false, id: 2 } ] } ] } التمرين 13.22 قدّم طريقةً يستطيع من خلالها التطبيق تعليم مدوّنة ضمن قائمة القراءة على أنها مقروءة، إذ يُنفَّذ الأمر بإجراء طلب PUT إلى الوجهة "api/readinglists/:id/" وإرساله مع القيمة: { "read": true } يمكن للمستخدم أن يعلّم مدوّنة على أنها مقروءةً إذا كانت فقط ضمن قائمة القراءة الخاصة به. يستوثق من المستخدم عادةً من خلال مفتاح الاستيثاق الذي يُرفق مع الطلب. التمرين 13.23 عدّل الوجهة التي تعيد معلومات مستخدم وحيد لكي يتحكم الطلب بالمدوّنة التي ينبغي إحضارها من قائمة القراءة: "GET /api/users/:id": يعيد كامل قائمة القراءة. "GET /api/users/:id?read=true": يعيد المدوّنات المقروءة. "GET /api/users/:id?read=false": يعيد المدوّنات غير المقروءة. ملاحظات عامة يمكننا عدّ حالة تطبيقنا الآن مقبولة، لكن لا بُدّ من إلقاء نظرةٍ على بعض الأفكار قبل أن نختم هذا القسم. إحضار البيانات الكسول lazy والمتلهف eager عندما ننشئ استعلامًا مستخدمين السمة include: User.findOne({ include: { model: note } }) يحدُث ما يُسمى الإحضار المُتلهِّف eager fetch للبيانات، إذ تُجلب جميع الصفوف في كل الجداول المرتبطة بالمستخدم بواسطة الاستعلام join بنفس الوقت في مثال الملاحظات التي يُنشئها مستخدم. هذا السلوك هو ما نحتاجه عادةً، لكن ستجد في المقابل حالات تحتاج فيها إلى ما يُدعى بالإحضار الكسول أو المحدود lazy fetch مثل البحث عن فُرقاء مرتبطةٍ بمستخدم إذا لزم الأمر. لنعدّل وجهة إحضار مستخدم واحد كي تُحضر الفُرقاء التي ينتمي إليها مستخدم إذا احتوى الاستعلام على المعامل teams: router.get('/:id', async (req, res) => { const user = await User.findByPk(req.params.id, { attributes: { exclude: [''] } , include:[{ model: note, attributes: { exclude: ['userId'] } }, { model: Note, as: 'marked_notes', attributes: { exclude: ['userId']}, through: { attributes: [] }, include: { model: user, attributes: ['name'] } }, ] }) if (!user) { return res.status(404).end() } let teams = undefined if (req.query.teams) { teams = await user.getTeams({ attributes: ['name'], joinTableAttributes: [] }) } res.json({ ...user.toJSON(), teams }) }) وهكذا لن يحضر الاستعلام User.findByPk الفُرقاء، لكنها ستُجلب عند الحاجة باستخدام التابع user.getTeams الذي تولِّده Sequelize تلقائيًا لكائن النموذج. تولَّد Sequelize تلقائيًا توابع -get مماثلة وتوابع أخرى مفيدة عندما تٌعرّف ارتباطات associations بين الجداول على مستوى قاعدة بيانات. ميزات النموذج قد تصادفنا حالات لا نريد فيها معالجة كل أسطر جدول محدد افتراضيًا، إذ من الممكن مثلًا ألا نرغب في عرض المستخدمين الذين أوقفت نشاطاتهم في التطبيق، ويمكننا في هذه الحالة تعريف مجالات الرؤية الافتراضية للنموذج على النحو التالي: class User extends Model {} User.init({ // field definition }, { sequelize, underscored: true, timestamps: false, modelName: 'user', defaultScope: { where: { disabled: false } }, scopes: { admin: { where: { admin: true } }, disabled: { where: { disabled: true } } } }) module.exports = User سيضم الاستعلام الناتج عن التابع ()User.findAll عبارة WHERE التالية: WHERE "user". "disabled" = false; يمكن أن نعرّف أيضًا مجالات رؤية أخرى للنماذج: User.init({ // field definition }, { sequelize, underscored: true, timestamps: false, modelName: 'user', defaultScope: { where: { disabled: false } }, scopes: { admin: { where: { admin: true } }, disabled: { where: { disabled: true } }, name(value) { return { where: { name: { [Op.iLike]: value } } } }, } }) تُستخدم مجالات الرؤية على النحو التالي: // جميع المدراء const adminUsers = await User.scope('admin').findAll() // جميع المستخدمين غير النشطين const disabledUsers = await User.scope('disabled').findAll() // في أسمائهم jami المستخدمون الذين لديهم سلسلة نصية const jamiUsers = User.scope({ method: ['name', '%jami%'] }).findAll() كما يمكن سلسلة مجالات الرؤية (ربطها ببعضها): // في أسمائهم jami المدراء الذين لديهم سلسلة نصية const jamiUsers = User.scope('admin', { method: ['name', '%jami%'] }).findAll() وطالما أن نماذج هي أصناف جافا سكربت JavaScript، من الممكن إضافة توابع جديدة إليها، وإليك مثالين عن ذلك: const { Model, DataTypes, Op } = require('sequelize') const Note = require('./note') const { sequelize } = require('../util/db') class User extends Model { async number_of_notes() { return (await this.getNotes()).length } static async with_notes(limit){ return await User.findAll({ attributes: { include: [[ sequelize.fn("COUNT", sequelize.col("notes.id")), "note_count" ]] }, include: [ { model: Note, attributes: [] }, ], group: ['user.id'], having: sequelize.literal(`COUNT(notes.id) > ${limit}`) }) } } User.init({ // ... }) module.exports = User التابع الأول numberOfNotes هو تابع نسخة instance method، أي أن استدعاءه ممكن من نسخٍ instances عن النموذج: const jami = await User.findOne({ name: 'Jami Kousa'}) const cnt = await jami.number_of_notes() console.log(`Jami has created ${cnt} notes`) تشير الكلمة this في تابع النسخة إلى نسخة النموذج نفسها: async number_of_notes() { return (await this.getNotes()).length } أما التابع الثاني للنموذج، فيعيد هؤلاء المستخدمين الذين يملكون على الأقل "X" وهي القيمة التي يحملها المعامل وتدل على كمية الملاحظات في الصنف، أي التي تُستدعى مباشرةً عن طريق النموذج: const users = await User.with_notes(2) console.log(JSON.stringify(users, null, 2)) users.forEach(u => { console.log(u.name) }) قابلية التكرار في النماذج وملفات التهجير لقد رأينا أن الشيفرة في النموذج أو ملف التهجير تتكرر كثيرًا، فلو أخذنا نموذج الفُرقاء teams: class Team extends Model {} Team.init({ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, name: { type: DataTypes.TEXT, allowNull: false, unique: true }, }, { sequelize, underscored: true, timestamps: false, modelName: 'team' }) module.exports = Team وملف التهجير فإنهما يضمان كمًّا كبيرًا من نفس الشيفرة: const { DataTypes } = require('sequelize') module.exports = { up: async ({ context: queryInterface }) => { await queryInterface.createTable('teams', { id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, name: { type: DataTypes.TEXT, allowNull: false, unique: true }, }) }, down: async ({ context: queryInterface }) => { await queryInterface.dropTable('teams') }, } هل يمكن تحسين الشيفرة كي يُصدّر النموذج مثلًا الأجزاء المشتركة التي يحتاجها ملف التهجير؟ تكمن المشكلة في احتمال تغيُّر تعريف النموذج مع الوقت، فقد يتغير مثلًا الحقل أو يتغير نوع البيانات المُخزّنة فيه، وعلى ملف التهجير أن يُنفَّذ بنجاح في أي وقت من البداية إلى النهاية. إذا اعتمَدت ملفات التهجير على النموذج في الحصول على محتوى معين، فقد لا يكون هذا متاحًا خلال شهر أو سنة؛ لهذا ورغم وجود الكثير من الشيفرة للنسخ واللصق، لكن يُعد فصل ملف التهجير عن النموذج كاملًا أمرًا ضروريًا. قد يكون أحد الحلول هو استخدام أداة سطر أوامر Sequelize الذي يولّد كلًا من النموذج وملف التهجير بناءً على الأوامر التي تُنفِّذها، إذ سيُنفِّذ الأمر التالي النموذج User الذي يمتلك السمات name و username و admin، إضافةً إلى ملف التهجير الذي يدير شؤون إنشاء جدول قاعدة البيانات: npx sequelize-cli model:generate --name User --attributes name:string,username:string,admin:boolean يمكننا أيضًا تنفيذ أمر التراجع عن التهجيرات انطلاقًا من سطر الأوامر. لم يكتمل بعد لسوء الحظ توثيق سطر الأوامر هذا، لهذا قررنا إنشاء النماذج وملفات التهجير يدويًا في المنهاج، وقد يكون أو لايكون ما أنجزناه من حلول جيدًا. التمرين 13.24 نهاية عظيمة: أشرنا في نهاية القسم 4 إلى مشكلة جدّية في مفتاح الاستيثاق، فلو قررنا إيقاف نشاط مستخدم بعد أن دخل إلى المنظومة، سيبقى هذا المستخدم قادرًا على استخدام مفتاح الاستيثاق الذي يمتلكه وبالتالي استخدام المنظومة. الحل الإعتيادي للمشكلة هو تخزين سجلات بكل مفتاح استيثاق مُنح إلى عميل في قاعدة بيانات الواجهة الخلفية، ثم التحقق من صلاحية كل طلب، ويمكن في هذه الحالة إزالة صلاحية هذا المفتاح مباشرةً عند الحاجة. يُشار إلى هذا الأسلوب عادةً بجلسة عمل على الخادم server-side session. لنوسّع الآن المنظومة كي يُمنع المستخدم الذي فقد قدرته على الوصول إليها من إجراء أي تفاعل يتطلب تسجيل دخول. قد تحتاج إلى ما يلي لإنجاز الأمر: عمودٌ يضم قيمًا منطقية في جدول المستخدمين يشير إلى كون المستخدم نشطًا أم لا. يكفي في تمريننا أن توقف نشاط مستخدم أو تعيده من خلال قاعدة البيانات مباشرةً. جدولٌ يُخزّن جلسات العمل الجارية تُخزَّن الجلسة عند تسجيل الدخول (عند تنفيذ الطلب "POST /api/login"). يجري التحقق من وجود جلسة أو صلاحيتها عندما يُنفِّذ المستخدم عمليةً تتطلب تسجيل دخول. وجهةٌ تسمح للمستخدم بتسجيل خروجه من المنظومة لإزالة الجلسة من قاعدة البيانات، وقد يكون للوجهة المسار التالي "DELETE /api/logout". تذكر أنه لا يسمح بنجاح أي عملية تتطلب تسجيل دخول إذا كان مفتاح الاستيثاق منتهي الصلاحية، مثل الحالة التي يسجل فيها المستخدم خروجه. قد ترغب أيضًا في استخدام مكتبة npm مخصصة للتعامل مع الجلسات، ولا تنسى استخدام ملف التهجير لتنفيذ التغييرات اللازمة على قاعدة البيانات. ترجمة -وبتصرف- للفصل migrations, many-to-many relationships من سلسلة Deep Dive Into Modern Web Development. اقرأ أيضًا المقال السابق: ضم الجداول والاستعلامات المشتركة في قواعد البيانات باستخدام Sequelize المفاهيم الأساسية في قواعد البيانات وتصميمها تهجير قواعد البيانات في Laravel 5 تجريد إعداد قواعد البيانات في لارافيل باستعمال عملية التهجير Migration والبذر Seeder
  20. تجد في HTML الكثير من العناصر المستخدَمة في تنسيق النصوص ولم نأت على ذكرها في مقال هيكلة النصوص باستخدام لغة HTML، فليست جميع العناصر التي سنذكرها في هذا المقال معروفةً جيدًا، لكن من الجيد الاطلاع عليها (مع ذلك لن تكتمل قائمة العناصر كلها!)، إذ سنتعلم في مقالنا التعامل مع الاقتباسات وقوائم الوصف وطريقة عرض الشيفرات البرمجية والنصوص المتعلقة بها بالإضافة إلى كتابة نصوص مرتفعة عن نسق الكتابة الرئيسي superscript أو منخفضة عنه subscript، وكذلك عرض معلومات التواصل وغيرها. لا بد قبل الشروع في قراءة المقال الاطلاع على أساسيات HTML كما تحدثنا عنها في مقال تعرَّف على لغة HTML وكذلك هيكلة النصوص باستخدام لغة HTML. قوائم الوصف Description lists تعلمنا في مقالات سابقة طريقة إنشاء قوائم HTML بسيطة، لكننا لم نذكر النوع الثالث الذي قد نصادفه أحيانًا وهي قوائم الوصف description lists، إذ يُعَدّ الغرض الرئيسي لهذه القوائم هو تعداد مجموعة من العناصر وإدراج وصف لها أيضًا مثل المصطلحات وتعريفاتها أو الأسئلة وأجوبتها، وإليك مثالًا عن مجموعة من المصطلحات وتعريفها: soliloquy In drama, where a character speaks to themselves, representing their inner thoughts or feelings and in the process relaying them to the audience (but not to other characters.) monologue In drama, where a character speaks their thoughts out loud to share them with the audience and any other characters present. aside In drama, where a character shares a comment only with the audience for humorous or dramatic effect. This is usually a feeling, thought or piece of additional background information تُغلَّف عناصر هذه القائمة داخل عنصر قوائم مختلف وهو <dl>، كما يُغلَّف كل من عناصرها بالعنصر <dt>، في حين يوضَع وصف أو تعريف كل عنصر ضمن العنصر <dd> بعده مباشرةً. مثال عن قائمة وصف سنستخدِم قائمة الوصف لتنسيق النص السابق: <dl> <dt>soliloquy</dt> <dd>In drama, where a character speaks to themselves, representing their inner thoughts or feelings and in the process relaying them to the audience (but not to other characters.)</dd> <dt>monologue</dt> <dd>In drama, where a character speaks their thoughts out loud to share them with the audience and any other characters present.</dd> <dt>aside</dt> <dd>In drama, where a character shares a comment only with the audience for humorous or dramatic effect. This is usually a feeling, thought, or piece of additional background information.</dd> </dl> يعرض المتصفح قائمة الوصف افتراضيًا بحيث ينزاح فيه وصف أو تعريف العنصر قليلًا عن مستوى المصطلح. تعريفات متعددة لمصطلح واحد يُسمح في قوائم الوصف وجود أكثر من وصف أو تعريف لمصطلح، وإليك مثالًا: <dl> <dt>aside</dt> <dd>In drama, where a character shares a comment only with the audience for humorous or dramatic effect. This is usually a feeling, thought, or piece of additional background information.</dd> <dd>In writing, a section of content that is related to the current topic, but doesn't fit directly into the main flow of content so is presented nearby (often in a box off to the side.)</dd> </dl> تطبيق: توصيف مجموعة من التعريفات حان الوقت لتجرّب بنفسك قوائم الوصف، لذلك أضف العناصر المناسبة إلى النص الموجود في حقل مدخلات محرر الشيفرة في الأسفل لكي يظهر على صورة قائمة وصف ضمن حقل المخرجات output field، كما يمكنك تجريب مصطلحات وتعريفات خاصة بك أيضًا. إذا ارتكبت خطأً، فيمكنك مسح ما كتبته بالنقر على زر إعادة الضبط Reset، في حين إذا وجدت نفسك تائهًا كليًا، فانقر على زر أظهر الحل Show solution لترى الحل الصحيح. الاقتباسات Quotations تتيح لغة HTML عناصر للدلالة على الاقتباسات quotations، ويختلف العنصر المُستخدَم لإنشاء اقتباس إذا كنت توصِّف اقتباسًا مأخوذًا من عنصر كتلي Block أو سطري Inline. الاقتباسات الكتلية إذا اقتُبس جزء من محتوى عنصر كتلي مثل فقرة نصية أو عدة فقرات أو قائمة من مكان ما، فلا بد من تضمينه داخل العنصر <blockquote> لإظهاره على أساس فقرة وإضافة عنوان URL إلى مصدر الاقتباس بواسطة السمة cite، ويعرض المثال التالي محتوًى مأخوذًا من صفحة العنصر <blockquote> على شبكة مطورِي موزيللا MDN: <p>The <strong>HTML <code><blockquote></code> Element</strong> (or <em>HTML Block Quotation Element</em>) indicates that the enclosed text is an extended quotation.</p> علينا كتابة ما يلي لتحويل هذا المحتوى إلى اقتباس عن عنصر كتلي: <p>Here below is a blockquote...</p> <blockquote cite="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blockquote"> <p>The <strong>HTML <code><blockquote></code> Element</strong> (or <em>HTML Block Quotation Element</em>) indicates that the enclosed text is an extended quotation.</p> </blockquote> سيصيّر المتصفح هذا الاقتباس افتراضيًا على هيئة فقرة نصية تنزاح قليلًا عن بداية السطر ليدل على أنها اقتباس، وقد وضعنا الفقرة النصية في الأعلى لتوضيح هذه الفكرة. الاقتباس السطري يعمل بطريقة مماثلة للاقتباس عن عنصر كتلي إلا أنه يستخدِم العنصر <q>، إذ يعرض المثال التالي محتوًى مأخوذًا من صفحة العنصر <q> على شبكة مطورِي موزيللا وحوّلناه إلى اقتباس سطري: <p>The quote element — <code><q></code> — is <q cite="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q">intended for short quotations that don't require paragraph breaks.</q></p> سيعرض المتصفح هذه الاقتباس على صورة نص عادي محاط بإشارتَي تنصيص مزدوجتين على السطر نفسه: الاقتباسات المرجعية Citations على الرغم من أنّ محتوى السمة cite يبدو مفيدًا إلّا أنّ المتصفحات وقارئات الشاشة لا يعيرانه اهتمامًا، إذ لا توجد طريقة مباشرة تطلب بها من المتصفح إظهار محتوى هذه السمة سوى بابتكار حلول خاصة بك من خلال استخدام جافاسكربت أو CSS، فإذا أردت إتاحة مصدر الاقتباس للعرض أو الاستخدام، فعليك إظهاره ضمن نص أو رابط أو أية طريقة ملائمة أخرى، كما يوجد أيضًا العنصر <cite>، إلا أنّ الغاية منه هو احتواء عنوان المصدر الذي أُخذ منه الاقتباس مثل اسم كتاب، لكن لا يوجد أبدًا ما يمنع ربط النص الموجود داخل هذا العنصر بمصدر الاقتباس بطريقة أو بأخرى: <p>According to the <a href="/en-US/docs/Web/HTML/Element/blockquote"> <cite>MDN blockquote page</cite></a>: </p> <blockquote cite="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blockquote"> <p>The <strong>HTML <code><blockquote></code> Element</strong> (or <em>HTML Block Quotation Element</em>) indicates that the enclosed text is an extended quotation.</p> </blockquote> <p>The quote element — <code><q></code> — is <q cite="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q">intended for short quotations that don't require paragraph breaks.</q> -- <a href="/en-US/docs/Web/HTML/Element/q"> <cite>MDN q page</cite></a>.</p> تُعرَض الاقتباسات المرجعية افتراضيًا بخط مائل. تطبيق: من صاحب القول؟ حان الوقت لتمرين آخر، انظر إلى النص الموجود في حقل مدخلات محرر الشيفرة في الأسفل وحاول: تحويل المقطع في المنتصف إلى اقتباس كتلي يتضمن السمة cite. تحويل الجملة "The Need To Eliminate Negative Self Talk" في المقطع الثالث إلى اقتباس سطري يتضمن السمة cite. تغليف عنوان كل مصدر داخل العنصر <cite> وتحويل العنوان إلى رابط، واستخدم المرجعين التاليين للربط: للاقتباس عن كونفوشيوس: http://www.brainyquote.com/quotes/authors/c/confucius.html. للجملة في المقطع الثالث: http://example.com/affirmationsforpositivethinking. إذا ارتكبت خطأً، فيمكنك مسح ما كتبته بالنقر على زر إعادة الضبط Reset، في حين إذا وجدت نفسك تائهًا كليًا، فانقر على زر أظهر الحل Show solution لترى الحل الصحيح. الاختصارات تُعَدّ الاختصارات Abbreviations عناصر شائعة الاستخدام نوعًا ما في ويب وتميزها بالوسم <abbr>، إذ تستخدَم للدلالة على اختصارات لمصطلحات أو نحوت من عدة كلمات، فالنحت هو الأخذ بأوائل الحروف في كلمات جملة، كما تعطي الاختصارات وصفًا للمصطلح عند تمرير مؤشر الفأرة فوقه عند استخدام السمة title. مثال عن الاختصارات لنلق نظرةً على المثال التالي: <p>We use <abbr title="Hypertext Markup Language">HTML</abbr> to structure our web documents.</p> <p>I think <abbr title="Reverend">Rev.</abbr> Green did it in the kitchen with the chainsaw.</p> ستكون النتيجة مشابهةً للتالي: ملاحظة: دعمَت النسخ الأولى من HTML العنصر <acronym>، لكنه حُذِف ليحل محله <abbr> الذي يُستخدَم للدلالة على الاختصارات والنحوت معًا. تطبيق: الدلالة على اختصار نطلب منك في هذا التمرين البسيط إنشاء اختصار، واستخدم العينة الموجودة في الأسفل أو استبدلها بما تشاء. الدلالة على معلومات التواصل يُستخدَم العنصر <address> في لغة HTML لإدراج معلومات التواصل مع مسؤول الصفحة أو الموقع، إليك مثالًا كما يلي: <address> Chris Mills, Manchester, The Grim North, UK </address> يمكن احتواء العنصر على عناصر أخرى أو نموذج للتواصل لإدراج معلومات أكثر، وإليك مثالًا كما يلي: <address> <p> Chris Mills<br> Manchester<br> The Grim North<br> UK </p> <ul> <li>Tel: 01234 567 890</li> <li>Email: me@grim-north.co.uk</li> </ul> </address> يمكنك تنظيم المعلومات ضمن العنصر <address> بالصورة التالية أيضًا: <address> Page written by <a href="../authors/chris-mills/">Chris Mills</a>. </address> ملاحظة: يجب ألا يستخدَم العنصر <address> سوى لإدراج معلومات التواصل في الصفحة أسفل أقرب عنصر مقال <article> أو أسفل جسم الصفحة <body>، ومن الصحيح أيضًا وضعه في تذييل الصفحة لتُعرض معلومات التواصل في جميع صفحات الموقع، أو داخل عنصر المقال لعرض معلومات التواصل مع مؤلفه، لكن لا تستخدِمه لإدراج قائمة من العناوين التي لا تتعلق بمحتوى الصفحة. إزاحة الكتابة أعلى النسق أو أسفله نحتاج أحيانًا إلى رفع بعض الاحرف إلى أعلى نسق الكتابة أو أسفلها عندما نتعامل مع تواريخ مثلًا أو صيغ المواد الكيميائية أو المعادلات الرياضية لكي تشير إلى المعنى الحقيقي، إذ يُستخدم العنصران <sup> و <sub> لإنجاز الأمر، وإليك مثالًا كما يلي: <p>My birthday is on the 25<sup>th</sup> of May 2001.</p> <p>Caffeine's chemical formula is C<sub>8</sub>H<sub>10</sub>N<sub>4</sub>O<sub>2</sub>.</p> <p>If x<sup>2</sup> is 9, x must equal 3 or -3.</p> سيعرِض المتصفح النتيجة على الصورة التالية: إدراج شيفرة برمجية تقدِّم HTML مجموعةً من العناصر لعرض الشيفرات البرمجية بطريقة مميزة: العنصر <code>: للدلالة على أن المحتوى هو مقتطفات من شيفرة برمجية. العنصر <pre>: الذي يُبقي على المسافات الفارغة الزائدة في المحتوى والتي قد تكون إزاحةً للشيفرة، إذ يتجاهل المتصفح المسافات الفارغة الزائدة في النصوص العادية، لكن عند تغليف النص داخل هذا العنصر، فسيعرضه المتصفح كما هو تمامًا دون إهمال أيّ شيء. العنصر <var>: ويستخدَم للدلالة على المتغيرات على وجه الخصوص. العنصر <kbd>: ويستخدَم للدلالة على مدخلات لوحة المفاتيح أو غيرها من المدخلات إلى الحاسوب. العنصر <samp>: ويستخدَم للدلالة على خرج برنامج حاسوبي. لنلق نظرةً على بعض الأمثلة، كما عليك التجريب بنفسك، ولهذا ننصح بتنزيل نسخة من الملف other-semantics.html. <pre><code>var para = document.querySelector('p'); para.onclick = function() { alert('Owww, stop poking me!'); }</code></pre> <p>You shouldn't use presentational elements like <code><font></code> and <code><center></code>.</p> <p>In the above JavaScript example, <var>para</var> represents a paragraph element.</p> <p>Select all the text with <kbd>Ctrl</kbd>/<kbd>Cmd</kbd> + <kbd>A</kbd>.</p> <pre>$ <kbd>ping mozilla.org</kbd> <samp>PING mozilla.org (63.245.215.20): 56 data bytes 64 bytes from 63.245.215.20: icmp_seq=0 ttl=40 time=158.233 ms</samp></pre> ستُعرض الشيفرة السابقة في المتصفح كما يلي: الدلالة على الوقت والتاريخ تقدِّم لغة HTML العنصر <time> لتوصيف الوقت والتاريخ بطريقة يمكن للآلة قراءتها، وإليك مثالًا كما يلي: <time datetime="2016-01-20">20 January 2016</time> ما الفائدة من هذا العنصر؟ يستخدِم البشر طرقًا مختلفةً لكتابة التاريخ، فقد يُكتب التاريخ في المثال السابق على الصورة: 20 January 2016 20th January 2016 Jan 20 2016 20/01/16 01/20/16 The 20th of next month 20e Janvier 2016 20 كانون الثاني 2016 وغير ذلك. لكن لا تستطيع الحواسب التمييز بسهولة بين طرق الكتابة تلك، فماذا إذا أردت الحصول تلقائيًا على تواريخ جميع المناسبات في صفحة ويب وإضافتها إلى التقويم الخاص بك؟ لهذا السبب، يُستخدَم العنصر <time> لإضافة تنسيق للوقت والتاريخ، بحيث يكون مفهومًا بالنسبة إلى الحواسيب وغيرها من الآلات، كما يعرض لك المثال السابق تاريخًا بسيطًا يمكن للآلة فهمه، لكن هناك خيارات عدة أخرى، وإليك بعض الأمثلة: <!-- تاريخ بسيط بصيغة معيارية --> <time datetime="2016-01-20">20 January 2016</time> <!--فقط السنة و الشهر--> <time datetime="2016-01">January 2016</time> <!-- فقط الشهر و اليوم --> <time datetime="01-20">20 January</time> <!-- الوقت فقط، ساعات ودقائق --> <time datetime="19:30">19:30</time> <!-- يمكنك إضافة الثواني وأجزاء الثواني --> <time datetime="19:30:01.856">19:30:01.856</time> <!-- الوقت والتاريخ --> <time datetime="2016-01-20T19:30">7.30pm, 20 January 2016</time> <!-- الوقت والتاريخ مع إزاحة زمنية حسب خطوط الزمن--> <time datetime="2016-01-20T19:30+01:00">7.30pm, 20 January 2016 is 8.30pm in France</time> <!-- استدعاء أسبوع محدَّد من السنة وفق رقمه --> <time datetime="2016-W04">The fourth week of 2016</time> خلاصة نكون بهذا قد أنهينا دراسة دلالة العناصر التي تنسِّق النصوص في HTML، وتذكَّر تمامًا أنّ العناصر التي تعرَّفت عليها في سلسلة المقالات لا تمثِّل القائمة كلها، فقد حاولنا تغطية الأساسية منها أو بعض العناصر التي يشيع استخدامها أو التي رأينا بأنك ستجدها مهمةً، ويمكنك العودة إلى توثيق لغة HTML باللغة العربية أو شبكة مطوري موزيللا للاطلاع على بقية العناصر، كما سنتحدث في المقال التالي عن عناصر لغة HTML التي نستخدِمها في هيكلة الأجزاء المختلفة من صفحة ويب. ترجمة -وبتصرف- للمقال Advanced text formatting. اقرأ أيضًا HTML و CSS للمبتدئين: مقدمة إلى تنسيقات CSS تنسيق نصوص صفحات الويب باستخدام CSS تنسيق النصوص وتحليلها في PHP كيفية تنسيق النصوص في بايثون 3
  21. يدعم محرر النصوص "مستندات جوجل Google Docs" ميزة مشاركة العمل على أكثر من جهة أو مساهم، مما يسمح بتوزيع حمل العمل على فريق بأكمله، بحيث يختص كل فرد فيه بناحية معينة مثل التحرير والتنقيح وإضافة المصادر والتنسيق والمراجعات العامة بأسلوب عصري مريح يعزز تكامل العمل للحصول على نتائج أفضل. كل ذلك ضمن بيئة عمل متطورة مدعومة بتقنيات سحابية تؤمن سهول الوصول والمشاركة مع مستوى عال من الأمان. سنتحدث في هذا المقال عن ميزة مشاركة مستندات جوجل بتفاصيلٍ وافيةٍ لتكون قادرًا على توزيع أية أعمال تتعلق بإعداد الملفات النصية بفعالية كبيرة وجهد أقل. ولتوضيح النقاط التي ذكرناها، سنعمل عبر مثال تطبيقي بسيط يتضمن إنشاء مستند جوجل دوكس، ثم العمل على مشاركته مع فريقك وتوضيح خيارات المشاركة وآليات العمل الجماعي عليه. إنشاء مستند جديد في مستندات جوجل ينبغي أن يكون لديك بدايةً حساب على منصة جوجل، ويُعَد هذا الحساب وثيقة دخولك إلى جميع الخدمات التي تقدمها المنصة. يرتبط حسابك على منصة جوجل ببريد إلكتروني تُنشئه تلقائيًا عند تسجيل حساب جديد، وعليك تذكره دائمًا مع كلمة السر التي اخترتها لتتمكن من الولوج إلى أي خدمة من خدمات جوجل (علمًا أن بعضها لا يتطلب تسجيل دخول). إن كنت تملك حسابًا، فهذا أمر جيد وإن لم يكن لديك حساب فيجب عليك التسجيل على حساب جديد حتى تتمكن من متابعة العمل معنا في هذا المقال. يمكن الدخول إلى جوجل دوكس من خلال موقع ويب جوجل دوكس مباشرةً، أو من خلال منصة جوجل درايف Google Drive. حاول إنشاء ملف جديد بالنقر على "أيقونة (+)" يمين أسفل الصفحة إن كنت تستخدم موقع مستندات جوجل، كما يمكنك النقر على قائمة "جديد" على يمين الشاشة، ثم النقر على خيار "مستندات Google" إن اخترت أن تعمل على درايف. ولمزيد من التفاصيل عن كيفية إنشاء مستند جوجل، عُد إلى المقال "مقدمة إلى تطبيق مستندات جوجل Google Docs". لنلق نظرةً الآن على واجهة تطبيق مستندات جوجل (واجهة الحاسوب)، التي تتيح لك إمكانية تحرير المستند الجديد الذي أنشأته: تُحفظ مستندات جوجل تلقائيًا بالاسم الافتراضي "بلا عنوان"، لذلك انقر على الخيار "ملف" من شريط القوائم أعلى الصفحة، وانتقل بعدها إلى الخيار "إعادة تسمية" وانقر عليه وستلاحظ وجود عنوان جديد مقترح قد حل مكان العنوان السابق. بإمكانك اعتماد الاسم المقترح أو كتابة العنوان الذي تريد، كما يمكنك النقر مباشرةً على العنوان الافتراضي "بلا عنوان" وكتابة العنوان الجديد للمستند. وبالنسبة لمثالنا التطبيقي، سنختار العنوان "نصائح عن تبادل الأفكار في مستندات جوجل"، وعندها سيُحفظ الملف تلقائيًا في المجلد "ملفاتي". مشاركة مستند جوجل لمشاركة المستند، انقر ببساطة على زر "مشاركة" على يسار الصفحة، وستظهر عندها النافذة التالية: انقر على مربع النص "إضافة أشخاص ومجموعات" أعلى النافذة لكتابة البريد الإلكتروني أو اسم الشخص الذي ترغب بمشاركته هذا الملف (إن كان ضمن قائمة معارفك)، ثم اختر الصفة التي تريده أن يشارك بها من خلال الصندوق الذي يظهر إلى جوار البريد الإلكتروني للشخص المدعو. بإمكانك دعوة أي شخص إلى المساهمة في العمل وفق صفات ثلاث: محرِّر: وتمنحه بذلك وصولًا كاملًا إلى الملف لقراءته وتعديله وترك التعليقات وإضافة مساهمين جدد وتغيير أذونات الوصول (افتراضيًا) وحفظ التغييرات التي أحدثها معلِّق: بإمكان الشخص المدعو الاطلاع على محتوى الملف وترك التعليقات التي يرغب بها دون القدرة على تحرير المحتوى أو تغييره، ويبقى قادرًا أيضًا على استخدام خيارات النسخ والطباعة (افتراضيًا). عارض: لكي تسمح للشخص المدعو أن يقرأ فقط محتوى المستند دون أي قدرة على التفاعل. إن أردت أن تبلغ الشخص بمشاركتك له في العمل على هذا المستند، فانقر مربع التحقق "إشعار الأشخاص"، ثم صُغ رسالة لتوضيح المطلوب منه، بعدها أنقر على الزر "إرسال" أسفل النافذة؛ أما إن لم ترغب بإشعاره بذلك (كأن تبلغه بطريقة أخرى)، فلا تفعّل الخيار "إشعار الأشخاص" وانقر على زر "مشاركة" أسفل النافذة. دعوة شخص لا يملك حساب جوجل: بإمكانك أن تكتب عنوان بريد إلكتروني لشخص لا يمتلك حسابًا على جوجل. وهنا سيبلغك التطبيق في هذه الحالة أن هذا البريد الإلكتروني لا يرتبط بحساب جوجل وأنك تمنح إذنًا لأي شخص يصله رابط المشاركة من خلال هذا البريد، بحيث يترك لك خيار إكمال عملية المشاركة أو إلغائها، إذ يُعَد ذلك ثغرةً أمنيةً بطريقة أو بأخرى. عند إكمال المشاركة تظهر نافذة منبثقة صغيرة أعلى التطبيق تبلغك أن إمكانية الوصول قد تغيّرت، وبإمكانك معرفة عدد الأشخاص الذي تشاركهم العمل من خلال تمرير مؤشر الفأرة فوق زر "مشاركة". انقر على زر المشاركة من جديد لتضيف مساهمًا جديدًا إلى الملف أو لتستعرض المساهمين الحاليين أو لإجراء أية تغييرات على إمكانية الوصول، وستبدو نافذة التحكم بالمشاركة الآن كالتالي: تُظهر النافذة وجود مالك للمستند، وهو بالطبع أنت، إذ يظهر بريدك الإلكتروني وإلى جواره الصفة "مالك"، يليه مساهمين اثنين حتى الآن، حيث يمتلك المساهم الأول البريد الإلكتروني "address@mysite.com" بصفة "معلِّق"، أما الآخر فيمتلك البريد الإلكتروني "address@website.com" بصفة "محرر". تمرين تطبيقي تأكد من قدرتك على مشاركة مستند "نصائح عن تبادل الأفكار في مستندات جوجل" مع ثلاث مساهمين: الأول محرر والثاني معلّق والثالث عارض، إذ ستحتاج ذلك لاحقًا. دعوة شخص تجهل بريده الإلكتروني للمساهمة في مستند جوجل ربما تجمعك علاقة عمل طيبة من زميل، لكنك تجهل في الواقع عنوان البريد الإلكتروني الذي يستخدمه. فإن أردت دعوته للمساهمة في العمل على هذا المستند، يمكنك بكل بساطة اتباع الخطوات التالية: انقر على زر "مشاركة". انقر على زر "نسخ الرابط" أسفل يسار نافذة المشاركة، وسيُخزّن رابط الوصول إلى الملف في الحافظة. أرسل الرابط إلى زميلك بنسخه في رسالة نصية أو عبر إحدى وسائل التواصل الاجتماعي أو بالطريقة التي تشاء. عندما يصل الرابط إلى الشخص المحدد ويحاول الدخول إلى المستند سيتعذر عليه ذلك وتظهر له صفحة تحثّه على طلب إذن منك للمساهمة في الملف. بمجرد أن يطلب إذنًا للوصول إلى مستندك، ستصلك رسالة بريد إلكتروني توضح رغبة هذا الشخص بالمساهمة في العمل على مستندك، وعندما تفتح مستندك ستظهر لك نافذة المشاركة وفيها عنوان البريد الإلكتروني للشخص الذي دعوته للمساهمة كي تحدد الصفة التي تريده أن يساهم بها، وهكذا تكتمل العملية. تعديل إمكانية الوصول إلى مستند جوجل تتيح لك نافذة المشاركة مجموعة من الخيارات التي تساعدك في التحكم بصفات المساهمين ومنح أو إزالة بعض المزايا عن المساهمين عمومًا. السماح بالوصول العام إلى مستند جوجل قد ترغب في نشر مستندك للقراءة أو التعليق أو حتى التعديل من قِبل أكبر شريحة من المهتمين (قد يبدو الأمر محفوفًا بالمخاطر لكنه مفيد في حالات معينة). في هذه الحالة ما عليك سوى نسخ الرابط ونشره عبر الإنترنت، لكن عليك قبل ذلك تغيير حالة المشاركة في قسم "الوصول العام" من "حصري" إلى "أي مستخدم لديه رابط"، ثم اختيار الصفة التي يمكن للمساهمين العامين الدخول بها. تُمنح في هذه الحالة نفس الصفة لجميع المساهمين العامين سواءً اخترتهم أن يكونوا محررين أو معلقين أو عارضين. تعديل صفة الوصول إلى مستند جوجل وإلغائها بإمكانك أن تغير صفة الوصول التي منحتها كمالك أو كمحرر لأحد المساهمين، كأن تمنحه صفة محرر في حال كان "معلّقًا" فقط أو "عارضًا"، والعكس بالعكس؛ بالإضافة إلى منع المساهم من الوصول مجددًا إلى الملف (إزالة الوصول). ولإنجاز الأمر عليك بالخطوات التالية: انقر على زر "مشاركة" لتعرض نافذة التحكم بمشاركة الملف. انقر على القائمة المنسدلة إلى جوار اسم المساهم واختر الصفة الجديدة له أو اختر "إزالة الوصول". انقر على الزر "حفظ" أسفل النافذة. نقل ملكية مستند جوجل يُعًد من أنشأ الملف أو رفعه مالكًا لهذا الملف ويُخزّن ضمن المساحة المخصصة للمالك على جوجل درايف، وعندما يُشارك هذا الملف، ستُرسل نسخة منه إلى المساهم وفق الصفة أو الإذن الممنوح له؛ لكن إن أردت لسبب ما نقل ملكية مستند أنشأته إلى أحد المساهمين، فهذا ممكن وإليك الطريقة: انقر على زر "مشاركة" لتعرض نافذة التحكم بمشاركة الملف. انقر على القائمة المنسدلة إلى جوار اسم المساهم واختر "نقل الملكية". تظهر لك رسالة مفادها أنك ترغب في إرسال دعوة إلى المساهم الذي اخترته ليكون مالكًا لهذا المستند، انقر عند ظهورها على الزر "إرسال الدعوة". بعد إرسال الدعوة ستبقى مالكًا للمستند حتى يصل الإشعار إلى المساهم المحدد ويقبل أن يكون المالك الجديد، عندها تفقد صفة المالك للمستند وتبقى محررًا حتى يغير المالك الجديد صفتك أو يمنعك من الوصول إلى المستند. التراجع عن نقل الملكية: طالما أن المساهم الذي نقلت ملكية الملف إليه لم يستجب لدعوة النقل إيجابًا أو سلبًا، تستطيع التراجع عن نقل الملكية بالنقر على اسم المساهم، ثم اختيار "إلغاء نقل الملكية". الوصول المحدود إلى مستندات جوجل المشاركة يتيح لك تطبيق مستندات جوجل بصفتك مالكًا للمستند إمكانية منع بعض الميزات الناتجة عن مشاركة الملف، وإليك بعضها: منع المساهمين من تنزيل أو نسخ أو طباعة مستند جوجل يمكن للمساهمين الذي يدخلون بصفة "محرر" أو "معلّق" أن ينزّلوا نسخةً من المستند على أجهزتهم أو يطبعوه أو ينسخوا أجزاء منه لأغراض شخصية، لكن بإمكانك منعهم من ذلك كما يلي: انقر على زر "مشاركة" لتعرض نافذة التحكم بمشاركة الملف. انقر على أيقونة الإعدادات في أعلى يسار النافذة لتظهر لك النافذة الجديدة التالية: الغِ تفعيل الخيار "تظهر للمشاركين والمعلقين خيارات التنزيل والطباعة والنسخ". انقر على زر "حفظ". هل هذا كافٍ؟ هذا ما يقدمه تطبيق مستندات جوجل رسميًا، علمًا أن المساهم قد يجد طرقًا كثيرةً للاستفادة من المحتوى وبأساليب مختلفة، لهذا اختر مساهميك بعناية إن كان الأمر مهمًا. منع المساهمين من مشاركة مستندات جوجل وتغيير الأذونات يمكن للمساهم الذي شاركته مستندك بصفة "محرر" أن يعيد مشاركة الملف مع آخرين أو يغيّر أذونات الوصول إليه. ولكي تحتفظ بهذه الميزة لك فقط، اتبع الخطوات التالية: انقر على زر "مشاركة" لتعرض نافذة التحكم بمشاركة الملف. انقر على أيقونة الإعدادات في أعلى يسار النافذة لتظهر لك نافذة الإعدادات السابقة. الغِ تفعيل الخيار "يمكن للمحرِّرين تغيير الأذونات ومشاركة العناصر". انقر على زر "حفظ". حذف مستندات جوجل المشاركة بصفتك مالكًا للمستند يمكنك حذف هذا الملف. وطالما أن الملف محذوف مؤقتًا (موجود في سلة المهملات)، يمكن للمساهمين الذين يمتلكون الأذونات اللازمة العمل على نسخهم من هذا المستند، لكن بمجرد أن تفرغ سلة المهملات سينتهي كل شيء؛ أما إن حذفت ملفًا شاركك به أحد الأشخاص (أي أنك لا تملكه)، فستُحذف نسختك فقط، ولن يؤثر ذلك على عمل بقية المساهمين. بإمكانك استعادة هذا الملف إن حذفته عن طريق الخطأ، وذلك بالوصول إلى الملف من خلال رابطه، ثم اختيار "ملف" يلي ذلك "إضافة إلى ملفاتي". خلاصة غطينا في هذا المقال النقاط التالية: الطرق المختلفة لإنشاء مستند جوجل جديد. طرق مشاركة مستندات جوجل وإضافة أذونات الوصول لمساهمين وإزالتها. تغيير أذونات وصول المساهمين. نقل ملكية مستندات جوجل. الوصول المحدود إلى مستندات جوجل المُشاركة وحذفها. اقرأ أيضًا كيفية المشاركة في كتابة وتعديل مستند باستعمال مستندات جوجل قائمة الأدوات في مستندات جوجل تاريخ النُسخ واستعمال الإضافات في مستندات جوجل
  22. تمتلك الروابط التشعبية أهميةً كبيرةً، فهي العناصر التي تجعل من الويب شبكة حقيقية، إذ نستعرض في هذا المقال الصيغة القواعدية اللازمة لإنشاء رابط، ونناقش أفضل الممارسات المتبعة في إنشائها، كما لا بد قبل المتابعة في قراءة هذا المقال الاطلاع على أساسيات HTML التي تحدثنا عنها في مقال تعرَّف على لغة HTML، وطريقة تنسيق صفحة HTML وهيكلة محتواها كما ورد في مقال هيكلة النصوص باستخدام لغة HTML. ما هو الرابط التشعبي؟ تُعَدّ الروابط التشعبية واحدةً من أكثر الابتكارات أهميةً في عالم الويب، فهي من أولى ميزات الويب منذ انطلاقها وهي بالفعل ما يجعل الويب شبكةً حقيقيةً، إذ تسمح لنا الروابط التشعبية بربط الصفحات بصفحات أخرى أو بأجزاء محددة منها أو بغيرها من الموارد أو الوصول إلى تطبيقات موجودة على عنوان ويب محدَّد، كما يمكن لأيّ محتوى التحول إلى رابط ينقل المستخدِم عند النقر عليه إلى عنوان ويب آخر URL. ملاحظة: يمكن لعنوان URL الإشارة إلى ملف HTML أو ملف نصي أو صور أو ملفات صوت ومقاطع فيديو أو أي شيء آخر على الويب، فإذا لم يتمكن المتصفح من التعامل مع نوع ما من الملفات، فسيسألك إذا كنت تريد فتحه (عندها ينقل مهمة فتح الملف إلى أحد التطبيقات المحلية على جهازك)، أو يُنزّل هذا الملف (لتتعامل معه لاحقًا). تشير الروابط في الصفحة الرئيسية لشبكة BBC مثلًا إلى قصص إخبارية متنوعة إضافية مختلفة، كما تشير إلى مناطق محتلفة من الموقع نفسه (إذ يقدم الموقع آليات للتنقل) وإلى صفحات تسجيل الدخول والاشتراك -أي أدوات المستخدِم- وغير ذلك. تشريح الرابط التشعبي ننشئ الرابط بأبسط أشكاله بتغليف نص أو محتوى معيَّن داخل العنصر <a>، ثم نستخدِم السمة href التي تُعرَف أيضًا باسم مرجع النص التشعبي أو الهدف، والتي تحتوي على العنوان الوجهة. <p>سينقلك هذا الرابط إلى <a href="https://academy.hsoub.com">أكاديمية حسوب</a>. </p> ستكون نتيجة الشيفرة السابقة ما يلي: سينقلك هذا الرابط إلى أكاديمية حسوب. إضافة معلومات داعمة من خلال السمة title قد ترغب أيضًا في إضافة السمة title إلى رابطك، إذ توضع ضمن هذه السمة معلومات إضافيةً عن الرابط مثل نوع المعلومات التي تقدمها الصفحة الهدف، أو الأشياء التي ينبغي الانتباه لها على الموقع. كما تظهر هذه المعلومات على صورة تلميح عند تمرير مؤشر الفأرة فوق الرابط. <p>سينقلك هذا الرابط إلى <a href="https://www.mozilla.org/en-US/" title="أكاديمية حسوب هو موقع تعليمي عربي يهدف إلى توجيه المهتمين العرب بمجال البرمجة و التقنية إلى مادة علمية صحيحة و باللغة العربية" >أكاديمية حسوب</a>. </p> ملاحظة: لا يظهر عنوان الرابط سوى عند تمرير مؤشر الفأرة فوق الرابط، ويعني هذا صعوبة الحصول على معلومات العنوان لمن يستخدِم لوحة المفاتيح أو شاشات اللمس للتنقل عبر صفحات الويب، فإذا كانت معلومات العنوان مهمةً بالفعل لاستخدام الصفحة الهدف، فلا بد من تقديمها بطريقة تمكِّن الجميع من الوصول إليها، كأن تضعها ضمن نص تقليدي. تطبيق: إنشاء رابط خاص بك أنشئ ملف HTML باستخدام القالب الذي نزّلته في مقال تعرف على لغة HTML ومحرِّر الشيفرة على حاسوبك، ثم نفّذ التعديلات التالية: أضف فقرةً نصيةً <p> أو أكثر داخل العنصر <body>، أو غير ذلك من العناصر التي تعلمتها. حوّل أجزاءً من المحتوى إلى روابط. أضف عناوين إلى هذه الروابط باستخدام السمة title. تحويل العناصر البنائية إلى روابط يمكن تحويل كما ذكرنا سابقًا أيّة عناصر إلى روابط حتى العناصر البنائية Block elements، فإذا أردت مثلًا تحويل صورة إلى رابط، فاستخدم العنصر <a> ثم العنصر <img> للدلالة على الصورة. <a href="https://www.mozilla.org/en-US/"> <img src="mozilla-image.png" alt="mozilla logo that links to the mozilla homepage"> </a> جولة سريعة على عناوين URL والمسارات ستحتاج إلى استيعاب عناوين URL ومسارات الملفات لكي تفهم تمامًا وجهة الروابط التشعبية، وهذا ما سنفعله في هذه الفقرة. يُعَدّ محدد موقع المورد Uniform Resource Locator أو URL اختصارًا- سلسلةً نصيةً تُحدِّد مكان تواجد غرض ما على ويب، فالصفحة الرئيسية لموقع حسوب موجودة على العنوان https://www.hsoub.com، إذ تستخدِم عناوين URL المسارات لإيجاد الملفات، في حين تحدد المسارات الموقع الذي يوجد فيه الملف ضمن نظام الملفات، ولنلق نظرةً على هيكلية مجلد: يُدعى المجلد الجذري creating-hyperlinks. سيقع الموقع بأكمله ضمن مجلد واحد عندما نعمل محليًا على موقع ويب، إذ ستجد ضمن المجلد الجذري الملفَين index.html و contacts.html، بحيث سيمثل الملف index.html في موقع الويب الحقيقي الصفحة الرئيسية أو صفحة الهبوط، أي الصفحة التي تُعَدّ نقطة البداية لموقع ويب أو لقسم محدد منه. لاحظ أيضًا وجود مجلدَين ضمن المجلد الجذري هما pdfs و projects، إذ يحتوي كل منهما على ملف وحيد، ولاحظ أنه من الممكن وجود ملفَين باسم index.html في المشروع طالما أنهما في موقعين مختلفين ضمن منظومة الملفات، وقد يكون الملف index.html الثاني الصفحة الرئيسية لمعلومات تتعلق بالمشروع. ملفات في المجلد نفسه: إذا أردت وضع رابط ضمن index.html الأول -أي الأعلى مستوى- يشير إلى الملف contacts.html، فضع اسم هذا الملف فقط لأن كلاهما يعود إلى المستوى نفسه ضمن نظام ترتيب الملفات، وسيكون عنوان URL المستخدم هو contacts.html: <p>Want to contact a specific staff member? Find details on our <a href="contacts.html">contacts page</a>.</p> ملفات في مجلدات فرعية: إذا أردت وضع رابط ضمن index.html الأول -أي الأعلى مستوى- يشير إلى الملف projects/index.html، فعليك الانتقال أولًا إلى المجلد projects ثم الإشارة إلى الملف الذي تريد إنشاء رابط إليه، ويكون ذلك بتحديد اسم المجلد يليه المحرف / ثم اسم الملف، وسيكون عنوان URL الصحيح في هذه الحالة هو projects/index.html: <p>Visit my <a href="projects/index.html">project homepage</a>.</p> ملفات في المجلدات الآباء: إذا أردت وضع رابط ضمن الملف projects/index.html يشير إلى الملف pdfs/project-brief.pdf، فعليك الانتقال إلى المستوى الأعلى مباشرةً ثم الانتقال ثانيةً إلى داخل المجلد pdfs، واستخدم نقطتين متتاليتين .. للانتقال إلى مستوى واحد أعلى، وسيكون العنوان الصحيح هو pdfs/project-brief.pdf/..: <p>A link to my <a href="../pdfs/project-brief.pdf">project brief</a>.</p> ملاحظة: يمكنك الدمج بين الطرق السابقة لكتابة عناوين URL مركّبة عند الحاجة مثل: ../../../complex/path/to/my/file.html الانتقال إلى قسم من صفحة HTML يمكن أن تستهدف من خلال الرابط التشعبي جزءًا محددًا من صفحة HTML -أو ما يعرف بتجزئة الصفحة document fragment- بدلًا من الانتقال إلى أعلى الصفحة، ولإنجاز الأمر لا بد من استخدام السمة id في العنصر الذي ستنتقل إليه في الصفحة المستهدَفة، ومن المنطقي أن تنشئ رابطًا إلى عنصر عنوان مثلًا، إذ سيبدو ذلك قريبًا من التالي: <h2 id="Mailing_address">Mailing address</h2> نضع بعد ذلك قيمة id للعنصر الذي سننتقل إليه في نهاية عنوان URL للصفحة المستهدَفة مسبوقًا بالمحرف #: <p>Want to write us a letter? Use our <a href="contacts.html#Mailing_address">mailing address</a>.</p> يمكن أيضًا استخدام الأسلوب ذاته في الانتقال إلى جزء مختلف من الصفحة نفسها: <p>The <a href="#Mailing_address">company mailing address</a> can be found at the bottom of this page.</p> عناوين URL المطلقة والنسبية ستصادف أثناء تجوالك في ويب مصطلحَي عنوان URL مطلق absolute URL وعنوان URL نسبي relative URL. عنوان URL مطلق يشير إلى موقع محَّدد عن طريق مساره المطلق أو الكامل بما في ذلك الجزء الذي يحدد البروتوكول واسم النطاق. فلو كان الملف index.html ضمن المجلد projects الموجود ضمن المجلد الجذري لموقع ويب التالي: https://www.example.com فسيكون العنوان المطلق إلى هذا الملف هو: https://www.example.com/projects/index.html أو يمكن أن يُكتب فقط على الصورة التالية: https://www.example.com/projects/ إذ معظم الخوادم ستبحث تلقائيًا عن صفحة بداية اسمها index.html، كما يشير العنوان المطلق دائمًا إلى الموقع نفسه أينما استخدِم. عنوان URL نسبي يشير العنوان النسبي إلى موقع ملف بالنسبة إلى الملف الذي تنوي ربطه به، فإذا أردت الربط بين الملف الذي رابطه: https://www.example.com/projects/index.html والملف project-brief.pdf الذي يقع في المجلد نفسه، فسيكون العنوان النسبي هو اسم الملف الهدف كما هو، في حين إذا كان هذا الملف في مجلد فرعي يُدعى pdfs ضمن المجلد نفسه project، فسيكون العنوان النسبي له pdfs/project-brief.pdf، ولاحظ أن المسار المطلق لنفس الملف هو: https://www.example.com/projects/pdfs/project-brief.pdf كما تشير العناوين النسبية إلى مواقع مختلفة تبعًا لموقع الملف الذي سيرتبط بها، فإذا نقلت الملف index.html في مثالنا السابق إلى المجلد الجذري، فسيشير عنوان URL النسبي pdfs/project-brief.pdf إلى الملف الموجود على العنوان التالي: https://www.example.com/pdfs/project-brief.pdf وليس إلى الملف الموجود على العنوان التالي: https://www.example.com/projects/pdfs/project-brief.pdf لم يتغير بالطبع موقع الملف project-brief.pdf ولا موقع المجلد pdfs عندما نقلنا الملف index.html، لكن كل ما هنالك أنّ العنوان النسبي سيشير في هذه الحالة إلى المكان الخاطئ ولن يعمل الرابط عند النقر عليه، فعليك الحذر إذًا. أفضل الممارسات لإنشاء الروابط التشعبية توجد بعض الممارسات الممتازة التي يجدر بك تعلمها لكتابة روابط تشعبية جيدة، لنلق نظرةً عليها. استخدم كلمات واضحة تدل على الرابط من السهل إلقاء الروابط هنا وهناك في صفحتك، وهذا الأمر بالطبع ليس كافيًا، إذ لا بد أن يستطيع جميع القراء الوصول إلى الوجهة الصحيحة أيًا كان وضعهم وأيًا كانت الأدوات التي يستخدمونها، وإليك بعض الأمثلة: يُفضِّل مستخدمِي قارئات الشاشة التنقل من رابط إلى آخر في الصفحة وقراءة هذه الروابط بمعزل عن بقية المحتوى. تستخدِم محركات البحث نص الرابط لفهرسة الملف الذي يستهدفه، فمن الجيد إذًا إضافة كلمات مفتاحية إلى نص الرابط لكي يصف ما يرتبط به بفعالية أكبر. يجول قارئو الصفحات بأنظارهم عبر الصفحة دون قراءة كل كلمة فيها، ويلفت انتباههم ما يبرز بوضوح فيها مثل الروابط، وبالتالي سيشعر المتابع بأهمية الرابط إذا كان النص الوصفي له مفيدًا. الرابط التالي جيد مثلًا: <p><a href="https://firefox.com/"> Download Firefox </a></p> أما هذا، فسيّئ: <p><a href="https://firefox.com/"> Click here </a> to download Firefox</p> تلميحات أخرى: لا تضع عنوان URL لرابط ضمن نص الرابط، إذ سيبدو سيئًا ويصبح أسوأ عندما ينطقه قارئ الشاشة حرفًا حرفًا. لا تكتب "رابط" أو "رابط إلى" ضمن نص الرابط لأنه أمر عبثي، إذ تخبر قارئات الشاشة المستخدِم بأنه رابط، كما يعلم بكل بساطة من يقرأ النص بأنه رابط لأنه سيظهر عمومًا بتنسيق مختلف وبسطر تحته. حاول أن يكون نص الرابط أقصر ما يمكن، فهذا مفيد لمستخدمِي قارئات الشاشة لأنها ستقرأ النص بأكمله. قلل الحالات التي تستخدِم فيها نص الرابط نفسه للدلالة على أماكن مختلفة، فقد يسبب ذلك مشاكل لمستخدمِي قارئات الشاشات إذا ظهرت قائمة من الروابط المتلاحقة التي تقول "انقر هنا". الارتباط بمورد مختلف عن صفحات HTML عند الارتباط بمورد قابل للتنزيل مثل ملف PDF أو وورد، أو بمورد يُتابع مباشرةً مثل ملفات الفيديو والصوت، أو قد يُظهر تأثيرات غير متوقعة مثل فتح نافذة منبثقة أو تحميل مقطع فلاش، فلا بد في هذه الحالات من اختيار كلمات مناسبة للرابط منعًا لأيّ التباس، وإليك بعض الأمثلة: قد يكون حجم باقة التراسل bandwidth connection لديك صغيرًا ثم تنقر فجأةً على رابط لتتفاجأ بتنزيل عدة ميغابايتات لم تتوقعها. قد لا يكون مشغل مقاطع فلاش مثبّتًا على جهازك ثم تنقر رابطًا يأخذك إلى صفحة تتطلب مشغل فلاش. لنلق نظرةً على أمثلة لنصوص يمكن استخدامها في هذه الحالات: <p><a href="https://www.example.com/large-report.pdf"> (PDF ، 10MB) حمل تقرير المبيعات </a></p> <p><a href="https://www.example.com/video-stream/" target="_blank"> (HD سيُعرض في نافذة منفصل وبدقة)‎ شاهد الفيديو </a></p> <p><a href="https://www.example.com/car-game"> (يتطلب مشغل فلاش‎) ‎‎شغِّل لعبة السيارة‎ </a></p> استخدم السمة download عندما ترتبط بمورد لتنزيله يمكن استخدام السمة download عندما تحاول إنشاء رابط إلى مورد لكي يُنزَّل بدلًا من أن يُفتَح في المتصفح، وذلك لكي تزوّد المستخدِم باسم لحفظ الملف، وإليك مثالًا عن رابط لتحميل آخر إصدارات فايرفوكس لنظام التشغيل ويندوز: <a href="https://download.mozilla.org/?product=firefox-latest-ssl&os=win64&lang=en-US" download="firefox-latest-64bit-installer.exe"> Download Latest Firefox for Windows (64-bit) (English, US) </a> تطبيق: إنشاء قائمة للتنقل يُطلَب منك في هذا التمرين ربط بعض الصفحات إلى قائمة تنقّل لإنشاء موقع ويب متعدد الصفحات، وهذه طريقة شائعة لإنشاء المواقع، إذ تُستخدَم الهيكلية نفسها لجميع الصفحات بما في ذلك قائمة التنقّل، وبالتالي عند النقر على أحد الروابط سيعطي ذلك انطباعًا بأنك لازلت في المكان ذاته لكن بمحتوى مختلف. عليك تخزين نسخ من الملفات التالية على حاسوبك وفي المجلد نفسه، كما يمكنك الحصول على جميع هذه الملفات من مستودع جيت-هاب المخصص للتمرين: index.html. projects.html. pictures.html. social.html. عليك: إضافة قائمة غير مرتبة في المكان المطلوب في الصفحة التي تضم أسماء بقية الصفحات التي سترتبط بها، فقائمة التنقل هي عادةً قائمة من الروابط، وبالتالي هذا الأسلوب صحيح دلاليًا. تحويل اسم كل صفحة إلى رابط إلى تلك الصفحة. نسخ قائمة التنقل إلى جميع الصفحات. إزالة الرابط من كل صفحة والذي يتعلق بها، فوجوده أمر غير ضروري ومربك، كما يعطي عدم ظهوره تذكيرًا بصريًا بالصفحة التي نتواجد فيها. سيبدو الحل عندما تنتهي منه قريبًا من الصفحة التالية: ملاحظة: إذا لم تستطع المتابعة أو لم تكن واثقًا من عملك، فيمكن التحقق من الحل في المستودع المخصص للتمرين على جيت-هاب. روابط البريد الإلكتروني من الممكن إنشاء روابط أو أزرار تَفتح عند النقر عليها نموذجًا أو تطبيقًا لكتابة رسالة بريد إلكتروني بدلًا من الانتقال إلى مورد معين. يُستخدم العنصر <a> لهذه الغاية بالإضافة إلى بروتوكول mailto:‎، إذ يشير البروتوكول :mailto في أكثر الحالات بساطةً وشيوعًا إلى عنوان البريد الإلكتروني للمستقبِل: <a href="mailto:nowhere@mozilla.org">Send email to nowhere</a> يُعَدّ عنوان البريد الإلكتروني اختياريًا في واقع الأمر، فإذا أزلت العنوان وأبقيت على البروتوكول :mailto فقط، فستظهر لك نافذة عميل البريد الإلكتروني المثبت على جهازك لإنشاء بريد إلكتروني جديد دون تحديد المستقبِل، ولهذه الميزة فوائدها في روابط المشاركة Share التي يمكن للمستخدِمين النقر عليها لإرسال بريد إلكتروني إلى عناوين من اختيارهم. تحديد بعض التفاصيل يمكن إضافة معلومات أخرى إلى عنوان البريد الإلكتروني، إذ يمكن إضافة أية حقول مستخدَمة في ترويسة البريد الإلكتروني إلى :mailto، وأكثر هذه المعلومات شيوعًا هي الموضوع Subject ونسخة إلى cc وجسم الرسالة body الذي لا يمثل حقلًا فعليًا لنص الرسالة، لكن بالإمكان استخدامه لكتابة محتوى قصير للرسالة، كما يُحدَّد كل حقل مع قيمته بأسلوب الاستعلام، وإليك مثالًا كما يلي: <a href="mailto:nowhere@mozilla.org?cc=name2@rapidtables.com&bcc=name3@rapidtables.com&subject=The%20subject%20of%20the%20email&body=The%20body%20of%20the%20email"> Send mail with cc, bcc, subject and body </a> ملاحظة: ينبغي أن تكون القيم في كل حقل مكتوبةً وفق ترميز عنوان URL، أي دون محارف لا تطبع شيئًا مثل محارف السطر الجديد والجدولة ودون فراغات، ولاحظ أيضًا استخدام إشارة الاستفهام ? في الفصل بين عنوان URL الرئيسي عن قيم الحقول والمحرف & للفصل بين الحقول، إذ تُعَدّ هذه الرموز رموزًا معياريةً لاستعلام URL، وإليك بعض الأمثلة عن عناوين :mailto بسيطة أخرى: mailto: mailto:nowhere@mozilla.org mailto:nowhere@mozilla.org,nobody@mozilla.org mailto:nowhere@mozilla.org?cc=nobody@mozilla.org mailto:nowhere@mozilla.org?cc=nobody@mozilla.org&subject=This%20is%20the%20subject خلاصة هذا كل ما تحتاجه عن الروابط، وسنعود إلى الروابط لاحقًا في سلسلة مقالاتنا عندما نتحدث عن أساليب التنسيق، كما سنتحدث في المقال التالي عن الدلالات التي تقدمها عناصر HTML، وسنطلع على بعض الميزات المتقدمة أو غير المألوفة والتي قد تعُدّها مفيدة، لذا سيكون التنسيق المتقدم للنصوص في لغة HTML هو خطوتنا التالية. احصل على موقع إلكتروني مخصص لأعمالك أبهر زوارك بموقع احترافي ومميز بالاستعانة بأفضل خدمات تطوير وتحسين المواقع على خمسات أنشئ موقعك الآن ترجمة -وبتصرف- للمقال Creating hyperlinks. اقرأ أيضًا مفهوم الروابط التشعبية في مواقع الويب كيفية إنشاء الارتباطات التشعبية (Hyperlinks) والإجراءات (Actions) في Microsoft PowerPoint ترويسة الصفحة والبيانات الوصفية في HTML
  23. نستعرض في هذا المقال طريقة هيكلة التطبيق الذي عملنا عليه في المقال السابق، والاستعلام عن معلومات متنوعة تضمها قاعدة البيانات العلاقية. هيكلية التطبيق لقد كتبنا حتى اللحظة كامل الشيفرة في نفس الملف، لهذا سنحاول الآن إعطاء التطبيق هيكلًا أوضح. لننشئ إذًا المجلدات والملفات وفق الهيكلية التالية: index.js util config.js db.js models index.js note.js controllers notes.js أما محتوى الملفات فستكون على النحو التالي: الملف "util/config.js": يهتم بالتعامل مع متغيرات البيئة environment variables: require('dotenv').config() module.exports = { DATABASE_URL: process.env.DATABASE_URL, PORT: process.env.PORT || 3001, } الملف "index.js": ويهتم بتهيئة وتشغيل التطبيق: const express = require('express') const app = express() const { PORT } = require('./util/config') const { connectToDatabase } = require('./util/db') const notesRouter = require('./controllers/notes') app.use(express.json()) app.use('/api/notes', notesRouter) const start = async () => { await connectToDatabase() app.listen(PORT, () => { console.log(`Server running on port ${PORT}`) }) } start() يختلف تشغيل التطبيق قليلًا عما رأيناه سابقًا، لأننا نريد التأكُّد من نجاح الاتصال بقاعدة البيانات قبل أن يبدأ التطبيق العمل الفعلي. الملف "util/d b.js": ويضم الشيفرة التي تُهيئ قاعدة البيانات: const Sequelize = require('sequelize') const { DATABASE_URL } = require('./config') const sequelize = new Sequelize(DATABASE_URL, { dialectOptions: { ssl: { require: true, rejectUnauthorized: false } }, }); const connectToDatabase = async () => { try { await sequelize.authenticate() console.log('connected to the database') } catch (err) { console.log('failed to connect to the database') return process.exit(1) } return null } module.exports = { connectToDatabase, sequelize } الملف "models/note.js": وتُخزّن فيه الملاحظات في النموذج المقابل للجدول الذي سيُحفظ. const { Model, DataTypes } = require('sequelize') const { sequelize } = require('../util/db') class Note extends Model {} Note.init({ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, content: { type: DataTypes.TEXT, allowNull: false }, important: { type: DataTypes.BOOLEAN }, date: { type: DataTypes.DATE } }, { sequelize, underscored: true, timestamps: false, modelName: 'note' }) module.exports = Note الملف "models/index.js": لا يُستخدم حاليًا تقريبًا بسبب وجود نموذج واحد فقط في التطبيق، لكنه سيصبح أكثر فائدةً عندما نبدأ إضافة نماذج جديدة، إذ سيلغي الحاجة إلى إدراج ملفات منفصلة تُعرِّف بقية النماذج: const Note = require('./note') Note.sync() module.exports = { Note } الملف "controllers/notes.js": ويضم الوجهات المرتبطة بالملاحظات، أي مسار التوجيه إلى ملاحظة: const router = require('express').Router() const { Note } = require('../models') router.get('/', async (req, res) => { const notes = await Note.findAll() res.json(notes) }) router.post('/', async (req, res) => { try { const note = await Note.create(req.body) res.json(note) } catch(error) { return res.status(400).json({ error }) } }) router.get('/:id', async (req, res) => { const note = await Note.findByPk(req.params.id) if (note) { res.json(note) } else { res.status(404).end() } }) router.delete('/:id', async (req, res) => { const note = await Note.findByPk(req.params.id) if (note) { await note.destroy() } res.status(204).end() }) router.put('/:id', async (req, res) => { const note = await Note.findByPk(req.params.id) if (note) { note.important = req.body.important await note.save() res.json(note) } else { res.status(404).end() } }) module.exports = router هكذا يبدو هيكل التطبيق جيدًا الآن، لكننا نلاحظ أنّ معالجات التوجيه route handlers الذي يتعامل مع ملاحظة واحدة يضم قليلًا من الشيفرات المكررة، فجميعها تبدأ بالسطر الذي يبحث عن الملاحظة التي يتعامل معها: const note = await Note.findByPk(req.params.id) لنعيد كتابة الشيفرة على شكل أداة وسطية middleware خاصةٍ بنا، ونطبّقها في معالجات التوجيه: const noteFinder = async (req, res, next) => { req.note = await Note.findByPk(req.params.id) next() } router.get('/:id', noteFinder, async (req, res) => { if (req.note) { res.json(req.note) } else { res.status(404).end() } }) router.delete('/:id', noteFinder, async (req, res) => { if (req.note) { await req.note.destroy() } res.status(204).end() }) router.put('/:id', noteFinder, async (req, res) => { if (req.note) { req.note.important = req.body.important await req.note.save() res.json(req.note) } else { res.status(404).end() } }) تستقبل معالجات الوجهة ثلاثة معاملات: الأول نصي يُعرّف الوجهة، والثاني الأداة الوسطية noteFinder المُعرفة مُسبقًا والتي تستخلص الملاحظة من قاعدة البيانات وتضعها في الخاصية note للكائن req. بإمكانك إيجاد الشيفرة الحالية للتطبيق كاملةً في المستودع المخصص على GitHub ضمن الفرع part13-2. التمرينات 13.5 إلى 13.7 حاول إنجاز التمارين التالية التمرين 13.5 غيّر هيكل تطبيقك ليشابه المثال السابق أو اتبع هيكليةً أخرى واضحة وملائمة. التمرين 13.6 قدِّم طريقةً تدعم تغيير عدد الإعجابات بمدوّنة في تطبيقك مستخدمًا العملية "PUT /api/blogs/:id"، إذ ينبغي أن يصل العدد الجديد للإعجابات مع الطلب: { likes: 3 } التمرين 13.7 استخدم أداةً وسطيةً للتحكم المركزي بمعالجة الأخطاء كما فعلنا في القسم 3 كما يمكنك استخدام الأداة الوسطية express-async-errors كما فعلنا في القسم 4. لا تهتم للبيانات المُعادة في سياق رسالة الخطأ. حتى اللحظة لا تتطلب سوى حالتين في التطبيق معالجةً للأخطاء، هما: إضافة مدوّنة جديدة وتغيير عدد الإعجابات، لذلك تأكد من قدرة معالج الأخطاء على التعامل مع هاتين الحالتين بما يلائمهما. إدارة المستخدمين سنضيف تاليًا جدول قاعدة بيانات يُدعى "users" تُخزّن في فيه بيانات مستخدمي التطبيق، كما سنضيف أيضًا وظيفةً لإضافة مستخدمين جدد وآلية تسجيل دخول مبنية على مفاتيح الاستيثاق كما فعلنا في القسم 4، ولكي نبسط العمل، سنُعدِّل ما أنجزناه سابقًا كي يكون لجميع المستخدمين كلمة المرور ذاتها وهي "secret". محتوى الملف "models/user.js" الذي يُعرّف المستخدمين واضحٌ تمامًا: const { Model, DataTypes } = require('sequelize') const { sequelize } = require('../util/db') class User extends Model {} User.init({ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, username: { type: DataTypes.STRING, unique: true, allowNull: false }, name: { type: DataTypes.STRING, allowNull: false }, }, { sequelize, underscored: true, timestamps: false, modelName: 'user' }) module.exports = User سيكون حقل اسم المستخدم "username" ذا قيم فريدة، لكن وعلى الرغم من إمكانية جعله المفتاح الرئيسي للجدول، إلا أننا قررنا أن ننشئ حقلًا field منفصلًا "id" ذا قيم صحيحة ليكون المفتاح الرئيسي. سيتوسع الملف "models/index.js" قليلًا: const Note = require('./note') const User = require('./user') Note.sync() User.sync() module.exports = { Note, User } لا يضم الملف "controllers/users.js" الذي يحتوي معالجات الوجهة التي تهتم بإنشاء مستخدمين جدد أي شيء مهم حاليًا سوى عرض كل المستخدمين: const router = require('express').Router() const { User } = require('../models') router.get('/', async (req, res) => { const users = await User.findAll() res.json(users) }) router.post('/', async (req, res) => { try { const user = await User.create(req.body) res.json(user) } catch(error) { return res.status(400).json({ error }) } }) router.get('/:id', async (req, res) => { const user = await User.findByPk(req.params.id) if (user) { res.json(user) } else { res.status(404).end() } }) module.exports = router أما معالج الوجهة الذي يتحكم بتسجيل الدخول (الملف "controllers/login.js") فسيكون على النحو التالي: const jwt = require('jsonwebtoken') const router = require('express').Router() const { SECRET } = require('../util/config') const User = require('../models/user') router.post('/', async (request, response) => { const body = request.body const user = await User.findOne({ where: { username: body.username } }) const passwordCorrect = body.password === 'secret' if (!(user && passwordCorrect)) { return response.status(401).json({ error: 'invalid username or password' }) } const userForToken = { username: user.username, id: user.id, } const token = jwt.sign(userForToken, SECRET) response .status(200) .send({ token, username: user.username, name: user.name }) }) module.exports = router سيُرفق اسم المستخدم وكلمة المرور مع طلب POST، ويُستخلص الكائن المتعلق باسم المستخدم أولًا من قاعدة البيانات باستخدام التابع findOne العائد للنموذج User: const user = await User.findOne({ where: { username: body.username } }) يمكنك أن تلاحظ من خلال الطرفية أن تعليمة SQL المتعلقة باستدعاء التابع السابق هي: SELECT "id", "username", "name" FROM "users" AS "User" WHERE "User". "username" = 'mluukkai'; إن وُجد المستخدم وكانت كلمة المرور صحيحة (وهي "secret" لجميع المستخدمين)، يُعاد مفتاح الاستيثاق "jsonwebtoken" متضمنًا معلومات المستخدم مع الاستجابة. لهذا سنُثبّت الاعتمادية "jsonwebtoken": npm install jsonwebtoken سيتوسع الملف "index.js" قليلًا: const notesRouter = require('./controllers/notes') const usersRouter = require('./controllers/users') const loginRouter = require('./controllers/login') app.use(express.json()) app.use('/api/notes', notesRouter) app.use('/api/users', usersRouter) app.use('/api/login', loginRouter) يمكنك الحصول على شيفرة التطبيق بوضعها الحالي من المستودع المخصص على GitHub ضمن الفرع part13-3. الاتصال بين الجداول يمكن الآن إضافة مستخدمين إلى التطبيق، كما يمكن للمستخدمين تسجيل الدخول، لكنها ليست ميزات مفيدة جدًا في وضعها الحالي، لذلك سنضيف ميزات لا يمكن للمستخدم الاستفادة منها ما لم يُسجل دخوله مثل إضافة ملاحظة جديدة، إذ ترتبط هذه الملاحظة بالمستخدم الذي أنشأها، ولهذا لا بُدّ من إضافة مفتاح خارجي foreign key إلى جدول "notes". يمكن تعريف المفتاح الخارجي عند استخدام مكتبة Sequelize بتعديل الملف "models/index.js" على النحو التالي: const Note = require('./note') const User = require('./user') User.hasMany(Note) Note.belongsTo(User) Note.sync({ alter: true }) User.sync({ alter: true }) module.exports = { Note, User } وهكذا نكون قد عرّفنا علاقة واحد-إلى-متعدد one-to-many تصل بين جدولي المستخدمين "users" والملاحظات "notes"، كما عدّلنا الخيارات في استدعاءات sync لتتطابق جداول قاعدة البيانات مع التغييرات التي حدثت على تعريفات النموذج. سيبدو مخطط قاعدة البيانات على شاشة الطرفية على النحو التالي: username=> \d users Table "public.users" Column | Type | Collation | Nullable | Default ----------+------------------------+-----------+----------+----------------------------------- id | integer | not null | nextval('users_id_seq'::regclass) username | character varying(255) | | not null | name | character varying(255) | | not null | Indexes: "users_pkey" PRIMARY KEY, btree (id) Referenced by: TABLE "notes" CONSTRAINT "notes_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE SET NULL username=> \d notes Table "public.notes" Column | Type | Collation | Nullable | Default -----------+--------------------------+-----------+----------+----------------------------------- id | integer | not null | nextval('notes_id_seq'::regclass) content | text | | not null | important | boolean | | | | date | timestamp with time zone | | | | user_id | integer | | | | Indexes: "notes_pkey" PRIMARY KEY, btree (id) Foreign-key constraints: "notes_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE SET NULL يشير المفتاح الخارجي user_id الذي أُنشئ في الجدول notes إلى أسطر في الجدول users. لنربط الآن كل ملاحظة جديدة بالمستخدم الذي أنشأها، ولكن قبل تقديم الأمر (ربط الملاحظة بمفتاح استيثاق المستخدم الذي استخدمه لتسجيل دخوله)، لا بُد من كتابة الملاحظة التي ترتبط بالمستخدم الأول الذي يُعثر عليه يدويًا في الشيفرة: router.post('/', async (req, res) => { try { const user = await User.findOne() const note = await Note.create({...req.body, userId: user.id}) res.json(note) } catch(error) { return res.status(400).json({ error }) } }) انتبه إلى وجود العمود user_id في جدول الملاحظات "notes" على مستوى قاعدة البيانات، ويُشار إلى اسم كل صف في قاعدة البيانات بالطريقة التقليدية للمكتبة Sequelize، وذلك بكتابته على نقيض أسلوب سنام الجمل "userId"، أي كما تُكتب تمامًا في الشيفرة (حروف صغيرة). من السهل تنفيذ استعلامات مشتركة في Sequelize، لهذا سنغيّر الوجهة التي تعيد كل المستخدمين لتعرض كل ملاحظات المستخدم: router.get('/', async (req, res) => { const users = await User.findAll({ include: { model: Note } }) res.json(users) }) يُنفَّذ الاستعلام المشترك باستخدام الخيار include مثل معامل استعلام، أما تعليمة SQL المولّدة من الاستعلام، فستُطبع على شاشة الطرفية على النحو التالي: SELECT "User". "id", "User". "username", "User". "name", "Notes". "id" AS "Notes.id", "Notes". "content" AS "Notes.content", "Notes". "important" AS "Notes.important", "Notes". "date" AS "Notes.date", "Notes". "user_id" AS "Notes.UserId" FROM "users" AS "User" LEFT OUTER JOIN "notes" AS "Notes" ON "User". "id" = "Notes". "user_id"; ستبدو النتيجة النهائية كما تتوقع: الإضافة الملائمة للملاحظات لنحاول تغيير طريقة إضافة الملاحظات لتعمل بالطريقة التي عملت بها من قبل، بحيث ينجح إنشاء الملاحظات فقط إذا حمل طلب إنشائها مفتاح استيثاق صحيح عند تسجيل الدخول. تُخزّن بعدها الملاحظة ضمن قائمة الملاحظات التي أنشأها المستخدم المُعرّف بواسطة مفتاح الاستيثاق: const tokenExtractor = (req, res, next) => { const authorization = req.get('authorization') if (authorization && authorization.toLowerCase().startsWith('bearer ')) { try { req.decodedToken = jwt.verify(authorization.substring(7), SECRET) } catch{ res.status(401).json({ error: 'token invalid' }) } } else { res.status(401).json({ error: 'token missing' }) } next() } router.post('/', tokenExtractor, async (req, res) => { try { const user = await User.findByPk(req.decodedToken.id) const note = await Note.create({...req.body, userId: user.id, date: new Date()}) res.json(note) } catch(error) { return res.status(400).json({ error }) } }) يُستخلص مفتاح الاستيثاق من ترويسة الطلب ويفُك تشفيره ويوضع ضمن الكائن req بواسطة الأداة الوسطية tokenExtractor. يُضاف زمن الإنشاء إلى الحقل date أيضًا عند إنشاء الملاحظة. ضبط الواجهة الخلفية تعمل الواجهة الخلفية حتى اللحظة بنفس الطريقة التي تعمل بها في نسخة القسم 4 من التطبيق ماعدا فكرة معالجة الأخطاء. لنغيّر الآن وجهات إحضار جميع الملاحظات وجميع المستخدمين قليلًا قبل توسيع هذه الواجهة. سنضيف إلى كل ملاحظة بعض المعلومات المتعلقة بالمستخدم الذي أنشأها: router.get('/', async (req, res) => { const notes = await Note.findAll({ attributes: { exclude: ['userId'] }, include: { model: User, attributes: ['name'] } }) res.json(notes) }) كنا قد قيّدنا سابقًا القيم التي يأخذها الحقل المطلوب، إذ كنا نعيد جميع حقول البيانات الخاصة بكل ملاحظة بما في ذلك اسم المستخدم "name" الذي يرتبط بالملاحظة لكن باستثناء حقل المعرّف الفريد للمستخدم "userId". لنجرِ التغيير ذاته على الوجهة route التي تحضر جميع المستخدمين والملاحظات وذلك بإزالة الحقل userId غير الضروري من الملاحظات المرتبطة بمستخدم معين على النحو التالي: router.get('/', async (req, res) => { const users = await User.findAll({ include: { model: Note, attributes: { exclude: ['userId'] } } }) res.json(users) }) يمكنك الحصول على شيفرة التطبيق بوضعها الحالي من المستودع المخصص على GitHub ضمن الفرع part13-4. قليل من الانتباه إلى تعريفات النماذج رغم وجود العمود ‍‍‌‍‍user_id، إلا أننا لم نغيّر النموذج الذي يعرّف الملاحظات، ولكن يمكننا مع ذلك إضافة مستخدم إلى كائن الملاحظة: const user = await User.findByPk(req.decodedToken.id) const note = await Note.create({ ...req.body, userId: user.id, date: new Date() }) يعود السبب وراء ذلك إلى أننا لم نحدّد وجود علاقة واحد-إلى-متعدد في الاتصال بين جدولي المستخدمين "users" والملاحظات "notes" ضمن الملف "models/index.js": const Note = require('./note') const User = require('./user') User.hasMany(Note) Note.belongsTo(User) // ... تُنشئ المكتبة Sequelize تلقائيًا سمةً تُدعى userId في النموذج Note تمنح وصولًا إلى العمود user_id عندما يُشار إليه. وتذكّر أنه يمكنك إنشاء ملاحظة باستخدام التابع build: const user = await User.findByPk(req.decodedToken.id) // إنشاء ملاحظة دون تخزينها بعد const note = Note.build({ ...req.body, date: new Date() }) // للملاحظة المُنشأة userId وضع المعرف الفريد للمستخدم في خاصية note.userId = user.id // تخزين كائن الملاحظة في قاعدة البيانات await note.save() هكذا نرى صراحة أن userId هي سمة attribute لكائن الملاحظات، وقد كان بالإمكان تعريف النموذج على النحو التالي للحصول على النتيجة ذاتها: Note.init({ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, content: { type: DataTypes.TEXT, allowNull: false }, important: { type: DataTypes.BOOLEAN }, date: { type: DataTypes.DATE }, userId: { type: DataTypes.INTEGER, allowNull: false, references: { model: 'users', key: 'id' }, } }, { sequelize, underscored: true, timestamps: false, modelName: 'note' }) module.exports = Note لا يوجد داعٍ للتعريف على مستوى الصنف في النموذج كما فعلنا سابقًا: User.hasMany(Note) Note.belongsTo(User) وبدلًا من ذلك يمكننا تحقيق الأمر بهذه الطريقة، ولا بُد من استخدام إحدى هاتين الطريقتين وإلا لن تُدرك مكتبة Sequelize كيف يتصل الجدولين ببعضهما على مستوى الشيفرة. التمرينات 13.8 إلى 13.11 حاول إنجاز التمارين الآتية: التمرين 13.8 زِد دعم التطبيق لمستخدميه، إذ لا بُد أن يضم جدول المستخدمين الحقول التالية إضافةً إلى الحقل "ID": name: ذو قيمة نصية (لا يمكن أن يكون فارغًا). username: ذو قيمة نصية (لا يمكن أن يكون فارغًا). وعلى خلاف ما أوردنا في الشروحات النظرية، لا تمنع مكتبة Sequelize حاليًا إنشاء البصمتين الزمنيتين create_dat و update_dat لجدول المستخدمين. يمكن إعطاء كلمة المرور نفسها لجميع المستخدمين، وكذلك اختيار طريقة التحقق من كلمة المرور كما في القسم 4، وعليك أيضًا إنجاز الوجهات التالية: "POST api/users": لإضافة مستخدم جديد. "GET api/users": عرض جميع المستخدمين. "PUT api/users/:username": لتغيير اسم المستخدم وليس المعرّف "id". تأكد من إدراج البصمات الزمنية تلقائيًا من قِبل مكتبة Sequelize وعلى النحو الصحيح عند إضافة مستخدم أو تغيير اسمه. التمرين 13.9 تزودنا مكتبة Sequelize بمجموعة قواعد تحقق معرّفةٌ مسبقًا للتأكد من قيم حقول النموذج تُنفّذها قبل تخزين الكائنات في قاعدة البيانات. ولأننا نريد تغيير سياسة إنشاء مستخدم جديد تتطلب أن يكون البريد الإلكتروني المُدخل صحيحًا كما هو حال اسم المستخدم، أنجِز طريقةً للتحقق من ذلك أثناء إنشاء المستخدم. عدّل أيضًا بالأداة الوسطية المستخدمة في معالجة الأخطاء لتعرض وصفًا أكثر وضوحًا لرسالة الخطأ مثل استخدام رسائل خطأ Sequelize على سبيل المثال. { "error": [ "Validation isEmail on username failed" ] } التمرين 13.10 وسّع التطبيق لكي تربط كل مستخدم سجّل دخوله بنجاح عبر مفتاح الاستيثاق بكل مدونة يضيفها. لا بُد من تقديم وصلة endpoint تسجيل دخول "POST /api/login" تُعيد مفتاح الاستيثاق. التمرين 13.11 تأكد أن المستخدم قادرٌ فقط على حذف المدونات التي يضيفها. التمرين 13.12 عدّل الوجهات كي تكون قادرًا على استخلاص جميع المدوّنات والمستخدمين كي تعرض كل مدوّنة المستخدم الذي أضافها وأن يعرض كل مستخدم المدوّنات التي أضافها. استعلامات أوسع لا يزال تطبيقنا حتى اللحظة بسيطًا من ناحية الاستعلامات التي يجريها، إذ اقتصر البحث على صف واحد بناءً على مفتاح رئيسي من خلال التابع "findByPk"، أو البحث عن كل الصفوف باستخدام التابع "findAll". كانت هذه الاستعلامات كافيةً بالنسبة للواجهة الأمامية في نسخة القسم 5 من التطبيق، لكننا سنوّسع الواجهة الخلفية لنتمكن من تنفيذ استعلامات أعقد قليلًا. لننجز أولًا طريقة للحصول على ملاحظات مصنّفة على أنها مهمة أو غير مهمة فقط، باستخدام معامل الاستعلام important: router.get('/', async (req, res) => { const notes = await Note.findAll({ attributes: { exclude: ['userId'] }, include: { model: user, attributes: ['name'] }, where: { important: req.query.important === "true" } }) res.json(notes) }) وهكذا ستتمكن الواجهة الخلفية من استخلاص الملاحظات المهمة عند وصول الطلب: http://localhost:3001/api/notes?important=true والملاحظات غير المهمة عند وصول الطلب: http://localhost:3001/api/notes?important=false يتضمن استعلام SQL الذي ولّدته Sequelize العبارة WHERE، التي تُرشّح الصفوف المُعادة في الحالة الطبيعية: SELECT "note". "id", "note". "content", "note". "important", "note". "date", "user". "id" AS "user.id", "user". "name" AS "user.name" FROM "notes" AS "note" LEFT OUTER JOIN "users" AS "user" ON "note". "user_id" = "user". "id" WHERE "note". "important" = true; وبالطبع لن ينفع إنجاز الأمر بهذه الطريقة ما لم يهتم الاستعلام بأهمية الملاحظة، كأن يكون على النحو التالي: http://localhost:3001/api/notes يمكن تدارك الموضوع بطرق عدة، أولها -وقد لا تكون الطريقة الأفضل- على النحو التالي: const { Op } = require('sequelize') router.get('/', async (req, res) => { let important = { [Op.in]: [true, false] } if ( req.query.important ) { important = req.query.important === "true" } const notes = await Note.findAll({ attributes: { exclude: ['userId'] }, include: { model: user, attributes: ['name'] }, where: { important } }) res.json(notes) }) يُخزّن الكائن important الآن شرط الاستعلام، وسيكون الشكل الافتراضي لهذا الاستعلام على النحو التالي: where: { important: { [Op.in]: [true, false] } } أي أن العمود قد يحمل إحدى القيمتين true أو false باستخدام العامل Op.in، وهو أحد عوامل Sequelize العديدة؛ فإذا حددنا قيمة للمعامل req.query.important، سيتغير الاستعلام ليصبح بأحد الشكلين التاليين: where: { important: true } أو where: { important: false } بناءً على قيمة معامل الاستعلام. يمكن زيادة القدرة الوظيفية للتطبيق بالسماح للمستخدم بتخصيص كلمة مفتاحية لإحضار البيانات، إذ يعيد الطلب التالي: http://localhost:3001/api/notes?search=database الملاحظات التي تشير إلى الكلمة "database". كما يعيد الطلب التالي: http://localhost:3001/api/notes?search=javascript&important=true جميع الملاحظات التي حُددت أنها مهمة "important" وتشير إلى الكلمة "javascript". سيكون تنفيذ الأمر على النحو التالي: router.get('/', async (req, res) => { let important = { [Op.in]: [true, false] } if ( req.query.important ) { important = req.query.important === "true" } const notes = await Note.findAll({ attributes: { exclude: ['userId'] }, include: { model: user, attributes: ['name'] }, where: { important, content: { [Op.substring]: req.query.search ? req.query.search : '' } } }) res.json(notes) }) يوّلد التابع Op.substring الاستعلام الذي نريده باستخدام كلمة "LIKE"، فإذا أنشأت الاستعلام التالي مثلًا: http://localhost:3001/api/notes?search=database&important=true فسيولّد استعلام SQL ما نتوقعه تمامًا: SELECT "note". "id", "note". "content", "note". "important", "note". "date", "user". "id" AS "user.id", "user". "name" AS "user.name" FROM "notes" AS "note" LEFT OUTER JOIN "users" AS "user" ON "note". "user_id" = "user". "id" WHERE "note". "important" = true AND "note". "content" LIKE '%database%'; لكن لا تزال هناك ثغرة مزعجة تتمثل في أن الطلب التالي: http://localhost:3001/api/notes والذي يعني أننا نريد الحصول على جميع الملاحظات، سيتسبب في وجود عبارة WHERE غير الضرورية في الاستعلام، والتي قد تؤثر في فعاليته وفقًا لآلية عمل محرك قاعدة البيانات: SELECT "note". "id", "note". "content", "note". "important", "note". "date", "user". "id" AS "user.id", "user". "name" AS "user.name" FROM "notes" AS "note" LEFT OUTER JOIN "users" AS "user" ON "note". "user_id" = "user". "id" WHERE "note". "important" IN (true, false) AND "note". "content" LIKE '%%'; لنحسّن الشيفرة لكي تُستخدم عبارة WHERE عند الحاجة فقط: router.get('/', async (req, res) => { const where = {} if (req.query.important) { where.important = req.query.important === "true" } if (req.query.search) { where.content = { [Op.substring]: req.query.search } } const notes = await Note.findAll({ attributes: { exclude: ['userId'] }, include: { model: user, attributes: ['name'] }, where }) res.json(notes) }) وإذا احتوى الطلب على شرط للبحث مثل: http://localhost:3001/api/notes?search=database&important=true سيتولد استعلام يضم عبارة WHERE: SELECT "note". "id", "note". "content", "note". "important", "note". "date", "user". "id" AS "user.id", "user". "name" AS "user.name" FROM "notes" AS "note" LEFT OUTER JOIN "users" AS "user" ON "note". "user_id" = "user". "id" WHERE "note". "important" = true AND "note". "content" LIKE '%database%'; وإذا لم يحتوي شرطًا، فلن يكون لوجود WHERE حاجة: SELECT "note". "id", "note". "content", "note". "important", "note". "date", "user". "id" AS "user.id", "user". "name" AS "user.name" FROM "notes" AS "note" LEFT OUTER JOIN "users" AS "user" ON "note". "user_id" = "user". "id"; يمكنك الحصول على شيفرة التطبيق بوضعها الحالي من المستودع المخصص على GitHub ضمن الفرع part13-5. التمرينات 13.13 إلى 13.16 حاول إنجاز التمرينات التالية: التمرين 13.13 قدِّم آليةً لترشيح filtering النتائج من خلال كلمة مفتاحية ضمن الوجهة التي تعيد كل المدوّنات، وينبغي أن تعمل الآلية على النحو التالي: "GET /api/blogs?search=react": تعيد كل المدونات التي تضم الكلمة "react" في الحقل علمًا أن البحث حساس لحالة الأحرف. "GET /api/blogs": يعيد جميع المدوّنات. يمكنك الاستفادة من عوامل Sequelize لتنفيذ التمرين. التمرين 13.14 وسّع آلية الترشيح للبحث عن كلمة مفتاحية في أحد الحقلين "field" أو "author"، إذ سيعيد الاستعلام: GET /api/blogs?search=jami كل المدوّنات التي تضم الكلمة في أحد الحقلين "field" أو "author". التمرين 13.15 عدّل وجهات المدوّنات لتعيد المدوّنات بناءً على عدد الإعجابات بترتيب تنازلي. اطلع من خلال توثيق Sequelize على تعليمات ترتيب نتائج الاستعلام. التمرين 13.16 أنشئ الوجهة "api/authors/" كي تعيد عدد المدونات التي أضافها المؤلف والعدد الكلي للإعجابات. قدّم العملية على مستوى قاعدة البيانات. قد تحتاج إلى وظيفة التجميع group by ودالة التجميع sequelize.fn. قد تبدو المعلومات المُعادة بتنسيق JSON على غرار المثال الآتي: [ { author: "Jami Kousa", articles: "3", likes: "10" }, { author: "Kalle Ilves", articles: "1", likes: "2" }, { author: "Dan Abramov", articles: "1", likes: "4" } ] مهمة لعلامة إضافية: رتِّب البيانات المُعادة بناءً على عدد الإعجابات، على أن تُنفّذ عملية الترتيب من خلال استعلام قاعدة البيانات. ترجمة -وبتصرف- للفصل Join tables and queries من سلسلة Deep Dive Into Modern Web Development اقرأ أيضًا المقال السابق: العمل مع قواعد بيانات علاقية باستخدام Sequelize النماذج واﻻستعلامات والتقارير في برنامج قواعد بيانات مايكروسوفت أكسس Microsoft Access التعامل مع قواعد البيانات تصميم الجداول ومعلومات المخطط وترتيب تنفيذ الاستعلامات في SQL
  24. قد يحصل المخترق على مفتاح التشفير، لكن لن يستطيع استخدامه لأسباب كثيرة.
  25. أحد المهام الرئيسية للغة HTML هي هيكلة النص لكي يتمكن المتصفح من عرض صفحات ويب بالطريقة التي يريدها المطوّر، إذ يصف هذا المقال طريقة استخدام لغة HTML لهيكلة صفحة نصية بإضافة عناوين وفقرات وإبراز بعض العبارات والكلمات وإنشاء قوائم وغير ذلك. لا بدّ قبل الشروع في القراءة من الاطلاع على أساسيات لغة HTML التي يغطيها المقال تعرَّف على لغة HTML التعامل مع الأساسيات: العناوين الرئيسية والفقرات تتكون معظم النصوص المهيكلة من عناوين وفقرات سواء أكان النص قصة أو صحيفة أو كتاب جامعي أو مجلة. تسهل النصوص المهيكلة تجربة القراءة وتزيد المتعة، وتُنظَّم النصوص ضمن فقرات في لغة HTML بتغليف النص داخل عنصر الفقرة النصية <p> كما يلي: <p>أكتب حاليًا فقرة نصية</p> بينما لا بد من استخدام عنصر عنوان مثل العنوان الرئيسي <h1> لإبراز النص على أساس عنوان للمحتوى الذي تعرضه الصفحة: <h1>هذا عنوان المقال</h1> تقدّم لغة HTML ستة مستويات للعناوين ابتداءً من الأعلى مستوى <h1> وانتهاءً بالأدنى<h6>، إذ يمثِّل العنصر <h1> العنوان الرئيسي ويمثِّل <h2> العناوين الفرعية للعنوان الرئيسي و<h3> العناوين الفرعية للعناوين الفرعية وهكذا، انظر توثيقها -العناصر h1-h6- في موسوعة حسوب. إنجاز الهيكلية المتدرجة لمحتوى صفحة HTML سنستخدِم في القصة التي نراها تاليًا العنصر <h1> ليمثل عنوان القصة و<h2> ليمثل عنوان كل فصل، بينما سيمثل العنصر <h3> الأقسام الفرعية لكل فصل. <h1>The Crushing Bore</h1> <p>By Chris Mills</p> <h2>Chapter 1: The dark night</h2> <p>It was a dark night. Somewhere, an owl hooted. The rain lashed down on the ...</p> <h2>Chapter 2: The eternal silence</h2> <p>Our protagonist could not so much as a whisper out of the shadowy figure ...</p> <h3>The specter speaks</h3> <p>Several more hours had passed, when all of a sudden the specter sat bolt upright and exclaimed, "Please have mercy on my soul!"</p> يعود الأمر إليك في طبيعة الحال لاختيار عناصر العناوين ودلالاتها طالما أنّ التسلسل الذي تتبعه في هيكلة المحتوى منطقي، لذلك لا بد أن تتذكر دائمًا بعض الممارسات الجيدة عند هيكلة العناوين: يفضَّل استخدام عنوان رئيسي <h1> واحد في كل صفحة، فهو العنوان ذو المستوى الأعلى وتتدرج تحته بقية العناصر. تأكد من استخدام عناصر العناوين بالترتيب الصحيح في الهيكلية المتدرجة للصفحة، فلا تستخدِم مثلًا <h3> ليشير إلى عنوان فرعي ثم تستخدم بعده <h2> ليمثل عنوانًا فرعيًا لعنوان فرعي، فهذا أمر غير منطقي وسيقود إلى نتائج غريبة. حاول عدم استخدام أكثر من ثلاثة مستويات في كل صفحة إلا إذا كان الأمر ضروريًا، فقد قَلَّ انتشار المستندات التي تعتمد هيكلية ذات مستويات عميقة نظرًا لصعوبة التنقل بين مستوياتها، ويفضل في حالات مثل هذه توزيع المحتوى ضمن صفحات عدة. ما فائدة إعطاء الصفحة هيكلية معينة؟ للإجابة على هذا السؤال، دعنا نلقي نظرةً على الملف text-start.html الذي سيكون نقطة انطلاق في تطبيقات هذا المقال (وصفة حمُّص جيدة)، لذلك انسخ هذا الملف وضعه على حاسوبك لأنك ستحتاجه لاحقًا، كما يحتوي جسم المستند محتوًى مكوّنًا من أجزاء مختلفة لم تُنظَّم بعد في هيكلية محددة، لكنها مفصولة عن بعضها بمحارف الأسطر الجديدة، فعندما تفتح الملف عبر المتصفح، فستجد أنّ النص قطعة واحدة كبيرة. السبب في ذلك هو عدم وجود أيّة عناصر لهيكلة هذا النص، فلن يميّز المتصفح بين العناوين أو الفقرات، بالإضافة إلى ذلك: يستعرض الزائر النص بسرعة بحثًا عن المحتوى الأساسي ويكتفي عادةً بقراءة العناوين ليختار نقطة البداية، إذ يقضي المستخدِم عادةً فترةً قصيرةً جدًا ضمن صفحة الويب، فإذا لم يستطع الزائر إيجاد أيّ شيء واضح أو مفيد خلال ثوان معدودة، فعلى الغالب سيتوقف عن القراءة ويغادر الصفحة. تفهرِس محركات البحث الصفحات آخذةً بالحسبان محتوى العناوين على أنها كلمات مفتاحية مهمة تؤثِّر على ترتيب نتائج البحث، فدون العناوين إذًا لن تُعَدّ صفحتك جيدةً وفق معايير السيو SEO أو تحسين محركات البحث Search Engine Optimization. لا يقرأ الزوار ذوو المشاكل البصرية الحادة صفحات ويب بأنفسهم، وإنما يستمعون إلى محتواها عن طريق برمجيات تُدعى قارئات الشاشة، إذ تتيح هذه البرمجيات الوصول السريع إلى محتوى نصي محدد، وتجري هذه العملية بطرق عدة منها تحديد خطوط عريضة للمحتوى بقراءة العناوين، مما يسمح للمستخدِم بإيجاد المعلومات التي يريدها بسرعة، فإذا لم تكن العناوين موجودةً، فسيضطر المستخدِم إلى الاستماع إلى النص بأكمله. إذا أردت تنسيق الصفحة باستخدام CSS أو جعلها تفاعليةً أكثر عن طريق جافاسكربت، فلا بد من تغليف المحتوى المطلوب بواسطة عناصر HTML لكي تستهدفه شيفرات CSS أو جافاسكربت بفعالية أكبر. لهذه الأسباب وغيرها سنحتاج إلى هيكلة محتوى الصفحة. تطبيق: هيكلة محتوى لننتقل مباشرةً إلى التطبيق العملي، إذ سنضيف عناصر إلى النص غير المنسق الموجود في حقل المدخلات Input field والذي يُعرض في حقل المخرجات Output field، فإذا ارتكبت خطأً، فيمكنك مسح ما كتبته بالنقر على زر "إعادة ضبط Reset"، في حين إذا وجدت نفسك تائهًا كليًا، فانقر على الزر"أظهر الحل Show solution" لترى الحل الصحيح. لماذا نحتاج إلى الدلالات؟ نعتمد على الدلالات في كل مكان حولنا، فنحن نعتمد على تجاربنا السابقة لتدلنا على وظيفة كل الأغراض التي حولنا، فعندما نرى هذا الغرض نعرف تمامًا عمله، فنحن نتوقع مثلًا أن تعني إشارة المرور الحمراء التوقف والخضراء الحركة، لكن سنرتبك بالفعل إذا تغيّرت الدلالات أو طبقناها بصورة خاطئة. لا بد في السياق عينه أن نتأكد من استخدام العناصر الصحيحة لكي نعطي المحتوى المعنى والوظيفة والمظهر الصحيح، فوفقًا لهذه المقاربة، فإن العنصر <h1> هو عنصر دلالي يعطي المحتوى الذي يغلفه صفة عنوان رئيسي لصفحتك بأعلى مستوى. <h1>This is a top level heading</h1> سيعطي المتصفح محتوى هذا العنوان حجمًا كبيرًا افتراضيًا لكي يظهر على أساس عنوان رئيسي (على الرغم من أنك قادر على تنسيقه بالطريقة التي تريد). والأهم من ذلك أن لدلالته استخدامات عدة في محركات البحث مثلًا أو في قارئات الشاشة، كما يمكنك من ناحية أخرى عرض أيّ عنصر وكأنه عنوان رئيسي. لاحظ الشيفرة التالية: <span style="font-size: 32px; margin: 21px 0; display: block;">Is this a top level heading?</span> ليس للعنصر <span> أية دلالات، ويستخدَم لتطبيق تنسيق لغة CSS على محتوى محدد أو جزء منه أو استهدافه بشيفرة جافاسكربت دون أن يدل ذلك على معنى جديد، وقد طبقنا في الشيفرة السابقة بعض التنسيقات على محتوى ليبدو تمامًا على أنه عنوان رئيسي، لكنك لن تجني أيّ فوائد أخرى من هذه العملية لأنّ هذا العنصر غير دلالي ولا يحمل معنًى محددًا، لذلك من الأفضل دائمًا استخدام عناصر HTML المخصصة لأداء وظيفة معينة. القوائم لنركِّز الآن على القوائم، فهي موجودة في كل مكان يحيط بنا ابتداءً من قائمة التسوق إلى قائمة الاتجاهات التي تسلكها يوميًا للوصول إلى المنزل، وستجد كذلك أنّ القوائم موجودة في كل مكان على الويب ويهمك منها ثلاثة أنواع سنناقشها بشيء من التفصيل. القوائم غير المرتبة تستخدَم القوائم غير المرتبة Unordered list لوصف مجموعة عناصر لا أولوية لترتيبها ضمن القائمة، ومن الأمثلة عليها قائمة التسوق: milk eggs bread hummus تبدأ القائمة غير المرتبة بالعنصر <ul> الذي يغلف جميع العناصر: <ul> milk eggs bread hummus </ul> وينبغي أن نغلف عناصر القائمة بالعنصر <li>: <ul> <li>milk</li> <li>eggs</li> <li>bread</li> <li>hummus</li> </ul> تطبيق: إنشاء قائمة غير مرتبة حاول استخدام محرر الشيفرة في الأسفل لإنشاء قائمة غير مرتبة خاصة بك: القوائم المرتبة تستخدَم القوائم المرتبة ordered list لوصف مجموعة عناصر ينبغي ترتيبها ضمن القائمة، ومن الأمثلة عليها الاتجاهات التي يجب أن تسلكها لتصل إلى مكان معيَّن: Drive to the end of the road Turn right Go straight across the first two roundabouts Turn left at the third roundabout The school is on your right, 300 meters up the road توصَّف القائمة المرتبة بأسلوب القوائم غير المرتبة نفسه باستثناء العنصر المستخدَم في تغليف العناصر الذي سيكون في هذه الحالة <ol> بدلًا من <ul>: <ol> <li>Drive to the end of the road</li> <li>Turn right</li> <li>Go straight across the first two roundabouts</li> <li>Turn left at the third roundabout</li> <li>The school is on your right, 300 meters up the road</li> </ol> تطبيق: إنشاء قائمة مرتبة حاول استخدام محرر الشيفرة في الأسفل لإنشاء قائمة مرتبة خاصة بك: تطبيق: هيكلة وصفة الطعام في مثالنا السابق أصبحت في هذه المرحلة قادرًا على هيكلة الصفحة التي تتحدث عن وصفة الحمُّص في مثالنا السابق، إذ يمكنك تخزين نسخة عن الملف text-start.html على جهازك ثم تجري التعديلات عليه، أو تنفيذ الأمر مستخدمًا محرر الشيفرة في الأسفل، وقد يكون تنفيذ التمرين على حاسوبك أفضل لأنك ستتمكن من حفظ عملك، في حين لن تتمكن من ذلك إذا استخدمت محرر الشيفرة في المقال لأن التغييرات التي أجريتها ستختفي عند إغلاق الصفحة. إذا وجدت نفسك تائهًا، فانقر على الزر "أظهر الحل Show solution" لترى الحل الصحيح، أو تحقق من الملف text-complete.html الذي يحتوي على العمل بأكمله. القوائم المتداخلة من الممكن جدًا أن تتداخل القوائم، فقد تحتاج إلى قائمة فرعية تحت أحد عناصر قائمة أخرى، ولنأخذ القائمة الثانية من مثال وصفة الطعام: <ol> <li>Remove the skin from the garlic, and chop coarsely.</li> <li>Remove all the seeds and stalk from the pepper, and chop coarsely.</li> <li>Add all the ingredients into a food processor.</li> <li>Process all the ingredients into a paste.</li> <li>If you want a coarse "chunky" hummus, process it for a short time.</li> <li>If you want a smooth hummus, process it for a longer time.</li> </ol> بما أن آخر عنصرين يتعلقان بالموضوع ذاته، فقد يبدوان مثل إرشادات فرعية تحت العنصر الذي يسبقهما، ومن المنطقي في هذه الحالة وضعهما ضمن قائمة غير مرتبة فرعية تحت العنصر الذي يسبقهما: <ol> <li>Remove the skin from the garlic, and chop coarsely.</li> <li>Remove all the seeds and stalk from the pepper, and chop coarsely.</li> <li>Add all the ingredients into a food processor.</li> <li>Process all the ingredients into a paste. <ul> <li>If you want a coarse "chunky" hummus, process it for a short time.</li> <li>If you want a smooth hummus, process it for a longer time.</li> </ul> </li> </ol> عُد إلى التطبيق السابق وحاول تعديل القائمة الثانية كما فعلنا هنا. إبراز النص والإشارة إلى أهميته نحاول أحيانًا إبراز بعض الكلمات عندما نتحدث للإيحاء بأهميتها أو لنعني أمرًا آخر، وقد نحاول إعطاء أهمية لكلمات أخرى أوتمييزها بطريقة أو بأخرى، وكذلك الأمر في HTML، إذ تضم اللغة عناصر دلالية لتمييز المحتوى النصي بأسلوب مماثل، وسنناقش تاليًا أكثر هذه العناصر شيوعًا. إبراز محتوى عندما نريد إبراز أو تفخيم عبارة في الكلام المحكي نشدِّد على كلمات محدَّدة لكي نغيّر عمدًا دلالة ما نقول، وهذا ما نفعله أيضًا أثناء الكتابة، إذ نميل إلى التشديد على الكلمات بكتابتها على نحو مائل، وتحمل الجملتين التاليتين على سبيل المثال دلالتين مختلفتين: سعيد لأنك لم تتأخر سعيد لأنك لم تتاخر تشير الجملة الأولى إلى راحة الشخص بأن الآخر لم يتأخر بالفعل، بينما تحمل الثانية نوعًا من الاستهزاء أو هجوم مبطن على الآخر للتعبير عن الانزعاج من تأخره. نستخدِم في HTML العنصر <em> لتوصيف حالات مثل هذه وزيادة المتعة أثناء قراءة النص، كما تُميِّز قارئات الشاشة هذه العناصر وتنطقها بنبرة صوتية مختلفة، وتعرض المتصفحات محتوى هذا العنصر بخط مائل افتراضيًا، لكن انتبه إلى استخدام العنصر لمجرد عرض النص مائلًا، وعليك عندها استخدام العنصر <i> أو أن تطبق تنسيق CSS مع عنصر بصنف محدد. <p>I am <em>glad</em> you weren't <em>late</em>.</p> إظهار أهمية محتوى لإبراز أهمية بعض الكلمات، فإننا نشدِّد عليها عند لفظها أو نكتبها بخط ثخين Bold مثل هذا السائل عالي السميّة. نستخدِم في HTML العنصر <strong> لإظهار أهمية الكلمات، إذ يعرض المتصفح محتوى هذا العنصر بخط ثخين افتراضيًا، كما تميزه قارئات الشاشة وتنطقها بنبرة صوتية مختلفة، ولا تحاول أيضًا استخدام العنصر لمجرد عرضه بخط ثخين، وعليك عندها استخدام العنصر <b> أو تطبيق تنسيق CSS مع عنصر بصنف محدد. <p>This liquid is <strong>highly toxic</strong>.</p> <p>I am counting on you. <strong>Do not</strong> be late!</p> يمكنك أيضًا المزج بين العنصرين <strong> و <em>. <p>This liquid is <strong>highly toxic</strong> — if you drink it, <strong>you may <em>die</em></strong>.</p> تطبيق: لنغير أهمية محتوى يوجد نص قابل للتعديل في محرِّر الشيفرة التالي، وحاول أن تبرز بعض الكلمات وتعطي أهمية لبعضها الآخر، كما ترى لكي تتمرن قليلًا: خط مائل أو ثخين أو تحته سطر تتميز العناصر التي تحدثنا عنها سابقًا بدلالات معنوية محددة، لكن حالة العناصر <b> و <i> و <u> أعقد قليلًا، فقد وضعت هذه العناصر كي يكتب الأشخاص كلمات محددة بخط ثخين أو مائل أو ليظهر تحتها سطر، وذلك في حقبة لم تكن تنسيقات CSS مدعومة أو كانت محدودة الدعم، إذ تؤثر هذه العناصر على طريقة العرض وليس على الدلالة، وتُعرف حاليًا بعناصر العرض presentational elements، كما لا ينبغي استخدام هذه العناصر حاليًا لأنها غير دلالية، وقد رأينا أهمية الدلالات على أصعدة مختلفة مثل سهولة الوصول accessibility وتحسين محركات البحث أو سيو SEO. أعادت HTML5 تعريف هذه العناصر مانحةً إياها دلالات قد تبدو مربكةً نوعًا ما، وإليك قاعدة مهمة جدًا: قد يكون استخدام العناصر <b> أو <i> أو <u> ملائمًا لإيصال معنى محدد يمكن إيصاله تقليديًا بإمالة الخط أو تثخينه أو وضع سطر تحته إذا لم توجد عناصر أخرى مناسبة، لكن عليك أن تبقي في حساباتك مفهوم سهولة الوصول أو الشمولية accessibility، فليس استخدام الخط المائل في غير سياقه مفيدًا لمستخدمِي قارئات الشاشة أو للأشخاص الذين يستخدِمون أبجديات مختلفة عن اللاتينية. <i>: يُستخدَم لإيصال معنى يمكن إيصاله تقليديًا بخط مائل مثل كلمات أجنبية، مرادفات، تصميم، مصطلحات تقنية، أفكار… <b>: يُستخدَم لإيصال معنى يمكن إيصاله تقليديًا بخط ثخين مثل كلمات مفتاحية، أسماء منتجات، جملة افتتاحية.. <u>: يُستخدَم لإيصال معنى يمكن إيصاله تقليديًا بخط تحته سطر مثل الأخطاء الإملائية والنحوية. ملاحظة: يربط الناس جدًا بين الكلمات التي تحتها سطر والروابط التشعبية، لذلك يفضَّل في صفحات الويب أن لا تضع سطرًا تحت محتوى ما لم يكن رابطًا، واستخدم <u> عندما تجد أنّ دلالته صحيحة، وفكّر دائمًا باستخدام CSS لتغيير المحتوى الذي تحته سطر إلى شكل أكثر ملاءمةً لويب، كما يشرح المثال التالي ما يمكن فعله: <!-- مصطلحات علمية --> <p> الطنان ياقوتي الحنجري (<i>Archilochus colubris</i>) هو الطنان الأكثر انتشارًا في أمريكا الشمالية. </p> <!-- كلمات أجنبية --> <p> كانت القائمة بحرًا من الكلمات الغريبة <i lang="uk-latn">vatrushka</i>, <i lang="id">nasi goreng</i> و <i lang="fr">soupe à l'oignon</i>. </p> <!-- أخطاء إملائية غير معروفة --> <p> Someday I'll learn how to <u style="text-decoration-line: underline; text-decoration-style: wavy;">spel</u> better. </p> <!-- تظليل كلمات عند عرض إرشادات --> <ol> <li> <b>شرِّح</b> قطعتين من الخبز </li> <li> <b>أدخل</b> شريحة طماطم وورقة خس بين شريحتي الخبز </li> </ol> خلاصة هذا كل شيء حتى الآن، ومن المفترض أن يكسبك هذا المقال فكرةً جيدةً عن توصيف النصوص وهيكلتها في HTML وأن يعرّفك على أهم العناصر المستخدمة لهذه الأغراض، كما هنالك الكثير من العناصر التي تحمل دلالات مختلفة وسنطَّلع على الكثير منها عند التطرق إلى التنسيق المتقدم للنصوص لاحقًا، وسنستعرض في المقال التالي تفاصيل إنشاء الروابط التشعبية في HTML والذي يُعَدّ العنصر الأهم تقريبًا في ويب. ترجمة -وبتصرف- للمقال HTML text fundamentals. اقرأ أيضًا هيكلة وتوزيع محتوى صفحات الويب HTML و CSS للمبتدئين: كيف تصمم أول صفحة ويب لك ترويسة الصفحة والبيانات الوصفية في HTML
×
×
  • أضف...