تطبيق ويب تفاعلي لاختبار معرفة القرآن الكريم من خلال ثلاثة أنواع من الاختبارات. يعمل التطبيق كـ Progressive Web App (PWA) ويمكن تثبيته على الأجهزة المحمولة والعمل بدون اتصال بالإنترنت بعد التحميل الأولي.
استلهمت هذا التطبيق من أحد المستودعات على غيتهب تحمل نفس الاسم، لكنها تستخدم Typescript وتستخدم نسخة JSON مخزنة من القرآن الكريم، ولا تمتلك واجهة سهلة الوصول، لذا قررت استخدام منطق مشابه في بناء واجهة مع مزج بين PWA والتخزين الهجين للبيانات والاعتماد على الAPI للوصول لبيانات قرآنية أكثر موثوقية.
تجربة التطبيق: من هنا
الكود المصدري: من غيتهب

نظرة عامة
التطبيق مبني باستخدام تقنيات الويب الأساسية (HTML5، CSS3، JavaScript ES6+) بدون أي إطارات عمل أو أدوات بناء معقدة. يدعم التطبيق ثلاثة أنواع من الاختبارات:
- تخمين الآية حسب السورة: عرض آية معينة، والمطلوب تخمين الآية التالية من السور المحددة
- تخمين الآية حسب الجزء: نفس الفكرة لكن بناءً على الأجزاء المحددة
- تخمين السورة حسب الآية: عرض آية معينة، والمطلوب تحديد السورة التي تنتمي إليها
دعم PWA (Progressive Web App)
التطبيق يعمل كتطبيق ويب تقدمي، مما يعني أنه يمكن تثبيته على الأجهزة المحمولة والوصول إليه من اختصار في شاشة الهاتف ودون اتصال إنترنت.
وقد مررت على تفاصيل هذه التقنية في موضوع سابق بشكل سريع، وقد مرّ عليها أيضاً الأخ عادل في موضوع مفصّل، وسبب اعتمادها هنا أنّ التطبيق يمكن أن يعمل ببساطة بدون اتصال انترنت بمجرد تحميل البيانات الأولية للمرة الأولى.
التخزين المحلي (LocalStorage)
جميع بيانات المستخدم تُحفظ محلياً في المتصفح باستخدام LocalStorage API. لا توجد قاعدة بيانات خارجية أو خادم.
هيكل التخزين
يتم تخزين البيانات في مفاتيح محددة:
quran-data-cache: بيانات القرآن الكاملة (نص الآيات)
quran-data-cache-timestamp: وقت آخر تحديث للكاش (للتأكد من صلاحية البيانات)
quiz-history: سجل محاولات الاختبار
quiz-state: حالة الاختبار الحالي (للاستئناف بعد إغلاق المتصفح)
theme: الثيم المختار (فاتح/داكن)
language: اللغة المختارة (عربي/إنجليزي)
إدارة كاش بيانات القرآن
يتم تحميل بيانات القرآن من API مرة واحدة وتخزينها محلياً. الكاش صالح لمدة 7 أيام، يمكن تمديد هذه المدّة أكثر بكثير نظراً لعدد التحديثات القليل الممكن أن يطرأ على البيانات القرآنيّة، لكن في حالة هذا المشروع لا بأس من ضبطه بهذه المدّة نظراً للحجم الصغير للبيانات: أردت فقط في هذا المشروع أن أظهر طريقة للمزج بين الاعتماد على التخزين المحلي والAPI لتخزين البيانات التي يحتاجها التطبيق في الإنطلاق الأول. وهو ما تطرّقت له الأخت علا في أحد المواضيع من قبل.
const CACHE_EXPIRY_DAYS = 7;
function isCacheValid() {
const timestamp = localStorage.getItem(CACHE_TIMESTAMP_KEY);
if (!timestamp) return false;
const cacheTime = parseInt(timestamp, 10);
const now = Date.now();
const expiryTime = CACHE_EXPIRY_DAYS * 24 * 60 * 60 * 1000;
return (now - cacheTime) < expiryTime;
}
إذا انتهت صلاحية الكاش، يتم تحديثه تلقائياً من API. في حالة عدم الاتصال بالإنترنت، يتم استخدام الكاش المنتهي الصلاحية كبديل.
حفظ حالة الاختبار
التطبيق يحفظ حالة الاختبار الحالي في LocalStorage، مما يسمح للمستخدم بالعودة إلى الاختبار بعد إغلاق المتصفح او تحديث الصفحة - هذا ضروري لكون بعض المتصفحات تغلق الصفحة في حال بقائها في الخلفية لفترة طويلة، خصوصاً على الهواتف المحمولة:
function saveQuizState() {
if (state.quizData && state.currentScreen === 'quiz') {
const stateToSave = {
quizData: state.quizData,
quizType: state.quizType,
currentQuestionIndex: state.currentQuestionIndex,
answers: state.answers,
selectedOptions: state.selectedOptions,
currentStreak: state.currentStreak,
maxStreak: state.maxStreak,
timestamp: Date.now(),
};
localStorage.setItem(QUIZ_STATE_KEY, JSON.stringify(stateToSave));
}
}
الحالة المحفوظة صالحة لمدة 24 ساعة فقط، بعدها يتم حذفها تلقائياً. يمكن أيضاً إطالة هذه المدّة، لكن أعتقد أنّ هذا الوقت كافٍ لمتابعة الاختبار وزيادة - خصوصاً أنّ الاختبارات عادةً ما تتم في جلسة واحدة.
سجل الاختبارات
يتم حفظ كل محاولة اختبار في سجل مع تفاصيل كاملة:
- نوع الاختبار
- النتيجة (عدد الإجابات الصحيحة/الإجمالي)
- أقصى سلسلة إجابات صحيحة متتالية
- تفاصيل كل سؤال (السؤال، الإجابة المختارة، الإجابة الصحيحة)
- معلومات الآية (رقم السورة، رقم الآية، رقم الصفحة)
يتم الاحتفاظ بآخر 100 محاولة فقط لتجنب استهلاك مساحة كبيرة في LocalStorage.
استخدام API للحصول على بيانات القرآن
التطبيق يستخدم Al-Quran Cloud API للحصول على بيانات القرآن الكريم، اخترت هذه الAPI كوني تعاملت معها من قبل وكونها لا تتطلب Authentication لذا يمكن استخدامها في هذا النوع من المشاريع البسيطة بسرعة وسهولة.
الحصول على بيانات القرآن الكاملة
يتم جلب بيانات القرآن الكاملة من API مرة واحدة وتخزينها محلياً:
const API_URL = 'https://api.alquran.cloud/v1/quran/quran-uthmani';
async function fetchFromApi() {
const response = await fetch(API_URL);
const data = await response.json();
return transformApiData(data);
}
تحويل بيانات API
يتم تحويل استجابة API إلى هيكل بيانات داخلي موحد:
function transformApiData(apiData) {
const verses = [];
let globalVerseId = 1;
for (const surah of apiData.data.surahs) {
for (const ayah of surah.ayahs) {
verses.push({
id: globalVerseId++,
chapter_id: surah.number,
juz_number: ayah.juz || 0,
text_uthmani: ayah.text,
page_number: ayah.page || 0,
verse_number: ayah.numberInSurah || 0,
});
}
}
return verses;
}
هذا الهيكل الموحد يسهل البحث والوصول إلى الآيات بناءً على السورة، الجزء، أو رقم الآية.
الحصول على بيانات الصفحة
عند عرض صفحة كاملة من القرآن (لرؤية سياق الآية)، يتم جلب بيانات الصفحة من API:
export async function fetchPageData(pageNumber) {
const response = await fetch(
`https://api.alquran.cloud/v1/page/${pageNumber}/quran-uthmani`
);
const data = await response.json();
return data.data;
}
يتم تخزين استجابات API في Service Worker cache للاستخدام بدون إنترنت.
توليد أسئلة الاختبار
التطبيق يحتوي على ثلاثة أنواع من الاختبارات، كل نوع له منطق توليد أسئلة مختلف:
1. تخمين الآية حسب السورة/الجزء
في هذا النوع، يتم:
- اختيار آية عشوائية من السور/الأجزاء المحددة
- استخدام الآية التالية مباشرة كإجابة صحيحة
- اختيار 3 آيات عشوائية أخرى كخيارات خاطئة
- خلط الخيارات عشوائياً
2. تخمين السورة حسب الآية
في هذا النوع:
- اختيار آية عشوائية من السور المحددة
- استخدام السورة التي تنتمي إليها الآية كإجابة صحيحة
- اختيار 3 سور عشوائية أخرى من السور المحددة كخيارات خاطئة
- خلط الخيارات عشوائياً
دعم العمل بدون إنترنت (Offline Support)
التطبيق يعمل بدون إنترنت بعد التحميل الأولي اعتماداً على:
- Service Worker: يحفظ جميع الملفات الأساسية في الكاش
- LocalStorage: يحفظ بيانات القرآن وسجل الاختبارات
- API Cache: يحفظ استجابات API في Service Worker cache
الاستضافة على GitHub Pages
التطبيق يستضاف مجاناً على GitHub Pages بدون الحاجة لخادم أو قاعدة بيانات. كما ذكرت في المواضيع السابقة، أختار هذا الخيار للكثير من المشاريع الصغيرة المشابهة كونه يرفع عني عبئ المتابعة على الاستضافة، وفي نفس الوقت أستخدم المستودع لتخزين الكود ومتابعة التعديلات على الملفات.
أين يمكن أن يصل هذا البرنامج
هذا التطبيق يثبت إمكانية بناء تطبيقات ويب تفاعلية باستخدام تقنيات الويب الأساسية فقط، بدون الحاجة لإطارات عمل معقدة أو أدوات بناء. يمكن التطوير عليه أو أخذ الأفكار منه لبناء تطبيقات أخرى.
إمكانيات التطوير المستقبلية
- إضافة المزيد من أنواع الاختبارات
- إضافة نظام إنجازات (achievements)