Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions examples/tip-calculator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Tip Calculator App

A simple tip calculator web application that demonstrates basic math operations, DOM manipulation, event handling, and input validation in JavaScript.

## Features

- **Bill Amount Input**: Enter the total bill amount
- **Tip Percentage Selection**: Choose from preset percentages (10%, 15%, 20%, 25%) or enter a custom percentage
- **Split Bill**: Divide the total among multiple people
- **Real-time Calculations**: Automatically calculates tip amount and total
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

README claims real-time calculations, but the app only updates results on 'Calculate' click; either update the doc to reflect click-to-calculate or call calculateTip() on input changes and tip button clicks to truly make it real-time.

Suggested change
- **Real-time Calculations**: Automatically calculates tip amount and total
- **Click-to-Calculate**: Calculates tip amount and total when you click "Calculate"

Copilot uses AI. Check for mistakes.
- **Input Validation**: Ensures numeric inputs are valid
- **Animations**: Smooth transitions when updating results

## How It Works

### HTML Structure (`index.html`)
- Input field for bill amount
- Buttons for common tip percentages
- Custom tip percentage input
- Number of people input for bill splitting
- Display areas for tip amount, total amount, and per-person cost

### CSS Styling (`style.css`)
- Modern, responsive design with gradient backgrounds
- Smooth animations and transitions
- Visual feedback for active buttons and input validation

### JavaScript Functionality (`script.js`)

#### Variable Management
```javascript
let billAmount = 0;
let tipPercentage = 20; // Default to 20%
let numPeople = 1;
```

#### Input Validation
The `validateNumericInput()` function checks if inputs are valid numbers within acceptable ranges:
- Bill amount must be >= 0
- Custom tip percentage must be >= 0
- Number of people must be >= 1

#### Tip Selection
- Preset buttons update the `tipPercentage` variable and highlight the active button
- Custom tip input overrides preset selection when entered

#### Calculation Logic
The `calculateTip()` function performs the core math:
1. Validates bill amount input
2. Calculates tip amount: `tipAmount = (billAmount * tipPercentage) / 100`
3. Calculates total: `totalAmount = billAmount + tipAmount`
4. If splitting, calculates per-person cost: `amountPerPerson = totalAmount / numPeople`

#### DOM Manipulation
- Results are updated with `animateResult()` function for smooth visual feedback
- Split bill section shows/hides based on number of people

#### Event Handling
- Tip buttons: Update tip percentage and visual state
- Custom tip input: Override button selection
- Calculate button: Trigger calculations
- Input fields: Real-time validation

## Usage

1. Open `index.html` in a web browser
2. Enter the bill amount
3. Select a tip percentage or enter a custom one
4. Optionally, enter number of people to split the bill
5. Click "Calculate" to see results

## Concepts Demonstrated

- **Basic Math Operations**: Multiplication and division for tip calculations
- **DOM Manipulation**: Updating text content and element visibility
- **Event Listeners**: Handling user interactions (clicks, input changes)
- **Input Validation**: Ensuring user inputs are valid before calculations
- **CSS Animations**: Smooth transitions and visual feedback
- **Responsive Design**: Works on different screen sizes

## Browser Compatibility

Works in all modern browsers that support ES6 features.
56 changes: 56 additions & 0 deletions examples/tip-calculator/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tip Calculator</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="calculator">
<h1>Tip Calculator</h1>
<div class="input-group">
<label for="bill-amount">Bill Amount ($):</label>
<input type="number" id="bill-amount" placeholder="0.00" min="0" step="0.01">
</div>

<div class="tip-selection">
<label>Tip Percentage:</label>
<div class="tip-buttons">
<button class="tip-btn" data-tip="10">10%</button>
<button class="tip-btn" data-tip="15">15%</button>
<button class="tip-btn" data-tip="20">20%</button>
<button class="tip-btn" data-tip="25">25%</button>
</div>
<div class="custom-tip">
<input type="number" id="custom-tip" placeholder="Custom %" min="0" max="100">
</div>
Comment on lines +17 to +27
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The custom tip input lacks an associated label and the tip controls aren’t grouped; wrap the percentage controls in a fieldset/legend and add a label or aria-label for the custom tip input to improve screen-reader accessibility.

Copilot uses AI. Check for mistakes.
</div>

<div class="split-bill">
<label for="num-people">Split among:</label>
<input type="number" id="num-people" value="1" min="1">
<span>people</span>
</div>

<button id="calculate-btn">Calculate</button>

<div class="results" id="results">
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dynamic result updates aren’t announced to assistive technologies; add aria-live="polite" (and optionally role="status") to the results container so changes to amounts are announced.

Suggested change
<div class="results" id="results">
<div class="results" id="results" aria-live="polite" role="status">

Copilot uses AI. Check for mistakes.
<div class="result-item">
<span>Tip Amount:</span>
<span id="tip-amount">$0.00</span>
</div>
<div class="result-item">
<span>Total Amount:</span>
<span id="total-amount">$0.00</span>
</div>
<div class="result-item" id="per-person" style="display: none;">
<span>Per Person:</span>
<span id="amount-per-person">$0.00</span>
</div>
</div>
</div>

