جاري التحميل...

مشروع مصغّر — قائمة مهام تفاعلية

في الصف المفتوح تقرأ buy tea، وفي الصف المنتهي تقرأ done: buy tea. بين هاتين الصورتين قائمة قصيرة تبنيها أنت: تُدخل buy tea، تعلّمها منتهية، ثم تسحبها من القائمة.

الصفحة السابقة لم تُظهر صفاً

أربع أدوات بين يديك: مصفوفة، ونموذج، وtextContent، ونقرة. الإضافة مشروطة ببقاء الصفحة، بلا إعادة تحميل. ما قبل هذه الصفحة اكتفى بأسماء المهارات ولم يرسم قائمة. القائمة هنا هي الدرس.

شكل العنصر

tasks مصفوفة كائنات، لا سلاسل عارية. في كل كائن حقلان: text وdone. العنصر الطازج يبدأ وdone على false. addTask يمرّر الكلام على trim، فإن لم يبقَ حرف امتنع عن push ولم يُخزّن شيئاً.

أول كائن داخل tasks

javascript
const tasks = [];
function addTask(text) {
const clean = text.trim();
if (!clean) {
return;
}
tasks.push({ text: clean, done: false });
}
addTask("buy tea");
console.log(tasks[0].text);
console.log(tasks[0].done);

السجلّان على الترتيب: buy tea ثم false. addTask(" ") يقصّ الفراغات حتى لا يبقى حرف، فلا تخزين، والطول يظل 0.

الإرسال يُسقط ما في الذاكرة

النموذج حين يُرسَل يعيد تحميل المستند. المصفوفة ليست على قرص، بل في الذاكرة، فإعادة التحميل تُسقطها وتصير القائمة فارغة أمامك. event.preventDefault() يقطع هذا المسار. بعده تُؤخذ الكلمات، يُفرَّغ الحقل، وتُرسم القائمة مرة أخرى.

إرسال يُبقي المستند

javascript
const form = document.querySelector("#add-form");
const input = document.querySelector("#task-text");
form.addEventListener("submit", (event) => {
event.preventDefault();
addTask(input.value);
input.value = "";
render();
});

المستند يبقى مكانه والحقل يرجع فارغاً، ثم يشتغل render. إن امتلأ الحقل بفراغات فقط فلن يُضاف صف.

زرّان في كل صف

render يفرّغ #tasks أولاً، ثم يصنع li واحداً لكل عنصر. المفتوح يعرض text. المنتهي يعرض done: وبعدها النص. على الصف زرّان، كلاهما type="button"، كي لا تُرسل النقرةُ النموذج. Done يقلب القيمة المنطقية. الإزالة تمر من splice. بعد الاثنين يُعاد الرسم، وإلا بقيت الفهارس القديمة تشير إلى العنصر الخطأ.

صف وقلبان وحذف

javascript
function toggleTask(index) {
tasks[index].done = !tasks[index].done;
}
function removeTask(index) {
tasks.splice(index, 1);
}
function render() {
const list = document.querySelector("#tasks");
list.textContent = "";
tasks.forEach((task, index) => {
const item = document.createElement("li");
item.textContent = task.done ? "done: " + task.text : task.text;
const doneButton = document.createElement("button");
doneButton.type = "button";
doneButton.textContent = "Done";
doneButton.addEventListener("click", () => {
toggleTask(index);
render();
});
const removeButton = document.createElement("button");
removeButton.type = "button";
removeButton.textContent = "Remove";
removeButton.addEventListener("click", () => {
removeTask(index);
render();
});
item.append(doneButton, removeButton);
list.append(item);
});
}

الترتيب: addTask("buy tea") ثم toggleTask(0) ثم render، فيصير الصف done: buy tea. تبديل ثانٍ يعيد الكتابة إلى buy tea. removeTask(0) يترك الطول 0، والرسم الذي يليه بلا صفوف. كلمات الحقل تدخل الصفحة عبر textContent، لا تُلصق كـ HTML.

