3124 words
16 minutes
Chrome Extensions

إيه هي إضافات كروم دي أصلاً؟#

بص يا سيدي، إضافات كروم دي زي برامج صغيرة كده بتركبها على متصفح كروم بتاعك عشان تضيفله حاجات جديدة. يعني مثلاً:

  • تخلي الإعلانات المزعجة دي تختفي خالص
  • تترجملك الصفحات اللي بتتفرج عليها لو مش فاهم حاجة
  • تحفظلك الباسوردات بتاعتك عشان متنساهاش
  • تغير شكل المواقع اللي بتدخل عليها
  • تعملك شغل روتيني كل شوية بدالك

طب إيه هستفيده من المقالة دي؟#

هنتعلم كل حاجة من الألف للياء:

  • هنعرف إزاي الإضافة متركبة من جوه
  • هنفهم الملف الأساسي اللي اسمه manifest.json ده
  • هنتكلم عن السكريبتات اللي بتشتغل في الباك جراوند
  • وكمان السكريبتات اللي بتشتغل جوه صفحات الويب نفسها
  • هنشوف إزاي الحتت دي بتكلم بعضها
  • هنعمل صفحة للإعدادات بتاعة الإضافة
  • هنخزن الداتا بتاعتنا فين وإزاي
  • هنعمل شكل حلو للإضافة
  • هنشوف إزاي نختبرها ونصلح الأخطاء
  • وأخيراً هنرفعها على المتجر بتاع جوجل

الـstructure الخاص بالإضافة#

الإضافة بتاعتنا دي متركبة من شوية ملفات، تعالى نشوف أهمهم:

extension-folder/ ├── manifest.json # البطاقة الشخصية للإضافة ├── background.js # سكريبت الخلفية ├── content.js # سكريبت المحتوى ├── popup/ │ ├── popup.html # واجهة الإضافة │ ├── popup.js # منطق واجهة الإضافة │ └── popup.css # تنسيق واجهة الإضافة ├── options/ │ ├── options.html # صفحة الإعدادات │ ├── options.js # منطق الإعدادات │ └── options.css # تنسيق الإعدادات └── images/ ├── icon16.png # أيقونة صغيرة ├── icon48.png # أيقونة متوسطة └── icon128.png # أيقونة كبيرة
  • manifest.json: ده زي البطاقة الشخصية بتاعة الإضافة، بيقول كل حاجة عنها يعني:

    • اسمها وإصدارها وبتعمل إيه
    • المسموح ليها تعمله وإيه لأ
    • السكريبتات بتاعتها هتشتغل فين وإمتى
    • إيه الملفات اللي المتصفح هيعرف يوصلها
  • ملفات الجافاسكريبت:

    • background.js: ده بيشتغل ورا الكواليس طول ما المتصفح شغال
    • content.js: ده بيشتغل جوه صفحات النت نفسها
    • popup.js: ده بيتحكم في الشكل اللي بيظهر لما تدوس على الإضافة
    • options.js: ده بيتحكم في صفحة الإعدادات بتاعتك
  • ملفات HTML و CSS:

    • popup.html/css: دول مسؤولين عن شكل وتنسيق الويندو اللي بتطلعلك لما تدوس على أيقونة الإضافة
    • options.html/css: دول مسؤولين عن شكل وتنسيق صفحة الإعدادات
  • الصور والأيقونات:

    • أيقونات بمقاسات مختلفة (16×16, 48×48, 128×128)
    • أي صور تانية الإضافة محتاجاها

الملف الأساسي manifest.json ؟#

ده أهم ملف في الموضوع كله، لازم يكون موجود في أول الفولدر بتاع الإضافة. تعالى نشوف مثال عليه:

