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

Ayman

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

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

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

كل منشورات العضو Ayman

  1. السلام عليكم والله يعطيكم العافية لدي سؤال وهو أنني صممت صفحة عادية html تحتوي على بعض الصور والنصوص واستخدمت css .. لكن لدي السؤال كيف يمكنني جعل هذه الصفحة متوافقة مع الموبايل والأجهزة المحمولة الأخرى؟ علماً أنني اطلعت على البوتستراب لكنني لم أعرف كيف يمكنني جعل صفحتي التي أنشأتها تدعم الموبايل. الرجاء شاركونا خبراتكم هل يجب على تصميم الصفحة من البداية واختبار حزمة بوتستراب علماً أن تصميم الصفحة يختلف عن الأمثلة المرفقة في موقع بوتستراب أم هناك طريقة سريعة جعل الموقع يعمل على أجهزة المحمول مباشرة باستخدام بوتستراب أيضاً شكراً لكم على وقتكم ونرجو الفائدة
  2. بعد التعرّف على كيفية تشكيل المحتوى باستخدام HTML. سيكون لِزامًا عليك -كمطور ويب- معرفة ماهية CSS (أوراق الأنماط المتتالية أو Cascading Style Sheets بالانجليزية) وكيفية إدراج التصميم مع صفحات HTML. يُعتبر CSS أحد التقنيات التي يجب التركيز عليها، إذ هي من سيحدد شكل موقع الويب وما يظهر للعيان. يُعتبر هذا الدرس مدخلا لأساسيات لغة التوصيف هذه، وستكون هيكلته كالتالي: ماهي لغة CSS؟ تعريف قوانين التصميم التعليقات مع CSS تجميع المنتقى استخدام منتقى CSS متقدم المنتقى العام المنتقى الفرعي المنتقى الابن المنتقى التسلسلي المنتقى الشقيق المحدّد Pseudo-classes العناصر Pseudo-elements اختزال CSS مقارنة القيم الفردية والمختزلة اختزال القيم بخاصية واحدة أيهما يجب اختياره الخاصية المفردة أم القيمة المختزلة؟ مراجع الاختزال إدراج CSS مع HTML الإدراج الداخلي الإدراج المضمر الإدراج خارجي استيراد ملف التصميم خاتمة أسئلة وتمارين ماهي لغة CSS؟ تحدّد لغة HTML هيكلة الصفحة وتُخبر المتصفحات بوظيفة كل عنصر في الصفحة (مثل رابط لصفحة أخرى أو عنوانا رئيسياً) في حين تقدّم لغة CSS تعليمات للمتصفح حول كيفية عرض العناصر في الصفحة من حيث التصميم والمسافة والموضع. ولو افترضنا أن HTML تقوم على بناء هيكل المنزل فإن CSS تقوم بطلاء المنزل و تصميم الديكور الخاص به. يكون ذلك عن طريق مجموعة من التعليمات التي سنتعرف على كيفية سردها لاحقا. تحدد تلك التعليمات (أو القواعد) ما يجب تنسيقه وتصميمه على عناصر HTML. كما تحتوي تلك القواعد على خاصيات معينة (مثل الألوان وحجم الخطوط ونوع الخط) لعناصر HTML والتي تقوم بالتعديل عليها وتحديد القيم التي ستغير من شكل تلك العناصر. وباستخدام تقنية CSS يمكننا إيجاد على سبيل المثال جميع وسوم <h2> ثم تلوينها جميعاً باللون الأخضر أو إيجاد جميع الأسماء الملحقة author-name الموجودة في وسوم الفقرات <p> وتغيير لون الخلفية إلى اللون الأحمر وتغيير حجم النص إلى ضعفين الحجم العادي وإضافة مسافة بين النصوص بحجم 10px. لا تعتبر لغة CSS لغة برمجية فهي ليست مثل JavaScript وليست لغة هيكلة مثل لغة HTML، وبصراحة لا يمكن مقارنة هذه اللغة بأي تقنية أخرى. قبل عمليات تطوير الويب كانت بعض التقنيات تعرّف واجهة الموقع وتدمج بين البنية والشكل التقديمي. وهذا ليس بالشيء الذكي الذي يجب فعله في البيئة التي تتغير باستمرار وخاصة صفحات الويب ولهذا السبب تم اختراع لغة CSS. دورة تطوير واجهات المستخدم ابدأ عملك الحر بتطوير واجهات المواقع والمتاجر الإلكترونية فور انتهائك من الدورة اشترك الآن تعريف قوانين التصميم دعونا نلقي نظرة على الكود في المثال التالي ثم سنقوم بشرحه تالياً: selector { property1: value; property2: value; property3: value; } شرح المثال السابق كالتالي: يحدّد المنتقى (selector) عنصر HTML والذي سيتم تطبيق القاعدة عليه. يتم تحديد العنصر من خلال اختيار اسم العنصر مثل body أو h1 أو من خلال اسم محدد class ويتم ضع الاسم كلاحقة ضمن العنصر لاستدعاء التصميم. تحتوي الأقواس المنحنية بداخلها زوجاً من الخاصية والقيمة وتفصل بين كل خاصية وقيمة فاصلة منقوطة (;). كما يتم الفصل بين الخاصية والقيمة بنقطتين (:). تحدّد الخاصية ما يجب فعله للعنصر أو العناصر الذي قمت باختيارهم. وذلك يحتوي على تنوع كبير حيث تؤثر على لون العنصر ولون الخلفية والموضع والهوامش الخارجية والهوامش الداخلية ونوع الخط وغيرها الكثير. تحدّد القيمة الصفة النهائية التي تود تطبيقها على العنصر الذي اخترته. وتعتمد القيم على نوع الخاصية التي اخترها فمثلاً الخاصية color تسمح باختيار الألوان ضمن الست عشرية مثل #336699 أو من خلال القيمة العامة للون RGB مثل (rgb(12,134,22 أو اسم اللون مثل red أو green أو blue. بينما الخاصيات التي تؤثر على الموضع والهوامش والطول والعرض يمكن قياسها من خلال pixels ،ems أو percentages (النسبة المئوية) أو centimeters وغيرها. لنلقي نظرة على هذا المثال المحدّد: p { margin: 5px; font-family: Arial; color: blue; } شرحاً للمثال السابق فإن القاعدة هي اختيار العنصر <p> أي جميع الوسوم <p> الموجودة في صفحة أو صفحات الويب HTML وسيتم تطبيق قاعدة CSS عليها. فقد قمنا بالتعديل على العنصر <p> في حيث الهوامش الخارجية هي 5px ونوع الخط هو Arial ولون النص هو blue. سنشرح لاحقاً بالتفصل كل خاصية والقيمة المناسبة لها لكن الآن الهدف هو تغطية أساسيات CSS. التعليقات في CSS أحد الأمور الهامة الذي يجب أن تعرفها هي وضع تعليقات ضمن تصميم CSS. حيث يمكنك إدراج تعليق من خلال كتابة النص بين هذه الاشارات /* */ حيث يمكنك تمديد التعليقات إلى أسطر عديدة كما أن المتصفح سيتجاهل هذه الأسطر. /* في هذه الكود أساسيات المنتقى */ selector { property1: value; property2: value; property3: value; } يمكنك أيضاً إدراج التعليقات بين أو ضمن مجموعة خاصيات. فمثلاً في الكود التالي قمنا بوضع الخاصية الثانية والثالثة ضمن تعليق وبما أنهم ضمن التعليق سيتجاهل المتصفح تلك التصاميم ولن يتم تطبيقها. تعتبر هذه الطريقة مفيدة جداً خاصة إذا كنت تفحص أي جزء تريد أن يتم تطبيقه. والجزء الذي لا تريده ضعه ضمن تعليق أفضل من حذفهم ثم احفظ شِفرة CSS وأعد تحميل صفحة HTML لمُعاينة النتيجة. selector { property1: value; /* property2: value; property3: value; */ } تحتوي لغة CSS على نظام التعليق المتعدد الأسطر وهذا بخلاف بقية اللغات الأخرى أي أنها لا تحتوي على تعليق بسطر واحد. في حال أردت إدراج تعليق بسطر واحد فقط ضع بداية ونهاية التعليقات /* و */. تجميع المنتقى يمكنك أيضاً جمع أكثر من منتقى مختلف. نريد تعيين التصميم إلى <h1> و<p> فيمكنك كتابة التصميم كما في المثال التالي: h1 { color:red; } p { color:red; } تعتبر استخدام هذه الطريقة غير مثالية عند تكرار المعلومات نفسها لكن يمكنك تقصيرها من خلال جمع أكثر من منتقى وتطبيق نفس التصميم على الجميع وذلك من خلال فصل كل منتقى عن الآخر بفاصلة. سيتم تطبيق التصميم في المثال التالي ضمن القوسين على جميع المنتقى الموجود: h1, p { color:red; } هناك العديد من المنتقى المختلف في كل مطابقة جزء مختلف في الترميز. الأنواع الثلاثة الأساسية والتي ستصادفك كثيراً هم: منتقى العنصر p ويطابق على جميع العناصر الموجودة بالصفحة كما شرحنا سابقاً. الصنف example. وهو يدل على اسم صنف (class)، ويتم تطبيق جميع القيم في التصميم للعنصر الذي يحمل هذا الصنف. فمثلاً في التصميم السابق للعنصر يمكن اضافة يمكن إضافة الصنف <"p class="example> أو <"li class="example> أو <"div class="example> أو أي عنصر يحمل الصنف example. المعرّف example# وهو يدل على اسم معرّف (ID) ويتم تطبيق جميع القيم في التصميم للعنصر الذي يحمل هذا الاسم. فمثلاً في التصميم السابق للعنصر يمكن إضافة الاسم المعرّف <"p id="example> أو <"li id="example> أو <"div id="example> أو أي عنصر يحمل الاسم example. تذكّر بأن المعرّف ID لا يفحص أسماء العناصر كما يمكنك اختيار اسماً واحداً لكل عنصر في صفحات HTML. يمكنك مشاهدة ما قمنا بعمله في الأمثلة التالية. من الملاحظ أنه يمكنك مشاهدة الأمثلة التالية على المتصفح وأيضاً تصميم التحذير الملوّن باللون الأحمر على كلاً من القائمة وأيضاً الفقرة. (إن لم تظهر علامات القامة فهذا لأنك قمت بتلوين النص باللون الأبيض وبخلفيه بيضاء). example-selectors.html selectors.css يمكنك أيضاً دمج أكثر من منتقى لتحديد قواعد أخرى أكثر تخصيصاً: يطابق p.warning جميع الفقرات التي تحتوي على الصنف warning. يطابق div#example جميع العناصر ضمن اللاحقة id والمعرّفة بكلمة example ولكن فقط مع العنصر <div>. يطابق p.info جميع الفقرات التي تحتوي على المعرّف info وأيضاً يطابق li.highlight القوائم المعرفّة بالكلمة highlight. في المثال استخدمت تلك الطرق لتحديد الاختلاف بين التصاميم المختلفة للمعّرف warning: example-specificselectors.html specificselectors.css استخدام منتقى CSS متقدم في القسم السابق تحدثنا عن أساسيات CSS واستخدام أيضاً المنتقى والعناصر والمعرّف class وid. وبتلك الأنواع من المنتقى يمكنك إنجاز الكثير ولكن هذا ليس كل شيء. حيث يوجد أنواع أخرى من المنتقى والتي تسمح لك باختيار عنصر وتطبيق تصميم عليه بأكثر تخصيص: المنتقى العام: يمكن استخدام لاختيار جميع العناصر في الصفحة. المنتقى الفرعي: حيث يمكنك اختيار أسماء معينة وإضافتها على العنصر كلاحقة وتسمح لك بتطبيق تصميم على عنصر محدد الذي يحمل ذلك الاسم. المنتقى الابن: يمكنك استخدام هذا النوع في حال أردت اختيار عنصر محدّد والتي تعتبر كأبناء للعناصر الأصلية. المنتقى التسلسلي: يمكنك استخدام هذا النوع في حال اخترت عنصر محدّد والتي تنحدر بتسلسل ضمن تلك العناصر (ليست متسلسلة بشكل مباشر ولكنها متسلسلة كشجرة العائلة أيضاً يمكن استخدام هذا النوع) المنتقى الشقيق: يمكن استخدام هذا النوع في حال أردت اختيار عنصر محدّد والذي يتبع عنصر محدد آخر. المحدّد Pseudo-classes: تسمح بتصميم العناصر التي لا تعتمد على العناصر نفسها لكن على العوامل المقتصرة على فئة معينة مثل مجموعة روابط تشعبية (أي إذا كانت تلك الروابط تم تمرير مؤشر الماوس عليها أو تم زيارتها فعلاً). العناصر Pseudo-elements: تسمح لك بالتصميم على جزء من العناصر وليس جميع العناصر (أي الحرف الأول ضمن العنصر). كما تسمح أيضاً بإدراج محتوى قبل أو بعد عناصر محددة. ستطلع أيضاً على مراجع لبعض الأنواع المعقدة للمنتقى في نهاية هذا الدرس. لا تقلق في حال لم تفهمهم فوراً لأنك ستحصل على الكثير من المعلومات والخبرة في الحياة العملية من خلال تصميم المواقع. من الأفضل أن تبدأ باستخدام أول ثلاثة أنواع المنتقى ومع الوقت تستخدم البقية. المنتقى العام ببساطة يحدد المنتقى العام كل عنصر في الصفحة ويُطبق التصميم عليه. على سبيل المثال: تقول القاعدة التالية بأنه يجب تقديم 1 بكسل بلون أسود كإطار لجميع العناصر في الصفحة. * { border: 1px solid #000000; } المنتقى الفرعي يسمح المنتقى الفرعي باختيار العناصر التي تعتمد على لواحقها. على سبيل المثال: يمكنك اختيار جميع وسوم <img> والتي تحتوي اللاحقة alt كما في المثال: img[alt] { border: 1px solid #000000; } لاحظ الأقواس المربعة. يمكنك الاختيار بأن تضع حدودًا سوداء حول أي صورة والتي تحتوي على اللاحقة alt باستخدام تلك الطريقة وأيضاً التعديل على الصور الأخرى ضمن الحدود الحمراء اللامعة وهي طريقة مفيدة في الاختبار. لكن أيضاً يمكننا اختيار قيمة اللاحقة لتطبيق عليها تصميم معين وهي طريقة مفيدة وفعالّة جداً. في المثال التالي سيتم تطبيق تصميم لجميع الصور والتي تحتوي اللاحقة src مع القيمة alert.gif كالتالي: img[src='alert.gif'] { border: 1px solid #000000; } لا يمكنك الجزم بأن هذه الطريقة فعالّة بشكل كبير جداً إلا أنها مفيدة لعمليات تصحيح الأخطاء وأيضاً مهمة في القدرة على اختيار جزء معين من اللواحق مثل امتداد الملفات. أيضاً مع الإصدار الأخير تقدم CSS3 ثلاث طرق جديدة تتعامل مع أنواع اللواحق عوضاً عن التركيز على النصوص في قيم اللواحق، يمكنك قراءة مقالة المبرمج Christopher Schmitt حول المنتقى الفرعي في CSS3. المنتقى الإبن يمكنك اختيار لمنتقى الابن لاختيار فقط العناصر التي تكون أبناء لعناصر أخرى محددة. على سبيل المثال، سيتم تطبيق التصميم على النص الموجود ضمن عنصر <strong> وهو ابن للعنصر <h3> إلي اللون الأزرق. أي فقط لذلك العنصر وليس لأي عنصر <strong> آخر: h3 > strong { color: blue; } المنتقى التسلسلي يشبه المنتقى التسلسلي بشكل كبير المنتقى الابن باستثناء أن المنتقى الابن يختار فقط العنصر الذي يليه مباشرة. بينما يمكنك اختيار أي عنصر في المنتقى التسلسلي ليس فقط الذي يليه مباشرة بل أي عنصر موجود لاحقاً. يمكنك مشاهدة المثال التالي مع وسوم HTML لفهم تماماً ما نقصد به: <div> <em>hello</em> <p>In this paragraph I will say <em>goodbye</em>.</p> </div> في المثال السابق يعتبر العنصر <div> الأب لجميع الوسوم الأخرى حيث أنه لديه إبنان وهما <em> وأيضاً <p>. والعنصر <p> لديه إبن واحد فقط وهو <em>. يمكنك اختيار المنتقى الابن لاختيار العنصر <em> مباشرة داخل العنصر <div> مثل: div > em { ... } يمكنك استخدام الطريقة التالية في حال أردت اختيار المنتقى التسلسلي كالتالي: div em { ... } وفي هذه العملية سيتم تحديد كلاً من العنصرين <em>. المنتقى الشقيق يسمح لك هذا النوع من المنتقى باختيار عنصر محدد والذي يأتي مباشرة بعد عنصر محدد آخر وبنفس المستوى من تسلسل العناصر. فمثلاً اذا أردت اختيار جميع عناصر <p> والتي تأتي مباشرة بعد جميع عناصر <h2> فقط وليس <p> بمفرده فيمكنك استخدام هذا النوع كالتالي: h2 + p { ... } المحدّد Pseudo-classes تستخدم تقنية Pseudo-classes لتصميم حالات متنوعة من العناصر وليس لعناصر محددة فقط. اشهر طريقة في استخدام هذا النوع النوع هو تطبيق تصميم على الروابط التشعبية. تشرح القائمة التالية الأنواع المختلفة من Pseudo-classes كما تشرح حالة الروابط التي يتم تطبيق التصميم عليها. link: — التصميم الطبيعي والافتراضي لجميع الروابط في الصفحة. visited: — وهي الروابط التي تم فتحها وزيارتها في المتصفح التي تستخدمه حالياً. focus: — وهي الروابط (متضمنة حقول النماذج) التي تضغط عليها ولم تحرّر مؤشر الماوس منها. hover: — وهي الروابط التي تمرّر مؤشر الماوس عليها. active: — وهي الروابط التي تم النقر عليها. يشرح كود CSS في المثال التالي ما قمنا بشرحه مسبقاً. في المثال التالي، تكون جميع ألوان الروابط بشكل افتراضي (وهذا يشمل جميع المتصفحات) ملوّنة بللون الأزرق. وعند تمرمر مؤشر الماوس عليها أو النقر بمؤشر الماوس وبدون تحرير الزر سيختفي الخط السفلي لنص الروابط. كما ستحول لون الرابط إلى اللون الرمادي عند زيارة الرابط. وأخيراً بعد مشاهدة الرابط سيصبح نص الرابط غامقاً. a:link { color: blue; } a:visited { color: gray; } a:hover, a:focus { text-decoration: none; } a:active { font-weight: bold; } كن حذراً في حال عدم ترتيب تلك القواعد بالتسلسل كما في المثال السابق، فلن تحصل على نتيجة كما تتوقع. وذلك بسبب قواعد التخصيص التي تحددها ملفات التصميم. يعتبر أيضاً focus: مفيد بسبب مرونة استخدامه مع النماذج. فمثلاً يمكنك التشديد على حقل الإدخال والذي تقوم بتعبئة البيانات فيه سيتغير لونه مباشرة كالتالي: input:focus { border: 2px solid black; background color: lightgray; } سنشرح تالياً first-child: — حيث يقوم باختيار العنصر الابن مباشرة بعد عنصر الأب. سنقوم في المثال التالي باختيار أول عنصر من القائمة (سواء أكانت القائمة مرقّمة أو منقوطة) وجعله نصّاً غامقاً: li:first-child { font-weight: bold; } وأخيراً وباختصار نذكر lang: pseudo-class والتي تقوم باختيار العناصر التي تحتوي على اللاحقة lang أي اللغة كما في المثال التالي: <p lang="en-US"> A paragraph of American text, gee whiz! <p> يُمكن استهداف الفقرة أعلاه بالمنتقى التالي: p:lang(en-US) { ... } العناصر Pseudo-elements تمتلك العناصر Pseudo elements هدفان أساسيان. أولاً: يمكنك اختيار هذا النوع لاختيار الحرف الأول أو السطر الأول من النص داخل العنصر المحدد. يمكنك استخدام القاعدة التالية في حال أردت إنشاء حرف كبير للحرف الأول من كل فقرة كالتالي: p:first-letter { font-weight: bold; font-size: 300% background-color: red; } والآن أصبح الحرف الأول في بداية كل فقرة غامقاً وحجمه أكبر 300% من الحجم الطبيعي كما أن لون الخلفية أحمر. يمكنك استخدام الطريقة التالية لجعل السطر الأول من الفقرة غامق: p:first-line { font-weight: bold; } الاستخدام الثاني للعنصر pseudo-elements هو إدراج محتوى من خلال CSS وذلك معقد جداً. يمكنك التحديد بأن المحتوى يجب أن يُدرج قبل أو بعد عنصر معين من خلال استخدام before: أو after: ثم يمكنك اختيار ما تريد إدراج قبل أو بعد العنصر المحدّد. ولتبسيط ذلك يمكنك مشاهدة المثال التالي وهو إضافة صور تزيينية بعد كل رابط في الصفحة: a:after { content: '' url(flower.gif); } يمكنك أيضاً استخدام الدّالة ()attr لإدراج قيم معينة للواحق العناصر بعد عنصر معيّن. فمثلاً يمكنك إدراج الوجهة لكل رابط في الصفحة ضمن قوسين كالتالي: a:after { content: '(' attr(href) ')'; } تساعد هذه القواعد في طباعة ملفات التصميم حيث يمكنك الكتابة بسهولة على ملفات التصميم والتي يتم تطبيق التصميم مباشرةً في حال طبع المستخدم الصفحة. وتعتبر هذه الميّزة مفيدة لأنها تقوم بإخفاء القوائم في حال الطباعة لأن المستخدم لا يمكنه فعل ذلك يدوياً. وأيضاً تسمح تلك التقنية بتزويد المستخدمين برابط الصفحة كمرجع عند الطباعة. يمكنك أيضاً إدراج قيم عددية بعد تكرار العناصر (مثل قائمة غير منقّطة أو فقرات) باستخدام الدّالة ()counter حيث يمكنك قراءة المزيد عنها في مقالة عدّاد CSS في موقع dev.opera.com. لا ينبغي عليك إدراج معلومات مهمة ضمن CSS أو المحتوى الذي من الممكن ألا يتوفر في بعض التقنيات المساعدة أو إذا لم يختار المستخدم صفحة التصميم لديك. القاعدة الأهم هي أن CSS للتصميم على الصفحات أما HTML لإدراج المحتوى. اختزال CSS سنشرح في هذا القسم نوع جديد من من التصميم وهو اختزال CSS. حيث يمكنك اختصار العديد من الخاصيات المتعلّقة أو المرتبطة ببعضها إلى خاصية واحدة وذلك لحفظ الكثير من الوقت والجهد لديك. سنشرح في هذا القسم الخاصيات التي يمكن اختزالها في عالم CSS. استخدمت سابقاً في هذا الدرس جزءاً من اختزال CSS لكنني لم أذكره وهو border: 2px solid black وهذا في طبيعة الحال جزءاً مختصراً من قاعدة أطول كالتالي: border-width: 2px; border-style: solid; border-color: black; مقارنة القيم الفردية والمختزلة شاهد هذا المثال الذي يشرح الهوامش الخارجية (يتم اختزال الهوامش الداخلية والحدود بنفس الطريقة): div.foo { margin-top: 1em; margin-right: 1.5em; margin-bottom: 2em; margin-left: 2.5em; } وبهذه الطريقة يمكن اختزالها كالتالي: div.foo { margin: 1em 1.5em 2em 2.5em; } اختزال القيم بخاصية واحدة يسمح الاختزال بدمج أربع قيم بخاصية واحدة، كما يمكن أن يأخذ الاختزال أقل مع أربع قيم حسب القائمة التالية. يتم ترتيب النتيجة حسب رقم القيمة المطلوبة: تطبيق نفس القيمة لجميع الجوانب الأربعة مثل margin: 2px. يمكن تطبيق القيمة الأولى للجانب العلوي والسفلي أما الثانية فلليمين واليسار مثل: margin: 2px 5px. يمكن تطبيق القيمة الأولى والثالثة للقسم العلوي والسفلي بينما القيمة الثانية لقسم اليمين واليسار مثل: margin: 2px 5px 1px. إذا كانت القيمة مفردة كما في رقم 1 فإنها على الترتيب من القسم العلوي فاليمين فالأسفل فاليسار. من الأفضل استخدام الطرق الأربع للاختزال في CSS وهذا أيضاً يطبّق على الهوامش الداخلية padding. أيهما يجب اختياره الخاصية المفردة أم القيمة المختزلة؟ تستخدم خاصيات الهوامش margin وpadding على نطاق واسع ولكن هناك بعض الحالات التي يتوجب عليك تجنّب استخدام القيم المختزلة أو استخدامهم بحذر كما في الحالات التالية: استخدام margin واحد فقط: في هذه الحالة وعند اختيار خاصية واحدة تقوم تطبيقها. يتم تعديل ملف التصميم الذي كتبته من قبل خبراء: في هذه الحالة لن تحتاج إلى التعديل بنفسك فيمكن للخبراء حل هذه المشكلة. تحتاج لتغيير قيمة معينة على حساب التصميم العام: هذه الخاصية ضعيفة ومفتقرة للتصميم في صفحات الموقع وأيضاً هذه من الصعب التعامل معها. مراجع الاختزال تم شرح مسبقاً الأنواع المختلفة لاختزال الحدود. ولكن نذكر نقطة أخرى بأنه يمكنك ضبط حدود لعنصر واحد بجميع أنواع خاصيات تصميم الحدود كما في المثال التالي: border-left-width: 2px; border-left-style: solid; border-left-color: black; تعمل الهوامش والحدود margin ،padding ،border بنفس الطريقة كما تعلمنا في الفقرة السابقة. اختزال أنواع النص font: حيث يمكنك تحديد حجم الخط ومدى غمقته وشكله وعائلة الخطوط التي ينتمي إليها كما في المثال التالي: font-weight: bold; font-style: italic; font-size: 1.5em; line-height: 200%; font-family: Georgia, 'Times New Roman', serif; لكن أيضاً يمكنك اختزال جميع ذلك بسطر واحد كالتالي: font: bold italic 1.5em/200% Georgia, 'Times New Roman', serif; اختزال الخلفية: حيث يمكنك تحديد لون الخلفية وصورة الخلفية ومكان الصورة وتكرار الصورة كما في المثال التالي: background-color: #000; background-image: url(image.gif); background-position: top left; background-repeat: no-repeat; أيضاً يمكنك اختزال جميع ذلك بسطر واحد كالتالي: background: #000 url(image.gif) top left no-repeat; اختزال تصميم القوائم: أيضاً يمكنك تحديد نوع التسلسل للقائمة الغير مرقّمة والمكان والصورة كما في المثال التالي: list-style-type: circle; list-style-position: inside; list-style-image: url(bullet.gif); أيضاً يمكنك اختزال جميع ذلك بسطر واحد كالتالي: list-style: circle inside url(bullet.gif); لاحظ بأن أيضاً شكل اللون يمكن اختزاله من حيث نوعه وهو الوحدة الستة عشرية #000 وهو مختزل من شكله المطوّل وهو #000000. وكمثال أكثر تعقيداً مثل #6C9 وهو اختزال للشكل المطوّل #66CC99. إدراج CSS مع HTML يوجد ثلاثة أنواع لإدراج CSS مع صفحات HTMLهم: الإدراج الداخلي، الإدراج المضمر والتصميم الخارجي. ينصح بشدّة استخدام النوع الثالث وهو استخدام الإدراج الخارجي (أي في ملف تصميم منفصل) ما لم يكن لديك سبباً لاستخدام أول نوعين. الإدراج الداخلي يمكنك تطبيق التصميم على عنصر معيّن من خلال استخدام اللاحقة style كما في المثال: <p style="background:blue; color:white; padding:5px;">paragraph</p> يمكنك إدراج جميع خاصيات التصميم CSS مع القيم الخاصة بهم (يجب فصل بعد كل خاصية وقيمة بإشارة الفاصلة المنقوطة) وبذلك يمكنك إدراج ماتريد من تصميم CSS. يمكنك مشاهدة مصدر هذا المثال. يمكنك مشاهدة المثال عبر استخدام المتصفح كما ستلاحظ بأن الفقرة التي قمنا بتطبيق تصميم داخلي فيها أصبح النص باللون الأبيض وأيضاً خلفية النص أصبحت باللون الأزرق كما في الشكل 1. الشكل 1: يظهر المتصفح Chrome الفقرة التي تم تطبيق الإدراج الداخلي عليها وهي مختلفة عن باقي الفقرات أحد مزايا الإدراج الداخلي بأنه يتم إجبار المتصفح على تطبيق تلك التصميمات حتى ولو كانت هذه الفقرة مخصصة بتصميم مضمر أي داخل وسم <head> في الصفحة كما سيتم تجاوز أي تصميمات أخرى وتطبيق الإدراج الداخلي للعنصر. تعتبر أكبر مشكلة في الإدراج الداخلي أن التعديل على تصميم العنصر أكثر صعوبة مما تتوقع لأن جوهر لغة CSS هي تطبيق تصميمات منفصلة وبعيدة عن بنية صفحة HTML. بالإضافة إلى أن مسألة التعديل بهذه الطريقة لاتمنحك جميع مزايا وخصائص CSS وهي الإنسابية وذلك يعني بأن تبدع في التصميم بشكل منفصل عن بنية صفحة HTML ثم إدراج ذلك التصميم على صفحات HTML والتي سيقوم المتصفح بتطبيقها دفعة واحدة وبمرونة أكثر. الإدراج المضمر تقع التصميمات المضمرة ضمن الوسم <head> في صفحة HTML وبداخل وسم التصميم <style> كما في المثال التالي: <style type="text/css" media="screen"> p { color: white; background: blue; padding: 5px; } </style> عند فتح الرابط السابق على المتصفح فسوف تشاهد بأن التصميم تم تطبيقه على جميع الفقرات في الصفحة كما في الصورة الشكل 2 كما يمكنك مشاهدة مصدر الصفحة ومشاهدة التصميم الذي قمنا باستخدامه وهو بداخل وسم head كما ذكرنا سابقاً. الشكل 2: يظهر متصفح Chrome جميع التصميم التي تم تطبيقها على جميع الفقرات ضمن وسم head الميزة من استخدام الإدراج المضمر وهي أنه لا تحتاج إلى إدراج تصميم لكل فقرة أو عنصر وبدلاً من ذلك يمكنك إدراج تصميم واحد لجميع العناصر أو الفقرات. وهذا يعني بأنك في حال أردت التعديل على التصميم مستقبلاً فيمكنك التوجّه لمكان واحد للتعديل. حتى مع هذه الطريقة فهناك محدودية في استخدام التصميم لأن هذا التصميم محصوراً ضمن صفحة واحدة. لكن ماذا لو أردت التعديل على جميع الفقرات في الموقع؟ ستحتاج إلى استخدام صفحات الإدراج الخارجية. صفحات الإدراج الخارجية نعني بصفحات الإدراج الخارجية أي وضع جميع تعريفات التصميم CSS في ملف خارجي وحفظه في ملف منفصل بامتداد css. ثم تطبيق جميع هذه التصميمات على جميع صفحات HTML باستخدام الوسم <link> داخل الوسم <head>. لاحظ بأن الوسم <head> يحتوي بداخله على الوسم <link> والذي يمكنك إرفاق رابط ملف تصميم CSS خارجي وبعد ذلك سيتم تطبيق جميع التصاميم بدل إرفاقه مباشرة. لنأخذ نظرة على كيفية عمل الوسم <link>: <link rel="stylesheet" href="styles.css" type="text/css" media="screen"> استخدمنا مع الوسم <link> في المثال السابق اللاحقة href وهي لتحديد مكان ملف التصميم كما استخدم نوع media أي الوسائط بالقيمة screen (لأننا في هذه الحالة لا نريد أن تكون الطباعة بنفس شكل الصفحة) كما أننا قمنا بتعريف النوع بأنه نص من نوع CSS وأيضاً الصفحة هي stylesheet أي صفحة تصميم خارجية لأنه لا يكفي فقط إدراج رابط التصميم بل يجب تعريفه أيضاً. الشكل 3: يظهر المتصفح Chrome جميع التصاميم التي تم تطبيقها في الصفحة وهي متصلة بملف تصميم خارجي باستخدام <link> باختصار, أفضل طريقة لاستخدام التصاميم هي استخدام النوع الأخير ملف تصميم خارجي لأنه يوفّر الجهد والوقت وأيضاً سهولة التعديل على جميع تصميم الموقع من خلال ملف واحد. أيضاً عند استخدام ملف واحد لجميع صفحات الموقع عندها سيقوم المتصفح بتحميل ملف واحد للتصميم وحفظه كملف مؤقت وتطبيقه على جميع صفحات الموقع وبكلمات أخرى تحميل أقل للموقع. استيراد ملف التصميم بصراحة هناك طريقة أخرى لاستيراد ملف تصميم خارجي إلى صفحات HTML وذلك من خلال استخدام خاصية import@ في الإدراج المضمر بنفس الطريقة كما شرحنا سابقاً شاهد المثال التالي: <style type="text/css" media="screen"> @import url(styles.css); /* باقي تعليمات الأنماط يمكن وضعها هنا */ </style> أحياناً ستشاهد حالة import@ بدون أقواس لكنها تفعل الشيء نفسه. أيضاً هناك شيء يجب أن تكون حذراً بأن حالة import@ يجب أن تكون في السطر الأول في الإدراج المضمر. وأخيراً يمكنك تخصيص ملفات التصميم المستوردة والتي سيتم تطبيق أنواع الوسائط المختلفة عليها وذلك بإدراج نوع الوسائط (media) في آخر الحالة شاهد المثال التالي: <style type="text/css"> @import url(styles.css) screen; /* باقي تعليمات الأنماط يمكن وضعها هنا */ </style> ربما ستسأل لماذا يجب عليّ استخدام نوع آخر لاستيراد ملف الإدراج الخارجي وتطبيقه على صفحات HTML؟ طبعاً يوجد القليل من المحاسن والمساوئ للخاصية import@ وعلى الرغم من أنها قليلة إلا أن الطريقة الشائعة والأفضل هي استيراد ملف الإدراج الخارجي من خلال <link>. لا تعرف الإصدارات القديمة من المتصفحات الخاصية import@ مطلقاً (مثل متصفح Netscape 4 وما قبل وأيضاً IE4 وما قبل)، من حسن الحظ أنه لم يعد لهذه المتصفحات وجود تقريبا، لكن وجب التنويه. يمكن استخدام الخاصية import@ لإخفاء بعض التصميمات والتي من المحتمل أن تستخدم بطريقة خاطئة من قبل المتصفحات القديمة. يمكنك أيضاً استخدام التصميمات الحديثة واستيرادها من خلال import@ واستخدام بعض التصميمات الأساسية التي لا تتعارض مع المتصفحات القديمة. وطبعاً نادراً ما تقوم باستخدام هذه الحالات لنُدرة المتصفحات القديمة هذه. يمكنك -جدلا- القول بأن استخدام تعليمات import@ متعددة ينتُج عنه شِفرة برمجية أقل من استعمال عناصر <link> متعددة، لكن فارق الحجم هنا مُهملٌ جدا لا يقتضي اعتباره. خاتمة تعلمنا في هذا الدرس بعضًا من تطبيقات CSS مع صفحات HTML، بجميع أنواع التضمين، كالإدراج الداخلي حيث يمكنك استخدام لاحقة style بداخل العنصر وأيضاً الإدراج المضمر حيث يمكنك إدراج التصميم داخل وسم <style> ضمن الوسم الرئيسي <head> وأخيرًا الإدراج الخارجي بملف مستقل. كما تعلمنا استيراد ملف الإدراج الخارجي من خلال استخدام الوسم <link> (وهي الطريقة الأكثر شيوعاً هذه الأيام) أو باستخدام الخاصية import@. أسئلة وتمارين ماهي المحاسن والمساوئ عند استخدام الإدراج الداخلي وكيف يمكن تطبيقه على العنصر؟ ماهي بنية تعليمات CSS؟ اشرح التركيبة والمكونات المختلفة. لنفترض أن لديك مجموعة من شِفرات التصميم. ماذا تحتاج أن تفعل لجعلهم ضمن ملف تصميم خارجي منفصل؟ بماذا يطابق هذا المنتقى ul#nav؟ ماهي المزايا أو المحاسن من تجميع المنتقى ؟ كيف تعرّف أوراق CSS المخصصة للطباعة؟ ترجمة -وبتصرّف- للمقال: CSS Basics لصاحبه Christian Heilmann.
  3. السلام عليكم أود أن أعرف كيف أبدأ بتصميم قالب احترافي وردبرس باستخدام تقنية bootstrap من خلال التكويد والبرمجة بعد الانتهاء من التصميم على الفوتوشوب من أي أبدأ وهل يوجد مصادر يمكن التعليم خطوة بخطوة؟
  4. Ayman

    تعرّف على IndexedDB

    ​أخي ممكن أن هناك تقديم وتأخير لكن الجملة واضحة
  5. تُقدم IndexedDB أو "قاعدة البيانات المُفَهرَسَة" طريقة حيوية وفعالّة لتخزين وجلب البيانات باستخدام المتصفح. و لكونها تتعامل مع قواعد البيانات في الخادم فإن IndexedDB تسمح بتوليد مفاتيح وبحث البيانات أو ترتيبها في حقل معين. سنُبحر في هذه المقالة في عالم IndexedDB حيث سنشرح واجهة التطبيقات البرمجية الخاصة بقواعد البيانات المفهرسة IndexedDB API وذلك من خلال بناء مدير مهام من نوع خاص. لكن أولاً دعونا نأخذ أولاً فكرة عن قواعد البيانات وIndexedDB. ماهي IndexedDB ؟تعتبر IndexedDB مخزن للكائنات key-value كما أنها غير متزامنة ومرحلية. تحتوي IndexedDB على الكثير من الأفكار والتي يمكن شرحها بجملة واحدة، لكنيّ سأقوم بشرح كل واحدةٍ منها على حدى. غير متزامنة: تعني بأن IndexedDB لاتحجب واجهة المستخدم. حيث أن العمليات تجري على نحوٍ لاحق، أي أنها ليست فورية. وهذا بالطبع يسمح لواجهة المستخدم بالإستجابة للأوامر الأخرى. وكمثال للمقارنة: يعتبر التخزين المحلي localStorage متزامنًا، أي أن العمليات تجري بشكل فوري وﻻ يحدث أي شيء حتى تكتمل العملية. وفي حال كان لدينا الكثير من أوامر القراءة والكتابة سيحدث بطؤُ ملحوظ في البرنامج.مرحلية: وتعني بأن جميع العمليات في IndexedDB يجب أن تتم جميعها أو لا تتم كليةً. يُمكن أن تفشل العملية للعديد من الأسباب عندها ستقوم قاعدة البيانات بالرجوع إلى حالتها السابقة. لأنه من غير الممكن أن يكون هناك أمر جزئي في قاعدة البيانات المُفَهرسَة IndexedDB.تخزين كائن key-value: تعني بأن كل أمر في قاعدة البيانات هو كائن بحد ذاته، أي أنه يُعارض مفهوم الصفوف (rows) في قاعدة البيانات. تُستخدم قواعد البيانات التقليدية نموذجًا منطقيًا، حيث أن البيانات يتم التعرّف عليها في جدول كما ترتبط بصلات بين قيمة جدول معين وأيضاً المفاتيح لجدول آخر. كما في الصورة1. من خلال عملية تخزين الكائن key-value كمفتاح وقيمة، يحتوي كل أمر على كائن مستقل خاص به. حيث لا يمكن الربط بين أي كائن وآخر، كما يمكن أن يكون في نفس مكان التخزين أكثر من كائن و كل كائن مختلفٌ نوعه عن الآخر. الصورة 1: كائنات في نفس مكان التخزين ولا يتوجب أن تكون تلك الكائنات بنفس الخاصيات. يُعتبر هذا الاختلاف الأكبر من نوعه بين قواعد البيانات المفهرسة IndexedDB وبين قواعد البيانات الأخرى مثل SQL أو MySQL. فمثلاً يجب أن يحتوي كل حقل قيمة معينة في قواعد البيانات SQL حتى وإن كانت خالية NULL ولكن باستخدام IndexedDB فإن بنية قاعدة البيانات يمكن أن تكون مرنة حسب احتياجك واستخدامك لها. و أيضاً تحتوي IndexedDB على كمّية هائلة من تخزين البيانات تفوق التخزين المحلي localStorage والذي يفرض مساحة لاتقل عن 250 ميجابايت على معظم المتصفحات. حيث أن هذه المساحة أساسية مع المتصفح Internet Explorer ولكن تستخدم Google Opera وOpera نسبة مئوية للتخزين بينما غير معروف عن كيمة ومحدودية التخزين الذي يستخدمها فَيرفُكس. بينما مع IndexedDB يمكن إنشاء تخزين ثنائي للبيانات وبكلمات أخرى تشبه هذه العملية تركيبة Javascript مع التعامل مع البيانات والقدرة على استيعاب قواعد بيانات الموقع. المتصفحات الداعمة الحاليةللأسف تُعتبر IndexedDB غير مدعومة من جميع المتصفحات بعد. فقط مع متصفح Opera الاصدار 15 ومافوق وGoogle Chrome 24 ومافوق وفَيرفُكس15 ومافوق، أيضاُ Internet Explorer 10 ومافوق. بينما الاصدارات الأقدم من Chrome وفَيرفُكس يتم دعمها من خلال التجارب فقط التي تدعم واجهة التطبيقات البرمجية API. ولن نتكلم عن هذا. لا يدعم متصفح سفاري IndexedDB مطلقاً ولا نسخة Presto-based المدعومة من متصفح Opera اصدرات ماقبل نسخة 12. و لكن عوضاً عن ذلك فهو يدعم قاعدة بيانات الموقع القديم. كما يدعم في معظم الأحيان IndexedDBShim. طبعاً و بالنسبة للمتصفحات التي لاتدعم أيًّا منهم فمن الأفضل البقاء على قواعد البيانات التي تعمل على الخادم. كيفية اختبار دعم IndexedDBيمكنك كتابة الشِفرة التالية لاختبار فيما إذا كان استخدام IndexedDB ممكناً: var hasIDB = typeof window.indexedDB != 'undefined';أو يمكنك استخدام Modernizr: var hasIDB = Modernizr.indexeddb;كن متأكداً بأن هذه الاختبار فقط للمتصفحات الأخيرة الداعمة لـِ IndexedDB. إنشاء مدير مهامهذه الفقرة فقط لخبراء المشروع التجريبي. لكن مع ذلك يمكنك تحميل المصدر وذلك للحصول على فهم أفضل حول عمل هذه الدوال في الكود. باستخدام مدير المهام ستحصل على مايلي: حفظ المهمة.ضبط بداية و نهاية التواريخ.ضبط أولوية المهمة.إضافة ملاحظة لكل مهمة.ميزة البحث عن المهام و الملاحظات السابقة.أولاً لنبدأ بإنشاء نموذج والذي سنستخدمه لإضافة المهام الجديدة في قاعدة البيانات: <form id="addnew"> <div> <!-- Used for updates --> <input type="hidden" name="key" id="key" value=""> <label for="task">What do you need to do? (required)</label> <input type="text" name="task" id="task" value="" required> </div> <div class="txtright"> <input type="checkbox" name="status" id="status"><label for="status">Completed?</label> </div> <div> <label for="start">Start date:</label> <input type="date" id="start" name="start" value=""> </div> <div> <label for="due">Due date:</label> <input type="date" id="due" name="due" value=""> </div> <div> <label for="priority">Priority:</label> <select id="priority" name="priority"> <option value="0">None</option> <option value="1">1 - High</option> <option value="2">2</option> <option value="3">3 - Medium</option> <option value="4">4</option> <option value="5">5 - Low</option> </select> </div> <div> <label for="tasknotes">Task notes</label> <textarea id="tasknotes" name="tasknotes" cols="30" rows="3"></textarea> <button type="submit" id="submit">Save entry</button> <button type="button" id="delete" class="hidden" >Delete entry</button> </div> </form>تشرح الشِفرة البرمجية السابقة المؤلفة من HTML وبعضٍ من CSS إنشاء نموذج مشابه نوعاً ما للصورة2. الصورة 2 : نموذج مدير المهام ماذا تحتاج أن تفعل؟ كل ما تحتاجه يجب أن يكون ضمن حقول النموذج حيث ستستخدم النموذج أيضاً لاحقاً للإضافة والتحديث على المهام. وبما أننا شرحنا ما يجب علينا فعله سنقوم الآن ببناء قاعدة البيانات. إنشاء قاعدة البياناتلإنشاء قاعدة بيانات IndexedDB يجب استخدام الطريقة ()open في كائن indexedDB. var idb = indexedDB.open('IDBTaskManager', 1);في البداية يجب وضع اسم مناسب لقاعدة البيانات، وهذا إجباري، يجب أن يكون الاسم نصًا عاديًا. يمكن تسمية قاعدة البيانات لأي شيء تريده لكن فقط نص عادي. وبكل الأحوال يجب أن يكون كل اسم لقاعدة البيانات فريدًا و أصليًا. أصلي نعني به أن يكون مكونًا من البروتوكول:اسم المستضيف:المنفذ مثل https://dev.opera.com أو http://www.example.com:80. ثانياً و اختيارياً يمكن وضع رقم اصدار قاعدة البيانات، بما أن هذا المشروع هو تجربتنا الأولى سنضع الرقم1. استخدام رقم الاصدار مهم جداً لأن الدالة open تقوم بإنشاء قاعدة اذا لم تكن موجودة من الأصل ووضع رقم الاصدار هو 1 وإذا لم تكن قاعدة البيانات موجودة سيتم إنشاء اتصال إليها. يجب أن يكون رقم الاصدار integer أي عبارة عن رقم فقط وأن يكون أكبر من الصفر. بينما الأرقام العشرية أو التي تحتوي على كسور سيتم حذف تلك الكسور لإرجاع الرقم الى رقم integer. أي أن الرقم 2.5 سيصبح 2 فقط وأيضاً 0.8 سيصبح 0 (حيث يسبب ذلك خطأ برمجي). الحد الأعلى لرقم الاصدار هو 253 أو 9,007,199,254,740,992. و هذا الحد الأعلى أيضاً يمكن تطبيقه على موّلد المفاتيح. ملاحظة: يمكن من خلال متصفح الاوبرا استخدام فحص تخزين كائنات IndexedDB وأيضاً المفاتيح والقيم من خلال الخيار المصادر الموجود في خيارات المطورون في المتصفح. وبذلك سيتم إنجاز العملية والتي ستعيد الكائن IDBOpenDBRequest كما سيتم نجاح الحدث. الآن دعونا نعرّف الأمر onsuccess لهذا الحدث. سنقوم بتعيين الكائن IDBDatabase مع event.target.result والذي سيتم تحويله إلى متغير والذي يقوم بنشر هدف إلى الدوال الأخرى. // Define a global variable to hold our database object var dbobject; idb.onsuccess = function(evt){ dbobject = evt.target.result; }مع استخدام IndexedDB فإن كل خطوة أو عملية في قاعدة البيانات يجب أن تكون ضمن دالّة لأمر مرجعي. ولفعل ذلك يحتاج كائن قاعدة البيانات لأن يكون متوفرًا على كل دالة والتي تقوم بالعملية كاملة. نسخة قاعدة البياناتعندما تكون نسخة قاعدة البيانات أعلى من التي تم تخزينها مسبقاً من قبل المستخدم فإن المتصفح عندها سيطرد الحدث upgradeneeded. وهذا يتضمن أول تشغيل للبرنامج عندما تكون النسخة الأولية هي صفر. يعتبر إنشاء الحدث upgradeneeded الطريقة الوحيدة لتغيير البنية الأصلية لقاعدة البيانات. تتضمن التغيرات الجذرية إنشاء وحذف تخزين الكائن أو اضافة فهرسات. يمكننا صنع هذه التغيرات ضمن الحدث المرجعي onupgradeneeded كما في المثال التالي: idb.onupgradeneeded = function (evt) { if (evt.oldVersion < 1) { // Create our object store and define indexes. } } لو افترضنا إنشاء تخزين أو فهرسة كائن موجود مسبقاً عندها سيحدث خطأ. لكن يمكننا استخدام الخاصية oldVersion التابعة للحدث upgradeneeded للتحكم بهذه التغيرات كما سنشرح ذلك في هذه المقالة. لكن عند طرده سيظهر الحدث upgradeneeded قبل حدث نجاح الاتصال success. و أيضاً لن يتم تعريف المتغير dbobject عند استدعاء idb.onupgradeneeded. فقط تذكر ذلك عند برمجة و تطوير التطبيقات. إنشاء تخزين الكائنلاقيمة لإنشاء قاعدة البيانات بمفردها. لحفظ و تعديل الأوامر ستحتاج أيضاً إلى إنشاء تخزين الكائن. تخزين الكائنات مشابهة لعملية الجداول في SQL وهي الوحدة التي تحمل جميع البيانات والصفوف. تتغير البنية عند إنشاء تخزين الكائن ولهذا سنقوم بهذه الخطوة ضمن الأمر المرجعي onupgradeneeded. الآن سنضيف تخزين الكائن وليكن اسمه tasks باستخدام الطريقة createObjectStore: idb.onupgradeneeded = function(evt){ var dbobject = evt.target.result; // Check our version number if (evt.oldVersion < 1) { dbobject.createObjectStore('tasks',{autoIncrement: true}); } };الوسيطة الأولى للكائن createObjectStore مطلوبة لأنها اسم تخزين الكائن بينما الوسيطة الثانية اختيارية. و مع ذلك يجب أن يكون هناك قاموس والذي يعرف خيارات المفتاح للمخزن. تشبه القواميس كائنات Javascript حرفياً. و لكنهم بصراحة تم تخصيصهم للمصفوفات مع المفاتيح والقيم الخاصة بهم. تسمح لنا القواميس بتخطي الوسائط بدون قلق حول ترتيبهم. ومع createObjectStore يُمكن أن يحتوي القاموس على الخاصيات والقيم التالية: keyPath: يحدد أيّ خاصية للكائن ينبغي استخدامها كمفتاح لكل صف . القيمة الافتراضية هي null.autoIncrement: هي قيمة منطقية و التي تحدد فيما اذا يمكن توليد مفاتيح تلقائي في الصفوف أو لا . القيمة الافتراضية خطأ false.يسمح لنا keyPath بتحويل خاصية معينة إلى اجبارية. فعلى سبيل المثال استخدام: {keyPath: 'task'} تعني بأن كل كائن يتم إضافته للمخزن يجب أن يحتوي على الخاصية task. ستلاحظ من خلال المشروع التجريبي بأننا نستحدم autoIncrement. و بإستخدام كلاً من autoIncrement أو keyPath لن نتمكن من تحديد الواسطة في الطرق add أو put. ملاحظة: يمكنك استخدام autoIncrement وkeyPath سوياً حيث سيصبح المفاتيح رقمية كما أن الكائنات ستحصل على حقول إجبارية. العمل على التسجيلاتيتألف العمل مع التسجيلات من أربع مراحل وهي الإضافة، التحديث، الحذف أو الاستعادة. إنشاء طريقة اتصال لكائن أو أكثر باستخدام readwrite أو readonly.تحديد اي كائن سيقوم بالأمر مع طريقة الاستجابة.إنشاء طلب باستخدام واحداً من طرق الطلبات أو تمرير كائن.افعل شيئاً ما مع النتيجة باستخدام الأمر المرجعي onsuccess.يعتبر صعباً العمل على صف واحد من قاعدة البيانات من العمل مع مجموعة من الصفوف. وفي هذا القسم سنعمل على صف واحد باستخدام المؤشر لاسترجاع صفوف متعددة. إضافة صف/ تسجيللإضافة صفٍ جديد لديك خياران: إما باستخدام ()add أو ()put. الفرق بينهما أن ()add يمكن استخدامها في حال اضافة صف جديد فقط بينما ()put تستخدم لإضافة أو تحديث صف معين. كلا الطريقتين تقبل نقاشين كحد أعلى. Value (مطلوب): حفظ الكائن.Key (اختياري): مفتاح الكائن وهو ضروري فقط في حال كانت الحالة خاطئة للطريقة autoIncrement ولم يتم تعريف keyPath.الآن دعونا نحفظ المهمة في قاعدة البيانات عندما يقوم المستخدم بالضغط على ارسال النموذج: var addnewhandler, addnew; addnew = document.getElementById('addnew'); addnewhandler = function (evt) { 'use strict'; evt.preventDefault(); var entry = {}, transaction, objectstore, request, fields = evt.target, o; // Build our task object. for (o in fields) { if ( fields.hasOwnProperty(o)) { entry[o] = fields[o].value; } } // Open a transaction for writing transaction = dbobject.transaction(['tasks'], 'readwrite'); objectstore = transaction.objectStore('tasks'); // Save the entry object request = objectstore.add(entry); transaction.oncomplete = function (evt) { displaytasks(dbobject); }; transaction.onerror = errorhandler; }; addnew.addEventListener('submit', addnewhandler);لانحتاج فعلاً لحذف تلك القيم بما أن المدخلات الضارة محدودة للمستخدم. يمكنك إدراج الأقواس < و > عند اظهار النتيجة. في حال أردت مزامنة بيانات IndexedDB مع قواعد بيانات السيرفر عندها كن متأكداً بفلترة وادراج مدخلات المستخدم كأولوية لقاعدة البيانات. الخطوة التالية هي: فتح طريق اتصال لكائن قاعدة البيانات باستخدام الطريقة transaction. حيث تقبل الطريقة transaction نقاشين هما: تسلسل أسماء تخزين الكائن وأيضاً صيغة هذا الاتصال. التسلسل هو قائمة من مخزن كائن واحد أو أكثر حيث ينتج اتصال وهنا سنقوم فقط بفتح مخزن الكائن tasks. دعونا نفترض بأن التطبيق يسمح لنا بتعيين مهام للآخرين. يتم تخزين أولئك الأشخاص ووظائفهم في الكائن assignees. إذا أردنا أيضاً استخدام وظائف القراءة والكتابة في assignees عندها يجب استخدامهم معاً. transaction = dbobject.transaction(['tasks', 'assignees'], 'readwrite');ملاحظة: تستخدم الأقواس [ وَ ] بشكل اختياري في معظم اصدارات المتصفحات في حال أردت إنشاء اتصال لكائن واحد. لكن بعض الاصدارات القديمة للمتصفحات لاتزال تطلبهم. ولأفضل توافقية استخدم تلك الأقواس. لدينا خياران مع صيغة نقاش وهما: readwrite وreadonly. يسمح لنا readwrite باسترجاع وإضافة وتحديث أو حذف الصفوف. وبكل الأحوال للحفاظ على سلامة البيانات يمكن استخدام readwrite مرة واحدة. لاسترجاع الصفوف لعرضها يمكنك فقط استخدام readonly. يمكن تشغيل أكثر من readonly مرة واحدة في نفس تخزين الكائن والذي بذلك يساعد على تحسين الأداء وبما أننا نضيف صفوف يجب أن نستخدم الصيغة readwrite. الخطوة الثالثة وهي اختيار أي كائن سنستخدمه في طلب الأمر: transaction.objectStore('tasks');أخيراً يكتب الأمر (request = objectstore.add(entry طريقة ادخال الكائن إلى قاعدة البيانات. سيتم استدعاء الدالة displaytasks عند اكتمال العملية. لإضافة صفوف متعددة يمكنك استدعاء الطريقة ()add مرات عديدة باستخدام نفس طلب الكائن. request = objectstore.add({object1:'Test object 1'}); request = objectstore.add({object2:'Test object 2'}); request = objectstore.add({object3:'Test object 3'});في هذه الحالة سيتم طرد الأحداث success وcomplete فور اكتمال جميع عمليات add وput عوضاً عن طردهم واحداً في كل مرة. تحديث الصف / التسجيليجب استخدام الطريقة put لجميع عمليات التحديث متضمنة مفتاح النقاش. ترك تلك الطريقة سيقوم بإنشاء صف جديد ولكن هذا ما لا نريده هنا. يستخدم تطبيقنا نفس المظهر في كلاً من عمليات الإضافة والتعديل على المهام. سنقوم باستخدام الدالّة addnewhandler للتعامل مع عمليات الإضافة والتعديل. فقط نحتاج تعديلها للتحرير بإضافة كود شرطي. يكون حقل key خالياً في النموذج لهذا سنقوم بإضافة صف جديد. و إذا كان يحمل أي قيمة سنقوم بالتعديل باستخدام put. addnewhandler = function (evt) { evt.preventDefault(); var entry = {}, transaction, objectstore, request, fields = evt.target, o; for (o in fields) { if ( fields.hasOwnProperty(o)) { entry[o] = fields[o].value; } } transaction = dbobject.transaction(['tasks'], 'readwrite'); objectstore = transaction.objectStore('tasks'); // Save the entry object with a key if one is available. if(fields.key.value){ // +fields.key.value converts our key to a number request = objectstore.put(entry, +fields.key.value); } else { request = objectstore.add(entry); } transaction.oncomplete = function (evt) { displaytasks(dbobject); }; transaction.onerror = errorhandler; };استرجاع الصفوفللتعديل على الصفوف تحتاج أولاً لاسترجاعها. وهنا يمكننا استخدام الطريقة get. سيتم إدراج الحدث hashchange عند الضغط على قائمة المهام. سنقوم الآن بتعريف الدالّة hashchangehandler لإستعادة العناصر المطابقة. hashchangehandler = function (evt) { var transaction, objectstore, request, key; if (window.location.hash) { // Extract digit characters from the hash, and convert to a number. // Generated IndexedDB keys are numbers. String values won’t work. key = +window.location.hash.match(/\d/g).join(''); // Run a read-only transaction on this object store. transaction = dbobject.transaction(['tasks'], 'readonly'); objectstore = transaction.objectStore('tasks'); // Retrieve the record by its key request = objectstore.get(key); // If it’s successful, update our form fields. request.onsuccess = function (successevent) { var o, data = successevent.target.result; for(o in data) { if( o == 'status') { addnew.status.checked = !!data.status; } addnew[o] = data[o]; } }; transaction.oncomplete = function (evt) { hide('#tasklist'); show('#addnew'); } };تسترجع الطريقة get الصفوف. كما أنها تقبل المُعامل (parameter) المفرد، أي سيتم استرجاع المفتاح كما أننا قمنا باستعادة الصف من خلال صيغة العمليات من خلال استخدام readonly. ملاحظة: تعتبر مفاتيح IndexedDB محددة من خلال النوع كما أن مولد المفاتيح هو عيارة عن أرقام. في حال قمت بتخطي get حتى ولو كان رقمي فإنه لن يعمل. ستحتاج إلى تحويل المُعامل إلى رقم كما فعلنا سابقاً. إذا كان الطلب ناجحاً سنقوم بتزويد النموذج addnew# بالنتيجة لعملية get. يجب تعريف الحدث onsuccess في أي وقت عند ظهور نتيجة الطلب. لقد قمنا أيضاً بتعريف oncomplete والذي سيتم إدراجه في حال اكتمال العملية. تنتهي العملية دوماً في حال فشل الطلب. حذف الصفسنقوم باستخدام طريقة مألوفة في حذف الصف كما فعلنا سابقاً مع استرجاع البيانات. ولكن هذه المرة سنقوم بحذف البيانات باستخدام الطريقة delete. تتطلب الطريقة delete مُعاملا واحدًا، ومفتاح الكائن سيقوم بحذف التخزين. أيضاً نحتاج لفتح عملية جديدة من خلال الصيغة readwrite. deletehandler = function (evt) { var transaction, objectstore, request, key; if (window.location.hash) { // Retrieve the key from the hash and convert it to a number key = +window.location.hash.match(/\d/g).join(''); // Perform the transaction transaction = dbobject.transaction(['tasks'], 'readwrite'); objectstore = transaction.objectStore('tasks'); request = objectstore.delete(key); // Recreate the task list display transaction.oncomplete = function (evt) { tbody.innerHTML = ''; displaytasks(dbobject); }; transaction.onerror = errorhandler; } };لقد عرفنا هنا المعالج oncomplete للكائن transaction وبما أن delete لن يقدم نتيجة بعد. حذف الصفوف و مولّد المفاتيحكما في قواعد البيانات التقليدية فإن حذف الصف لا يعيد القيمة إلى مولد المفاتيح . يمكن أن ترى في الصورة 3 أن لدينا 16 صفًا في قاعدة البيانات. كما أن أعلى قيمة للمفتاح هي 30. الصورة 3: لم يتم إعادة ضبط المفتاح بعد حذف الصف من قاعدة البيانات من الممكن إعادة استخدام المفتاح للصف المحذوف. حيث يمكنك تخطي المفتاح المطلوب كما في المناقشة الثانية للعمليات add أو put. استخدام المؤشر لاستعادة صفوف متعددةتعتبر عملية استرجاع مجموعة من الصفوف مختلفة نوعاً ما. لهذا نحتاج إلى استخدام المؤشر. المؤشرات هي تقنيات عابرة تستخدم لإعادة مجموعة صفوف في قاعدة البيانات (المؤشرات معرفة تلقائياً من IndexedDB). يُبقي المؤشر امتداد العمليات المتزامنة حيث يتحرك بترتيب تصاعدي أو تنازلي معتمداً على وجهة فتح المؤشر. تستخدم المؤشرات عملية الدوران while. دعونا نلقي نظرة على كيفية استعادة مجموعة من النتائج مع المؤشر: var displaytasks = function (database) { var transaction, objectstore, request; transaction = dbobject.transaction(['tasks'], 'readonly'); objectstore = transaction.objectStore('tasks'); request = objectstore.openCursor(IDBKeyRange.lowerBound(0), 'next'); request.onsuccess = function (successevent) { var task, tbody = document.querySelector('#list tbody'); if (request.result) { task = buildtask(request.result); tbody.appendChild(task); cursor.continue(); // advance to the next result } } }مجدداً، قمنا بإنشاء عمليات للكائن واختيار تخزين الكائن. مافعلناه مختلفًا نوعاً ما لكن لفتح كائن المؤشر يمكن استخدام الطريقة openCursor. تقبل الطريقة openCursor مُعاملين، كلاهما اختياري: المدى range: يجب أن يكون إما مفتاح أو مفتاح مدى.الاتجاه direction: يجب أن يكون واحداً من الطرق 'next' أو 'prev' أو 'nextunique' أو 'prevunique'.إنشاء مدىلإنشاء مفتاح مدى نحتاج إلى استخدام الواجهة IDBKeyRange وجميع الطرق التي فيها ثابتة. IDBKeyRange.lowerBound: يحدد مفتاح منخفض فقط.IDBKeyRange.upperbound: يحدد مفتاح مرتفع فقط.IDBKeyRange.bound: يحدد مفتاح منخفض و مرتفع.IDBKeyRange.only: يوافق على قيمة واحدة مشابهة تماماً لعملية get.لقد قمنا بتعيين أصغر قيمة مقيدة وهي الصفر باستخدام IDBKeyRange.lowerBound ولم نقم بتعيين القيمة العليا. كل صف في قيمة المفتاح والتي هي اكبر من صفر سيتم إرجاعها وذلك في كل صف في مخزن الكائن tasks ويتم ارجاع الأقدم أولاً. يعيد IDBKeyRange.upperBound جميع الكائنات مع قيمها والتي هي أقل مما هي عليه في المُعامل. على سبيل المثال يقوم (IDBKeyRange.upperBound(20 بإعادة جميع الكائنات التي تحتوي على قيمة 20 أو أقل. يعيد IDBKeyRange.bound جميع قيم الكائنات المحددة المدى من أصغر تحديد الى الأعلى تحديداً. ولإعادة الصفوف بين 11 و20 على سبيل المثال نستخدم (IDBKeyRange.bound(11,20. لاتوجد أي طريقة تعيد عدد النتائج. و عوضاً عن ذلك تعيد المفاتيج ضمن المدى المحدد. دعونا نقول على سبيل المثال بأن مفاتيح تخزين الكائن هي 1, 2, 4, 8, 9, 11, 15, 16, 20, 21, 22,23. قد قمنا بحذف بعض الصفوف لذلك هناك فجوة في تسلسل المفاتيح. يعيد (IDBKeyRange.lowerBound(0 جميع الكائنات.يعيد (IDBKeyRange.lowerBound(10 جميع الكائنات للمفاتيح 11, 15, 16, 20, 21, 22 , 23.لا يعيد (IDBKeyRange.upperBound(0 أي كائن.يعيد (IDBKeyRange.bound(0,20 جميع الكائنات ماعدا 21, 22, 23.بما أن مدى المفاتيح مقيد ضمن النتيجة فإنه من الممكن إظهارهم من خلال مناقشة جديدة باستخدام open. يجب أن تكون القيمة منطقية كما أن القيمة الافتراضية هي false. لتخطي أول صف من النتيجة التي قمنا بها على سبيل المثال يمكن استخدام (IDBKeyRange.lowerBound(0, true. يختلف الأمر IDBKeyRange.bound عن الآخرين إذ أنه يقبل مُعاملين إضافيين هُما: lowerOpen وupperOpen. إذا أردنا عرض أول وآخر نتيجة من IDBKeyRange.bound يجب علينا جعل القيمة صحيحة true مرتين: (IDBKeyRange.bound(0, 10, true, true. اختيار وجهة المؤشريُشير المُعامل الثاني openCursor إلى وجهة المؤشر وأين يجب أن يتجه. باستخدام next يعني بأن الصفوف سيتم ترتيبها حسب المفتاح بترتيب تصاعدي. أيضاً يمكننا إنشاء مفتاح مزدوج باستخدام nextunique وprevunique وتعتبر هذه الطريقة مفيدة جداً عند العمل مع الخاصيات المفهرسة. إضافة علامةقمنا سابقاً باستعادة المُدخلات من خلال المفتاح ومدى المفتاح. ولكن لأجل عملية قائمة المهام يجب استعادة وترتيب الصفوف من خلال اسم المهمة والأولوية والتاريخ المستحق والحالة. ملاحظة: لاتدعم IndexedDBShim فتح أو فهرسة المؤشرات. ستحتاج إلى وسلية أخرى لفعل ذلك. فكر بالعلامات المفهرسة بطريقة سريعة ورتّب صفوفك في قاعدة البيانات. تسمح لك العلامات بمشاهدة الكائنات من خلال خصائصهم فضلاً عن مفاتيحهم. لإضافة علامة لمخزن الكائن سنتحتاج الى استخدام طريقة createIndex. ولأن إضافة العلامة تقوم بتغيير البنية سنحتاج إلى فعل ذلك يدوياً من خلال الحدث versionchange واستخدام onupgradeneeded أيضا. دعنا نقوم بتحديث الدالّة onupgradeneeded: idb.onupgradeneeded = function (evt) { var tasks, transaction; dbobject = evt.target.result; if (evt.oldVersion < 1) { tasks = dbobject.createObjectStore('tasks', {autoIncrement: true}); // Create indexes on the object store. transaction = evt.target.transaction.objectStore('tasks'); transaction.createIndex('by_task', 'task'); transaction.createIndex('priority', 'priority'); transaction.createIndex('status', 'status'); transaction.createIndex('due', 'due'); transaction.createIndex('start', 'start'); } };تقبل الطريقة createIndex ثلاث مُعامِلات: Name (مطلوب): اسم العلامة التي تريد اضافتها.keyPath (مطلوب): خاصية الكائن.optionalParameters: وهو قاموس يحتوي على الضبط يتكون من unique و/أو multiEntry للعلامة.أضفنا علامة مفهرسة للحقول task, priority, status, start, due. يمكن أن تشارك هذه العلامات نفس الاسم و الخاصية التي تحملها. يتم إدخال مخزن العلامات فقط للكائنات التي تحتوي خاصيات الفهرسة. الصورة 4: انظر إلى علامة by_task في الجدول في متصفح Opera عند تغيير خاصية الصف عندها ستصبح تلك الخاصيات أكثر مرونة في الجدول، دعنا نلقي نظرة على الصفوف الأساسية في الفهرس. سنقوم بتحديث الدالّة displaytasks من الأعلى. استعادة الصفوف من خلال علاماتهمسنقوم بفتح المؤشر لمخزن الكائن من خلال النسخة السابقة من الدالّة displaytasks. وهنا سنحتاج لإضافة سطر جديد والذي سيعيد الفهرس by_task. ثم سنقوم باستدعاء openCursor كما في الشِفرة البرمجية التالية: var displaytasks = function (database) { var transaction, objectstore, request, index; transaction = dbobject.transaction(['tasks'], 'readonly'); objectstore = transaction.objectStore('tasks'); // New line to select the index index = objectstore.index('by_task'); // Our request opens a cursor on the index, // rather than the object store. request = index.openCursor(IDBKeyRange.lowerBound(0), 'next'); request.onsuccess = function (successevent) { var task, tbody = document.querySelector('#list tbody'); if (request.result) { task = buildtask(request.result); tbody.appendChild(task); cursor.continue(); } }ترتّب أيضاً العلامات القيم لكل خاصية. ستعيد الدالّة displaytasks قائمة المهام مُرتبة أبجدياً. محدودية العلاماتللأسف تفتقر IndexedDB لنوع البحث الذي يدعم جميع النصوص والذي يمكن أن تجده في قواعد البيانات SQL مثل MySQL وPostgreSQL. وعوضاً عن ذلك سنقوم بفلترة جميع النتائج لدينا باستخدام تعابير النمطية. دعنا نشاهد المثال الذي يستخدم نموذج بحث. سنقوم بانتزاع قيمة النموذج واستخدام تعبير نظامي عند الضغط على ارسال أمر البحث. ثم سنقوم باختبار المهمة للمطابقة. var searchhandler, search = document.getElementById('search'); searchhandler = function (evt) { evt.preventDefault(); var transaction, objectstore, index, request; transaction = dbobject.transaction(['tasks'], 'readwrite'); objectstore = transaction.objectStore('tasks'); index = objectstore.index('by_task'); request = index.openCursor(IDBKeyRange.lowerBound(0), 'next'); request.onsuccess = function (successevent) { var reg, cursor, task; reg = new RegExp(evt.target.find.value, "i"); cursor = request.result; if (cursor !== null) { if (reg.test(cursor.value.task)) { task = buildtask(cursor); } cursor.continue(); } } } search.addEventListener('submit', searchhandler); الصورة 5 : فلترة المهام من خلال استخدام تعبير بسيط للبحث تحتوي التعابير التي تقوم بالبحث على محدوديات. وبكل الأحوال البحث عن كلمة cafe لن يُطابق الكلمة café لأن الحرفان الأخيران في كلا الكلمتين غير متشابهين. و هنا يمكنك استخدام تقنية بسيطة لتخطي هذه المشكلة باستخدام تعبير نمطي ويقوم بالبحث تلقائياً عن caf فقط. الخاتمةتقدم IndexedDB قدرة قاعدة البيانات الأساسية للمتصفح، كما تجعل بناء تطبيقات ويب تعمل في حالة الإتصال (online) وفي حالة الانفصال (offline) مُمكنا. يمكنك الاطلاع أكثر على هذا النوع من قواعد البيانات لأنه سيحسن كثيراً لديك فكرة عملهم. في حال أردت تعلم أكثر حول المُدخلات والمخرجات لقاعدة البيانات IndexedDB. يمكنك قراءة تخصيصات IndexedDB. و هذا غير كافي للقراءة لأنه فقط مرجع تعريفي. يقدم أيضًا مطوروا شركة Mozilla بعض الشروحات حول الفكرة من وراء IndexedDB في حال كانت الفكرة من ورائها لا تزال مُبهمة لديك. ترجمة -وبتصرّف- للمقال: An Introduction to IndexedDB لصاحبته: Tiffany Brown.
×
×
  • أضف...