JavaScript

مشروع آلة حاسبة بـ JavaScript — دليل عملي شامل 2026

📅 2026-09-28⏱ 9 دقائق قراءة
في المقالات السابقة، تعلمت أساسيات JavaScript — المتغيرات، الشروط، الحلقات، الدوال، المصفوفات، والكائنات، وأخيراً DOM. الآن حان الوقت لتطبيق كل هذه المفاهيم في **مشروع حقيقي**: آلة حاسبة كاملة. في هذا الدليل العملي، سنبني آلة حاسبة من الصفر — HTML للهيكل، CSS للتصميم، JavaScript للوظائف. مع الكود الكامل والشرح خطوة بخطوة. ## ما سنبنيه آلة حاسبة بوظائف: - الجمع، الطرح، الضرب، القسمة - النسبة المئوية - تغيير الإشارة (+/-) - المسح (C) - الفاصلة العشرية - عرض النتيجة مباشرة ## لماذا هذا المشروع؟ - **تطبيق عملي:** تستخدم كل ما تعلمته. - **مشروع حقيقي:** يمكنك إضافته لمعرض أعمالك. - **فهم DOM:** تتعامل مع الأحداث والنصوص. - **منطق البرمجة:** تفكر كالمبرمجين. ## هيكل المشروع سننشئ 3 ملفات: ``` calculator/ ├── index.html ├── style.css └── script.js ``` ## الخطوة 1: HTML — هيكل الآلة الحاسبة **`index.html`:** ```html <!DOCTYPE html> <html lang="ar" dir="rtl"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>آلة حاسبة JavaScript</title> <link rel="stylesheet" href="style.css"> </head> <body> <div class="calculator"> <div class="display"> <div class="previous-operand"></div> <div class="current-operand">0</div> </div> <div class="buttons"> <button class="span-two" data-action="clear">C</button> <button data-action="delete">DEL</button> <button data-action="operator" data-operator="/">÷</button> <button data-number="7">7</button> <button data-number="8">8</button> <button data-number="9">9</button> <button data-action="operator" data-operator="*">×</button> <button data-number="4">4</button> <button data-number="5">5</button> <button data-number="6">6</button> <button data-action="operator" data-operator="-">−</button> <button data-number="1">1</button> <button data-number="2">2</button> <button data-number="3">3</button> <button data-action="operator" data-operator="+">+</button> <button data-action="sign">+/-</button> <button data-number="0">0</button> <button data-number=".">.</button> <button data-action="equals">=</button> </div> </div> <script src="script.js"></script> </body> </html> ``` **شرح:** - **`display`:** شاشة الآلة الحاسبة (رقم سابق + رقم حالي). - **`buttons`:** شبكة الأزرار. - **`data-*`:** خصائص لتحديد نوع الزر (رقم، عملية، وظيفة). ## الخطوة 2: CSS — تصميم الآلة الحاسبة **`style.css`:** ```css * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, sans-serif; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 20px; } .calculator { background: #1e293b; border-radius: 20px; padding: 20px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); width: 100%; max-width: 400px; } .display { background: #0f172a; border-radius: 12px; padding: 20px; margin-bottom: 20px; text-align: left; direction: ltr; min-height: 100px; display: flex; flex-direction: column; justify-content: space-between; word-wrap: break-word; word-break: break-all; } .previous-operand { color: #94a3b8; font-size: 1.2rem; min-height: 1.5rem; } .current-operand { color: #ffffff; font-size: 2.5rem; font-weight: bold; } .buttons { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; } button { padding: 20px; font-size: 1.3rem; font-weight: bold; border: none; border-radius: 12px; cursor: pointer; transition: all 0.2s; background: #334155; color: #ffffff; } button:hover { background: #475569; transform: scale(1.05); } button:active { transform: scale(0.95); } .span-two { grid-column: span 2; background: #ef4444; } .span-two:hover { background: #dc2626; } [data-action="operator"] { background: #f59e0b; } [data-action="operator"]:hover { background: #d97706; } [data-action="equals"] { background: #10b981; } [data-action="equals"]:hover { background: #059669; } [data-action="delete"], [data-action="sign"] { background: #64748b; } ``` ## الخطوة 3: JavaScript — منطق الآلة الحاسبة **`script.js`:** ```javascript // ==================== المتغيرات ==================== let currentOperand = "0"; let previousOperand = ""; let operation = undefined; // ==================== العناصر ==================== const currentOperandEl = document.querySelector(".current-operand"); const previousOperandEl = document.querySelector(".previous-operand"); const buttons = document.querySelectorAll("button"); // ==================== الدوال ==================== // تحديث الشاشة function updateDisplay() { currentOperandEl.textContent = currentOperand; if (operation != null) { previousOperandEl.textContent = `${previousOperand} ${getOperatorSymbol(operation)}`; } else { previousOperandEl.textContent = ""; } } // الحصول على رمز العملية function getOperatorSymbol(op) { const symbols = { "+": "+", "-": "−", "*": "×", "/": "÷", }; return symbols[op] || op; } // إضافة رقم function appendNumber(number) { if (number === "." && currentOperand.includes(".")) return; if (currentOperand === "0" && number !== ".") { currentOperand = number; } else { currentOperand += number; } } // اختيار العملية function chooseOperation(op) { if (currentOperand === "") return; if (previousOperand !== "") { compute(); } operation = op; previousOperand = currentOperand; currentOperand = ""; } // الحساب function compute() { let computation; const prev = parseFloat(previousOperand); const current = parseFloat(currentOperand); if (isNaN(prev) || isNaN(current)) return; switch (operation) { case "+": computation = prev + current; break; case "-": computation = prev - current; break; case "*": computation = prev * current; break; case "/": if (current === 0) { alert("لا يمكن القسمة على صفر!"); clear(); return; } computation = prev / current; break; default: return; } currentOperand = computation.toString(); operation = undefined; previousOperand = ""; } // المسح function clear() { currentOperand = "0"; previousOperand = ""; operation = undefined; } // الحذف function deleteNumber() { if (currentOperand.length === 1) { currentOperand = "0"; } else { currentOperand = currentOperand.slice(0, -1); } } // تغيير الإشارة function toggleSign() { if (currentOperand === "0") return; if (currentOperand.startsWith("-")) { currentOperand = currentOperand.slice(1); } else { currentOperand = "-" + currentOperand; } } // ==================== الأحداث ==================== buttons.forEach((button) => { button.addEventListener("click", () => { // أرقام if (button.dataset.number) { appendNumber(button.dataset.number); updateDisplay(); return; } // عمليات if (button.dataset.action === "operator") { chooseOperation(button.dataset.operator); updateDisplay(); return; } // وظائف switch (button.dataset.action) { case "clear": clear(); break; case "delete": deleteNumber(); break; case "equals": compute(); break; case "sign": toggleSign(); break; } updateDisplay(); }); }); // ==================== لوحة المفاتيح ==================== document.addEventListener("keydown", (event) => { const key = event.key; // أرقام if (/^[0-9.]$/.test(key)) { appendNumber(key); updateDisplay(); return; } // عمليات if (["+", "-", "*", "/"].includes(key)) { chooseOperation(key); updateDisplay(); return; } // مسح if (key === "Escape") { clear(); updateDisplay(); return; } // حذف if (key === "Backspace") { deleteNumber(); updateDisplay(); return; } // يساوي if (key === "Enter" || key === "=") { event.preventDefault(); compute(); updateDisplay(); return; } }); // ==================== التهيئة ==================== updateDisplay(); ``` ## شرح الكود خطوة بخطوة ### 1. المتغيرات الأساسية ```javascript let currentOperand = "0"; // الرقم الحالي المعروض let previousOperand = ""; // الرقم السابق let operation = undefined; // العملية الحالية (+, -, *, /) ``` ### 2. تحديث الشاشة ```javascript function updateDisplay() { currentOperandEl.textContent = currentOperand; if (operation != null) { previousOperandEl.textContent = `${previousOperand} ${operation}`; } else { previousOperandEl.textContent = ""; } } ``` ### 3. إضافة رقم ```javascript function appendNumber(number) { if (number === "." && currentOperand.includes(".")) return; if (currentOperand === "0" && number !== ".") { currentOperand = number; } else { currentOperand += number; } } ``` **المنطق:** - إذا كان الرقم نقطة عشرية والرقم يحتوي على نقطة، لا تفعل شيئاً. - إذا كان الرقم الحالي `"0"`، استبدله. - وإلا، أضف الرقم. ### 4. اختيار العملية ```javascript function chooseOperation(op) { if (currentOperand === "") return; if (previousOperand !== "") { compute(); // احسب العملية السابقة أولاً } operation = op; previousOperand = currentOperand; currentOperand = ""; } ``` ### 5. الحساب ```javascript function compute() { const prev = parseFloat(previousOperand); const current = parseFloat(currentOperand); if (isNaN(prev) || isNaN(current)) return; switch (operation) { case "+": computation = prev + current; break; case "-": computation = prev - current; break; case "*": computation = prev * current; break; case "/": if (current === 0) { alert("لا يمكن القسمة على صفر!"); clear(); return; } computation = prev / current; break; } currentOperand = computation.toString(); operation = undefined; previousOperand = ""; } ``` ### 6. الأحداث ```javascript buttons.forEach((button) => { button.addEventListener("click", () => { if (button.dataset.number) { appendNumber(button.dataset.number); } else if (button.dataset.action === "operator") { chooseOperation(button.dataset.operator); } else { // معالجة الأزرار الأخرى } updateDisplay(); }); }); ``` ## تمارين إضافية ### تمرين 1: إضافة زر النسبة المئوية أضف زر `%` يحول الرقم الحالي إلى نسبته. **الحل:** أضف في HTML: ```html <button data-action="percent">%</button> ``` أضف في JavaScript: ```javascript function percent() { currentOperand = (parseFloat(currentOperand) / 100).toString(); } // في switch case "percent": percent(); break; ``` ### تمرين 2: إضافة الذاكرة (M+ / M-) أضف زرين لحفظ واسترجاع قيمة. **الحل:** ```javascript let memory = 0; // M+ : إضافة للذاكرة function memoryAdd() { memory += parseFloat(currentOperand) || 0; } // M- : طرح من الذاكرة function memorySubtract() { memory -= parseFloat(currentOperand) || 0; } // MR : استرجاع الذاكرة function memoryRecall() { currentOperand = memory.toString(); } ``` ### تمرين 3: إضافة الجذر التربيعي أضف زر `√` لحساب الجذر التربيعي. **الحل:** ```javascript function squareRoot() { const num = parseFloat(currentOperand); if (num < 0) { alert("لا يمكن حساب جذر عدد سالب!"); return; } currentOperand = Math.sqrt(num).toString(); } ``` ### تمرين 4: سجل العمليات أضف قائمة تعرض آخر 5 عمليات. **الحل:** ```javascript const history = []; function addToHistory(expression, result) { history.push(`${expression} = ${result}`); if (history.length > 5) history.shift(); displayHistory(); } function displayHistory() { const historyEl = document.querySelector(".history"); historyEl.innerHTML = history.map(h => `<div>${h}</div>`).join(""); } ``` ### تمرين 5: الوضع الداكن/الفاتح أضف زراً لتبديل الوضع. **الحل:** ```javascript const themeBtn = document.querySelector(".theme-toggle"); themeBtn.addEventListener("click", () => { document.body.classList.toggle("light-mode"); }); ``` ## حل المشاكل الشائعة ### 🔴 المشكلة 1: القسمة على صفر **السبب:** محاولة القسمة على صفر تنتج `Infinity`. **الحل:** تحقق قبل القسمة: ```javascript if (current === 0) { alert("لا يمكن القسمة على صفر!"); clear(); return; } ``` ### 🔴 المشكلة 2: النقطة العشرية تتكرر **السبب:** إضافة `.` أكثر من مرة. **الحل:** ```javascript if (number === "." && currentOperand.includes(".")) return; ``` ### 🔴 المشكلة 3: الأرقام الطويلة تكسر التصميم **الحل:** استخدم `word-break: break-all` في CSS، وحدد عدد الأرقام: ```javascript if (currentOperand.replace(/[^0-9]/g, "").length > 12) return; ``` ### 🔴 المشكلة 4: النتائج العشرية غير دقيقة **السبب:** JavaScript تعاني من مشاكل دقة الأرقام العشرية (0.1 + 0.2 ≠ 0.3). **الحل:** استخدم `toFixed`: ```javascript currentOperand = computation.toFixed(10).replace(/\.?0+$/, ""); ``` ## قائمة تحقق نهائية <table> <thead> <tr> <th>المهمة</th> <th>الحالة</th> </tr> </thead> <tbody> <tr> <td>إنشاء ملف index.html</td> <td>⬜</td> </tr> <tr> <td>إنشاء ملف style.css</td> <td>⬜</td> </tr> <tr> <td>إنشاء ملف script.js</td> <td>⬜</td> </tr> <tr> <td>تشغيل الآلة الحاسبة في المتصفح</td> <td>⬜</td> </tr> <tr> <td>اختبار الجمع والطرح والضرب والقسمة</td> <td>⬜</td> </tr> <tr> <td>اختبار الحذف والمسح</td> <td>⬜</td> </tr> <tr> <td>حل تمرين واحد على الأقل إضافي</td> <td>⬜</td> </tr> </tbody> </table> ## ماذا بعد هذا المشروع؟ الآن بعد أن بنيت آلة حاسبة كاملة، أنت جاهز لـ: 1. **مشروع قائمة المهام** — تطبيق أكثر تعقيداً. 2. **Async/Await** — البرمجة غير المتزامنة. 3. **Fetch API** — التعامل مع الخوادم. ## الخلاصة في هذا المشروع، طبقت: - ✅ **HTML** لبناء الواجهة. - ✅ **CSS** لتصميم جميل. - ✅ **JavaScript** للمنطق والتفاعل. - ✅ **DOM** للتعامل مع العناصر. - ✅ **الأحداث** للتفاعل مع المستخدم. - ✅ **الدوال** لتنظيم الكود. **هذا المشروع هو حجر الأساس لبناء تطبيقات أكثر تعقيداً.** احتفظ بالكود، وطور فيه بنفسك! **هل بنيت آلة الحاسبة بنجاح؟ شاركنا في التعليقات!**