{ "manifest_version": 3, // لازم يكون 3 في كروم الجديد لأن الإصدار 2 مش مدعوم بعد 2023 "name": "Awesome Extension", // اسم الإضافة - لازم يكون واضح ومعبر وأقل من 45 حرف "version": "1.0", // رقم الإصدار - يفضل يتبع Semantic Versioning (MAJOR.MINOR.PATCH) "description": "This is a cool extension you'll love", // وصف مختصر - لازم يكون واضح وأقل من 132 حرف "icons": { // الأيقونات - لازم تكون PNG وبأحجام محددة "16": "images/icon16.png", // للتابات والفافيكون "48": "images/icon48.png", // للقائمة وصفحة الإضافات "128": "images/icon128.png" // لمتجر كروم وصفحة التثبيت }, "action": { // إعدادات زر شريط الأدوات (حل محل browser_action في MV2) "default_popup": "popup.html", // الصفحة اللي بتظهر - مينفعش يكون حجمها كبير "default_title": "Click here!" // تلميح الماوس - يفضل يكون قصير ومفيد }, "background": { // سكريبت الخلفية - بيتم تحميله وإيقافه حسب الحاجة "service_worker": "background.js" // لازم يكون Service Worker في MV3 }, "content_scripts": [ // سكريبتات بتتحقن في صفحات الويب { "matches": ["https://www.facebook.com/*"], // نمط URL - يدعم wildcards "js": ["content.js"], // ملفات JS - بتتنفذ بالترتيب "css": ["style.css"], // ملفات CSS - بتتطبق فوراً "run_at": "document_end" // توقيت التنفيذ - في options: document_start/end/idle } ], "permissions": [ // الصلاحيات - اطلب أقل عدد ممكن "activeTab", // للوصول للتاب الحالي فقط "storage", // للتخزين المحلي والسحابي "scripting", // لتنفيذ JS في التابات "tabs", // للتحكم في كل التابات "notifications" // لإظهار إشعارات سطح المكتب ], "host_permissions": [ // أذونات المواقع - حددها بدقة "https://www.google.com/" // يدعم wildcards و match patterns ], "options_page": "options.html", // صفحة الإعدادات - اختيارية "web_accessible_resources": [ // موارد متاحة لصفحات الويب { "resources": ["style.css", "images/"], // الملفات المسموحة - يدعم wildcards "matches": ["https://www.facebook.com/*"] // المواقع المصرح لها } ] }

كل سطر في الملف ده ليه معنى:

  • “manifest_version”: ده رقم الإصدار بتاع الملف نفسه، لازم يبقى 3 دلوقتي (مينفعش نستخدم 2 لأن جوجل بطلت تدعمه)
  • “name” و “version”: دول اسم الإضافة ورقم الإصدار بتاعها (لازم يكونوا واضحين ومعبرين)
  • “description”: ده وصف قصير للإضافة (مهم يكون دقيق وبيشرح الفايدة الرئيسية)
  • “icons”: دي الأيقونات بتاعة الإضافة بأحجام مختلفة (16 للتابات، 48 للقائمة، 128 للمتجر)
  • “action”: ده بيحدد إيه اللي هيحصل لما تدوس على أيقونة الإضافة (زي فتح بوب أب أو تنفيذ أمر معين)
  • “background”: ده السكريبت اللي هيشتغل في الخلفية طول الوقت (بيتحكم في الإضافة وبيعمل المهام المستمرة)
  • “content_scripts”: دول السكريبتات اللي هتشتغل جوه صفحات الويب نفسها (بتقدر تعدل في المحتوى وتضيف ميزات)
  • “permissions”: دي الصلاحيات اللي الإضافة محتاجاها (زي التخزين والتنبيهات، اطلب بس اللي محتاجه فعلاً)
  • “host_permissions”: دي المواقع اللي الإضافة هتقدر تشتغل عليها (حددها بدقة عشان الأمان)
  • “options_page”: دي صفحة الإعدادات بتاعة الإضافة (خليها سهلة وواضحة للمستخدم)
  • “web_accessible_resources”: دي الملفات اللي ممكن الويب يوصلها من الإضافة (زي الصور والستايلات، حددها كويس)
  • “commands”: ده بيحدد الشورت كتس بتاعة الإضافة (اختار مفاتيح سهل المستخدم يفتكرها)
  • “default_locale”: ده بيحدد اللغة الأساسية واللغات المدعومة (مهم للإضافات العالمية)
  • “offline_enabled”: ده بيحدد إذا كانت الإضافة بتشتغل من غير نت ولا لأ
  • “update_url”: ده لينك التحديثات التلقائية (لو هتنشر الإضافة برا متجر كروم)

سكريبتات الباك جراوند (Service Workers)#

تخيل معايا إن سكريبتات الباك جراوند دي زي المخ بتاع الإضافة - بتفضل شغالة في الخلفية وبتتحكم في كل حاجة. يعني هي اللي:

  • بتحفظ كل البيانات وبتفتكر إعدادات المستخدم
  • بتعرف لما حد يثبت الإضافة أو يعمل تحديث وتتصرف
  • بتتواصل مع كل أجزاء الإضافة وتنسق بينهم
  • بتعمل شغل مهم في الباك جراوند زي إنها تبعت تنبيهات

خلينا نشوف مثال عملي يوضح الكلام ده:

// background.js // لما حد يثبت الإضافة أو يحدثها chrome.runtime.onInstalled.addListener(({ reason, previousVersion }) => { if (reason === 'install') { console.log("أهلاً بيك! الإضافة اتثبتت لأول مرة"); // نظبط الإعدادات الأساسية chrome.storage.sync.set({ theme: 'light', notifications: true }); } else if (reason === 'update') { console.log(`تم التحديث من نسخة ${previousVersion}`); // نعمل اللي محتاجينه للنسخة الجديدة } }); // لما المستخدم يدوس على أيقونة الإضافة chrome.action.onClicked.addListener(async (tab) => { try { // نتأكد إن التاب مسموح نشتغل عليه if (!tab.url.startsWith('chrome://')) { // نضيف واجهة المستخدم في الصفحة await chrome.scripting.executeScript({ target: { tabId: tab.id }, function: injectUI, }); // نغير شكل الأيقونة عشان نعرف إنها شغالة await chrome.action.setIcon({ tabId: tab.id, path: { 16: 'icons/active-16.png', 48: 'icons/active-48.png' } }); } } catch (error) { console.error("في مشكلة حصلت:", error); // نقول للمستخدم إن في مشكلة chrome.notifications.create({ type: 'basic', iconUrl: 'icons/error.png', title: 'عندنا مشكلة', message: error.message }); } }); // الواجهة اللي هتظهر للمستخدم function injectUI() { const dialog = document.createElement('dialog'); dialog.innerHTML = ` <h2>أهلاً بيك!</h2> <button onclick="this.parentElement.close()">قفل</button> `; document.body.appendChild(dialog); dialog.showModal(); // نستنى لو المستخدم عمل حاجة في الصفحة window.addEventListener('message', (event) => { if (event.source === window && event.data.type === 'EXTENSION_ACTION') { // نتصرف بناءً على اللي المستخدم عمله } }); } // نعمل مزامنة كل شوية chrome.alarms.create('sync', { periodInMinutes: 30 }); chrome.alarms.onAlarm.addListener((alarm) => { if (alarm.name === 'sync') { // نزامن البيانات syncData(); } }); // نراقب لو حد غير في الإعدادات chrome.storage.onChanged.addListener((changes, namespace) => { for (let [key, { oldValue, newValue }] of Object.entries(changes)) { console.log(`الإعداد ${key} اتغير من ${oldValue} لـ ${newValue}`); } });

وفي كمان حاجات تانية ممكن نعملها زي:

  • التحكم في طلبات النت اللي بتطلع من الإضافة
  • نشغل عمليات تقيلة في الباك جراوند
  • نربط الإضافة بخدمات على النت
  • نخلي الإضافة خفيفة وسريعة
  • نتعامل مع أي مشاكل بشكل محترف
  • نخلي الإضافة تشتغل حتى من غير نت

سكريبتات المحتوى (Content Scripts)#

سكريبتات المحتوى (Content Scripts) هي جزء أساسي في تطوير إضافات المتصفح. هذه السكريبتات تعمل داخل سياق صفحات الويب مباشرةً، مما يتيح لك التفاعل مع محتوى الصفحة وتعديله.

التكوين في manifest.json#

لتفعيل سكريبت المحتوى، يجب أولاً تعريفه في ملف manifest.json:

{ "content_scripts": [ { "matches": ["*://*.example.com/*"], "js": ["content.js"], "css": ["styles.css"], "run_at": "document_idle" } ] }

مثال أساسي لسكريبت محتوى#

هذا مثال بسيط لسكريبت محتوى يغير لون خلفية الصفحة:

console.log("تم تفعيل سكريبت المحتوى"); document.body.style.backgroundColor = "lightblue"; // تغيير اللون كل 5 ثواني setInterval(() => { const colors = ["lightblue", "lightgreen", "lightpink", "lightyellow"]; const randomColor = colors[Math.floor(Math.random() * colors.length)]; document.body.style.backgroundColor = randomColor; }, 5000);

هديك نبذة عن إمكانياته#

1. الوصول للصفحة على طول#

  • تقدر تستخدم كل حاجة في الصفحة زي querySelector و getElementById
  • تتعامل مع العناصر في الصفحة - تضيف وتشيل وتعدل براحتك
  • تقدر تسمع لما حد يدوس أو يسكرول في الصفحة
  • تغير الشكل والألوان بتاعة أي حاجة في الصفحة

2. بيشتغل لوحده#

  • السكريبت بيشتغل على طول لما الصفحة تفتح من غير ما تعمل حاجة
  • تقدر تتحكم امتى يشتغل من خلال run_at:
    • document_start: قبل ما الصفحة تحمل
    • document_end: بعد ما الصفحة تحمل
    • document_idle: بعد ما كل حاجة تخلص
  • ممكن تخليه يشتغل في صفحات معينة بس

3. تحديد المواقع اللي هيشتغل عليها#

  • تحط المواقع اللي عايزها في matches جوة manifest.json
  • في طرق كتير زي:
    • *://*.example.com/*: كل الصفحات في موقع معين
    • http://*/*: كل المواقع العادية
    • https://*/*: كل المواقع المؤمنة
  • ممكن تستثني مواقع معينة بـ exclude_matches
  • تقدر تحط قواعد معقدة عشان تحدد الصفحات بالظبط
  • استخدم chrome.runtime.sendMessage عشان تبعت رسايل للباك جراوند

متنساش تحط الصلاحيات المطلوبة في manifest.json

مثال متقدم: مراقبة تغييرات الصفحة#

const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.type === 'childList') { // معالجة العناصر الجديدة mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { // تطبيق التغييرات على العناصر الجديدة customizeElement(node); } }); } } }); observer.observe(document.body, { childList: true, subtree: true }); function customizeElement(element) { // تطبيق التخصيصات المطلوبة element.style.border = '1px solid blue'; }

نظام الرسايل (Message Passing)#

هو إيه نظام الرسايل ده؟#

ببساطة، ده نظام بيخلي أجزاء الإضافة بتاعتك تتكلم مع بعض. زي ما لو عندك جروب واتساب، كل حد فيه بيبعت رسايل للتاني 😄

ليه محتاجين نظام الرسايل؟#

عشان:

  • نخلي الكونتنت سكريبت يكلم الباك جراوند
  • نبعت داتا من البوب أب للباك جراوند
  • نعمل أي اتصال بين أي جزئين في الإضافة

أنواع الرسايل#

النوع الأول: رسالة واحدة (One-Time Message)#

ده زي لما تبعت رسالة لصاحبك وتستنى رده. بص على المثال ده:

// في الكونتنت سكريبت // في الكونتنت سكريبت - بنبعت رسالة للباك جراوند عشان نجيب داتا chrome.runtime.sendMessage({ type: "getData", request: "get statistics" }, (response) => { console.log("Got response:", response); }); // في الباك جراوند - بنستقبل الرسالة ونرد عليها chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { // بنشوف لو الرسالة نوعها getData if (message.type === "getData") { // بنرد بأوبجكت فيه الداتا اللي عايزينها sendResponse({ status: "success", data: { visits: 100, views: 500 } }); } return true; // لازم نرجع true عشان نقدر نبعت الرد بعدين });

النوع التاني: محادثة مستمرة (Connection)#

ده زي ما تفتح شات مع صاحبك وتفضلوا تتكلموا. شوف كده:

// في الكونتنت سكريبت - بنفتح اتصال مع الباك جراوند const port = chrome.runtime.connect({name: "chat"}); // بنستمع للرسايل اللي جاية من الباك جراوند port.onMessage.addListener((message) => { console.log("New message received:", message); }); // بنبعت رسالة للباك جراوند port.postMessage({ type: "greeting", content: "How are you basha?" }); // في الباك جراوند - بنستقبل الاتصال من الكونتنت سكريبت chrome.runtime.onConnect.addListener((port) => { // بنستمع للرسايل اللي جاية من الكونتنت سكريبت port.onMessage.addListener((message) => { // لو الرسالة نوعها تحية، بنرد عليها if (message.type === "greeting") { port.postMessage({ type: "reply", content: "El7amdullah, enta 3amel eh?" }); } }); });

طريقة حلوة للتعامل مع الرسايل (ده ليك مخصوص عشان وصلت هنا 😎)#

class MessageManager { constructor() { // بنعمل اوبجكت فيه كل الفانكشنز اللي هتتنفذ لما نستقبل رسايل this.handlers = { 'getData': this.getData, 'saveData': this.saveData, 'doSomething': this.doSomething }; this.listenToMessages(); } listenToMessages() { // بنسمع للرسايل اللي جاية ونشوف هننفذ ايه chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (this.handlers[message.type]) { this.handlers[message.type](message).then(sendResponse); return true; } }); } async getData(message) { // هنا بنجيب الداتا اللي محتاجينها return { status: "success", data: {} }; } async saveData(message) { // هنا بنحفظ الداتا اللي جاية return { status: "saved successfully" }; } async doSomething(message) { // هنا بنعمل اي حاجة تانية محتاجينها return { status: "completed" }; } }

نصايح مهمة (عشان متقعش في مشاكل 😅)#

  1. تأكد من الرسايل وصلت

    // طريقة آمنة لإرسال رسالة function sendMessageSafely(message) { // بنعمل وعد جديد عشان نتأكد ان الرسالة وصلت return new Promise((resolve, reject) => { // بنبعت الرسالة للباك جراوند chrome.runtime.sendMessage(message, response => { // لو في مشكلة حصلت if (chrome.runtime.lastError) { console.error("Error occurred:", chrome.runtime.lastError); reject(chrome.runtime.lastError); } else { // لو كله تمام نرجع الرد resolve(response); } }); }); }
  2. متبعتش داتا كبيرة

    • لو عندك داتا كبيرة، قسمها على أجزاء
    • أو استخدم chrome.storage بدل الرسايل
  3. اعمل تايم آوت للرسايل

    async function sendMessageWithTimeout(message, timeout = 5000) { // بنعمل وعد بيبعت الرسالة بطريقة آمنة const messagePromise = sendMessageSafely(message); // بنعمل وعد تاني بيرفض لو عدى الوقت المحدد const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject("Timeout exceeded"), timeout); }); // بنشوف مين هيخلص الأول - الرسالة ولا الوقت return Promise.race([messagePromise, timeoutPromise]); }

مثال عملي وبسيط#

تخيل إنك عايز تعمل إضافة بتعد عدد الصور في أي صفحة وتحفظ الإحصائيات:

// في الكونتنت سكريبت // بنجيب عدد الصور في الصفحة const imageCount = document.querySelectorAll('img').length; // بنبعت رسالة للباك جراوند عشان يحفظ الاحصائيات chrome.runtime.sendMessage({ type: "save_stats", data: { url: window.location.href, images_count: imageCount, timestamp: new Date().toISOString() } }); // في الباك جراوند const statistics = {}; // بنستمع للرسايل اللي جاية chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.type === "save_stats") { const url = message.data.url; statistics[url] = message.data; sendResponse({ status: "saved" }); } return true; });
  • الرسايل نوعين: رسالة واحدة أو محادثة مستمرة
  • لازم تتأكد إن الرسايل وصلت
  • ممكن تعمل نظام متقدم للرسايل حسب احتياجك
  • خلي بالك من حجم الداتا اللي بتبعتها

    صفحة الإعدادات (Options Page)#

1. تعريف الصفحة في manifest.json#

أولاً، نحتاج لتعريف صفحة الإعدادات في ملف manifest.json:

{ "options_ui": { "page": "options.html", "open_in_tab": true, // فتح في تاب جديد "chrome_style": true // استخدام ستايل كروم } }

هذا التعريف يخبر متصفح كروم:

  • أي صفحة سيتم عرضها في الإعدادات
  • كيف سيتم عرضها (في تاب أو نافذة منبثقة)
  • هل ستستخدم ستايل كروم الافتراضي

2. HTML#

نقوم بإنشاء صفحة الإعدادات الأساسية:

<!DOCTYPE html> <html dir="rtl" lang="ar"> <head> <meta charset="UTF-8"> <title>إعدادات الإضافة</title> <link rel="stylesheet" href="options.css"> </head> <body> <div class="container"> <!-- قسم إعدادات المظهر --> <section class="settings-section"> <h2>المظهر</h2> <!-- إعداد لون الخلفية --> <div class="setting-item"> <label>لون الخلفية: <input type="color" id="backgroundColor"> </label> </div> <!-- إعداد حجم الخط --> <div class="setting-item"> <label>حجم الخط: <select id="fontSize"> <option value="small">صغير</option> <option value="medium">متوسط</option> <option value="large">كبير</option> </select> </label> </div> </section> <!-- قسم الإعدادات المتقدمة --> <section class="settings-section"> <h2>إعدادات متقدمة</h2> <!-- إعداد الإشعارات --> <div class="setting-item"> <label> <input type="checkbox" id="notifications"> تفعيل الإشعارات </label> </div> <!-- إعداد فترة التحديث --> <div class="setting-item"> <label>فترة التحديث (دقائق): <input type="number" id="updateInterval" min="1" max="60"> </label> </div> </section> <!-- أزرار التحكم --> <div class="actions"> <button id="save" class="primary">حفظ الإعدادات</button> <button id="reset" class="secondary">إعادة الضبط</button> </div> </div> <script src="options.js"></script> </body> </html>

3. CSS#

نضيف التنسيقات في ملف options.css:

/* تنسيقات عامة */ body { font-family: system-ui, -apple-system, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; } /* تنسيق الحاوية الرئيسية */ .container { max-width: 800px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } /* تنسيق أقسام الإعدادات */ .settings-section { margin-bottom: 24px; padding-bottom: 16px; border-bottom: 1px solid #eee; } /* تنسيق عناصر الإعدادات */ .setting-item { margin: 12px 0; } /* تنسيق الأزرار */ .actions { display: flex; gap: 10px; margin-top: 20px; } button { padding: 8px 16px; border-radius: 4px; border: none; cursor: pointer; } .primary { background-color: #1a73e8; color: white; } .secondary { background-color: #f1f3f4; color: #202124; }

4. JavaScript#

نكتب المنطق البرمجي في ملف options.js:

// الإعدادات الافتراضية const DEFAULT_SETTINGS = { backgroundColor: '#ffffff', fontSize: 'medium', notifications: true, updateInterval: 5 }; // دالة تحميل الإعدادات function loadSettings() { chrome.storage.sync.get(DEFAULT_SETTINGS, (settings) => { // تعيين قيم الإعدادات في الواجهة document.getElementById('backgroundColor').value = settings.backgroundColor; document.getElementById('fontSize').value = settings.fontSize; document.getElementById('notifications').checked = settings.notifications; document.getElementById('updateInterval').value = settings.updateInterval; }); } // دالة حفظ الإعدادات function saveSettings() { // جمع القيم من الواجهة const settings = { backgroundColor: document.getElementById('backgroundColor').value, fontSize: document.getElementById('fontSize').value, notifications: document.getElementById('notifications').checked, updateInterval: parseInt(document.getElementById('updateInterval').value) }; // حفظ الإعدادات chrome.storage.sync.set(settings, () => { showNotification('تم حفظ الإعدادات بنجاح!'); // إرسال إشعار للـ background script chrome.runtime.sendMessage({ type: 'SETTINGS_UPDATED', settings: settings }); }); } // دالة إعادة الضبط function resetSettings() { if (confirm('هل أنت متأكد من إعادة ضبط جميع الإعدادات؟')) { chrome.storage.sync.set(DEFAULT_SETTINGS, () => { loadSettings(); showNotification('تم إعادة ضبط الإعدادات'); }); } } // دالة إظهار الإشعارات function showNotification(message) { const notification = document.createElement('div'); notification.className = 'notification'; notification.textContent = message; document.body.appendChild(notification); // إزالة الإشعار بعد 3 ثواني setTimeout(() => { notification.remove(); }, 3000); }

5. إعداد الأحداث Events#

نضيف الأحداث في نهاية ملف options.js:

// تحميل الإعدادات عند فتح الصفحة document.addEventListener('DOMContentLoaded', loadSettings); // ربط الأزرار بالدوال المناسبة document.getElementById('save').addEventListener('click', saveSettings); document.getElementById('reset').addEventListener('click', resetSettings); // مراقبة التغييرات في الإعدادات document.querySelectorAll('input, select').forEach(element => { element.addEventListener('change', () => { // إضافة class للإشارة إلى وجود تغييرات غير محفوظة document.getElementById('save').classList.add('modified'); }); });

تخزين البيانات (Storage API)#

  1. مقدمة عن Storage API
  2. أنواع التخزين
  3. العمليات الأساسية
  4. أمثلة عملية

تعريف في manifest.json#

{ "permissions": [ "storage" ] }
  • يجب إضافة صلاحية storage في ملف manifest
  • بدون هذه الصلاحية لن تستطيع استخدام API التخزين

أنواع التخزين#

1. التخزين المتزامن (chrome.storage.sync)#

// تخزين البيانات chrome.storage.sync.set({ userName: "أحمد", preferences: { theme: "dark", fontSize: 16 } }); // قراءة البيانات chrome.storage.sync.get(['userName', 'preferences'], (result) => { console.log(result.userName); // أحمد console.log(result.preferences); // {theme: "dark", fontSize: 16} });
  • يتزامن مع حساب جوجل للمستخدم
  • الحد الأقصى: 100 كيلوبايت
  • مناسب للإعدادات والتفضيلات

2. التخزين المحلي (chrome.storage.local)#

// تخزين البيانات chrome.storage.local.set({ cachedData: largeDataObject, lastUpdate: new Date().getTime() }); // قراءة البيانات chrome.storage.local.get(['cachedData'], (result) => { console.log(result.cachedData); });
  • تخزين محلي على الجهاز فقط
  • الحد الأقصى: 10 ميجابايت
  • مناسب للبيانات الكبيرة والمؤقتة

العمليات الأساسية#

1. التخزين (set)#

// تخزين قيمة واحدة chrome.storage.sync.set({ key: "value" }, () => { console.log("تم التخزين بنجاح"); }); // تخزين عدة قيم chrome.storage.sync.set({ key1: "value1", key2: "value2", key3: { nestedKey: "nestedValue" } }, () => { console.log("تم تخزين كل القيم"); });

2. القراءة (get)#

// قراءة قيمة واحدة chrome.storage.sync.get(['key'], (result) => { console.log(result.key); }); // قراءة عدة قيم chrome.storage.sync.get(['key1', 'key2'], (result) => { console.log(result.key1, result.key2); }); // قراءة كل القيم chrome.storage.sync.get(null, (result) => { console.log(result); });

3. الحذف (remove)#

// حذف قيمة واحدة chrome.storage.sync.remove('key', () => { console.log("تم حذف القيمة"); }); // حذف عدة قيم chrome.storage.sync.remove(['key1', 'key2'], () => { console.log("تم حذف القيم"); });

4. مسح كل البيانات (clear)#

chrome.storage.sync.clear(() => { console.log("تم مسح كل البيانات"); });

أمثلة عملية#

1. مثال: إدارة إعدادات المستخدم#

// الإعدادات الافتراضية const defaultSettings = { theme: 'light', fontSize: 14, notifications: true, language: 'ar' }; // حفظ الإعدادات function saveSettings(settings) { return new Promise((resolve, reject) => { chrome.storage.sync.set({ userSettings: settings }, () => { if (chrome.runtime.lastError) { reject(chrome.runtime.lastError); } else { resolve(); } }); }); } // قراءة الإعدادات function loadSettings() { return new Promise((resolve, reject) => { chrome.storage.sync.get(['userSettings'], (result) => { if (chrome.runtime.lastError) { reject(chrome.runtime.lastError); } else { resolve(result.userSettings || defaultSettings); } }); }); } // استخدام الدوال async function updateSettings(newSettings) { try { await saveSettings(newSettings); console.log("تم حفظ الإعدادات بنجاح"); } catch (error) { console.error("حدث خطأ:", error); } }

2. مثال: مراقبة التغييرات#

// بنراقب التغييرات اللي بتحصل في التخزين chrome.storage.onChanged.addListener((changes, namespace) => { for (let key in changes) { let change = changes[key]; console.log(` Change Key: ${key} Old Value: ${change.oldValue} New Value: ${change.newValue} Storage Type: ${namespace} `); } });

واجهات المستخدم (UI) المتقدمة#

ممكن تستخدم زي React أو Vue عشان تعمل واجهة مستخدم جامدة. مثال بسيط باستخدام React:

// بوب_أب.js import React, { useState, useEffect } from 'react'; import ReactDOM from 'react-dom'; function PopUp() { const [color, setColor] = useState(''); useEffect(() => { chrome.storage.sync.get(['backgroundColor'], (result) => { if (result.backgroundColor) { setColor(result.backgroundColor); } }); }, []); const handleColorChange = (e) => { const newColor = e.target.value; setColor(newColor); chrome.storage.sync.set({ backgroundColor: newColor }); }; return ( <div> <h1>الإضافة الجامدة</h1> <p>اختار لون الخلفية يا معلم:</p> <input type="color" value={color} onChange={handleColorChange} /> </div> ); } ReactDOM.render(<PopUp />, document.getElementById('root'));

(Debugging)#

  • افتح Chrome DevTools واختار الإضافة بتاعتك من القائمة.
  • استخدم console.log() كتير في الكود بتاعك.
  • اعمل breakpoints في الكود عشان توقف التنفيذ عند نقط معينة.

نشر الإضافة على متجر كروم#

  1. اعمل ملف zip للإضافة بتاعتك.
  2. روح على Chrome Web Store Developer Dashboard.
  3. ادفع 5 دولار مرة واحدة عشان تبقى developer.
  4. ارفع الملف zip بتاعك وحط كل البيانات المطلوبة.
  5. استنى لحد ما جوجل توافق على الإضافة.

نصائح مهمة جداً:#

  • استخدم Offscreen Documents عشان تتعامل مع DOM بره الصفحة الرئيسية.

  • استعمل Declarative Net Request API عشان تتحكم في طلبات الشبكة بكفاءة.

  • اتعلم Cross-origin communication عشان تتعامل مع مواقع مختلفة.

  • طبق Security Best Practices عشان تحمي الإضافة بتاعتك من الهاكرز.

  • استخدم Web Workers عشان تشغل كود معقد من غير ما تعطل الصفحة.

  • جرب Service Workers عشان تخلي الإضافة تشتغل حتى لو الإنترنت مقطوع.

  • استخدم IndexedDB عشان تخزن بيانات كبيرة بكفاءة.

  • جرب WebAssembly عشان تشغل كود سريع جداً في الإضافة.

Join our whatsapp group here
My Channel here