JavaScript: مشروع التخرج: تطبيق المهام
تهانينا على الوصول إلى الدرس الأخير! خلال الدروس الـ 27 السابقة تعلمت المتغيرات، الدوال، المصفوفات، DOM، الأحداث، JSON، البرمجة غير المتزامنة، الفئات، والمزيد. الآن حان الوقت لجمع كل شيء معاً وبناء تطبيق صغير كامل. هذا المشروع سيكون تتويجاً لكل ما تعلمته.
1. نظرة عامة على المشروع
سنبني تطبيق مهام (Todo App) — قد يبدو بسيطاً، لكنه يلامس幾乎 كل مفهوم درسته. عندما تنتهي، سيكون لديك أداة إنتاجية حقيقية قابلة للاستخدام.
▶ مثال
// مثال على الكود في هذا القسم
const message = "مرحبا بالعالم!";
console.log(message);
هذا أبسط مثال; الأقسام التالية ستشرح بالتفصيل.## 2. متطلبات الميزات
- إضافة عناصر مهام — حقل إدخال + زر؛ الضغط على Enter أو النقر يُضيف عنصراً جديداً
- تبديل مكتمل/غير مكتمل — انقر على مهمة لتبديل حالتها (تأثير الشطب)
- حذف عناصر مهام — كل عنصر لديه زر حذف بجانبه
- التصفية — ثلاثة أزرار: الكل / النشط / المكتمل
- التخزين المحلي — استخدم
localStorageلحفظ البيانات عبر تحديثات الصفحة - عدد العناصر المتبقية — اعرض "X عناصر متبقية" في الأسفل
2. المتطلبات التقنية
| المفهوم | أين يُستخدم |
|---|---|
| الفئة | فئة TodoApp تنظم كل المنطق |
| async/await | لف قراءة/كتابة localStorage كدوال غير متزامنة |
| التلاعب بـ DOM | إنشاء عناصر القائمة ديناميكياً، تحديث المحتوى، إزالة العناصر |
| معالجة الأحداث | نموذج submit، قائمة click، زر التصفية click |
| JSON | JSON.stringify للحفظ، JSON.parse للتحميل |
| طرق المصفوفات | filter، map، find لإدارة بيانات المهام |
| النصوص القالبية | بناء سلاسل HTML |
3. معايير القبول
- [ ] جميع الميزات الـ 6 تعمل بشكل صحيح
- [ ] البيانات تبقى بعد تحديث الصفحة
- [ ] الكود يحتوي على تعليقات واضحة
- [ ] بدون
alert/prompt/confirm— استخدم تفاعلات داخل الصفحة بدلاً منها - [ ] الإدخال الفارغ يُرفض (مع رسالة)
4. أفكار تنفيذ مرجعية
أدناه تلميحات واتجاهات — وليس كوداً كاملاً. كتابته بنفسك هو حيث يحدث التعلم الحقيقي.
(1) هيكل البيانات
<script>
// كل عنصر مهمة هو كائن
const todo = {
id: Date.now(), // معرّف فريد
text: 'تعلم JavaScript',
completed: false
};
console.log(todo);
// جميع عناصر المهام تُخزن في مصفوفة
let todos = [];
</script>
(2) هيكل الفئة
<script>
class TodoApp {
constructor() {
this.todos = []; // البيانات
this.filter = 'all'; // التصفية الحالية
this.init(); // التهيئة
}
async init() {
// 1. تحميل البيانات من localStorage
// 2. ربط الأحداث
// 3. عرض القائمة
}
async save() {
// JSON.stringify → localStorage.setItem
}
async load() {
// localStorage.getItem → JSON.parse
}
addTodo(text) { /* إنشاء كائن، دفع، حفظ، عرض */ }
toggleTodo(id) { /* العثور على العنصر، قلب completed، حفظ، عرض */ }
deleteTodo(id) { /* تصفية العنصر، حفظ، عرض */ }
render() {
// 1. تصفية المهام بناءً على this.filter
// 2. مسح حاوية القائمة
// 3. المرور على المصفوفة المصفاة، إنشاء عقد DOM
// 4. تحديث عرض العدد
}
}
</script>
(3) استراتيجية ربط الأحداث
- نموذج
submit:e.preventDefault()← الحصول على قيمة الإدخال ←addTodo(text)← مسح الإدخال - قائمة
click(تفويض الأحداث): تحقق ما إذا كانe.targetهو زر التبديل أو الحذف ← استدعاء الطريقة المناسبة - زر التصفية
click: تحديثthis.filter→render()
(4) استراتيجية العرض
<script>
render() {
const filtered = this.todos.filter(function(todo) {
if (this.filter === 'active') return !todo.completed;
if (this.filter === 'completed') return todo.completed;
return true; // 'all'
}.bind(this));
// مسح الحاوية
this.listEl.innerHTML = '';
filtered.forEach(function(todo) {
var li = document.createElement('li');
// تعيين المحتوى، الأنماط، سمات البيانات
// ربط أحداث التبديل والحذف
this.listEl.appendChild(li);
}.bind(this));
// تحديث العدد
var activeCount = this.todos.filter(function(t) { return !t.completed; }).length;
this.countEl.textContent = activeCount + ' عنصر متبقي';
}
</script>
5. أفكار توسعة
بمجرد عمل الميزات الأساسية، جرب هذه التحديات المتقدمة:
- 🔄 السحب والإفلات للترتيب — نفّذ بـ HTML5 Drag & Drop API أو أحداث اللمس
- ✏️ التحرير — انقر مزدوجاً على مهمة لدخول وضع التحرير، اضغط Enter للحفظ
- 🏷️ علامات التصنيف — أضف تسميات (عمل / دراسة / حياة) وتصفية حسب العلامة
- 🎨 تبديل السمة — سمة داكنة/فاتحة، تُحفظ بـ
localStorage - ⏰ تذكيرات تاريخ الاستحقاق — أضف تاريخ استحقاق لكل عنصر، وظّف المتأخرة بالأحمر
6. الواجب
أكمل تطبيق المهام أعلاه بجميع الميزات، واجتاز جميع معايير القبول.
خطوات مقترحة:
- ابدأ بهيكل HTML ونمط CSS
- ابنِ هياكل فئة
TodoApp(constructor،init) - نفّذ
addTodoوrender— اجعل الإضافة تعمل أولاً - نفّذ
toggleTodo— تبديل مكتمل/غير مكتمل - نفّذ
deleteTodo— إزالة العناصر - نفّذ التصفية
- نفّذ
saveوload— حفظ البيانات - اعرض عدد العناصر المتبقية
- اللمسات الأخيرة: التحقق من الإدخال الفارغ، تعديلات الأنماط
▶ مثال
تطبيق Todo كامل باستخدام الأصناف و localStorage:
<!DOCTYPE html>
<html>
<head><style>body{font-family:Arial;max-width:600px;margin:20px auto;}.done{text-decoration:line-through;color:gray;}</style></head>
<body>
<h1>تطبيق Todo</h1>
<input id="taskInput" placeholder="مهمة جديدة">
<button id="addBtn">إضافة</button>
<ul id="taskList"></ul>
<script>
class Todo {
constructor() { this.tasks = JSON.parse(localStorage.getItem('tasks') || '[]'); }
add(text) { this.tasks.push({text, done: false}); localStorage.setItem('tasks', JSON.stringify(this.tasks)); this.render(); }
toggle(i) { this.tasks[i].done = !this.tasks[i].done; localStorage.setItem('tasks', JSON.stringify(this.tasks)); this.render(); }
render() { document.getElementById('taskList').innerHTML = this.tasks.map((t, i) => `<li class="${t.done ? 'done' : ''}">${t.text}</li>`).join(''); }
}
const todo = new Todo();
document.getElementById('addBtn').onclick = () => todo.add(document.getElementById('taskInput').value);
todo.render();
</script>
</body>
</html>
يوضح تطبيق Todo كامل مع إدارة الحالة القائمة على الأصناف، والاستمرارية عبر localStorage، وعرض DOM الديناميكي.
❓ أسئلة شائعة
constructor، this، والوراثة لها بناء نحوي واضح. في العمق لا تزال نماذج أولية، لكنها تقرأ كلغات OOP وأسهل في الصيانة.JSON.stringify قبل الحفظ.e.target لمعالجة أحداث العناصر الفرعية. عناصر المهام الجديدة تحصل على معالجة الأحداث تلقائياً — أداء أفضل.📖 ملخص
- هذا المشروع يربط المتغيرات والدوال والمصفوفات والكائنات ومعالجة DOM والأحداث وJSON وClass معاً
- استخدام Class ينظم الكود —
constructorيهيئ، والدوال تعيش في prototype - localStorage يخزن البيانات عبر إعادة تحميل الصفحة
- تفويض الأحداث يستغل فقاعات الأحداث لتقليل الروابط المكررة
- التصفية تعمل بالحفاظ على حالة
filterوالتصيير الشرطي — أساس نمط MVVM
📝 تمارين
- أساسي: أكمل ميزات المهام الأساسية — إضافة، تبديل مكتمل، حذف — باستخدام مصفوفة للتخزين (بدون حاجة لـ
localStorage). - متوسط: أضف
localStorageللحفظ والتصفية (الكل / النشط / المكتمل) للنسخة الأساسية؛ البيانات يجب أن تبقى بعد تحديث الصفحة. - تحدي: نفّذ ميزة توسعة واحدة على الأقل (السحب والإفلات للتحرير، التحرير، علامات التصنيف، تبديل السمة، أو تذكيرات تاريخ الاستحقاق)، واكتب شريحاً مختصراً لنهجك.