البحث في الموقع
المحتوى عن 'متصفح'.
-
تمتلك معظم المتصفّحات أداة خفيّة تسمح لنا بالنظر خلف كواليس ما يجري في صفحة الويب. يُعتبر هذا أمرًا مفيدًا جدًّا من أجل تفحّص مواقع الويب الخاصّة بنا أو من أجل الاستلهام من مواقع ويب الأخرى. يمتلك متصفّح Chrome حاليًّا أكثر هذه الأدوات فعاليةً والتي تُسمّى أدوات المطوّر Developer Tools أو اختصارًا DevTools. تعلمنا في الدروس السابقة كيفية إنشاء و نشر و تنسيق موقعنا الأول باستخدام CSS. سنقوم في هذا الدرس باستعمال أدوات المطور لتفحص موقعنا والتعديل عليه. فتح أدوات التطوير البرمجية في Chrome يمكن الوصول إلى هذه الأدوات بعدّة أساليب: انقر بز الفأرة الأيمن على أيّ عنصر في صفحة الويب ثم اختر Inspect. استخدم Ctrl+Shift+I من لوحة المفاتيح أو اضغط F12. لنجرّب ذلك على موقعنا الحالي. يجب أن تحصل على شكل شبيه بما يلي: ملاحظة: إذا كنت تستخدم المحرّر Brackets فمن الأفضل أن تلغي تفعيل ميزة Live Preview (التي تحدّثنا عنها في الدرس الأوّل: كيف تصمم أول صفحة ويب لك) ثم أعد تحميل الصفحة ضمن المتصفّح وإلّا فيمكن أن ترى معرّفات id إضافية في الشيفرة يُضيفها المحرّر Brackets تلقائيًّا. تفحص عنصر يمكن باستخدام أدوات التطوير DevTools تفحُّص أي عنصر ضمن صفحة الويب. كمثال على ذلك، لنتفحّص العنصر h2 وذلك بالنقر بزر الفأرة الأيمن على العنوان "Welcome" ثم اختيار Inspect. سيؤدّي ذلك إلى فتح DevTools والانتقال إلى العنصر h2 ضمن الرماز مباشرةً: تظهر هذه الأدوات في القسم الأيمن لنافذة المتصفّح (وقد تظهر في القسم السفلي منها)، ومن هناك يمكننا أن نشاهد التنسيقات المُطبّقة على العنصر h2. يجب أن نرى الألوان التي عرّفناها مسبقًا ضمن الملف main.css. يمكننا أن نرى قسمًا اسمه user agent stylesheet وفيه نجد قواعد التنسيق الافتراضيّة المطبّقة على أيّ عنصر من النوع h2 وذلك من قِبَل المتصفّح. أمّا في الأسفل فيمكننا أن نرى مستطيلًا ملوّنًا. يرينا هذا المستطيل الحجم والمسافات والحدود لعنصر h2 الذي نتفحّصه. فمثلًا نجد أنّ الخاصية padding تحمل القيمة 5 كما عرّفناها مسبقًا ضمن الملف main.css. تغيير التنسيقات ورماز HTML سنتناول الآن الجزء المثير في هذا الدرس. يمكننا تغيير أو إلغاء تفعيل التنسيقات بالإضافة إلى تحرير رماز HTML مباشرةً ضمن المتصفّح. فمثلّا إذا نقرنا على المربّع الملوّن الموجود بجوار background-color يمكننا تغيير لون الخلفيّة للعنصر h2 الذي نتفحّصه حاليًّا، أو حتى يمكننا إدخال قيمة جديدة للخاصيّة padding. يمكن تغيير رماز HTML بالنقر بزر الفأرة الأيمن على رماز العنصر في نافذة DevTools ثم اختيار Edit as Html. يُعتبر هذا الأسلوب فعّالًا للغاية في إجراء التجارب دون التعديل الفعلي ضمن مستند HTML الأصلي، فأيّة تعديلات نجريها ضمن المتصفّح ستكون مؤقّتة وستختفي بمجرّد إعادة تحميل الصفحة ضمنه. الاستلهام من مواقع ويب الأخرى يمكننا تفحّص أي صفحة باستخدام أدوات DevTools وإجراء تغييرات مؤقّتة عليها. حاول أن تصبح عادةً لديك، فمثلًا إذا رأيت لونًا أعجبك بينما تتصفّح صفحات ويب، يمكنك استخدام DevTools للحصول على القيمة الست عشرية له، بحيث تستطيع استخدام هذا اللون ضمن ملفات css الخاصّة بك. من أجل مواقع ويب الأكبر يمكن أن يكون عدد التنسيقات كبيرًا، فيمكن أن توجد عدة قواعد css لنفس العنصر تتجاوز بعضها البعض. يمكن أن نرى مثالًا على ذلك مع عنصر h1. فحجم الخط الذي عرّفناه لهذا العنصر في الدرس (مقدمة إلى تنسيقات CSS) يتجاوز حجم الخط القياسي المعرّف افتراضيًّا ضمن المتصفّح له، وهكذا يتم تجاوز التعريف الأصلي. سننشئ في الدرس الخامس ثلاث صفحات من أجل المدوّنة Blog والمشاريع Projects والتواصل Contact لمشروعنا. ترجمة -وبتصرّف- للمقال HTML & CSS Tutorial - Part 4: Development Tools in the Browser لصاحبه Marco Jakob.
-
قدّمنا في الدرس السابق جافاسكريبت وذكرنا تموضعها في الوِب بالنسبة لـHTML وCSS، كما تحدّثنا عن واجهات برمجة التطبيقات الخاصّة بالمتصفّح وتلك القادمة من طرف ثالث. سنبدأ في هذا الجزء من الدرس النظر في بعض التعليمات البرمجية، وأثناء ذلك، يمكنك استكشاف ما يحدث فعليًا عند تشغيل شفرة جافاسكريبت في صفحتك. ما الذي تفعله جافاسكريبت على صفحتك؟ لنلخص بإيجاز قصة ما يحدث عند تنزيل صفحة وِب في المتصفح. عندما يُنزّل المتصفّح الشفرة التي كتبتها، والمكوّنة من وسوم HTML، أنماط CSS وتعليمات جافاسكريبت فإنه يشغّلها داخل بيئة تنفيذ (علامة تبويب Tab في المتصفح). تشبه هذه العملية المصنع الذي يأخذ المواد الخام (الشفرة) ويُخرج المنتح (صفحة وِب). يُنفّذ معالج جافاسكريبت JavaScript engine الموجود في المتصفّح تعليمات جافاسكريبت بعد أن تُجمَّع HTML وCSS وتوضعان في صفحة وِب. يضمن انتظار التجميع أنّ بنية الصفحة وتنسيقها موجودان بالفعل عند بدء تشغيل جافاسكريبت، أي أنه عند تشغيل جافاسكريبت تكون جميع تعليمات HTML وCSS موجودة ونفّذها المتصفّح. يعدّ هذا الأمر جيّدًا، إذ أن استخدام جافاسكريبت الشائع هو تعديل HTML و CSS ديناميكًيا لتحديث واجهة المستخدم، من خلال واجهة برمجة التطبيقات DOM التي تحدّثنا عنها في الدرس السابق؛ فإن نُزِّلت تعليمات جافاسكريبت وحاول المتصفّح تشغيلها قبل أن يكون هناك HTML و CSS فستحدث أخطاء. أمان المتصفح تعدّ كل علامة تبويب Tab في المتصفح دلوًا منفصلًا لتشغيل الشفرة (تسمى هذه الدلاء “بيئات التنفيذ” Execution environments من الناحية التقنية). يعني هذا أنه في معظم الحالات تُشغَّل الشفرة في كل علامة تبويب منفصلة عن الشفرات في بقية التبويبات، ولا يمكن للتعليمات البرمجية في علامة تبويب واحدة أن تؤثر مباشرة على التعليمات البرمجية في علامة تبويب أخرى - أو على موقع وِب آخر. هذا إجراء أمني جيد. لو لم يكن هذا هو الحال، لكان بإمكان القراصنة كتابة صفحات وِب مهمّتها سرقة المعلومات من المواقع الأخرى التي يزورها المتصفّح، وغيرها من هذه الأشياء السيئة. ملاحظة: هناك طرق لإرسال التعليمات البرمجية والبيانات بين مواقع وِب / علامات تبويب مختلفة بطريقة آمنة، ولكن هذه تقنيات متقدمة لن نغطيها في هذا الدرس. جافاسكريبت قيد التشغيل عندما يواجه المتصفح كتلة تعليمات برمجية من جافاسكريبت، فإنه يعمل بالترتيب، من أعلى إلى أسفل. وهذا يعني أنك تحتاج إلى توخي الحذر في الترتيب الذي تضعه فيه. على سبيل المثال، في شفرة جافاسكريبت التي شاهدناها في المثال الأول: var para = document.querySelector('p'); para.addEventListener('click', updateName); function updateName() { var name = prompt('Enter a new name'); para.textContent = 'Player 1: ' + name; } قمنا باختيار فقرة نصية (السطر 1)، ثم استدعينا مستمع الحدث Event listener بتطبيق التابعaddEventListener على الفقرة النصية المُخزَّنة في المتغيّر para (السطر 3)؛ لذا عندما يُنقر على النص تُشغَّل كتلة التعليمات البرمجية في الدالة updateName(). تُسمّى كُتلة التعليمات البرمجية القابلة لإعادة الاستخدام باستدعاء اسمها “وظائف” أو “دوال” Functions. يُطلب من المستخدم اسم جديد ثم يُدرَج هذا الاسم في الفقرة لتحديث العرض. إذا بدّلت ترتيب أول سطرين من التعليمات البرمجية، فإن الشفرة البرمجية لن تعمل . ستحصُل بدلا من ذلك خطأ في وحدة تحكم المتصفح الخاصة بالمطورين Console: -TypeError: para is undefined. وهذا يعني أن الكائن para غير موجود بعد، لذلك لا يمكننا إضافة مستمع الحدث إليه. ملاحظة: هذا خطأ شائع جدًا. يجب أن تكون حذرًا أن الكائنات Objects المشار إليها في التعليمات البرمجية موجودة قبل محاولة استخدامها. الفرق بين التعليمات البرمجية المفسَّرة Interpreted و المُترجَمة Compiled لابد وأنك سمعت في سياق البرمجة بمصطلحيْ التفسير Interpretation و الترجمة Compilation. جافاسكريبت هي لغة مفسَّرة. تُشغَّل تعليمات جافاسكريبت البرمجية من أعلى إلى أسفل وتُرجَع نتيجة تشغيل التعليمات البرمجية على الفور. لست بحاجة لتحويل التعليمات البرمجية إلى شكل مختلف قبل أن يقوم المتصفح بتشغيلها. من ناحية أخرى، تُحوَّل الشفرة البرمجية للغات المترجمة إلى شكل آخر قبل أن يشغّلها الحاسوب. على سبيل المثال تُجمَّع C / C ++ في لغة Assembly التي يُشغّلها الحاسوب. لكلّ من المنهجيْن ميزات، لكنّها خارج نطاق هذا الدرس. جانب الخادوم Server Side و جانب العميل Client Side لابد وأنك سمعت بمصطلحي من جانب الخادوم وجانب العميل، خاصة في سياق تطوير الوِب: الشفرة من جانب العميل Client side هي شفرة تُشغَّل على حاسوب المستخدم. يُنزّل العميل التعليمات البرمجية التي ينفّذها المتصفّح ليعرض صفحة الوٍب. سنتحدّث في هذا الدرس عن جافاسكريبت من جانب العميل. من ناحية أخرى تُنفَّذ شفرة جانب الخادوم على الخادوم، ثم تُرسَل نتيجة التنفيذ إلى المتصفّح ليعرضها. مثال على لغات الوِب الشائعة من جانب الخادوم PHP وبايثون وروبي و ASP.NET. وجافاسكريبت! يمكن أيضا استخدام جافاسكريبت كلغة من جانب الخادوم، على سبيل المثال في بيئة Node.js الشائعة. يُستخدم مصطلح ديناميكي Dynamic لوصف كل من جافاسكريبت من جانب العميل واللغات من جانب الخادوم. تشير كلمة ديناميكي إلى القدرة على تحديث عرض صفحة الوِب أو التطبيق لإظهار أشياء مختلفة في ظروف مختلفة، وتوليد محتوى جديد حسب الطلب. تُنشِئ الشفرة من جانب الخادوم ديناميكيا محتوى جديدًا على الخادوم، سحب بيانات من قاعدة بيانات على سبيل المثال؛ في حين أن جافاسكريبت من جانب العميل تُنشِئ ديناميكيا محتوى جديدًا داخل المتصفح (العميل)، على سبيل المثال إنشاء جدول HTML جديد، وإدراج البيانات المطلوبة من الخادوم في ذلك الجدول، ثم عرض الجدول في صفحة وِب للمستخدم. يعمل المنهجان معًا عادة. يشار إلى صفحة الوِب التي لا يوجد بها محتوى ديناميكي بأنها ثابتة Static، حيث يظهر دائما نفس المحتوى بغضّ النظر عن الزائر. كيف تضيف جافاسكريبت إلى صفحتك؟ يتم تطبيق جافاسكريبت على صفحة HTML بطريقة مشابهة لـ CSS. في حين تستخدم CSS عناصر <link> لتطبيق الأنماط الخارجية وعناصر <style> لتطبيق الأنماط الداخلية على HTML، تحتاج جافاسكريبت إلى صديق واحد فقط في عالم HTML، العنصر <script>. دعونا نتعلم كيفية العمل. جافاسكريبت داخلي أنشئ مستند HTML بالمحتوى التالي <!DOCTYPE html> <html lang="ar" dir="rtl"> <head> <title>مثال تطبيقي لجافاسكريبت</title> </head> <body> <button>انقر هنا</button> </body> </html> </html> استعرض الملف في متصفح الوِب وفي محرر النصوص. سترى أن HTML أنشأت صفحة وِب بسيطة تحتوي على زر قابل للنقر. بعد ذلك، انتقل إلى محرر النصوص وأضف ما يلي مباشرة قبل وسم الإغلاق <body/>: <script> // سنضع تعليمات جافاسكريبت هنا </script> سنضيف الآن بعض جافاسكريبت داخل عنصر <script> لجعل الصفحة تفعل شيئا أكثر إثارة للاهتمام. أضف الشفرة التالية أسفل السطر // سنضع تعليمات جافاسكريبت هنا مباشرة: function createParagraph() { var para = document.createElement('p'); para.textContent = 'لقد نقرت على الزرّ!'; document.body.appendChild(para); } var buttons = document.querySelectorAll('button'); for (var i = 0; i < buttons.length ; i++) { buttons[i].addEventListener('click', createParagraph); } احفظ الملف في محرّر النصوص ثم حدّث الصفحة على المتصفح. يجب أن الآن أن تظهر فقرة جديدة أسفل الزّر عند النقر عليه. ملاحظة: إذا لم يعمل المثال الخاص بك، راجع الخطوات مرة أخرى وتحقق من أنك فعلت كل شيء بشكل صحيح. هل حفظت الملف بامتداد html (مثلا lesson1.html)؟ هل أضفت العنصر <script> بعد علامة <body/> مباشرة؟ هل أدخلت جافاسكريبت تماما كما هو موضح؟ جافاسكريبت حساسة لحالة الأحرف +(صغيرة Lowercase أو كبيرة Uppercase)، لذلك تحتاج إلى إدخال الشفرة تماما كما هو مبين، وإلا فلن تعمل. جافاسكريبت خارجي ماذا لو أردنا وضع جافاسكريبت لدينا في ملف خارجي؟ دعونا نستكشف هذا الآن. أولا، أنشئ ملفًا جديدا في نفس المجلّد الذي يوجد به ملف HTML وسمّه script.js. تأكد من أنه يحتوي على امتداد اسم الملفjs، وهذه هي الطريقة التي يُتعرَّف بها على أننا أمام ملف يحوي شفرات برمجية مكتوبة بجافاسكريبت. بعد ذلك، انسخ كل النص البرمجي من الوسم <script> في ملف HTML (تلك التعليمات الموجودة في الخطوة 4 من الفقرة السابقة) وألصقه في الملف script.js ثم احفظ هذا الملف. الآن استبدل العنصر الحالي <script> بما يلي. <script src="script.js"></script> يصبح محتوى الملف HTML على النحو التالي: <!DOCTYPE html> <html lang="ar" dir="rtl"> <head> <title>مثال تطبيقي لجافاسكريبت</title> </head> <body> <button>انقر هنا</button> <script src="script.js"></script> </body> </html> احفظ الملفّ ثم حدّث صفحة الوِب في المتصفح. ويجب أن تحصُل على نفس النتيجة تمامًا! ولكن الآن لدينا جافاسكريبت في ملف خارجي. يعدّ هذا عموما أمرا جيّدًا من حيث تنظيم التعليمات البرمجية الخاصة بك، وجعلها قابلة لإعادة الاستخدام عبر ملفات HTML متعددة. بالإضافة إلى HTML تصبح أسهل للقراءة دون وجود قطع ضخمة من السكربت ملقاة فيها. شفرات جافاسكريبت المضمنة في وسوم HTML تُكتَب شفرة جافاسكريبت أحيانا داخل HTML على النحو التالي: function createParagraph() { var para = document.createElement('p'); para.textContent = 'You clicked the button!'; document.body.appendChild(para); } <button onclick="createParagraph()">Click me!</button> استعرض المثال على Jsfiddle لهذا العرض التوضيحي نفس الوظائف التي عرضناها في الحالتيْن أعلاه، إلا أن عنصر <button> يتضمن خاصيّة onclick مضمنة لتشغيل وظيفة الدالة عند الضغط على زر. رجاءً لا تضمّن شفرات جافاسكريبت في وسوم HTML فهذه الطريقة من الممارسات السيئة (خلط HTML مع جافاسكريبت)، كما أنها غير فعالة لأن عليك أن تدرج الخاصيّة onclick="createParagraph()" على كل زر تريد تطبيق جافاسكريبت عليه. أما باستخدام شفرة جافاسكريبت منفصلة عن HTML فيمكنك تحديد كافة الأزرار باستخدام تعليمة واحدة. الشفرة التي استخدمناها أعلاه لهذا الغرض تبدو كما يلي: var buttons = document.querySelectorAll('button'); for (var i = 0; i < buttons.length ; i++) { buttons[i].addEventListener('click', createParagraph); } قد يبدو هذا الأمر أطول قليلا من الخاصيّة onclick، ولكنه سيعمل على جميع الأزرار بغض النظر عن عدد الأزرار الموجودة في الصفحة وعدد العناصر التي أُضيفت أو أزيلت، فلا يلزم تغيير جافاسكريبت. ملاحظة: جرب تعديل ملف HTML وإضافة بضعة أزرار أخرى في الملف. عند إعادة التحميل، يجب أن تجد أن جميع الأزرار عند النقر عليها سوف تُظهر فقرة. هذا رائع أليس كذلك؟ التعليقات Comments كما هو الحال مع HTML و CSS، يمكنك كتابة تعليقات في شفرة جافاسكريبت وسيتجاهلها المتصفح. تُستخدم التعليقات لتقديم توضيحات لزملائك المطورين عن كيفية عمل الشفرة، كذلك تمثل مرجعًا لك عن التعليمات البرمجية التي كتبتها عندما تعود مثلًا بعد 6 أشهر لتتذكر مالذي فعلته. التعليقات مفيدة جدًا، ويجب عليك استخدامها في كثير من الأحيان، وخاصة بالنسبة للتطبيقات الكبيرة. هناك نوعان من التعليقات: تعليقات من سطر واحد، وتُكتَب بعد عموديْن مائليْن؛ مثلا: // هذا تعليق تعليق متعدّد الأسطر. يُكتَب بين المحارف / * و * / على النحو التالي: /* هذا تعليق هو الآخر */ تطبيق التعليقات على تعليمات جافاسكريبت السابقة: // دالة تنشئ فقرة جديدة وتضيفها إلى نهاية متن الصفحة function createParagraph() { var para = document.createElement('p'); para.textContent = 'You clicked the button!'; document.body.appendChild(para); } /* 1. نحصُل على مراجع بجميع الأزرار الموجودة في الصفحة ونضعها في مصفوفة 2. نمرّ على جميع الأزرار ونضيف إلى كلّ منها مستمعًا لحدث النقر تُنفَّذ الدالة ()createParagraph عندما ينقر على زرّ في الصفحة. */ var buttons = document.querySelectorAll('button'); for (var i = 0; i < buttons.length ; i++) { buttons[i].addEventListener('click', createParagraph); } كانت هذه خطوتك الأولى في عالم جافاسكريبت. لقد بدأنا بالمفاهيم النظرية فقط لجعلك تفهم لماذا تُستخدم جافاسكربت وما الأشياء التي يمكنك القيام بها باستخدام هذه اللغة. تعرّفت في هذا المقال على بضعة أمثلة من التعليمات البرمجية وتعلّمت كيفية تتفاهم لغةُ جافاسكريبت مع بقية التعليمات البرمجية على موقع الوِب. قد تبدو الآن جافاسكريبت شاقة بعض الشيء، ولكن لا تقلق - في هذه السلسلة من الدروس سوف نأخذك في خطوات بسيطة تمكّنك من المضي قدمًا في عالم جافاسكربت. ترجمة - بتصرف - للمقال What is JavaScript? الذي اشترك في كتابته مساهمو موزيللا.
-
سنتعرّف في هذا الدّرس على لغة البرمجة جافاسكربت من منظور عامّ وسنجيب على تساؤلاتك ماهي جافاسكربت؟ وما الذي تقوم بعمله في صفحات الوِب؟ و نتأكد من أن مفهوم جافاسكريبت قد أصبح واضحًا لديك. ملحوظة: يتوجَّه هذا الدرس، والدروس التي تليه - إلى من لديهم معرفة سابقة بأساسيّات التطوير للوِب: HTML وCSS. التعريف العام لجافاسكريبت تُعرَّف جافاسكريبت بأنّها لغة برمجة تسمح لك بتنفيذ أشياء معقدة على صفحات الوِب، تعدّ صفحات الوِب الحديثة أكثر من مجرد موقع يُظهر لك معلومات ثابتة تتصفحها، فهي - مثلًا - تعرض تحديثات المحتوى المباشرة، أو تعرض الخرائط التفاعلية، أو الرسومات المتحركة ثنائية أو ثلاثية الأبعاد، أو تمرّر ملفات الفيديو المصورة، وما إلى ذلك. تمثّل جافاسكريبت الطبقة الثالثة من طبقات تقنيات الوِب القياسية. الطبقتان الأوليان هما HTML وCSS. HTML هي لغة توصيف Markup نستخدمها لإنشاء محتوى الوِب وإضفاء معنى عليه، مثل تعريف الفقرات، العناوين وجداول البيانات أو تضمين الصور ومقاطع الفيديو في الصفحة. CSS هي لغة تعرّف قواعد الأنماط Style rules التي نستخدمها لتطبيق التصميم على المحتوى الذي تصفه HTML، على سبيل المثال، تعيين ألوان وخطوط الخلفية، ووضع محتوى ما في أعمدة متعددة. Javascript هي لغة برمجة تمكّنك من إنشاء محتوى حيوي وديناميكي، التحكم في الوسائط المتعددة، وتحريك الصور وأمورا وكل شيء آخر. حسنًا، ليس كل شيء، ولكن من المدهش ما يمكنك تحقيقه مع بضعة أسطر من شفرة جافاسكريبت. سنأخذ مثالًا يوضح كيفية بناء هذه الطبقات الثلاث فوق بعضها بشكل لطيف. لنأخذ تسمية نص بسيط كمثال. سنضع علامة عليه باستخدام HTML لإعطائه هيكلًا وغرضًا: <p>Player 1: Chris</p> النتيجة حتى الآن. سنضيف تحسينات على الشكل باستخدام CSS: p { font-family: 'helvetica neue', helvetica, sans-serif; letter-spacing: 1px; text-transform: uppercase; text-align: center; border: 2px solid rgba(0,0,200,0.6); background: rgba(0,0,200,0.3); color: rgba(0,0,200,0.6); box-shadow: 1px 1px 2px rgba(0,0,200,0.4); border-radius: 10px; padding: 3px 10px; display: inline-block; cursor:pointer; } أصبحت النتيجة الآن كالتالي. وأخيرًا، نضيف بضعة تعليمات جافاسكريبت لتنفيذ السلوك الديناميكي. var para = document.querySelector('p'); para.addEventListener('click', updateName); function updateName() { var name = prompt('Enter a new name'); para.textContent = 'Player 1: ' + name; } غرضنا الآن ليس شرح التعليمات السابقة (سنفعل ذلك لاحقا) ولكن توضيح ما يمكن أن تضيفه جافاسكريبت لـHTML وCSS. عند النقر على النص تظهر نافذة تطلُب إدخال اسم ثم عند إدخال الاسم في النافذة المنبثقة والنقر على زر OK تأخذ النص وتطبعه أمام الجملة Player 1. استعرض المثال على Jsfiddle. الشفرة المصدرية الكاملة للمثال. التشغيل المباشر. يمكن لجافاسكريبت أن تفعل أكثر من ذلك بكثير؛ دعونا نستكشف مزيدًا من التفاصيل. مالذي يمكن لجافاسكريبت فعله حقا؟ تتكون لغة جافاسكريبت الأساسية من بعض ميزات البرمجة الشائعة التي تسمح لك بفعل أشياء مثل: تخزين قيم مفيدة داخل متغيرات Variables. في المثال السابق على سبيل المثال، نطلب من المستخدم ادخال اسم جديد ثم يُخزَّن هذا الاسم في متغير يسمى name. العمليات على أجزاء من النص (المعروفة باسم سلاسل المحارف Strings في البرمجة). في المثال أعلاه، نأخذ سلسلة المحارف Player 1: ونضمها إلى متغير الاسم المذكور أعلاه لإنشاء الجملة الكاملة، على سبيل المثال Player 1: Chris. تشغيل التعليمات البرمجية ردًا على أحداث معينة تحدث على صفحة وِب. في المثال أعلاه استخدمنا الحدث click الذي يخبرنا أن المستخدم نقر على النصّ ثم نشغّل الشفرة التي تعمل على تحديث النص. والكثير! ولكن ما هو أكثر إثارة هو وظيفة بُنيت على لبّ لغة جافاسكريبت الأساسي. تسمى APIs وهي اختصار لـ Application Programming Interface واجهات برمجة التطبيقات. تتيح لك واجهات برمجة التطبيقات (APIs) المزيد من القوة لاستخدامها في شفرة جافاسكريبت. تُعرّف واجهات برمجة التطبيقات (APIs) على أنها مجموعات جاهزة من الشفرات البرمجيّة تسمح لمطور البرامج بتنفيذ برامج سيكون من الصعب أو المستحيل تنفيذها من دون استخدام واجهة برمجة التطبيقات. لأوضح لك الفكرة، تخيل أنك تريد عمل رف خشبي للكتب، سيكون الأمر أسهل بكثير لو توافرت لك الألواح الخشبية والمسامير بالمقاسات المناسبة وما عليك سوى تجميعها معًا لعمل رف الكتب. في حين سيكون الأمر أصعب بكثير لو توجّب عليك أنت العثور على الخشب المناسب وتقطيعه إلى الحجم والشكل الصحيح وإيجاد قياس المسامير المناسب ومن ثم تجميعها معًا لعمل رف الكتب. هذا ما تسهل عمله واجهات برمجة التطبيقات. تنقسم واجهات برمجة التطبيقات (APIs) عمومًا إلى فئتين. واجهات برمجة التطبيقات الخاصّة بالمتصفّح Browser APIs: تُدمَج واجهات برمجة تطبيقات في المتصفّح لإعطائه القدرة على عرض البيانات من بيئة الحاسوب المحيطة به، أو القيام بأشياء معقدة مفيدة. فمثلا: تسمح لك واجهة برمجة تطبيقات DOM وهي اختصار لـ Document Object Model بمعالجة HTML و CSS، إنشاء، إزالة وتغيير HTML ، تطبيق أنماط Styles جديدة ديناميكيًّا على صفحتك، وما إلى ذلك. في كل مرة تُعرَض فيها نافذة منبثقة تظهر على صفحة، أو يُعرَض محتوى جديد -كما رأينا أعلاه في مثالنا السابق- فواجهة DOM هي المسؤولة عن ذلك. تستعيد واجهة برمجة التطبيقات Geolocation API معلومات الموقع الجغرافي، ممّا يفسّر كيف يمكن لخرائط جوجل Google Maps أن تعثُر على موقعك الجغرافي وترسمه على الخريطة. تسمح لك واجهات برمجة تطبيقات اللوحة Canvas و WebGL بإنشاء رسومات متحركة ثنائية الأبعاد وثلاثية الأبعاد. يقوم الأشخاص بأشياء مدهشة باستخدام تقنيات الوِب هذه. تسمح لك واجهات برمجة التطبيقات الصوتية والمرئية مثل HTMLMediaElement و WebRTC بالقيام بأشياء مثيرة للاهتمام حقًا مع الوسائط المتعددة، مثل تشغيل الصوت والفيديو مباشرة في صفحة وِب أو التقاط الفيديو من كاميرا الوِب الخاصة بك وعرضها على جهاز كمبيوتر شخص آخر (جرب العرض التوضيحي snapshot) ملاحظة: لن تعمل العديد من العروض التوضيحية المذكورة أعلاه في متصفح أقدم. عند التجربة، من الأفضل استخدام متصفح حديث مثل فايرفوكس أو كروم أو Edge أو Opera لتشغيل الشفرة. واجهة برمجة التطبيقات الخاصّة بطرف ثالث Third party APIs. لا تُضمَّن هذه الواجهات في المتصفح مبدئيًّا. وعليك عمومًا الحصول على التعليمات البرمجية والمعلومات من مكان ما على الوِب. فمثلا: تتيح لك واجهة برمجة تطبيقات تويتر Twitter API إجراء أشياء مثل عرض أحدث التغريدات على موقعك الإلكتروني. تسمح لك واجهة برمجة التطبيقات لخرائط غوغل Google Maps API بتضمين الخرائط المخصصة في موقعك على الوِب، وغير ذلك من الوظائف. ملاحظة: واجهات برمجة التطبيقات هذه متقدمة، ولن يتم تغطية أي منها في هذه الدّروس، ولكن الروابط الواردة أعلاه توفّر وثائق شاملة إذا كنت مهتمًا بمعرفة المزيد. يوجد الكثير لتعلّمه، لا تفقد حماسك. دراسة جافاسكريبت لمدة 24 ساعة لن تعطيك القدرة على بناء فيسبوك، خرائط غوغل أو إنستغرام. هناك الكثير من الأساسيات التي يجب تغطيتها أولًا. وهذا هو السبب في أنك هنا. سنكمل في الدرس السابق حديثنا عن جافاسكريبت وستكون فرصة على للنظر الفعلي في شفرات برمجية. ترجمة - بتصرف - للمقال What is JavaScript? الذي اشترك في كتابته مساهمو موزيللا.
-
تُقدم 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.
- 2 تعليقات
-
- قاعدة بيانات محلية
- indexeddb
-
(و 1 أكثر)
موسوم في: