JavaScript📅 2026-09-28⏱ 9 دقائق قراءة📜 مقال 9 من 10

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

مشروع عملي لبناء آلة حاسبة كاملة بـ JavaScript و HTML و CSS — خطوة بخطوة مع الكود الكامل والشرح

في المقالات السابقة، تعلمت أساسيات JavaScript — المتغيرات، الشروط، الحلقات، الدوال، المصفوفات، والكائنات، وأخيراً DOM. الآن حان الوقت لتطبيق كل هذه المفاهيم في مشروع حقيقي: آلة حاسبة كاملة.

في هذا الدليل العملي، سنبني آلة حاسبة من الصفر — HTML للهيكل، CSS للتصميم، JavaScript للوظائف. مع الكود الكامل والشرح خطوة بخطوة.

ما سنبنيه

آلة حاسبة بوظائف:

  • الجمع، الطرح، الضرب، القسمة
  • النسبة المئوية
  • تغيير الإشارة (+/-)
  • المسح (C)
  • الفاصلة العشرية
  • عرض النتيجة مباشرة

لماذا هذا المشروع؟

  • تطبيق عملي: تستخدم كل ما تعلمته.
  • مشروع حقيقي: يمكنك إضافته لمعرض أعمالك.
  • فهم DOM: تتعامل مع الأحداث والنصوص.
  • منطق البرمجة: تفكر كالمبرمجين.

هيكل المشروع

سننشئ 3 ملفات:

calculator/
├── index.html
├── style.css
└── script.js

الخطوة 1: HTML — هيكل الآلة الحاسبة

index.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:

* {
  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:

// ==================== المتغيرات ====================
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. المتغيرات الأساسية

let currentOperand = "0";      // الرقم الحالي المعروض
let previousOperand = "";      // الرقم السابق
let operation = undefined;     // العملية الحالية (+, -, *, /)

2. تحديث الشاشة

function updateDisplay() {
  currentOperandEl.textContent = currentOperand;
  if (operation != null) {
    previousOperandEl.textContent = `${previousOperand} ${operation}`;
  } else {
    previousOperandEl.textContent = "";
  }
}

3. إضافة رقم

function appendNumber(number) {
  if (number === "." && currentOperand.includes(".")) return;
  if (currentOperand === "0" && number !== ".") {
    currentOperand = number;
  } else {
    currentOperand += number;
  }
}

المنطق:

  • إذا كان الرقم نقطة عشرية والرقم يحتوي على نقطة، لا تفعل شيئاً.
  • إذا كان الرقم الحالي "0"، استبدله.
  • وإلا، أضف الرقم.

4. اختيار العملية

function chooseOperation(op) {
  if (currentOperand === "") return;
  if (previousOperand !== "") {
    compute();  // احسب العملية السابقة أولاً
  }
  operation = op;
  previousOperand = currentOperand;
  currentOperand = "";
}

5. الحساب

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. الأحداث

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:

<button data-action="percent">%</button>

أضف في JavaScript:

function percent() {
  currentOperand = (parseFloat(currentOperand) / 100).toString();
}

// في switch
case "percent":
  percent();
  break;

تمرين 2: إضافة الذاكرة (M+ / M-)

أضف زرين لحفظ واسترجاع قيمة.

الحل:

let memory = 0;

// M+ : إضافة للذاكرة
function memoryAdd() {
  memory += parseFloat(currentOperand) || 0;
}

// M- : طرح من الذاكرة
function memorySubtract() {
  memory -= parseFloat(currentOperand) || 0;
}

// MR : استرجاع الذاكرة
function memoryRecall() {
  currentOperand = memory.toString();
}

تمرين 3: إضافة الجذر التربيعي

أضف زر √ لحساب الجذر التربيعي.

الحل:

function squareRoot() {
  const num = parseFloat(currentOperand);
  if (num < 0) {
    alert("لا يمكن حساب جذر عدد سالب!");
    return;
  }
  currentOperand = Math.sqrt(num).toString();
}

تمرين 4: سجل العمليات

أضف قائمة تعرض آخر 5 عمليات.

الحل:

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: الوضع الداكن/الفاتح

أضف زراً لتبديل الوضع.

الحل:

const themeBtn = document.querySelector(".theme-toggle");

themeBtn.addEventListener("click", () => {
  document.body.classList.toggle("light-mode");
});

حل المشاكل الشائعة

🔴 المشكلة 1: القسمة على صفر

السبب: محاولة القسمة على صفر تنتج Infinity.

الحل: تحقق قبل القسمة:

if (current === 0) {
  alert("لا يمكن القسمة على صفر!");
  clear();
  return;
}

🔴 المشكلة 2: النقطة العشرية تتكرر

السبب: إضافة . أكثر من مرة.

الحل:

if (number === "." && currentOperand.includes(".")) return;

🔴 المشكلة 3: الأرقام الطويلة تكسر التصميم

الحل: استخدم word-break: break-all في CSS، وحدد عدد الأرقام:

if (currentOperand.replace(/[^0-9]/g, "").length > 12) return;

🔴 المشكلة 4: النتائج العشرية غير دقيقة

السبب: JavaScript تعاني من مشاكل دقة الأرقام العشرية (0.1 + 0.2 ≠ 0.3).

الحل: استخدم toFixed:

currentOperand = computation.toFixed(10).replace(/\.?0+$/, "");

قائمة تحقق نهائية

المهمةالحالة
إنشاء ملف index.html⬜
إنشاء ملف style.css⬜
إنشاء ملف script.js⬜
تشغيل الآلة الحاسبة في المتصفح⬜
اختبار الجمع والطرح والضرب والقسمة⬜
اختبار الحذف والمسح⬜
حل تمرين واحد على الأقل إضافي⬜

ماذا بعد هذا المشروع؟

الآن بعد أن بنيت آلة حاسبة كاملة، أنت جاهز لـ:

  1. مشروع قائمة المهام — تطبيق أكثر تعقيداً.
  2. Async/Await — البرمجة غير المتزامنة.
  3. Fetch API — التعامل مع الخوادم.

الخلاصة

في هذا المشروع، طبقت:

  • ✅ HTML لبناء الواجهة.
  • ✅ CSS لتصميم جميل.
  • ✅ JavaScript للمنطق والتفاعل.
  • ✅ DOM للتعامل مع العناصر.
  • ✅ الأحداث للتفاعل مع المستخدم.
  • ✅ الدوال لتنظيم الكود.

هذا المشروع هو حجر الأساس لبناء تطبيقات أكثر تعقيداً. احتفظ بالكود، وطور فيه بنفسك!

هل بنيت آلة الحاسبة بنجاح؟ شاركنا في التعليقات!

📚 مقالات ذات صلة

JavaScript

مشروع قائمة المهام بـ JavaScript — دليل عملي شامل 2026

مشروع عملي لبناء تطبيق قائمة مهام كامل بـ JavaScript — إضافة، حذف، تعديل، حفظ تلقائي، مع الكود الكامل والشرح

JavaScript

التعامل مع DOM في JavaScript — دليل شامل 2026

دليل عملي مفصل للتعامل مع DOM في JavaScript — التحديد، التعديل، الأحداث، الإنشاء، مع تمارين وحلول

JavaScript

المصفوفات والكائنات في JavaScript — دليل شامل 2026

دليل عملي مفصل للمصفوفات والكائنات في JavaScript — الإنشاء، التعديل، الطرق الشائعة، مع تمارين وحلول