<script src="script.js"></script>
</body>
</html>
114 changes: 114 additions & 0 deletions examples/tip-calculator/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Get DOM elements
const billAmountInput = document.getElementById('bill-amount');
const tipButtons = document.querySelectorAll('.tip-btn');
const customTipInput = document.getElementById('custom-tip');
const numPeopleInput = document.getElementById('num-people');
const calculateBtn = document.getElementById('calculate-btn');
const tipAmountSpan = document.getElementById('tip-amount');
const totalAmountSpan = document.getElementById('total-amount');
const perPersonDiv = document.getElementById('per-person');
const amountPerPersonSpan = document.getElementById('amount-per-person');

// Variables to store current values
let billAmount = 0;
let tipPercentage = 20; // Default to 20%
let numPeople = 1;

// Function to validate numeric input
function validateNumericInput(input, min = 0) {
const value = parseFloat(input.value);
if (isNaN(value) || value < min) {
input.style.borderColor = '#e74c3c';
return false;
} else {
input.style.borderColor = '#ddd';
return true;
}
}

// Function to update tip percentage from buttons
function updateTipPercentage(percentage) {
tipPercentage = percentage;
customTipInput.value = '';

// Update active button
tipButtons.forEach(btn => {
btn.classList.remove('active');
if (parseInt(btn.dataset.tip) === percentage) {
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a radix with parseInt or use Number() for clarity: parseInt(btn.dataset.tip, 10) or Number(btn.dataset.tip).

Suggested change
if (parseInt(btn.dataset.tip) === percentage) {
if (parseInt(btn.dataset.tip, 10) === percentage) {

Copilot uses AI. Check for mistakes.
btn.classList.add('active');
}
});
Comment on lines +35 to +40
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to the visual .active class, set aria-pressed to reflect selection state for assistive technologies (e.g., btn.setAttribute('aria-pressed', String(parseInt(btn.dataset.tip, 10) === percentage))); also set aria-pressed="false" when clearing active states in the custom tip handler.

Copilot uses AI. Check for mistakes.
}

// Function to calculate tip and total
function calculateTip() {
if (!validateNumericInput(billAmountInput, 0)) {
alert('Please enter a valid bill amount.');
return;
}

billAmount = parseFloat(billAmountInput.value);

// Check if custom tip is entered
if (customTipInput.value !== '') {
if (!validateNumericInput(customTipInput, 0)) {
alert('Please enter a valid tip percentage.');
return;
}
tipPercentage = parseFloat(customTipInput.value);
tipButtons.forEach(btn => btn.classList.remove('active'));
}
Comment on lines +53 to +60
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HTML sets max="100" for the custom tip, but validation only enforces a minimum; extend validateNumericInput to accept a max and enforce it here (e.g., validateNumericInput(customTipInput, 0, 100)) to keep behavior consistent with the input attributes.

Copilot uses AI. Check for mistakes.

numPeople = parseInt(numPeopleInput.value) || 1;
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows 0 or negative values (they’re truthy/falsey but not clamped); enforce the min by clamping: numPeople = Math.max(1, parseInt(numPeopleInput.value, 10) || 1).

Suggested change
numPeople = parseInt(numPeopleInput.value) || 1;
numPeople = Math.max(1, parseInt(numPeopleInput.value, 10) || 1);

Copilot uses AI. Check for mistakes.

const tipAmount = (billAmount * tipPercentage) / 100;
const totalAmount = billAmount + tipAmount;

// Animate result updates
animateResult(tipAmountSpan, tipAmount.toFixed(2));
animateResult(totalAmountSpan, totalAmount.toFixed(2));

// Handle split bill
if (numPeople > 1) {
const amountPerPerson = totalAmount / numPeople;
perPersonDiv.style.display = 'block';
animateResult(amountPerPersonSpan, amountPerPerson.toFixed(2));
} else {
perPersonDiv.style.display = 'none';
}
}

// Function to animate result updates
function animateResult(element, newValue) {
element.textContent = '$' + newValue;
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] String-concatenating '$' assumes USD and locale; consider Intl.NumberFormat for currency formatting (e.g., new Intl.NumberFormat(navigator.language, { style: 'currency', currency: 'USD' }).format(value)).

Suggested change
element.textContent = '$' + newValue;
element.textContent = new Intl.NumberFormat(navigator.language, { style: 'currency', currency: 'USD' }).format(Number(newValue));

Copilot uses AI. Check for mistakes.
element.parentElement.classList.add('updated');
setTimeout(() => {
element.parentElement.classList.remove('updated');
}, 500);
}

// Event listeners for tip buttons
tipButtons.forEach(button => {
button.addEventListener('click', () => {
updateTipPercentage(parseInt(button.dataset.tip));
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specify a radix with parseInt or use Number(): updateTipPercentage(parseInt(button.dataset.tip, 10)) or updateTipPercentage(Number(button.dataset.tip)).

Suggested change
updateTipPercentage(parseInt(button.dataset.tip));
updateTipPercentage(parseInt(button.dataset.tip, 10));

Copilot uses AI. Check for mistakes.
});
});

// Event listener for custom tip input
customTipInput.addEventListener('input', () => {
if (customTipInput.value !== '') {
tipButtons.forEach(btn => btn.classList.remove('active'));
tipPercentage = parseFloat(customTipInput.value) || 0;
}
});

// Event listener for calculate button
calculateBtn.addEventListener('click', calculateTip);

// Event listeners for real-time validation
billAmountInput.addEventListener('input', () => validateNumericInput(billAmountInput, 0));
customTipInput.addEventListener('input', () => validateNumericInput(customTipInput, 0));
numPeopleInput.addEventListener('input', () => validateNumericInput(numPeopleInput, 1));

// Initialize with default values
updateTipPercentage(20);
Loading