-
Notifications
You must be signed in to change notification settings - Fork 183
Add example solution for Tip Calculator App (#325) #347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
| - **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. | ||
| 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
|
||||||
| </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"> | ||||||
|
||||||
| <div class="results" id="results"> | |
| <div class="results" id="results" aria-live="polite" role="status"> |
| 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) { | ||||||
|
||||||
| if (parseInt(btn.dataset.tip) === percentage) { | |
| if (parseInt(btn.dataset.tip, 10) === percentage) { |
Copilot
AI
Oct 6, 2025
There was a problem hiding this comment.
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
AI
Oct 6, 2025
There was a problem hiding this comment.
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
AI
Oct 6, 2025
There was a problem hiding this comment.
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).
| numPeople = parseInt(numPeopleInput.value) || 1; | |
| numPeople = Math.max(1, parseInt(numPeopleInput.value, 10) || 1); |
Copilot
AI
Oct 6, 2025
There was a problem hiding this comment.
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)).
| element.textContent = '$' + newValue; | |
| element.textContent = new Intl.NumberFormat(navigator.language, { style: 'currency', currency: 'USD' }).format(Number(newValue)); |
Copilot
AI
Oct 6, 2025
There was a problem hiding this comment.
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)).
| updateTipPercentage(parseInt(button.dataset.tip)); | |
| updateTipPercentage(parseInt(button.dataset.tip, 10)); |
There was a problem hiding this comment.
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.