الفهرس ملك هذه الجولة فقط

forEach يمسك فهرس الجولة التي يمر بها الآن. بعد splice تشير الأرقام القديمة إلى مهمة غير المقصودة. الرسم من جديد يرمي الأزرار السابقة ويصنع أزراراً بفهارس حالية. من دون هذا الرسم الثاني يحذف Remove الصف الخطأ.

ثلاثة أدوار

النموذج مسؤول عن الإضافة. القائمة مسؤولة عن الرسم. Done وRemove لا يمسّان غير tasks، ثم يُعاد الرسم.

النموذج والقائمة معاً

html
<!DOCTYPE html>
<html>
<body>
<form id="add-form">
<input id="task-text" type="text">
<button type="submit">Add</button>
</form>
<ul id="tasks"></ul>
<script>
const tasks = [];
function addTask(text) {
const clean = text.trim();
if (!clean) return;
tasks.push({ text: clean, done: false });
}
function toggleTask(index) {
tasks[index].done = !tasks[index].done;
}
function removeTask(index) {
tasks.splice(index, 1);
}
function render() {
const list = document.querySelector("#tasks");
list.textContent = "";
tasks.forEach((task, index) => {
const item = document.createElement("li");
item.textContent = task.done ? "done: " + task.text : task.text;
const doneButton = document.createElement("button");
doneButton.type = "button";
doneButton.textContent = "Done";
doneButton.addEventListener("click", () => {
toggleTask(index);
render();
});
const removeButton = document.createElement("button");
removeButton.type = "button";
removeButton.textContent = "Remove";
removeButton.addEventListener("click", () => {
removeTask(index);
render();
});
item.append(doneButton, removeButton);
list.append(item);
});
}
document.querySelector("#add-form").addEventListener("submit", (event) => {
event.preventDefault();
addTask(document.querySelector("#task-text").value);
document.querySelector("#task-text").value = "";
render();
});
</script>
</body>
</html>

اكتب buy tea واضغط Add: يظهر صف buy tea ويصفو الحقل. اضغط Done فيصير النص done: buy tea. اضغط Remove فتفرغ القائمة. Add على حقل فارغ لا يضيف شيئاً.

خمس حركات على الطاولة

  1. استدعِ addTask("buy tea"): أول text هو buy tea وdone ما زال false.
  2. أرسل النموذج: الصفحة لا تعيد التحميل، والصف يظهر.
  3. اضغط Done: الصف يقرأ done: buy tea.
  4. اضغط Remove: tasks.length يصير 0.
  5. أرسل فراغات فقط: القائمة لا تكبر.

أخطاء تقع في الشغل الحقيقي

  • ترك preventDefault، فيموت المصفوفة عند الإرسال
  • عمل push لسلسلة فارغة
  • وضع type="submit" على Done أو Remove
  • مسح الصف من الشاشة ونسيان splice، فيُرجعه الرسم التالي
  • تخطي render بعد splice
  • كتابة محتوى الحقل في الصفحة كـ HTML

ما يبقى معك

  • المهمة كائن { text, done } وdone يبدأ false
  • addTask("buy tea") يخزّن buy tea، والفارغ لا يخزّن شيئاً
  • preventDefault يُبقي الصفحة، وrender يرسم الصفوف
  • Done يُظهر done: buy tea، وRemove يترك الطول 0
  • بعد كل تغيير ارسم من جديد

🧠 اختبر معرفتك

جاهز للبدء

اختبر معرفتك

تحدى نفسك مع هذا الاختبار التفاعلي واكتشف مدى فهمك للموضوع

❓
5
الأسئلة
🎯
70%
للنجاح
♾️
∞
الوقت
🔄
∞
المحاولات

📝 التعليمات

  • اقرأ كل سؤال بعناية
  • اختر أفضل إجابة لكل سؤال
  • يمكنك إعادة الاختبار عدة مرات كما تريد
  • سيتم عرض تقدمك في الأعلى