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

لوحة المتصدرين

  1. علي الكاسر

    علي الكاسر

    الأعضاء


    • نقاط

      4

    • المساهمات

      192


  2. Mohammed Hhhh

    Mohammed Hhhh

    الأعضاء


    • نقاط

      2

    • المساهمات

      219


  3. Mazz Ibraheem

    Mazz Ibraheem

    الأعضاء


    • نقاط

      2

    • المساهمات

      26


  4. علي عبد محسن

    علي عبد محسن

    الأعضاء


    • نقاط

      2

    • المساهمات

      651


المحتوى الأكثر حصولًا على سمعة جيدة

المحتوى الأعلى تقييمًا في 09/22/22 في كل الموقع

  1. الحين انا لاحظت ان بعض المواقع تعمل انميشن لل عانصر لما توصل عندها scroll كيف ممكن اسوي هذا الشيء و شكرا
    2 نقاط
  2. لدي قاعدة بيانات عملت لها تصدير واحتاج الى اضافتها باوامر sql من دون الحاجه لاداة ماي تدمن مجددا كيف ذلك
    2 نقاط
  3. الإصدار 1.0.0

    14167 تنزيل

    ما زال التسويق يمثل محور العمل في أي شركة، فهو يتعلق بتحديد حاجات الزبائن وإشباعها من خلال توفير منتجات قيّمة وتوضيح مزاياها لهم، ويمكن اعتماد ما سبق تعريفًا موجزًا للتسويق ويعرّف هذا الكتاب القارئ على استراتيجيات التسويق وأدواته التي يستخدمها المسوقون لتسويق منتجاتهم. لقد حرص المؤلف في هذا الكتاب على إبراز مجالات التسويق المختلفة وكيفية الدمج بينها لبناء إستراتيجية تسويق مترابطة، لذلك عمد في هذا الكتاب إلى تعريف كل مجال من مجالات التسويق وتوضيح نقاط الضعف والقوة فيه، ثم تطرق إلى كيفية الدمج بين أدوات التسويق لبناء خطة إستراتيجية متكاملة. ويبدأ الكتاب بنقاش حول التخطيط بشكل عام، ثم يتناول بالتفصيل الخطوات الأولى لبناء الخطة التسويقية، ثم ينتهي بأفضل الأساليب المتاحة للمسوّقين. ويهدف الكتاب إلى التعريف بكيفية تخطيط برنامج تسويقي فعّال وتنفيذه وتقييمه من الألف إلى الياء. يتناول فصل "مقدمة إلى التسويق" تأثير الأحداث العالمية على أساليب التسويق، فيما يتطرق فصل "التسويق في الأسواق العالمية" إلى تأثير التسويق على العالم، كذلك يتناول الكتاب في جميع أجزائه أمثلة مختلفة من السوق العالمي لشركات على أرض الواقع، إذ تساعد الأمثلة والقصص الواقعية القارئ على الربط بين استيعاب النظرية وتطبيقها، وتتسم هذه الأمثلة بكونها حيّة ومعاصرة ومتنوعة، فهي تتراوح بين قائمة فورتشن لأكبر 500 شركة أمريكية، إلى الشركات الصغيرة الخاصة كذلك يتناول الكتاب أمثلة لشركات دولية من جميع الأحجام. يناقش الكتاب أيضًا في مواضع مختلفة منه أهمية التكنولوجيا وتأثيراتها المختلفة إذ لا يخفى أثر التكنولوجيا على أساليب التسويق، كما تُعد شبكة الإنترنت وقواعد البيانات وأجهزة التعقب ومحاكيات السوق أمثلة جيّدة تعكس مدى تأثير التكنولوجيا في عالم التسويق. بني هذا الكتاب على كتاب Introducing Marketing لمؤلفه Prof. John Burnett (البروفيسور جون بورنِت) المرخص تحت رخصة CC BY والذي ألف وشارك في تأليف عشرين كتابًا وما يربو على 60 بحثًا أكاديميًا، وقد تقاعد مؤخرًا بعد 41 عامًا من العمل محاضرًا في مجال التسويق. ولقد انصبّ معظم تركيز البروفيسور خلال السنوات العشرين الماضية على التسويق لصالح المنظمات غير الربحية وقد كانت حصيلة مجهوداته إقامة العديد من ورش العمل، ووضع الكثير من الخطط التسويقية، بالإضافة إلى تأليف كتاب بعنوان "Nonprofit Marketing Best Practices" (أفضل أساليب التسويق للمنظمات غير الربحية). ساهم بالعمل على هذا الكتاب، علاء أيمن ترجمةً، والطبيب نادر حوري والبروفيسور علي اليعقوبي مراجعةً وتدقيقًا، وجميل بيلوني تحريرًا وإشرافًا، وأخرجه فنيًا فرج الشامي. نرجو أن نكون قد وفقنا في هذا العمل بتوفير مرجع أكاديمي نافع يثري المكتبة العربية. هذا الكتاب مرخص بموجب رخصة المشاع الإبداعي Creative Commons «نسب المُصنَّف - غير تجاري - الترخيص بالمثل 4.0». نشرنا فصول هذا الكتاب على شكل مقالات مبسَّطة على الويب تحت الوسم «مدخل إلى التسويق» لتسهيل قراءتها: الفصل الأول: مقدمة إلى التسويق مقدمة إلى علم التسويق أنواع التسويق الأساسية مكونات التسويق الاستراتيجية الفصل الثاني: فهم السوق والتعامل معه فهم السوق والتعامل معه تجزئة المستهلكين في العملية التسويقية تجزئة الشركات في العملية التسويقية الفصل الثالث: بحوث التسويق وصناعة القرار بحوث التسويق وصناعة القرار التسويقي تصميم خطة البحث التسويقي تنفيذ خطة البحث التسويقي الفصل الرابع: فهم سلوك المستهلكين فهم سلوك المستهلكين العوامل المؤثرة في سلوك المستهلكين سلوك الشراء لدى الشركات الفصل الخامس: أثر العوامل الخارجية على عملية التسويق أبرز العوامل السياسية والاقتصادية والقانونية المؤثرة على عملية التسويق أهم الاتجاهات الاجتماعية المؤثرة على عملية التسويق الفصل السادس: التسويق في الأسواق العالمية التسويق العالمي: الأهداف والأسباب والمعوقات والمراحل كيف تؤثر البيئة المحيطة على التسويق العالمي؟ الفصل السابع: طرح المنتج وإدارته تعريف المنتج وأنواعه التخطيط للمنتج واستراتيجيات الدخول إلى السوق تطوير منتج جديد خطوة بخطوة الفصل الثامن: التواصل مع الأسواق الاتصال التسويقي المتكامل: تعريفه وأهدافه وأنواعه أدوات الاتصال التسويقي المتكامل الفصل التاسع: تسعير المنتج أهداف تسعير المنتج، واستراتيجياته المختلفة كيف تختار السعر الملائم للمنتج؟ الفصل العاشر: قنوات توزيع المنتج قنوات توزيع المنتج: مهامها، وأبرز مؤسساتها كيف تدير قنوات توزيع المنتج؟
    1 نقطة
  4. السلام عليكم انا اعمل على تطبيق يستخدم قاعدة بيانات محليه واقوم بمعالجتها باستخدام SQLiteAssetsHelper اذاً كيف اقوم بتشفيرها بكلمه مرور لمنع الوصول اليها؟ كيف اضيف كلمه المرور وكيف اقوم بفتحها باستخدام SQLiteAssetsHelper في android studio بحثت مطولا عن الاجابه فلم اجدها لأني قد تعبت كثيرا لتجهيز هذه البيانات، وشكرا مقدما على الرد
    1 نقطة
  5. السلام عليكم محتارة بين لاب hp أو mac قريت ان swift ما اقدر اسوي تطبيق ios منها الا اذا كان عندي mac وابغى اسأل هل اقدر اسوي تطبيق على نظام ios بلاب توب زي hp باستخدام react native ولا برضو لازم mac عشان الios؟ وكمان لو ابغى اسوي تطبيق android هل احتاج اشتري جهاز جوال android ؟ ولو اخترت mac هل استطيع العمل عليه باللغات الأخرى غير swift وبناء مواقع وتطبيقات عليه ولا hp أفضل ؟ عذرًا على كثرة الأسئلة بس اختيار اللاب توب مرحلة صعبة بالنسبة لي 💔
    1 نقطة
  6. كلامك مضبوط، ليس هنالك خلال. لقد جربته في codesandbox وكل شيء يعمل، لكن لسبب ما. لا يعمل في مشروعي. لا استطيع شرح المشكلة أكثر. لكن يبدو لي أن هنالك خلل كبير في الأساس الذي بنيت عليه، بيحث لم يعد ريأكت يعمل بالشكل الصحيح. كان ينبغي عليّ التحقق من العملية خارج مشروعي في codesandbox قبل طرح السؤال.
    1 نقطة
  7. هل يمكن اعادة كتابة الكود والاتصال بقاعدة البيانات بطريقة pdo لان هذه الطريقه قديمه ولم تعد تعمل
    1 نقطة
  8. اخي لقد ظهر لي هذا الخطأ Fatal error: Uncaught Error: Call to undefined function Controllers\mysql_connect() in /storage/emulated/0/wanachat/Controllers/Database.php:21 Stack trace: #0 /storage/emulated/0/wanachat/Core/Route.php(24): Controllers\Database->index() #1 /storage/emulated/0/wanachat/wepUrl.php(20): Route::get('database', Array) #2 /storage/emulated/0/wanachat/index.php(9): require('/storage/emulat...') #3 {main} thrown in /storage/emulated/0/wanachat/Controllers/Database.php on line 21 ويشير الخطاء الى هذا السطر mysql_connect($mysql_host, $mysql_username, $mysql_password) or die('Error connecting to MySQL server: ' . mysql_error());
    1 نقطة
  9. يمكنك البحث في google عن سبل تحقيق الخطوات الي وضعناها لك في الرد السابق.
    1 نقطة
  10. مرحبا اسماعيل، ضمن دورة PHP يوجد مسار تطبيق مشاركة فيدوهات هل اطلعت عليه؟ ربما فيه إجابة لتساؤلاتك فهو يحوي نفس الأمور التي طرحتها في السؤال مثل التخزين السحابي مثلاً خدمات أمازون مع توفير الفيديو بأكثر من دقة مختلفة حجم و سرعة تحميل الفيديو مرتبط بسرعة الانترنت في الاستضافة / حسب عدد المستخدمين للتطبيق في نفس الوقت يمكن من خلال خصائص خادم الويب زيادة مدة Timeout (فترة انتظار انتهاء رفع/تحميل الفيديو) لقيمة مناسبة للمستخدمين أي أكثر من القيم الافتراضية مثل إعداد PHP *** PHP configuration /etc/php/<phpversion>/fpm/php.ini => max_execution_time = 1800 # 30 min /etc/php/<phpversion>/fpm/pool.d/www.conf => request_terminate_timeout = 0 # no timeout و إعاداد خادم Nginx configuration /etc/nginx/sites-available/yoursite.conf location ~ \.php$ { # ... fastcgi_read_timeout 300; # ... } /etc/nginx/sites-available/yoursite.conf fastcgi_read_timeout = 2000; # 2000 secs وفي حال استخدام مكتبة في الواجهة الأمامية مثل dropzonejs لها خاصية timeout لزيادة وقت الانتظار var myDropzone = new Dropzone("#uploader", { // ... timeout: "4321", // ... });
    1 نقطة
  11. بداية من الضروري فهم أن SQLiteAssetHelper غير قادر على فتح قاعدة بيانات مشفرة من النوع sqlite ولكن فقط يستطيع التعامل مع قاعدة البيانات المفتوحة من خلال الدالتين getReadableDatabase و getWritableDatabase. ما تستطيع فعله بهذا الخصوص هو إستعمال الأداة SQLCipher for Android مع SQLiteAssetHelper لحل المشكلة ولكن بطريقة نوعاً ما يشوبها التعقيد. أولاً - نستعمل SQLCipher لإنشاء قاعدة بيانات فارغة مشفرة. ثانياً - نستعمل SQLiteAssetHelper لقراءة قاعدة البيانات المفتوحة لديك ومن ثم نسخ بياناتها الى المشفرة سابقة ألإنشاء. ثالثاً - نحذف قاعدة البيانات المفتوحة بعد إغلاقها. تستطيع الإطلاع أكثر على SQLCipher هنا
    1 نقطة
  12. في حال وجدت طريقة للقيام بإيجاد روابط المواقع فإن ذلك يعني أنك استطعت الحصول على عدد من المقاطع المشفرة و ما يقابلها من المقاطع بعد فك تشفيرها، و هذا سيجعل هناك نقطة ضعف في التشفير. فبالتالي من غير المنطقي وجود هكذا أمر، في حال كان الكود يخصك و تمتلك مفتاح فك التشفير فإن ذلك ممكن، حتى من دون فك التشفير بشكل مباشر، حيث أن فك التشفير و تخزين الكود بشكل غير مشفر قد يسبب مشاكل أمنية، هناك طرق تقوم بالتعامل مع الكود المشفر بشكل مباشر و لكنها طرق معقدة و قد لا تنجح مع جميع خوارزميات التشفير و جميع المعدات الحاسوبية.
    1 نقطة
  13. النظر إلى أمثلة التصميم السيئ مقابل التصميم الجيد ممتع ومفيد في الوقت نفسه، فهو وسيلة مهمة لأخذ دروس مفيدة للمصممين، من خلال تسليط الضوء على الأخطاء التي وقع فيها مصممون آخرون من أجل تلافيها، بالإضافة إلى تمكين ترجمة النظريات التصميمية إلى أمثلة حية على أرض الواقع. يقول الكاتب والباحث الأمريكي جاريد سبول الخبير في مجال قابلية الاستخدام: "العيوب هي ما تجعلنا ننتبه للتصميم، ليكون التصميم جيدًا يجب ألا نلاحظه". لننظر فيما يلي إلى خمسة أمثلة عن تصاميم من الواضح أنها سيئة، ونبيّن كيف يمكن أن تكون أفضل، ونستخلص بعض الدروس المستفادة التي تساعدنا على تصميم تجربة مستخدم رائعة وخالية من أخطاء تشد الانتباه. 1. كثرة المعلومات المثال السيئ: لافتات وقوف السيارات في لوس أنجلوس لطالما كانت لافتات وقوف السيارات في لوس أنجلوس مثالًا على كثرة المعلومات، فقد اشتهرت على مدى عقود من الزمن بصعوبة فهمها، وذلك بسبب تعقيد قواعد المرور التي تطلبت إدراج الكثير من المعلومات في المساحة المحدودة للّافتة، لهذا فقد كانت مربكةً كثيرًا. انظر إلى المثال التالي من لافتات تعود لعام 2010: هذا مثال بسيط عن تعقيد لافتات الوقوف في لوس أنجلوس. بفرض أنك سائق تقود سيارتك على هذا الطريق في الساعة التاسعة من صباح يوم الثلاثاء، هل يمكنك ركن السيارة هنا وفقًا لما تمليه هذه اللافتات؟ يستغرق هذا السؤال البسيط الكثير من التفكير للوصول إلى الإجابة. يواجه المصممون العديد من الحالات التي تتطلب منهم تضمين الكثير من المعلومات وعرضها في مساحة صغيرة. قد يكون هذا المثال للافتات الوقوف حالةً خاصة، لكن تصميم تطبيقات الجوال يطرح التحدي نفسه على المصممين في كثيرٍ من الأحيان. إذًا هل هناك حل أفضل لتضمين قواعد المرور والمحافظة على تصميم جيد؟ المثال الجيد: لافتة وقوف السيارات من تصميم نيكي سيليانتينغ إذا أردت تصميم لافتة شاملة لكل المعلومات وسهلة الفهم في الوقت نفسه، فقد يبدو الأمر أشبه بالمهمة المستحيلة، لكن هذا بالضبط ما تمكنت مصممة بروكلين نيكي سيليانتينغ من فعله. استُخدم تصميم نيكي سيليانتينغ للافتة الوقوف أخيرًا في لوس أنجلوس ضمن حملة تجريبية، ومن أسباب نجاحه أنه يركز على المستخدم، فقد أدركت نيكي أن السائقين يريدون ببساطة أن يعرفوا ما إذا كان بإمكانهم أن يركنوا سياراتهم في مكان معين أم لا، هذا كل ما يحتاجونه، وهو ما وفّرته لهم لافتة الوقوف. ركّز تصميمها أيضًا على العناصر المرئية بدلًا من النص من أجل إيصال المعلومات، من خلال استعمال إشارات بديهية سهلة الفهم، فالأخضر يدل على إمكانية الوقوف، والأحمر يدل على منع الوقوف، كما راعى التصميم الأشخاص المصابين بعمى ألوان من خلال استعمال الأشرطة للدلالة على منع الوقوف. والآن عندما تنظر إلى اللافتة، ستتمكن بسهولة من معرفة أن الوقوف غير مسموع يوم الثلاثاء عند الساعة التاسعة صباحًا على سبيل المثال، إذ تتضمن الأعمدة كافة المعلومات المطلوبة بكل وضوح. الدروس المستفادة لتجربة أفضل حدد ما يحتاج المستخدم إلى معرفته، ثم صمم وفقًا لذلك، فهذا يساعدك على اختيار المعلومات اللازمة. إذا وجدت أن هناك الكثير من المعلومات المطلوبة، حاول استخدام العناصر المرئية بدلًا من النص. 2. التنقل المبهم في المواقع المثال السيئ: موقع lazoroffice.com أشار فينسينت فلاندرز في كتابه "صفحات الويب الرديئة Web Pages That Suck" إلى ظاهرة التنقل المبهَم في المواقع، والتي أطلق عليها مصطلح تنقُّل اللحوم الغامضة، في إشارةٍ منه إلى الحالات التي تكون فيها وجهة الروابط غير معروفة إلى أن ينقر المستخدم عليها أو يوجه المؤشر إليها، وأتى هذا المصطلح من اللحوم التي كانت تقدَّم في مطاعم المدارس العامة الأمريكية، والتي كانت تعالَج كثيرًا بحيث يتعذر تمييز نوعها. المزعج في هذه الظاهرة أنها تصعِّب قابلية اكتشاف الوجهات التي تقود إليها العناصر الموجودة في صفحات الويب، مما يزيد من العبء الواقع على عاتق المستخدم في محاولة إيجاد طريقة التنقل في الموقع أو معرفة وظيفة الأزرار والعناصر التي يمكن النقر عليها. رغم أن هذه الظاهرة غالبًا ما تتجلى في المواقع القديمة، إلا أنها منتشرة أيضًا في مواقع حديثة، مثل موقع Lazor Office لشركة تصميم تختص بإنشاء منازل مسبقة الصنع. تحتوي الصفحة الرئيسية لموقع LazorOffice.com على مجموعة صور مصفوفة، وكما نلاحظ فإن الواجهة لا تقود جيدًا إلى المكان الذي يجب الذهاب إليه، فهي تعرض تسع صور تحيّر المستخدم أكثر مما تدفعه للتفاعل مع الصفحة. هل هذه الصور قابلة للنقر عليها؟ نعم، فعند وضع السهم فوقها يتغير إلى مؤشر. وما الذي يحدث عند النقر؟ لا شك بأن الجواب "انقر لتكتشف!". من غير الممكن أن يكون هذا حلًا جيدًا لتجربة مستخدم ناجحة، ومن المرجح أن يترك المستخدمون الموقع ويجدوا حلًا بديلًا في موقع شركة منافسة. المثال الجيد: بطاقات دورات تدريبية من موقع Interaction Design Foundation لحسن الحظ فإن مشاكل التنقل المبهم في المواقع سهلة الحل، فالسر هو أن تسمّي الروابط بوضوح. على سبيل المثال، لتحسين قابلية استخدام موقع LazorOffice.com المذكور أعلاه، يمكن ببساطة إضافة عبارة "عرض المشروع" بحيث تظهر عندما يكون المؤشر فوق الصور. بطاقات الدورة التدريبية غير غامضة، والروابط ذات مسميات واضحة. بالنسبة لبطاقات الدورات في موقع Interaction Design Foundation، نلاحظ وجود خيار "عرض الدورة" أسفل كل بطاقة للدلالة على الإجراء الذي سيحصل عند النقر، وتظهر لديهم أيضًا عبارة "الذهاب إلى الدورة" في حالة حَوَمان المؤشر فوق البطاقة. تتبع الكثير من مواقع الويب أساليب مشابهة تعزّز قابلية استخدام المواقع. الدروس المستفادة لتجربة أفضل اجعل الروابط ذات مسميات واضحة دائمًا، فالمستخدم لا يرغب بالنقر على روابط مبهمة. 3. إضافة الاحتكاك إلى إجراءات المستخدم المثال السيئ: موقع iFly50.com يتعين على المصممين تحرّي الحذر الشديد عند إضافة احتكاك إلى إجراءات المستخدم، ما لم يكن الهدف هو ثني المستخدم عن فعل هذا الإجراء، لكن أحيانًا قد يُضاف الاحتكاك إلى إجراءات المستخدم دون قصد (لأسباب جمالية أو تجديدية غالبًا)، مما يؤدي إلى تجربة مستخدم سلبية. من الأمثلة على ذلك موقع iFly50.com الذي أُنشئ احتفالًا بالذكرى السنوية لمجلة iFly التابعة للخطوط الجوية الملكية الهولندية، وهو موقع ذو تمرير عمودي يعرض 50 وجهة للسفر، وفي بعض الوجهات (كتلك التي في الصورة) يطلُب من المستخدم أن ينقر باستمرار لبضع ثوانٍ لعرض المزيد من الصور. يطلب موقع iFly50.com النقر باستمرار لبضع ثوانٍ كلما أراد المستخدم رؤية المزيد من الصور. يؤثر وجود بضع ثوانٍ من الاحتكاك لكل إجراء تأثيرًا كبيرًا في إضعاف تجربة المستخدم. تخيل مثلًا أنك تحتاج للنقر باستمرار لثانيتين بدلًا من النقر الآني على كل خيار أو رابط أثناء تصفحك للإنترنت، لا شك بأنك حينها ستتوقف عن التصفح بعض بضع نقرات مستمرة. كثيرًا ما ينجرّ المصممون إلى أحدث الأنماط أو الإجراءات التفاعلية، لكن هذا يتطلب منهم الانتباه إلى مدى إضافتها للاحتكاك إلى إجراءات المستخدم. غالبًا ما تعمل الأنماط المعروفة والمجرَّبة (مثل النقر البسيط أو السحب) بصورة مثالية. المثال الجيد: التمرير المرن في نظام iOS من المثير للاهتمام أن إضافة الاحتكاك إلى إجراءات المستخدم قد تعطي في بعض الأحيان تصميمًا رائعًا إذا كانت مدروسةً بعناية. من أهم ابتكارات أبل في هذا السياق إنشاؤها خاصية التمرير المرن في نظام تشغيل الهواتف المحمولة iOS، حيث تزداد صعوبة التمرير في بعض الحالات، مثل نهاية صفحة الويب. أُضيف الاحتكاك بصورة مدروسة في بعض الحالات من خلال خاصية التمرير المرن في iOS. الدروس المستفادة لتجربة أفضل تجنب إضافة أي نوع من الاحتكاك إلى إجراءات المستخدم قدر الإمكان، وإذا كان لا بد من إضافة احتكاك فأضفه بعناية فائقة. 4. التصاميم "الذكية" التي لا تراعي قابلية الاستخدام المثال السيئ: موقع Bolden.nl أحيانًا تؤثر التصاميم الذكية سلبًا على تجربة المستخدم، والأسوأ من ذلك أن المصممين يميلون إلى هذه التصاميم ويَعُدّونها مثيرةً للاهتمام. للأسف فإن غالبية البشر لا يعتقدون ذلك لأنهم لا يفكرون مثل المصممين. وبالنتيجة، ليست كل التصاميم "الذكية" تصاميم جيدة، خصوصًا عندما تسبب مشاكل في قابلية الوصول والاكتشاف والاستخدام. لنأخذ مثالًا من موقع إستديو Bolden الهولندي للتصميم والتطوير الإستراتيجي: هل يمكنك قراءة النص الذي تعرضه الصفحة الرئيسية؟ إذا كان الجواب لا، فهذا لأن عليك أن تضع المؤشر في زاوية الصفحة لتتمكن من رؤية الرسالة بوضوح. لا شك بأنه تصميم ذكي، لكنه بالتأكيد تصميم سيئ في الوقت نفسه! إنه من أفضل الأمثلة عن التصميم الملائم للمصممين وليس للمستخدمين العاديين، فقد أثّر التصميم فعليًا على وضوح العناوين الرئيسية بسبب إصرار منشئيه على إيجاد فكرة جديدة في التصميم، كما أن المصمم نسي أن يضيف أي نص ينبّه المستخدم لوضع مؤشر الفأرة في الزاوية، مما يعني أن اكتشاف الفكرة من التصميم يعتمد على الصدفة. وفوق ذلك، حتى عندما يظهر العنوان الرئيسي، فإن التباين بين النص والخلفية يكون ضعيفًا بسبب استمرار ظهور النص المتراكب. يؤثر كل ذلك على ملاءمة موقع الويب للمستخدم. المثال الجيد: موقع cultivatedwit.com موقع Cultivated Wit مثال معاكس ممتاز عن التصميم الذكي الذي يولي قابلية الاستخدام اهتمامًا كبيرًا. تعرض الصفحة الرئيسية لموقع CultivatedWit تمثيلًا لصورة بومة. عند وضع مؤشر الفأرة فوق صورة البومة في الصفحة الرئيسية للموقع، فإنها تغمز بعينها. مفاجأة! البومة تغمز لك عند وضع المؤشر عليها، لاحظ أيضًا الاستخدام المناسب للمساحة البيضاء. الفرق الأساسي هنا هو أن عنصر التصميم الذكي مجرد جزء من التصميم، ولن تتأثر قابلية الاستخدام في حال لم يدركه المستخدم. بالإضافة لذلك، يظهر سهم واضح يشير إلى الأسفل للدلالة على وجود شيء تحته، وعند التمرير للأسفل، تظهر هذه الشاشة: ويمكن إنشاء صفحات ويب ذكية دون إهمال تجربة المستخدم. تؤدي العبارة المكتوبة (المقروءة وذات التباين الجيد مع الخلفية) الغرضَ الذي كان موقع Bolden يحاول الوصول إليه وهو التعبير عن الذكاء، دون التقليل من جودة تجربة المستخدم في الموقع. المشكلة الوحيدة هي أن عبارة "انضم إلى نادي البريد الإلكتروني الخاص بنا" يجب أن تكون أكثر وضوحًا، لكن بصورة عامة يُعَد موقع Cultivated Wit مثالًا ممتازًا عن التصميم الذكي الذي لا يؤثر سلبًا على تجربة المستخدم. الدروس المستفادة لتجربة أفضل يجب أن تكون التصاميم الذكية مفهومةً قدر الإمكان، ويجب اختبارها على مستخدمين فعليين، فأحيانًا قد تؤدي بعض التصاميم الذكية إلى نتائج عكسية وتؤثر سلبًا على قابلية الاستخدام. 5. إضافة الكثير من التأثيرات المتحركة المثال السيئ: وصل باي بال المقترح على منصة دريبل Dribbble التأثيرات المتحركة عنصر أساسي في التصميم التفاعلي، لكن يجب دائمًا أن تخدم غايةً معينة، لا أن تكون غايةً بحد ذاتها. لسوء الحظ فإن المصممين كثيرًا ما يستمتعون بالعمل على التأثيرات المتحركة ويستغرقون بهذه العملية دون إدراك حدود الأمر. من الأمثلة على التأثيرات المتحركة غير المناسبة؛ مقترَح التأثيرات المتحركة لوصل باي بال عبر البريد الإلكتروني الذي نشره فلاديسلاف تايزون على منصة دريبل. التأثيرات المتحركة جميلة، لكنها زائدة عن اللزوم، كما أن رؤية تفاصيل المعاملة المالية تستغرق 3.5 ثانية بسبب هذه التأثيرات. من الأفضل اختيار تأثير بسيط مثل الظهور التدريجي fade-in للوصل الذي يستغرق وقتًا أقل، فهذا يجعله أنسب للمستخدم. تصبح هذه المشكلة خطيرةً عندما لا يكتفي المصممون بقدر معين من التأثيرات المتحركة. حصلت تأثيرات فلاديسلاف الحركية منذ عام 2016 على أكثر من 500 إعجاب و8000 استعراض، مما يعكس مدى انجرار المصممين وراء التأثيرات الحركية وعدّها غايةً وليست وسيلة. إن إدراكك لمشكلة ميل المصممين نحو التصاميم غير المباشرة وتفضيلها على التمثيلات المباشرة، وتجنُّبك الوقوع في شراك هذه التأثيرات الحركية، سيوفران عليك المال ويبعدان عنك المشاكل. تذكر أن المستخدم يزور موقعك لهدفٍ ما، ويجب أن تحاول إيصاله إلى هدفه بسرعة دون الإطالة عليه أو إجباره على إجراء جولة طويلة في الموقع. المثال الجيد: التأثيرات المتحركة في موقع Stripe Checkout عندما استخدام التأثيرات المتحركة لأداء غرض معين، فإن النتائج تكون رائعة، كما هو الحال في التأثيرات المتحركة لموقع Stripe Checkout التي تظهر عندما يتلقى المستخدم رمز تأكيد: يستخدم موقع Stripe هذه التأثيرات الحركية ليجعل العملية تبدو أسرع مما هي عليه: فهي تُطلِع المستخدم على التحديثات الجارية (مثل "تم الإرسال")، حتى قبل أن تصله الرسالة، وهذا ما يخفف من عبء الانتظار على المستخدم، ويؤكد له أن الرسالة في طريقها إليه. تقترح راشيل نابورس خبيرة التأثيرات المتحركة على الويب من منظمة W3C (رابطة شبكة الويب العالمية) خمسةَ مبادئ تجب مراعاتها عند تصميم التأثيرات المتحركة: ادرس التأثيرات المتحركة: فكر بتأنٍّ بكل حركة قبل إنشائها. يحتاج الأمر إلى أكثر من 12 مبدأ: المبادئ الاثنا عشر التي وضعتها ديزني مناسبة للتأثيرات والرسوم المتحركة في الأفلام، لكن ليس بالضرورة أن تنطبق على مواقع الويب أو التطبيقات. الفائدة والضرورة ثم الجمال: بالنسبة لتجربة المستخدم؛ يجب أن يكون التأثير عمليًا وضروريًا في المقام الأول، ثم تأتي الناحية الجمالية. ضاعف السرعة: التأثيرات المتحركة الجيدة لا تُلاحَظ، مما يعني أنها يجب أن تمرّ بسرعة. أضف خيار الإيقاف: بالنسبة للتأثيرات المتحركة الكبيرة مثل التحرك الظاهري parallax، يُنصح بإضافة خيار إيقاف التأثير. الدروس المستفادة لتجربة أفضل احرص على وجود غاية من كل تأثير متحرك، فكثرة التأثيرات المتحركة تؤثر سلبًا على تجربة المستخدم، وتذكر أن الناحية الجمالية يجب أن تقترن بتصميم عملي يحقق غايات معينة. الخلاصة مشاهدة أمثلة التصميم السيئ ممتعة ومفيدة في الوقت نفسه، وفيما يلي أهم الدروس المستخلَصة من هذه الأمثلة: حدد ما يحتاج إليه المستخدم ثم فكر بطريقة إيصال هذه المعلومات. استخدم العناصر المرئية بدلًا من المكتوبة إذا كانت المعلومات التي ترغب بإيصالها كثيرة. ضع مسميات واضحة للروابط، فالمستخدم لا يفضل الروابط المبهمة. تجنب إضافة أي نوع من الاحتكاك ضمن إجراءات المستخدم، إلا إذا كنت تريد ثنيه عن الإجراء. اختبر التصاميم الذكية وتأكد من كونها ملائمة. استخدام التأثيرات المتحركة له حدود، وإذا أكثرت منها تفقد تأثيرها. في المرة التالية التي تصادف فيها تصميمًا سيئًا أو مزعجًا، فكر للحظة بالسبب الذي جعله يبدو سيئًا، وابحث عن أمثلة لتصاميم فعلت الأمر نفسه بصورة صحيحة، لتتمكن من استخلاص دروس مفيدة من هذه التجارب. ترجمة -وبتصرّف- للمقال Bad Design vs. Good Design: 5 Examples We can Learn From لصاحبه Teo Yu Siang. اقرأ أيضًا اختيار الألوان المناسبة لواجهة المستخدم التأثير النفسي للألوان في تصميم الويب لماذا نحتاج إلى الشعار؟ العلامة التجارية من منظور نفسي مدخل إلى تجربة المستخدم (User Experience - UX)
    1 نقطة
  14. يُعَدّ بروتوكول نقل النصوص الفائقة Hypertext Transfer Protocol الذي ذكرناه في مقال علاقة جافاسكريبت بتطور الإنترنت والمتصفحات آليةً تُطلب البيانات وتوفَّر من خلالها على الشبكة العالمية، كما سننظر فيه بالتفصيل ونشرح الطريقة التي تستخدِمه بها جافاسكربت المتصفحات. البروتوكول إذا كتبت eloquentjavascript.net/18_http.html في شريط العنوان لمتصفحك، فسيبحث المتصفح أولًا عن عنوان الخادم المرتبط بـ eloquentjavascript.net ويحاول فتح اتصال TCP معه على المنفَذ 80 الذي هو المنفَذ الافتراضي لحركة مرور HTTP، فإذا كان الخادم موجودًا ويقبل الاتصال فقد يرسل المتصفح شيئًا مثل هذا: GET /18_http.html HTTP/1.1 Host: eloquentjavascript.net User-Agent: Your browser's name ثم يستجيب الخادم من خلال نفس قناة الاتصال: HTTP/1.1 200 OK Content-Length: 65585 Content-Type: text/html Last-Modified: Mon, 08 Jan 2018 10:29:45 GMT <!doctype html> ... the rest of the document تكون أول كلمة هي التابع الخاص بالطلب، إذ تعني GET أننا نريد الحصول على مصدر بعينه، كما هناك توابع أخرى مثل DELETE لحذف المصدر وPUT لإنشائه أو استبداله وPOST لإرسال معلومات إليه. لاحظ أنّ الخادم ليس عليه تنفيذ جميع الطلبات التي تأتيه، فإذا ذهبتَ إلى موقع ما وطلبت حذف صفحته الرئيسية فسيرفض الخادم؛ أما الجزء الذي يلي اسم التابع، فيكون مسار المورد الذي يُطبق الطلب عليه، حيث يكون ملفًا على الخادم في أبسط حالاته، لكن البروتوكول لا يشترط كونه ملفًا فقط، بل قد يكون أي شيء يمكن نقله كما لو كان ملفًا، كما تولِّد العديد من الخوادم الاستجابات التي تنتجها لحظيًا، فإذا فتحت https://github.com/marijnh مثلًا، فسيبحث الخادم في قاعدة بياناته عن مستخدِم باسم marijnh، فإذا وجده فسيولِّد صفحة مستخدِم له. يذكر أول سطر في الطلب بعد مسار المورد الـ HTTP/1.1 للإشارة إلى نسخة بروتوكول HTTP الذي يستخدِمه، كما تستخدِم مواقع كثيرة النسخة الثانية من HTTP عمليًا، إذ تدعم المفاهيم نفسها التي تدعمها النسخة الأولى 1.1، لكنها أعقد منها لتكون أسرع، كما ستبدِّل المتصفحات إلى البروتوكول المناسب تلقائيًا أثناء التحدث مع الخادم المعطى، وسيكون خرج الطلب هو نفسه بغض النظر عن النسخة المستخدَمة، لكننا سنركز على النسخة 1.1 بما أنها أبسط وأسهل في التعديل عليها. ستبدأ استجابة الخادم بالنسخة أيضًا تليها بحالة الاستجابة مثل شيفرة حالة من ثلاثة أرقام أولًا، ثم مثل سلسلة نصية مقروءة من قِبَل المستخدِم. HTTP/1.1 200 OK تبدأ رموز الحالة بـ 2 لتوضح نجاح الطلب؛ أما الطلبات التي تبدأ بـ 4 فتعني أنّ ثمة شيء خطأ في الطلب، ولعل أشهر رمز حالة HTTP هنا هي 404، والتي تعني أن المصدر غير موجود أو لا يمكن العثور عليه؛ أما الرموز التي تبدأ بالرقم 5، فتعني حدوث خطأ على الخادم ولا تتعلق المشكلة بالطلب نفسه، وقد يُتبع أول سطر من الطلب أو الاستجابة بعدد من الترويسات، وهي أسطر في صورة name: value توضِّح معلومات إضافية عن الطلب أو الاستجابة، وهي جزء من المثال الذي يوضح الاستجابة: Content-Length: 65585 Content-Type: text/html Last-Modified: Thu, 04 Jan 2018 14:05:30 GMT يخبرنا هذا بحجم مستند الاستجابة ونوعه، وهو مستند HTML في هذه الحالة حجمه 65585 بايت، كما يخبرنا متى كانت آخر مرة عُدِّل فيها. يملك كل من العميل والخادم في أغلب الترويسات حرية إدراجها في الطلب أو الاستجابة، لكن بعض الترويسات يكون إدراجها إلزاميًا مثل ترويسة HOST التي تحدد اسم المضيف hostname، إذ يجب إدراجها في الطلب لأن الخادم قد يخدِّم عدة أسماء مضيفين على عنوان IP واحد، فبدون الترويسة لن يعرف أيّ واحد فيها يقصده العميل الذي يحاول التواصل معه، وقد تدرِج الطلبات أو الاستجابات سطرًا فارغًا بعد اسم المضيف متبوعًا بمتن body يحتوي على البيانات المرسلة، كما لا ترسل طلبات GET وDELETE أيّ بيانات، على عكس طلبات PUT وPOST، وبالمثل فقد لا تحتاج بعض أنواع الاستجابات إلى متن مثل استجابات الخطأ error responses. المتصفحات وHTTP رأينا في المثال السابق أنّ المتصفح سينشئ الطلب حين نكتب الرابط URL في شريط عنوانه، فإذا أشارت صفحة HTML الناتجة إلى ملفات أخرى مثل صور أو ملفات جافاسكربت، فسيجلب المتصفح هذه الملفات أيضًا، ومن المعتاد للمواقع متوسطة التعقيد إدراج من 10 إلى 200 مصدر مع الصفحة، كما سترسل المتصفحات عدة طلبات GET في الوقت نفسه بدلًا من انتظار الاستجابة الواحدة، ثم إرسال طلب آخر من أجل تحميل الصفحة بسرعة، وقد تحتوي صفحات HTML على استمارات forms تسمح للمستخدِم بملء بيانات وإرسالها إلى الخادم، وفيما يلي مثال عن استمارة: <form method="GET" action="example/message.html"> <p>Name: <input type="text" name="name"></p> <p>Message:<br><textarea name="message"></textarea></p> <p><button type="submit">Send</button></p> </form> تصف الشيفرة السابقة استمارةً لها حقلين أحدهما صغير يطلب الاسم والآخر أكبر لكتابة رسالة فيه، وتُرسَل الاستمارة عند الضغط على زر إرسال Send، أي يحزَّم محتوى حقلها في طلب HTTP وينتقل المتصفح إلى نتيجة ذلك الطلب. تضاف المعلومات التي في الاستمارة إلى نهاية رابط action على أساس سلسلة استعلام نصية إذا كانت سمة العنصر method الخاص بالاستمارة <form> هي GET -أو إذا أُهملت-، وقد ينشئ المتصفح طلبًا إلى هذا الرابط: GET /example/message.html?name=Jean&message=Yes%3F HTTP/1.1 تحدِّد علامة الاستفهام نهاية جزء المسار من الرابط وبداية الاستعلام، كما تُتبع بأزواج من الأسماء والقيم تتوافق مع سمة name في عناصر حقول الاستمارة ومحتوى تلك العناصر على الترتيب، ويُستخدَم محرف الإضافة ampersand أي & لفصل تلك الأزواج، في حين تكون الرسالة الفعلية المرمَّزة في الرابط هي "Yes?‎"، لكن ستُستبدَل شيفرة غريبة بعلامة الاستفهام، كما يجب تهريب بعض المحارف في سلاسل الاستعلامات النصية، فعلامة الاستفهام الممثلة بـ ‎%3F هي أحد تلك المحارف، وسنجد أن هناك شبه قاعدة غير مكتوبة تقول أنّ كل صيغة تحتاج إلى طريقتها الخاصة في تهريب المحارف، وهذه التي بين أيدينا تُسمى ترميز الروابط التشعبية URL encoding، حيث تستخدِم علامة النسبة المئوية ويليها رقمين ست-عشريين يرمِّزان شيفرة المحرف، وفي حالتنا تكون 3F -التي هي 63 في النظام العشري- شيفرة محرف علامة الاستفهام، وتوفِّر جافاسكربت الدالتين encodeURIComponent وdecodeURIComponent من أجل ترميز تلك الصيغة وفك ترميزها أيضًا. console.log(encodeURIComponent("Yes?")); // → Yes%3F console.log(decodeURIComponent("Yes%3F")); // → Yes? إذا غيرنا السمة method لاستمارة HTML في المثال الذي رأيناه إلى POST، فسيستخدِم طلب HTTP الذي أنشئ لإرسال الاستمارة التابع POST ويضع سلسلة الاستعلام النصية في متن الطلب بدلًا من إضافتها إلى الرابط. POST /example/message.html HTTP/1.1 Content-length: 24 Content-type: application/x-www-form-urlencoded name=Jean&message=Yes%3F يجب استخدام طلبات GET للطلبات التي تطلب معلومات فقط وليس لها تأثيرات جانبية؛ أما الطلبات التي تغيِّر شيئًا في الخادم مثل إنشاء حساب جديد أو نشر رسالة، فيجب التعبير عنها بتوابع أخرى مثل POST، كما تدرك برامج العميل مثل المتصفحات أنها يجب ألا تنشئ طلبات POST عشوائيًا وإنما تنشئ طلبات GET أولًا ضمنيًا لجلب المصدر التي تظن أنّ المستخدِم سيحتاجه قريبًا، كما سنعود إلى كيفية التفاعل مع الاستمارات من جافاسكربت لاحقًا في هذا المقال. واجهة Fetch تُسمى الواجهة التي تستطيع جافاسكربت الخاصة بالمتصفح إنشاء طلبات HTTP من خلالها باسم fetch، وبما أنها جديدة نسبيًا فستستخدم الوعود promises وهو الأمر النادر بالنسبة لواجهات المتصفحات. fetch("example/data.txt").then(response => { console.log(response.status); // → 200 console.log(response.headers.get("Content-Type")); // → text/plain }); يعيد استدعاء fetch وعدًا يُحل إلى كائن Response حاملًا معلومات عن استجابة الخادم مثل شيفرة حالته وترويساته، وتغلَّف الترويسات في كائن شبيه بالخارطة ‎Map-like الذي يهمل حالة الأحرف في مفاتيحه -أي أسماء الترويسات- لأنه لا يفترض أن تكون أسماء الترويسات حساسةً لحالة الأحرف. هذا يعني أن كلا من الآتي: ‎headers.get("Content-Type")‎0. ‎headers.get("content-TYPE")‎. سيُعيدان القيمة نفسها. لاحظ أن الوعد الذي تعيده fetch يُحل بنجاح حتى لو استجاب الخادم برمز خطأ، وقد يُرفض إذا كان ثمة خطأ في الشبكة أو لم يوجد الخادم الذي أرسل إليه الطلب، كما يجب أن يكون أول وسيط لواجهة fetch هو الرابط الذي يراد طلبه، فإذا لم يبدأ الرابط باسم البروتوكول - http‎:‎ مثلًا- فسيعامَل نسبيًا، أي يفسَّر وفق المستند الحالي، وإذا بدأ بشرطة مائلة / فسيستبدل المسار الحالي الذي يكون جزء المسار الذي يلي اسم الخادم، أما إذا لم يبدأ بالشرطة المائلة، فسيوضع جزء المسار الحالي إلى آخر محرف شرطة مائلة -مع الشرطة نفسها- أمام الرابط النسبي. يُستخدَم التابع text للحصول على المحتوى الفعلي للاستجابة، ويعيد هذا التابع وعدًا لأن الوعد الأولي يُحَل عند استقبال ترويسات الاستجابة، ولأنّ قراءة متن الاستجابة قد تستغرق وقتًا أطول. fetch("example/data.txt") .then(resp => resp.text()) .then(text => console.log(text)); // → This is the content of data.txt يعيد التابع json -وهو تابع شبيه بالسابق- وعدًا يُحل إلى القيمة التي تحصل عليها حين تحلل المتن مثل JSON أو يُرفض إذا لم يكن JSON صالحًا، كما تستخدِم واجهة fetch التابع GET افتراضيًا لإنشاء طلبها ولا تدرِج متن الطلب، كما يمكنك إعدادها لغير ذلك بتمرير كائن له خيارات إضافية على أساس وسيط ثاني، فهذا الطلب مثلًا يحاول حذف example/data.txt: fetch("example/data.txt", {method: "DELETE"}).then(resp => { console.log(resp.status); // → 405 }); يعني رمز الحالة 405 أنّ "الطلب غير مسموح به" وهو أسلوب خادم HTTP ليقول "لا أستطيع فعل هذا"، كما يمكن إضافة الخيار body لإضافة متن الطلب، كما يُستخدَم الخيار headers لضبط الترويسات، فهذا الطلب مثلًا يضمِّن الترويسة Range التي تخبر الخادم بإعادة جزء من الاستجابة فقط. fetch("example/data.txt", {headers: {Range: "bytes=8-19"}}) .then(resp => resp.text()) .then(console.log); // → المحتوى سيضيف المتصفح بعض ترويسات الطلب تلقائيًا مثل Host وتلك المطلوبة كي يعرف الخادم حجم المتن، لكن ستكون إضافة ترويساتك الخاصة مفيدةً إذا أردنا إضافة أشياء مثل معلومات التوثيق، أو لنخبر الخادم بصيغة الملف التي نريد استقبالها. صندوق اختبارات HTTP لا شك أنّ إنشاء طلبات HTTP في سكربتات صفحة الويب سيرفع علامات استفهام حول الأمان، فالشخص الذي يتحكم بالسكربت قد لا تكون لديه دوافع الشخص نفسها التي يشغلها على حاسوبه، فإذا زرنا الموقع themafia.org مثلًا، فلا نريد لسكربتاته أن تكون قادرةً على إنشاء طلب إلى mybank.com باستخدام معلومات التعريف من متصفحنا مع تعليمات بتحويل جميع أموالنا إلى حساب عشوائي، لهذا تحمينا المتصفحات من خلال عدم السماح للسكربتات بإنشاء طلبات HTTP إلى نطاقات أخرى (أسماء نطاقات مثل themafia.org وmybank.com)، وتُعَدّ هذه مشكلةً مؤرقةً عند بناء أنظمة تريد الوصول إلى عدة نطاقات من أجل أسباب مشروعة ومنطقية، ولحسن الحظ تستطيع الخوادم إدراج ترويسة لهذا الغرض في استجابتها لإخبار المتصفح صراحةً أن هذا الطلب يمكن أن يأتي من نطاق آخر: Access-Control-Allow-Origin: * تقدير HTTP هناك عدة طرق مختلفة لنمذجة التواصل بين برامج جافاسكربت العاملة في المتصفح -جانب العميل- والبرنامج الذي على الخادم -أي جانب الخادم-، وإحدى أكثر تلك الطرق استخدامًا هي استدعاءات الإجراءات البعيدة remote procedure calls، إذ يتبع التواصل في هذا النموذج أنماط استدعاءات الدوال العادية عدا أنّ الدالة تعمل فعليًا على حاسوب آخر، حيث يتطلب استدعاؤها إنشاء طلب إلى الخادم الذي يتضمن اسم الدالة والوسائط، كما تحتوي استجابة ذلك الطلب على القيمة المعادة. عند التفكير في شأن استدعاءات الإجراء البعيد، لا يكون HTTP أكثر من أنه وسيلة تواصل، وستكتب على الأرجح طبقةً مجردةً تخفيه كليًا، كما يوجد هناك منظور آخر نبني فيه التواصل حول مفهوم الموارد وتوابع HTTP، فبدلًا من addUser المستدعى استدعاءًا بعيدًا، فإننا سنستخدم طلب PUT إلى ‎/users/larry، وبدلًا من ترميز خصائص ذلك المستخدِم في وسائط دالة، فإنك تعرِّف صيغة مستند JSON من أجل تمثيل المستخدِم أو تستخدم صيغةً موجودةً من قبل لذلك. كما يُجلَب المورد بإنشاء طلب GET إلى رابط المورد -‎/users/larry مثلًا- والذي يُعيد المستند الممثل للمورد، إذ يسهل هذا المنظور استخدام بعض المزايا التي يوفرها HTTP مثل دعم تخزين الموارد -أي إنشاء نسخة مؤقتة عند العميل من أجل تسريع الوصول-، كما توفر المفاهيم المستخدَمة في HTTP مجموعة مبادئ مفيدة في تصميم واجهة الخوادم الخاصة بك بما أنها جيدة التصميم. الأمان وHTTP تمر البيانات المتنقلة عبر الانترنت في طرق محفوفة بالمخاطر، إذ تمر خلال أي طريق نتواجد فيه سواء كان نقطة اتصال في مقهى أو شبكات تتحكم بها شركات أو دول بغية الحصول على وجهتها، وقد تُعتَرض وتفتَّش في أي نقطة في طريقها وقد تُعدَّل أيضًا، وهنا لا يكفي بروتوكول HTTP العادي بما أنّ بعض البيانات سرية مثل كلمات مرور بريدك أو يجب عليها الوصول إلى وجهتها دون تعديل مثل رقم الحساب الذي تحوِّل المال إليه من خلال موقع البنك الذي تتعامل معه. نستخدِم هنا بروتوكولًا أحدث هو HTTPS الذي نجده في الروابط التي تبدأ بـ https://‎، إذ يغلِّف حركة مرور HTTP بطريقة تصعب قراءتها والتعديل عليها، ويؤكد الطرف العميل قبل إرسال البيانات أنّ الخادم الذي يطلبها هو نفسه وليس منتحلًا له من خلال التأكد من شهادة مشفرة مصدَرة من جهة توثيق يعتمدها المتصفح، ثم تشفَّر جميع البيانات بطريقة تمنع استراق النظر إليها أو التعديل عليها، وعليه يمنع HTTPS أيّ جهة خارجية من انتحال الموقع الذي تريد التواصل معه ومن اختلاس النظر أو التجسس على تواصلكما، لكنه ليس مثاليًا بالطبع فقد وقعت عدة حوادث فشل فيها HTTPS بسبب شهادات مزورة أو مسروقة وبسبب برامج مخترَقة أو معطوبة، لكنه أكثر أمانًا من HTTP العادي. حقول الاستمارات صُمِّمت الاستمارات ابتداءً للويب قبل مجيء جافاسكربت من أجل السماح لمواقع الويب إرسال البيانات التي يدخلها المستخدِم في هيئة طلب HTTP، حيث يفترِض هذا التصميم أنّ التفاعل مع الخادم سيحدث دائمًا من خلال الانتقال إلى صفحة جديدة، غير أنّ عناصرها جزء من نموذج كائن مستند DOM مثل بقية الصفحة، كما تدعم عناصر DOM التي تمثِّل حقول الاستمارة عددًا من الخصائص والأحداث التي ليست موجودةً في العناصر الأخرى، حيث تمكننا من فحص حقول الإدخال تلك والتحكم فيها ببرامج جافاسكربت وأمور أخرى مثل إضافة وظيفة جديدة إلى استمارة أو استخدام استمارات وحقول على أساس وحدات بناء في تطبيق جافاسكربت. تتكون استمارة الويب من عدد من حقول الإدخال تُجمع في وسم <form>، وتسمح HTML بعدة تنسيقات من الحقول بدايةً من أزرار الاختيار checkboxes إلى القوائم المنسدلة وحقول إدخال النصوص، ولن نناقش كل أنواع الحقول في هذا الكتاب لكن سنبدأ بنظرة عامة عليها. تستخدِم أكثر أنواع الحقول وسم <input> وتُستخدَم السمة type الخاصة بهذا الوسم من أجل اختيار تنسيق الحقل، وفيما يلي أكثر أنواع <input> المستخدَمة: table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } أجل تمثيل المستخدِم أو تستخدم صيغةً موجودةً من قبل لذل text حقل نصي ذو سطر واحد password حقل نصي مثل text لكن يخفي النص الذي يُكتَب فيه checkbox مفتاح تشغيل/إغلاق radio جزء من حقل اختيار من متعدد file يسمح للمستخدِم اختيار ملف من حاسوبه يمكن وضع حقول الاستمارات في أي مكان في الصفحة ولا يشترط ظهورها في وسوم <form> وحدها، كما لا يمكن إرسال تلك الحقول التي تكون مستقلة بذاتها وخارج استمارة، فالاستمارات وحدها هي التي ترسَل، لكن على أيّ حال لا نحتاج إلى إرسال محتوى حقولنا بالطريقة التقليدية عند استخدام جافاسكربت في الاستجابة للمدخلات. <p><input type="text" value="abc"> (text)</p> <p><input type="password" value="abc"> (password)</p> <p><input type="checkbox" checked> (checkbox)</p> <p><input type="radio" value="A" name="choice"> <input type="radio" value="B" name="choice" checked> <input type="radio" value="C" name="choice"> (radio)</p> <p><input type="file"> (file)</p> تختلف واجهة جافاسكربت لمثل تلك العناصر باختلاف نوع العنصر. تمتلك الحقول النصية متعددة الأسطر وسمًا خاصًا بها هو <textarea>، وذلك بسبب غرابة استخدام سمة لتحديد قيمة ابتدائية لسطر متعدد، كما يجب إغلاق هذا الوسم بأسلوب الإغلاق المعتاد في HTML بإضافة ‎</textarea>‎، حيث يُستخدَم النص الموجود بين هذين الوسمين على أساس نص ابتدائي بدلًا من سمة value. <textarea> one two three </textarea> أخيرًا، يُستخدَم الوسم <select> لإنشاء حقل يسمح للمستخدِم بالاختيار من عدد من الخيارات المعرَّفة مسبقًا، ويُطلَق الحدث "change" كلما تغيرت قيمة حقل من حقول الاستمارة. <select> <option>Pancakes</option> <option>Pudding</option> <option>Ice cream</option> </select> التركيز Focus تستطيع حقول الاستمارات الحصول على نشاط لوحة المفاتيح عند النقر أو تفعيلها بأيّ شكل آخر على عكس أغلب عناصر مستندات HTML، بحيث تصبح هي العنصر المفعَّل الحالي ومستقبل إدخال لوحة المفاتيح، وعلى ذلك نستطيع الكتابة في الحقل النصي حين يكون مركَّزًا فقط؛ أما الحقول الأخرى فتختلف في استجابتها لأحداث لوحة المفاتيح، إذ تحاول قائمة <select> مثلًا الانتقال إلى الخيار الذي يحتوي النص الذي كتبه المستخدِم وتستجيب لمفاتيح الأسهم عبر تحريك اختيارها لأعلى وأسفل. يمكننا التحكم في التركيز باستخدام جافاسكربت من خلال التابعَين focus وblur، حيث ينقل focus التركيز إلى عنصر DOM الذي استدعي عليه؛ أما الثاني فسيزيل التركيز منه، وتتوافق القيمة التي في document.activeElement مع العنصر المركَّز حاليًا. <input type="text"> <script> document.querySelector("input").focus(); console.log(document.activeElement.tagName); // → INPUT document.querySelector("input").blur(); console.log(document.activeElement.tagName); // → BODY </script> يُتوقَّع من المستخدِم في بعض الصفحات أن يرغب في التفاعل مع أحد حقول الاستمارة فورًا، ويمكن استخدام جافاسكربت لتركيز ذلك الحقل عند تحميل المستند، لكن توفر HTML السمة autofocus أيضًاـ، والتي تعطينا التأثير نفسه وتخبر المتصفح بما نحاول فعله، وهذا يعطي المتصفح خيار تعطيل السلوك إذا كان غير مناسب كما في حالة محاولة المستخدِم تركيز حقل آخر أو عنصر آخر، وقد تعارفت المتصفحات على تمكين المستخدم من نقل التركيز خلال المستند بمجرد ضغط زر جدول أو tab على لوحة المفاتيح، ونستطيع هنا التحكم في الترتيب الذي تستقبل به العناصر ذلك التركيز باستخدام السمة tabindex، وسيجعل المثال التالي التركيز يقفز من المدخلات النصية إلى زر OK بدلًا من المرور على رابط المساعدة أولًا: <input type="text" tabindex=1> <a href=".">(help)</a> <button onclick="console.log('ok')" tabindex=2>OK</button> السلوك الافتراضي لأغلب عناصر HTML أنها لا يمكن تركيزها، غير أنك تستطيع إضافة سمة tabindex إلى أي عنصر لجعله قابلًا للتركيز؛ أما إذا جعلنا قيمتها ‎-1 فسيتم تخطي العنصر حتى لو كان قابلًا للتركيز. الحقول المعطلة يمكن تعطيل أيّ حقل من حقول الاستمارات من خلال السمة disabled الخاصة بها، وهي سمة يمكن تحديدها دون قيمة، إذ يعطِّل وجودها الحقل مباشرةً، ولا يمكن تركيز الحقول المعطَّلة أو تغييرها، كما ستظهرها المتصفحات بلون رمادي وباهت. <button>أنا مركَّز الآن</button> <button disabled>خرجت!‏</button> إذا عالج البرنامج إجراءً سببه زر أو تحكم آخر قد يحتاج إلى تواصل مع الخادم وسيستغرق وقتًا، فمن الأفضل تعطيل التحكم حتى انتهاء ذلك الإجراء، وهكذا لن يتكرر الإجراء إذا نفذ صبر المستخدِم ونقر عليه مرةً أخرى. الاستمارات على أساس عنصر كامل إذا احتوى الحقل على العنصر <form> فسيحتوي عنصر DOM الخاص به على الخاصية form التي تربطه إلى عنصر DOM الخاص بالاستمارة، ويحتوي العنصر <form> بدوره على خاصية تسمى elements تحوي تجميعةً شبيهةً بالمصفوفة من الحقول التي بداخلها. كذلك تحدِّد السمة name الموجودة في حقل الاستمارة الطريقة التي تعرَّف بها قيمتها عند إرسال الاستمارة، كما يمكن استخدامها على أساس اسم خاصية عند الوصول إلى الخاصية elements الخاصة بالاستمارة والتي تتصرف على أساس كائن شبيه بالمصفوفة -يمكن الوصول إليه بعدد-، وخريطة map -يمكن الوصول إليها باسم-. <form action="example/submit.html"> Name: <input type="text" name="name"><br> Password: <input type="password" name="password"><br> <button type="submit">Log in</button> </form> <script> let form = document.querySelector("form"); console.log(form.elements[1].type); // → password console.log(form.elements.password.type); // → password console.log(form.elements.name.form == form); // → true </script> يرسل الزر الذي فيه سمة type الخاصة بـ submit الاستمارة عند الضغط عليه، كما سنحصل على التأثير نفسه عند الضغط على زر الإدخال Enter وإذا كان حقل الاستمارة مركَّزًا، ويعني إرسال الاستمارة غالبًا أنّ المتصفح ينتقل إلى الصفحة التي تحددها سمة action الخاصة بالاستمارة مستخدِمًا أحد الطلبَين GET أو POST، لكن يُطلَق الحدث "submit" قبل حدوث ذلك، وتستطيع معالجة هذا الحدث بجافاسكربت، ونمنع ذلك السلوك الافتراضي من خلال استدعاء preventDefault على كائن الحدث. <form action="example/submit.html"> Value: <input type="text" name="value"> <button type="submit">Save</button> </form> <script> let form = document.querySelector("form"); form.addEventListener("submit", event => { console.log("Saving value", form.elements.value.value); event.preventDefault(); }); </script> يملك اعتراض أحداث "submit" في جافاسكربت فوائدً عديدةً، حيث نستطيع كتابة شيفرة للتحقق من أنّ القيم التي أدخلها المستخدِم منطقية، ونخرِج رسائل خطأ له إذا وجدنا أخطاءً في تلك القيم بدلًا من إرسال الاستمارة، أو نستطيع تعطيل الطريقة المعتادة في إرسال الاستمارة بالكامل كما في المثال، ونجعل البرنامج يعالِج المدخلات باستخدام fetch لإرسالها إلى خادم دون إعادة تحميل الصفحة. الحقول النصية تشترك الحقول التي أنشئت بواسطة الوسوم <textarea> أو <input> مع النوع text وpassword واجهةً مشتركةً، كما تحتوي عناصر DOM الخاصة بها على الخاصية value التي تحمل محتواها الحالي على أساس قيمة نصية، وإذا عيّنا تلك الخاصية إلى سلسلة نصية أخرى فسيتغيَّر محتوى الحقل. تعطينا كذلك الخصائص selectionStart وselectionEnd الخاصة بالحقول النصية معلومات عن المؤشر والجزء المحدَّد في النص، فإذا لم يكن ثمة نص محدَّد، فستحمل هاتان الخاصيتان العدد نفسه والذي يشير إلى موضع المؤشر، حيث يشير 0 مثلًا إلى بداية النص، ويشير 10 إلى أنّ المؤشر بعد المحرف العاشر؛ أما إذا حدِّد جزء من الحقل، فستختلف هاتين الخاصيتين لتعطينا بداية النص المحدَّد ونهايته، كما يمكن الكتابة فيهما مثل value تمامًا. لنفترض أنك تكتب مقالةً عن الفرعون "خع سخموي Khasekhemwy" لكن لا نستطيع تهجئة اسمه، لذا تساعدنا هنا الشيفرة التالية التي توصل وسم <textarea> بمعالج حدث يُدخل النص Khasekhemwy لك إذا ضغطت على مفتاح F2. <textarea></textarea> <script> let textarea = document.querySelector("textarea"); textarea.addEventListener("keydown", event => { // The key code for F2 happens to be 113 if (event.keyCode == 113) { replaceSelection(textarea, "Khasekhemwy"); event.preventDefault(); } }); function replaceSelection(field, word) { let from = field.selectionStart, to = field.selectionEnd; field.value = field.value.slice(0, from) + word + field.value.slice(to); // ضع المؤشر بعد الكلمة field.selectionStart = from + word.length; field.selectionEnd = from + word.length; } </script> تستبدِل الدالة replaceSelection الكلمة الصعبة المعطاة والتي نريدها بالجزء المحدَّد حاليًا من محتوى الحقل النصي، ثم تنقل المؤشر بعد تلك الكلمة كي يستطيع المستخدِم متابعة الكتابة، ولا يُطلَق الحدث "change" للحقل النصي في كل مرة يُكتب شيء ما، بل عندما يزال التركز عن أحد الحقول بعد تغيُّر محتواه فقط، ويجب علينا تسجيل معالج للحدث "input" من أجل الاستجابة الفورية للتغيرات الحادثة في الحقل النصي، حيث يُطلَق في كل مرة يكتب المستخدِم فيها محرفًا أو يحذف نصًا أو يعدِّل في محتوى الحقل، ويظهر المثال التالي حقلًا نصيًا وعدّادًا يعرض الطول الحالي للنص في الحقل: <input type="text"> length: <span id="length">0</span> <script> let text = document.querySelector("input"); let output = document.querySelector("#length"); text.addEventListener("input", () => { output.textContent = text.value.length; }); </script> أزرار الاختيار وأزرار الانتقاء يُعَدّ حقل زر الانتقاء checkbox مخيّر فهو يخير بين اختياره من المجموعة أو لا ويمكن استخراج قيمته أو تغييرها من خلال الخاصية checked الخاصة به والتي تحمل قيمةً بوليانيةً. <label> <input type="checkbox" id="purple"> Make this page purple </label> <script> let checkbox = document.querySelector("#purple"); checkbox.addEventListener("change", () => { document.body.style.background = checkbox.checked ? "mediumpurple" : ""; }); </script> يربط وسم العنوان <label> جزءًا من المستند بحقل إدخال، فإذا نقرنا في أيّ مكان على العنوان فسنفعِّل الحقل ونغير قيمته إذا كان زر اختيار أو زر انتقاء. يشبه زر انتقاء أو زر الانتقاء زر الاختيار لكنه يرتبط ضمنيًا بأزرار انتقاء أخرى لها سمة name نفسها كي يُنتقى واحد منها فقط، وقد سميت بهذا الاسم لأنها تشبه أزرار اختيار محطات الراديو في أجهزة المذياع في شكلها ووظيفتها، حيث إذا ضغطنا على أحد تلك الأزرار فإن بقية الأزرار تقفز إلى الخارج ولا تعمل سوى المحطة صاحبة الزر المنضغط. Color: <label> <input type="radio" name="color" value="orange"> Orange </label> <label> <input type="radio" name="color" value="lightgreen"> Green </label> <label> <input type="radio" name="color" value="lightblue"> Blue </label> <script> let buttons = document.querySelectorAll("[name=color]"); for (let button of Array.from(buttons)) { button.addEventListener("change", () => { document.body.style.background = button.value; }); } </script> تُستخدَم الأقواس المربعة في استعلام CSS المعطى إلى querySelectorAll لمطابقة السمات، وهي تختار العناصر التي تكون سمة name الخاصة بها هي "color". حقول التحديد تسمح حقول التحديد select fields للمستخدِم الاختيار من بين مجموعة خيارات كما في حالة أزرار الانتقاء، لكن مظهر الوسم <select> يختلف بحسب المتصفح على عكس أزرار الانتقاء التي نتحكم في مظهرها، كما تحتوي هذه الحقول على متغير يشبه قائمة أزرار الاختيار أكثر من أزرار الانتقاء، فإذا أعطينا وسم <select> السمة multiple، فسيسمح للمستخدِم اختيار أيّ عدد من الخيارات التي يريدها بدلًا من خيار واحد، وسيكون مظهر هذا مختلفًا باختلاف المتصفح، لكنه سيختلف عن حقل التحديد العادي الذي يكون تحكمًا منسدلًا drop-down control لا يعرض الخيارات إلا عند فتحه. يحتوي كل وسم <option> على قيمة يمكن تعريفها باستخدام السمة value، وإذا لم تعطى تلك القيمة، فسيُعَدّ النص الذي بداخل الخيار هو قيمته، كما تعكس خاصية value الخاصة بالعنصر <select> الخيار المحدَّد حاليًا، لكن لن تكون هذه الخاصية ذات شأن في حالة الحقل multiple بما أنها ستعطي قيمة خيار واحد فقط من الخيارات المحدَّدة الحالية، ويمكن الوصول إلى وسوم <option> الخاصة بحقل <select> على أساس كائن شبيه بالمصفوفة من خلال خاصية الحقل options، كما يحتوي كل خيار على خاصية تسمى selected توضِّح هل الخيار محدَّد حاليًا أم لا، ويمكن كتابة الخاصية لتحديد خيار ما أو إلغاء تحديده. يستخرِج المثال التالي القيم المحدَّدة من حقل التحديد multiple ويستخدِمها لتركيب عدد ثنائي من بِتّات منفصلة، لتحديد عدة خيارات اضغط باستمرار على زر control -أو command على ماك Mac-. <select multiple> <option value="1">0001</option> <option value="2">0010</option> <option value="4">0100</option> <option value="8">1000</option> </select> = <span id="output">0</span> <script> let select = document.querySelector("select"); let output = document.querySelector("#output"); select.addEventListener("change", () => { let number = 0; for (let option of Array.from(select.options)) { if (option.selected) { number += Number(option.value); } } output.textContent = number; }); </script> حقول الملفات صُمِّمت حقول الملفات ابتداءً على أساس طريقة لرفع الملفات من حاسوب المستخدِم من خلال استمارة؛ أما في المتصفحات الحديثة، فهي توفر طريقةً لقراءة تلك الملفات، ولكن من خلال برامج جافاسكربت، إذ يتصرف الحقل على أساس حارس لبوابة، بحيث لا تستطيع السكربت البدء بقراءة ملفات خاصة بالمستخدِم من حاسوبه، لكن إذا اختار المستخدِم ملفًا في حقل كهذا، فسيفسِّر المتصفح ذلك الإجراء على أنه سماح للسكربت بقراءة الملف، ويبدو حقل الملف على أساس زر عليه عنوان مثل "اختر الملف" أو "تصفح الملف" مع معلومات عن الملف المختار تظهر إلى جانبه. <input type="file"> <script> let input = document.querySelector("input"); input.addEventListener("change", () => { if (input.files.length > 0) { let file = input.files[0]; console.log("You chose", file.name); if (file.type) console.log("It has type", file.type); } }); </script> تحتوي الخاصية files لعنصر حقل الملف على الملفات المختارة في الحقل، وهي كائن شبيه بالمصفوفة وليست مصفوفةً حقيقيةً، كما تكون فارغةً في البداية، والسبب في عدم وجود خاصية مستقلة باسم file، هو دعم الحقول لسمة multiple التي تجعل من الممكن تحديد عدة ملفات في الوقت نفسه، كما تحتوي الكائنات في كائن files على خاصيات مثل name لاسم الملف وsize لحجمه مقدَّرًا بالبايت -الذي هو وحدة قياس تخزينية تتكون من 8 بِتّات-، كما تحتوي على الخاصية type التي تمثِّل نوع وسائط media الملف التي قد تكون نصًا عاديًا text/plain أو صورةً image/jpeg، لكن ليس لتلك الكائنات خاصيةً يكون فيها محتوى الملف، وبما أنّ قراءة الملف من القرص ستستغرق وقتًا، فيجب أن تكون الواجهة غير تزامنية لتجنب تجميد أو تعليق المستند أثناء قراءته. <input type="file" multiple> <script> let input = document.querySelector("input"); input.addEventListener("change", () => { for (let file of Array.from(input.files)) { let reader = new FileReader(); reader.addEventListener("load", () => { console.log("File", file.name, "starts with", reader.result.slice(0, 20)); }); reader.readAsText(file); } }); </script> تتم عملية قراءة الملف من خلال إنشاء كائن FileReader الذي يسجِّل معالج الحدث "load" له، ويستدعي التابع readAsText الخاص به ليعطيه الملف الذي نريد قراءته، كما ستحتوي الخاصية result الخاصة بالقارئ على محتويات الملف بمجرد انتهاء التحميل، ويطلق الكائن FileReader أيضًا حدث خطأ "error" عند فشل قراءة الملف لأيّ سبب، إذ سيؤول كائن الخطأ نفسه في خاصية error الخاصة بالقارئ، ورغم تصميم تلك الواجهة قبل أن تصبح الوعود promises جزءًا من جافاسكربت، إلا أنك تستطيع تغليفها بوعد كما يلي: function readFileText(file) { return new Promise((resolve, reject) => { let reader = new FileReader(); reader.addEventListener( "load", () => resolve(reader.result)); reader.addEventListener( "error", () => reject(reader.error)); reader.readAsText(file); }); } تخزين البيانات في جانب العميل ستكون صفحات HTML البسيطة التي فيها قليل من جافاسكربت صيغةً رائعةً من أجل التطبيقات المصغَّرة، وهي برامج مساعِدة صغيرة تؤتمت مهامًا أساسية عبر توصيل بعض حقول الاستمارات بمعالِجات الأحداث، حيث يمكنك فعل أيّ شيء بدءًا من التحويل بين وحدات القياس المختلفة إلى حساب كلمات المرور من كلمة مرور رئيسية واسم موقع. لكن لا نستطيع استخدام رابطات جافاسكربت إذا احتاج مثل ذلك التطبيق إلى تذكر أمر بين جلساته sessions، ذلك أنّ هذه الرابطات تُحذَف عند كل إغلاق للصفحة، غير أنه يمكن إعداد خادم وتوصيله بالانترنت وجعل التطبيق يخزن هناك، وسنرى كيفية فعل ذلك في مقال لاحق، لكن المقام يقصر هنا عن شرحه لتعقيده، وعلى أيّ حال، من المفيد أحيانًا إبقاء البيانات في المتصفح، كما يمكن استخدام الكائن localStorage لتخزين البيانات بطريقة تبقيها موجودةً حتى مع إعادة تحميل الصفحات، حيث يسمح لك ذلك الكائن بتصنيف قيم السلاسل النصية تحت أسماء. localStorage.setItem("username", "marijn"); console.log(localStorage.getItem("username")); // → marijn localStorage.removeItem("username"); تبقى القيمة الموجودة في localStorage إلى أن يُكتَب فوقها، كما تُحذَف باستخدام removeItem أو بمحو المستخدِم لبياناته المحلية، وتحصل المواقع التي هي من نطاقات مختلفة على أقسام تخزين مختلفة، ويعني هذا نظريًا عدم إمكانية قراءة وتعديل البيانات المخزَّنة في localStorage من قِبل موقع ما إلا بسكربتات من نفس الموقع، كما تضع المتصفحات حدًا لحجم البيانات التي يمكن للمتصفح أن يخزِّنها في localStorage، وذلك لمنع تلك الخاصية من ملء أقراص المستخدِمين الصلبة ببيانات عديمة الفائدة واستخدام مساحات كبيرة بها، وتنفِّذ الشيفرة التالية تطبيقًا لكتابة الملاحظات، حيث يحتفظ بمجموعة من الملاحظات المسماة ويسمح للمستخدِم بتعديل الملاحظات وإنشاء الجديدا منها. Notes: <select></select> <button>Add</button><br> <textarea style="width: 100%"></textarea> <script> let list = document.querySelector("select"); let note = document.querySelector("textarea"); let state; function setState(newState) { list.textContent = ""; for (let name of Object.keys(newState.notes)) { let option = document.createElement("option"); option.textContent = name; if (newState.selected == name) option.selected = true; list.appendChild(option); } note.value = newState.notes[newState.selected]; localStorage.setItem("Notes", JSON.stringify(newState)); state = newState; } setState(JSON.parse(localStorage.getItem("Notes")) || { notes: {"shopping list": "Carrots\nRaisins"}, selected: "shopping list" }); list.addEventListener("change", () => { setState({notes: state.notes, selected: list.value}); }); note.addEventListener("change", () => { setState({ notes: Object.assign({}, state.notes, {[state.selected]: note.value}), selected: state.selected }); }); document.querySelector("button") .addEventListener("click", () => { let name = prompt("Note name"); if (name) setState({ notes: Object.assign({}, state.notes, {[name]: ""}), selected: name }); }); </script> تحصل هذه السكربت على حالتها من القيمة "Notes" المخزَّنة في localStorage، وإذا لم تكن موجودة، فستنشئ حالة مثال وليس فيها إلا قائمة تسوق، ونحصل على القيمة nullعند محاولة قراءة حقل غير موجود من localStorage، كما أنّ تمرير null إلى JSON.parse سيجعله يحلل السلسلة النصية "null" ويُعيد null، وعلى ذلك يمكن استخدام العامِل || لتوفير قيمة افتراضية في مثل هذه المواقف؛ أما التابع setState فيتأكد أنّ DOM يُظهِر حالةً معطاةً ويخزِّن الحالة الجديدة في localStorage، كما تستدعي معالِجات الأحداث هذه الدالة للانتقال إلى حالة جديدة. كان استخدام Object.assign في المثال السابق من أجل إنشاء كائن جديد يكون نسخة من state.notes القديم، لكن مع خاصية واحدة مضافة أو مكتوب فوقها، وتأخذ Object.assign وسيطها الأول وتضيف جميع الخاصيات من أيّ وسائط آخرين إليه، فإذا أعطيناها كائنًا فارغًا فسنجعلها تملأ كائنًا جديدًا، وتُستخدَم الأقواس المربعة في الوسيط الثالث لإنشاء خاصية اسمها يكون مبنيًا على قيمة ديناميكية، كما لدينا كائن آخر يشبه localStorage اسمه sessionStorage، والاختلاف بين الاثنين هو أنّ محتوى الأخير يُنسى بنهاية كل جلسة، حيث يحدث عند إغلاق المتصفح وذلك بالنسبة لأغلب المتصفحات. خاتمة ناقشنا في هذا المقال كيفية عمل بروتوكول HTTP، وقلنا أنّ العميل يرسل الطلب الذي يحتوي على تابع يكون GET في الغالب ومسار يحدِّد المصدر، ثم يقرر الخادم بعدها ماذا يفعل بذلك الطلب ويستجيب بشيفرة حالة ومتن استجابة، وقد يحتوي كل من الطلب والاستجابة على ترويسات توفر لنا معلومات إضافية، كما تُسمى الواجهة التي تستطيع جافاسكربت التي في المتصفح إنشاء طلبات HTTP منها باسم fetch، إذ يبدو إنشاء الطلب كما يلي: fetch("/18_http.html").then(r => r.text()).then(text => { console.log(`The page starts with ${text.slice(0, 15)}`); }); كما عرفنا من قبل، تنشئ المتصفحات طلبات GET لجلب الموارد المطلوبة لعرض صفحة ويب، وقد تحتوي الصفحة على استمارات تسمح للمستخدِم بإدخال المعلومات التي سترسلها بعدها في صورة طلب إلى الصفحة الجديدة عند إرسال الاستمارة، كما تستطيع HTML تمثيل عدة أنواع من حقول الاستمارات مثل الحقول النصية وأزرار الاختيار وحقول الاختيار من متعدد ومختارات الملفات file pickers، إذ يمكن فحص مثل تلك الحقول وتعديلها باستخدام جافاسكربت، وهي تطلق الحدث "change" عند تعديلها والحدث "input" عند كتابة نص فيها، كما تستقبل أحداث لوحة المفاتيح عند انتقال نشاط لوحة المفاتيح إليها. عرفنا أيضًا أنّ الخاصيات مثل value -المستخدَمة في النصوص وحقول التحديد- أو checked -المستخدَمة في أزرار الاختيار وأزرار الانتقاء-، تُستخدَم من أجل قراءة أو تعيين محتوى الحقل، كما رأينا أن الحدث "submit" يُطلَق عند إرسال الاستمارة عليها، ويستطيع معالِج جافاسكربت استدعاء preventDefault على ذلك الحدث من أجل تعطيل السلوك الافتراضي للمتصفح، كما قد توجد عناصر حقول الاستمارات خارج وسم الاستمارة نفسه. إذا اختار المستخدم ملفًا من نظام ملفاته المحلي في حقل مختار الملفات، فيمكن استخدام الواجهة FileReader من أجل الوصول إلى محتويات ذلك الملف من برنامج جافاسكربت، كما يُستخدم الكائنان localStorage وsessionStorage لحفظ المعلومات حتى مع إعادة التحميل، إذ يحتفظ الكائن الأول بالبيانات احتفاظًا دائمًا أو إلى أن يقرر المستخدِم محوها؛ أما الثاني فيحفظها إلى حين إغلاق المتصفح. تدريبات التفاوض على المحتوى أحد الأمور التي يفعلها بروتوكول HTTP هو التفاوض على المحتوى content negotiation، حيث تُستخدَم ترويسة الطلب Accept لإخبار الخادم بنوع المستند الذي يريده العميل، وتتجاهل العديد من الخوادم هذه الترويسة، لكن إذا عرف الخادم عدة طرق لترميز أحد الموارد، فسينظر حينها في هذه الترويسة ويرسل نوع الملف الذي يريده العميل. لقد هُيء الرابط https://eloquentjavascript.net/author ليستجيب للنصوص المجردة أو HTML أو JSON وفقًا لما يطلبه العميل، وتعرَّف تلك الصيغ بأنواع وسائط قياسية هي text/plain وtext/html وapplication/json. أرسِل طلبات لجلب هذه الصيغ الثلاث من ذلك الرابط، واستخدم الخاصية headers في كائن الخيارات الممرَّر إلى fetch لضبط الترويسة Accept إلى نوع الوسائط media المفضل، ثم حاول طلب نوع الوسائط application/rainbows+unicorns، وانظر إلى شيفرة الحالة التي تنتجها. تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى codepen. // شيفرتك هنا. إرشادات الحل ابن شيفرتك على أمثلة fetch الموجودة في المقال أعلاه. إذا طلبت أنواع وسائط كاذبة فستحصل على استجابة بالرمز 406، بعنى "غير مقبول" أو Not acceptable، وهو الرمز الذي يجب أن يعيده الخادم إذا لم يستطع تحقيق الترويسة Accept. طاولة عمل جافاسكربت ابن واجهةً تسمح للناس بكتابة شيفرات جافاسكربت ويشغلونها، وضَع زرًا بجانب حقل <textarea>، بحيث إذا ضُغط عليه يمرِّر الباني Function الذي رأيناه في مقال الوحدات Modules في جافاسكريبت لتغليف النص في دالة واستدعائها، ثم حوِّل القيمة التي تعيدها الدالة أو أيّ خطأ ترفعه إلى سلسلة نصية واعرضها تحت الحقل النصي. تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى codepen. <textarea id="code">return "hi";</textarea> <button id="button">Run</button> <pre id="output"></pre> <script> // شيفرتك هنا. </script> إرشادات الحل استخدم document.querySelector أو document.getElementById من أجل الوصول إلى العناصر المعرَّفة في HTML لديك، وبالنسبة للحدَثين "click" و"mousedown"، فسيستطيع معالِج الحدث الحصول على الخاصية value للحقل النصي واستدعاء Function عليها، وتأكد من تغليف كل من الاستدعاء إلى Function والاستدعاء إلى نتيجته في كتلة try كي تستطيع التقاط الاستثناءات التي ترفعها، كما أننا لا نعرف في حالتنا هذه نوع الاستثناء الذي لدينا، لذا يجب التقاط كل شيء. يمكن استخدام الخاصية textContent الخاصة بعنصر الخرج لملئها برسالة نصية؛ أما في حالة الرغبة في الحفاظ على المحتوى القديم، فأنشئ عقدةً نصيةً جديدةً باستخدام document.createTextNode وألحقها بالعنصر، وتذكّر إضافة محرف سطر جديد إلى النهاية كي لا يظهر كل الخرج على سطر واحد. لعبة حياة كونويل تُعَدّ لعبة حياة كونويل Conway game of life محاكاةً بسيطةً تنشئ حياةً صناعيةً على شبكة، بحيث تكون كل خلية في تلك الشبكة إما حيةً أو ميتةً، وتطبق القواعد التالية في كل جيل -منعطف-: تموت أيّ خلية حية لها أكثر من ثلاثة جيران أحياء أو أقل من اثنين. تبقى أيّ خلية حية على قيد الحياة حتى الجيل التالي إذا كان لها جاران أحياء أو ثلاثة. تعود أيّ خلية ميتة إلى الحياة إذا كان لها ثلاثة جيران أحياء حصرًا. يُعرَّف الجار على أنه أيّ خلية مجاورة بما في ذلك الخلايا المجاورة له قطريًا. لاحظ أنّ تلك القواعد تطبَّق على كامل الشبكة مرةً واحدةً وليس على مربع واحد في المرة، وهذا يعني أنّ عدد الجيران مبني على الموقف الابتدائي، ولا تؤثر التغيرات الحادثة في الخلايا المجاورة في هذا الجيل على الحالة الجديدة للخلية. نفِّذ هذه اللعبة باستخدام أيّ هيكل بيانات تجده مناسبًا، واستخدم Math.random لتوليد عشوائي لأماكن الخلايا في الشبكة في أول مرة، واعرضها على هيئة شبكة من حقول أزرار الاختيار مع زر بجانبها للانتقال إلى الجيل التالي، كما يجب عند حساب الجيل التالي إدراج التغييرات الحادثة عند اختيار المستخدم لزر الاختيار أو إلغاء الاختيار له. تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى codepen. <div id="grid"></div> <button id="next">Next generation</button> <script> // شيفرتك هنا. </script> إرشادات الحل حاول النظر إلى حساب الجيل على أنه دالة محضة تأخذ شبكةً واحدةً وتنتج شبكةً جديدةً تمثِّل الدورة التالية. يمكن تمثيل المصفوفة matrix بالطريقة المبينة في مقال الحياة السرية للكائنات في جافاسكريبت، حيث تَعد الجيران الأحياء بحلقتين تكراريتين متشعبتَين تكرران على إحداثيات متجاورة في كلا البعدين. لا تَعد الخلايا التي تكون خارج الحقل وتجاهل الخلية التي تكون في المركز عند عَد خلايا مجاورة لها. يمكن ضمان حدوث التغييرات على أزرار الاختيار في الجيل التالي بطريقتين؛ إما أن يلاحظ معالج حدث تلك التغييرات ويحدِّث الشبكة الحالية لتوافق ذلك، أو نولد شبكةً جديدةً من القيم التي في أزرار الاختيار قبل حساب الدورة التالية. إذا اخترت أسلوب معالج الحدث فربما يجب عليك إلحاق سمات تعرِّف الموضع الموافق لكل زر اختيار كي يسهل معرفة الخلية التي يجب تغييرها؛ أما لرسم شبكة من أزرار الاختيار، فيمكن استخدام العنصر <table> أو وضعها جميعًا في العنصر نفسه ووضع عناصر <br> -أي فواصل الأسطر- بين الصفوف. ترجمة -بتصرف- للفصل الثامن عشر من كتاب Elequent Javascript لصاحبه Marijn Haverbeke. اقرأ أيضًا المقال السابق: الرسم على لوحة في جافاسكربت رموز الإجابة فيHTTP
    1 نقطة
×
×
  • أضف...