دليل واضح خطوة بخطوة لبناء تطبيق صغير لقائمة مهام: خطط الميزات، أنشئ الشاشات، أضف المنطق، احفظ البيانات، اختبر وانشر.

عندما أقول "تطبيق" في هذا الدليل أعني تطبيق ويب صغير: صفحة واحدة تفتحها في المتصفح وتستجيب لما تنقره وتكتبه. لا تثبيتات، لا حسابات، ولا إعدادات معقدة—مجرد مشروع بسيط يمكنك تشغيله محليًا.
بنهاية الدليل ستحصل على تطبيق قائمة مهام يمكنه:
localStorage (حتى لا تختفي عند إغلاق التبويب)لن يكون مثاليًا أو "مناسبًا للمؤسسات" — وهذا مقصود. هذا مشروع للمبتدئين مصمم ليعلّم الأساسيات دون إغراقك بأدوات كثيرة.
ستبني التطبيق خطوة بخطوة وتلتقط قطعًا أساسية من كيفية عمل تطبيقات الواجهة الأمامية:
اجعل الأمور بسيطة. كل ما تحتاجه:
إذا يمكنك إنشاء مجلد وتحرير بعض الملفات فأنت جاهز.
قبل أن تكتب أي كود، قرر كيف يبدو "النجاح". هذا الدليل يبني تطبيقًا صغيرًا بمهمة واحدة واضحة: مساعدتك على تتبع المهام التي تريد تنفيذها.
اكتب هدفًا في جملة واحدة تحافظ على تركيزك أثناء البناء:
"هذا التطبيق يتيح لي إضافة مهام إلى قائمة حتى لا أنساها."
هذا كل ما في الأمر. إذا شعرت برغبة في إضافة تقاويم أو تذكيرات أو وسوم أو حسابات، ضعها جانبًا لوقت لاحق.
كوّن قائمتين سريعًا:
الضروري (لهذا المشروع):
localStorage لاحقًا)المرغوب (ليس مطلوبًا اليوم): تواريخ استحقاق، أولويات، فئات، بحث، سحب وإفلات، مزامنة سحابية.
تقليص قائمة "الضروري" يساعدك على إتمام المشروع فعليًا.
يمكن أن تكون الشاشة صفحة واحدة تحتوي على:
كن محددًا حتى لا تعلق:
مع هذا القرار، أنت جاهز لإعداد ملفات المشروع.
قبل كتابة أي كود، دعنا نُنشئ "منزل" صغير ونظيف للتطبيق. التنظيم من البداية يجعل الخطوات التالية أسهل.
افتح مجلدًا جديدًا على جهازك وسَمِّه مثل todo-app. هذا المجلد سيحمل كل شيء للمشروع.
داخل المجلد أنشئ ثلاثة ملفات:
index.html (هيكل الصفحة)styles.css (المظهر والترتيب)app.js (السلوك والتفاعل)تأكد من أنك لا تنتهي بلاحقة إضافية مثل index.html.txt—وهو خطأ شائع للمبتدئين.
افتح مجلد todo-app في محرر الكود (VS Code، Sublime Text، إلخ). ثم افتح index.html في المتصفح.
قد تكون الصفحة فارغة في هذه المرحلة—وهذا طبيعي. سنضيف المحتوى في الخطوة التالية.
عند تعديل ملفاتك، المتصفح لن يحدث تلقائيًا (إلا إذا استخدمت أداة تفعل ذلك).
الحلقة الأساسية هي:
إذا لم يحدث شيء، فالتحديث هو أول شيء تجربه.
يمكنك بناء هذا التطبيق بفتح index.html مباشرة، لكن خادم محلي بسيط يمنع مشاكل غريبة لاحقًا (خاصة عند حفظ البيانات أو تحميل ملفات).
خيارات بسيطة للمبتدئين:
python -m http.server
ثم افتح العنوان الذي يظهر (غالبًا http://localhost:8000) في المتصفح.
الآن سننشئ هيكلًا نظيفًا للتطبيق. هذا الـ HTML لن يجعل التطبيق تفاعليًا بعد (هذا لاحقًا)، لكنه يمنح JavaScript أماكن واضحة للقراءة والكتابة.
سنشمل:
الأسماء الواضحة (IDs/classes) تسهل على JavaScript الوصول للعناصر بدون لبس.
index.html\u003c!doctype html\u003e
\u003chtml lang="en"\u003e
\u003chead\u003e
\u003cmeta charset="utf-8" /\u003e
\u003cmeta name="viewport" content="width=device-width, initial-scale=1" /\u003e
\u003ctitle\u003eTo‑Do App\u003c/title\u003e
\u003clink rel="stylesheet" href="styles.css" /\u003e
\u003c/head\u003e
\u003cbody\u003e
\u003cmain class="app" id="app"\u003e
\u003ch1 class="app__title" id="appTitle"\u003eMy To‑Do List\u003c/h1\u003e
\u003cform class="task-form" id="taskForm"\u003e
\u003clabel class="task-form__label" for="taskInput"\u003eNew task\u003c/label\u003e
\u003cdiv class="task-form__row"\u003e
\u003cinput
id="taskInput"
class="task-form__input"
type="text"
placeholder="e.g., Buy milk"
autocomplete="off"
/\u003e
\u003cbutton id="addButton" class="task-form__button" type="submit"\u003e
Add
\u003c/button\u003e
\u003c/div\u003e
\u003c/form\u003e
\u003cul class="task-list" id="taskList" aria-label="Task list"\u003e\u003c/ul\u003e
\u003c/main\u003e
\u003cscript src="app.js"\u003e\u003c/script\u003e
\u003c/body\u003e
\u003c/html\u003e
هذا كل شيء للهيكل. لاحظ أننا استخدمنا id="taskInput" و id="taskList"—هما العنصران اللذان ستتعامل معهما كثيرًا في JavaScript.
الآن الصفحة موجودة، لكنها ربما تبدو مستندًا عاديًا. قليل من CSS يجعلها أسهل للاستخدام: تباعد أوضح، نص مقروء، وأزرار تبدو قابلة للنقر.
صندوق مركزي يبقي التطبيق مركزًا ويمنع المحتوى من الامتداد عبر الشاشة.
/* Basic page setup */
body {
font-family: Arial, sans-serif;
background: #f6f7fb;
margin: 0;
padding: 24px;
}
/* Centered app container */
.container {
max-width: 520px;
margin: 0 auto;
background: #ffffff;
padding: 16px;
border-radius: 10px;
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
}
كل مهمة يجب أن تبدو كصف منفصل بمسافة مريحة.
ul { list-style: none; padding: 0; margin: 16px 0 0; }
li {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 10px 12px;
border: 1px solid #e7e7ee;
border-radius: 8px;
margin-bottom: 10px;
}
.task-text { flex: 1; }
عندما تكتمل المهمة يجب أن يتغير مظهرها بحيث يمكنك تمييزها بسرعة.
.done .task-text {
text-decoration: line-through;
color: #777;
opacity: 0.85;
}
حافظ على أزرار بنفس الحجم والنمط لتبدو جزءًا من نفس التطبيق.
button {
border: none;
border-radius: 8px;
padding: 8px 10px;
cursor: pointer;
}
button:hover { filter: brightness(0.95); }
هذا يكفي لواجهة نظيفة وودية—لا حاجة لخدع متقدمة. التالي سنربط السلوك باستخدام JavaScript.
الآن بعد أن لديك الإدخال والزر والقائمة على الصفحة، ستجعلها تعمل. الهدف: عند كتابة مهمة والضغط على أضف (أو الضغط على Enter) تظهر مهمة جديدة في القائمة.
في ملف JavaScript، امسك أولًا بالعناصر التي تحتاجها، ثم استمع لحدثي: نقرة الزر وضغط Enter داخل الحقل.
const taskInput = document.querySelector('#taskInput');
const addBtn = document.querySelector('#addBtn');
const taskList = document.querySelector('#taskList');
function addTask() {
const text = taskInput.value.trim();
// Block empty tasks (including ones that are just spaces)
if (text === '') return;
const li = document.createElement('li');
li.textContent = text;
taskList.appendChild(li);
// Clear the input and put the cursor back
taskInput.value = '';
taskInput.focus();
}
addBtn.addEventListener('click', addTask);
taskInput.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
addTask();
}
});
trim() لحذف الفراغات الزائدة من البداية والنهاية.\u003cli\u003e جديدًا، يضع نصه، ويضيفه إلى القائمة.إذا لم يحدث شيء، تحقق أن معرفات العناصر في HTML تطابق منتقيات JavaScript تمامًا (هذا سبب شائع لمشاكل المبتدئين).
الآن بعد أن يمكنك إضافة مهام، اجعلها قابلة للتنفيذ: يجب أن تتمكن من وسم المهمة كمكتملة وإزالتها.
بدلًا من تخزين المهام كنصوص فقط، خزنها ككائنات. هذا يعطي كل مهمة هوية ثابتة ومكانًا لتتبع حالة "مكتمل":
text: نص المهمةdone: true أو falseid: رقم فريد لنتمكّن من العثور على المهمة أو حذفهامثال:
let tasks = [
{ id: 1, text: "Buy milk", done: false },
{ id: 2, text: "Email Sam", done: true }
];
عند عرض كل مهمة في الصفحة، أضف إما خانة اختيار أو زر "تم"، بالإضافة إلى زر "حذف".
الاستماع للأحداث هو "الطريقة التي نستجيب بها للنقرات"—تربط مستمع حدث على الزر (أو على الحاوية كلها)، وعند النقر يقوم الكود بالتشغيل.
نمط مناسب للمبتدئين هو تفويض الأحداث: ضع مستمع نقرة واحد على حاوية القائمة، ثم تحقّق مما نقر عليه.
function toggleDone(id) {
tasks = tasks.map(t => t.id === id ? { ...t, done: !t.done } : t);
renderTasks();
}
function deleteTask(id) {
tasks = tasks.filter(t => t.id !== id);
renderTasks();
}
document.querySelector("#taskList").addEventListener("click", (e) => {
const id = Number(e.target.dataset.id);
if (e.target.matches(".toggle")) toggleDone(id);
if (e.target.matches(".delete")) deleteTask(id);
});
في دالة renderTasks():
data-id="${task.id}" لكل زر..done).حاليًا عند تحديث الصفحة تختفي كل المهام. هذا لأن المهام موجودة في ذاكرة JavaScript فقط. عند إعادة تحميل الصفحة تُعاد هذه الذاكرة إلى الحالة الافتراضية.
localStorage مدمج في المتصفح. فكر فيه كصندوق صغير يمكنك تخزين نص تحت اسم (مفتاح). مناسب للمشاريع المبتدئة لأنه لا يحتاج خادمًا أو حسابات.
سنخزن قائمة المهام كاملة كنص JSON، ثم نعيد تحميلها عند فتح الصفحة.
عند إضافة مهمة، وسم مهمة، أو حذفها، استدعِ saveTasks().
const STORAGE_KEY = "todo.tasks";
function saveTasks(tasks) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
}
في كل مكان يحدث فيه تحديث للمصفوفة tasks، قم باستدعاء هذا بعده:
saveTasks(tasks);
renderTasks(tasks);
عند تحميل الصفحة، اقرأ القيمة المحفوظة. إذا لم توجد، عد إلى قائمة فارغة.
function loadTasks() {
const saved = localStorage.getItem(STORAGE_KEY);
return saved ? JSON.parse(saved) : [];
}
let tasks = loadTasks();
renderTasks(tasks);
هذا كل شيء: الآن التطبيق يتذكّر المهام بعد إعادة التحميل.
تلميح: localStorage يخزن سلاسل نصية فقط، لذا JSON.stringify() يحول المصفوفة إلى نص، وJSON.parse() يعيدها لمصفوفة عند التحميل.
الاختبار قد يبدو مملًا، لكنه أسرع طريقة لتحويل تطبيقك من "يعمل عندي" إلى "يعمل دومًا". قم بمرور سريع بعد كل تغيير صغير.
مرّ خلال التدفق الرئيسي بالترتيب التالي:
إذا فشل أي خطوة، أصلحها قبل إضافة ميزات جديدة. تكديس المشكلات يجعل الأمور فوضوية.
الحالات الحديّة هي مدخلات لم تصمم لها لكن المستخدمين سيجربونها:
إصلاح شائع هو حظر المهام الفارغة:
const text = input.value.trim();
addButton.disabled = text === "";
(يمكن تشغيل ذلك عند كل حدث input، ومرة أخرى قبل الإضافة.)
عندما لا تستجيب النقرات، عادةً ما يكون السبب:
app.js غير موجود).عندما يبدو أن الأمور عشوائية، سجّل قيمًا:
console.log("Adding:", text);
console.log("Current tasks:", tasks);
افتح Console في المتصفح وابحث عن أخطاء (نص أحمر). بعد إصلاح المشكلة، احذف السجلات حتى لا تملأ المشروع.
التطبيق "مكتمل" حين يكون مريحًا للاستخدام حقيقيًا—على الهواتف، بالكيبورد، ومع أدوات قراءة الشاشة.
على الشاشات الصغيرة، الأزرار الصغيرة مزعجة. امنح العناصر القابلة للنقر مساحة كافية:
زيادة padding، font-size، و gap في CSS تحدث فرقًا كبيرًا.
قارئات الشاشة تحتاج أسماء واضحة للتحكمات.
\u003clabel\u003e حقيقيًا (الأفضل). إذا أردت إخفاءه بصريًا يمكنك ذلك عبر CSS لكن ابقِ العنصر في HTML.aria-label="Delete task" حتى تعلنها قارئات الشاشة بشكل مفهوم.هذا يساعد المستخدمين على فهم كل تحكم دون تخمين.
تأكد من أن بإمكانك استخدام التطبيق كله بدون ماوس:
<form> لجعل Enter يعمل طبيعيًا)استخدم حجم خط مقروء (16px نقطة انطلاق جيدة) وتباين لوني قوي (نص غامق على خلفية فاتحة مثلاً). لا تعتمد على اللون وحده لتمييز الحالة—أضف شطبًا أو نمطًا واضحًا لحالة "تم".
الآن بعد أن كل شيء يعمل، خذ 10–15 دقيقة لتنظيف المشروع. هذا يسهل الإصلاحات المستقبلية ويساعدك عندما تعود للمشروع لاحقًا.
حافظ على كل شيء صغيرًا ومتوقعًا:
/index.html — هيكل الصفحة/styles.css — المظهر/app.js — السلوك (إضافة، وسم مكتمل، حذف، حفظ/تحميل)/README.md — ملاحظات قصيرة لـ "أنت في المستقبل"إذا فضلت مجلدات فرعية:
/css/styles.css/js/app.jsفقط تأكد أن \u003clink\u003e و \u003cscript\u003e تستخدم المسارات الصحيحة.
بعض التحسينات السريعة:
taskInput, taskList, saveTasks()مثال للدوال السهلة المسح:
renderTasks(tasks)addTask(text)toggleTask(id)deleteTask(id)يمكن أن يكون README.md بسيطًا:
index.html في المتصفح)عند انتهاء كل مرحلة مهمة (مثل "الآن localStorage يعمل") اصنع أرشيف مضغوط للمجلد. إن أردت تاريخًا للتعديلات فاستخدام Git مفيد لكنه اختياري. نسخة واحدة تكفي لتجنّب الحذف العرضي.
النشر يعني وضع ملفات التطبيق (HTML، CSS، JavaScript) في مكان عام على الإنترنت كي يفتح الآخرون الرابط ويستخدموه. بما أن التطبيق موقع ثابت (static site) فيمكن استضافته مجانًا بسهولة.
خطوات عامة:
/)إذا كانت ملفاتك منفصلة تأكد من تطابق أسماء الملفات في الروابط (مثل styles.css مقابل style.css).
إذا أردت أبسط طريقة:
إن نجح كل شيء، أرسل الرابط لصديق واطلب منه التجربة—العين الخارجية تكتشف الأخطاء بسرعة.
بنيت تطبيقًا يعمل. إذا أردت مواصلة التعلم دون القفز لمشروع ضخم، هذه الترقيات تضيف قيمة حقيقية وتعلّمك أنماط مفيدة.
أضف زر "تعديل" لكل مهمة. عند النقر استبدل تسمية المهمة بحقل إدخال صغير (معبأ مسبقًا)، ومعه زري "حفظ" و"إلغاء".
تلميح: احتفظ بهيكل البيانات كمصفوفة كائنات (id و text). التعديل يصبح: اعثر على المهمة بالـ id، حدّث text، عدِّر العرض، واحفظ.
أضف ثلاثة أزرار في الأعلى: الكل، النشط، المكتمل.
خزن الفلتر الحالي في متغير مثل currentFilter = 'all'. عند العرض، أظهر:
اجعلها خفيفة:
YYYY-MM-DD) اعرضه بجانب المهمةحتى حقل إضافي واحد يعلمك كيف تحدث نموذج البيانات والواجهة معًا.
الفكرة الكبرى: بدل حفظ المهام في localStorage، أرسلها إلى واجهة برمجة تطبيقات (API) باستخدام fetch(). الخادم يخزنها في قاعدة بيانات، فيُصبح بالإمكان المزامنة عبر أجهزة.
إذا رغبت بالتدرج دون إعادة بناء كل شيء، منصات توليد المشاريع أو التجربة يمكن أن تساعدك في إنشاء نسخة لاحقة تحتوي على واجهة API وقاعدة بيانات.
جرّب بناء تطبيق ملاحظات (مع بحث) أو تتبّع عادات (علامات يومية). كلاهما يعيد استخدام نفس المهارات: عرض القوائم، التحرير، الحفظ، وتصميم واجهة بسيطة.