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** للتعامل مع العناصر.
- ✅ **الأحداث** للتفاعل مع المستخدم.
- ✅ **الدوال** لتنظيم الكود.
**هذا المشروع هو حجر الأساس لبناء تطبيقات أكثر تعقيداً.** احتفظ بالكود، وطور فيه بنفسك!
**هل بنيت آلة الحاسبة بنجاح؟ شاركنا في التعليقات!**