Events And User Input
Section overview
In this lesson, we'll bring our tip calculator to life with JavaScript. You will learn how to capture user input from form inputs, respond to events when users interact with the interface, and use event listeners to keep your HTML and JavaScript separate.
By the end of this lesson, you'll have a solid foundation in handling user interaction.
Adding a script tag
JavaScript code can be written directly in HTML using the <script> tag. Let's add one at the bottom of our HTML, just above the closing </body> tag:
</main>
<script>
// JavaScript will go here
</script>
</body>
</html>
- The
<script>tag tells the browser that everything inside should be treated as JavaScript. - We place it at the bottom so the HTML elements load first, making them available to control using JavaScript.
- Comments in JavaScript use
//for single lines.
Creating an update function
Let's create a function that will be called whenever the user interacts with our inputs:
<script>
function update() {
console.log("function called");
}
</script>
function update()creates a new function namedupdate.console.log()outputs a message to the browser's developer console, useful for testing and to know if the function has been called.- The function doesn't do anything yet until we call it.
Calling the function with onclick
Now let's make the function run when the user types in the bill input. Update the bill input element to include an onclick attribute:
<input
type="number"
placeholder="Your bill"
id="yourBill"
onclick="update()"
/>
onclick="update()"tells the browser to call theupdatefunction when the input is clicked.- The parentheses
()afterupdatewill tell JavaScript to execute the function.
Test this in your browser and open the developer console (F12 or right-click > Inspect > Console). Click the input field and you should see "function called" appear.
Using oninput for continuous updates
The onclick event only fires once when clicked. We want our function to run every time the user types. Change onclick to oninput:
<input
type="number"
placeholder="Your bill"
id="yourBill"
oninput="update()"
/>
oninputfires every time the input value changes (as the user types).- Now the function will be called continuously as you type, not just on click.
Try typing in the bill input now and you should see "function called" appear multiple times in the console.
Accessing input values
Now we know our function is being called, let's grab the actual values the user enters. Update your update function:
<script>
function update() {
let bill = document.getElementById("yourBill").value;
let tipPercent = document.getElementById("tipInput").value;
let split = document.getElementById("splitInput").value;
console.log(bill, tipPercent, split);
}
</script>
document.getElementById("yourBill")finds the HTML element with the ID "yourBill"..valuegets the current value from that input element.letdeclares a variable to store the value.- We grab all three inputs: the bill amount, tip percentage, and split number.
Logging as an object
For easier reading in the console, we can log the values as an object. Update the console.log line:
console.log({ bill, tipPercent, split });
- The curly braces
{}creates an object. { bill, tipPercent, split }is shorthand for{ bill: bill, tipPercent: tipPercent, split: split }.- This displays the values with labels in the console, making debugging easier.
When the property name and variable name are the same, JavaScript allows you to use this shorthand syntax. This is called object property shorthand and was introduced in ES6.
The problem with single event handlers
Try adjusting the sliders, you will notice that the console doesn't update! This is because we only added oninput="update()" to the bill input, not to the range sliders.
We could add oninput="update()" to each input individually, but there's a better approach.
Using event delegation
Since all our inputs are inside the <main> container, we can attach a single event handler to the container that will catch events from all its child inputs. Update the bill input to remove the inline event handler:
<input type="number" placeholder="Your bill" id="yourBill" />
We could move this event handler to the <main id="container"> element. But instead, we will remove this and place alongside the rest of our JavaScript code.
Adding an event listener
Now let's add an event listener using JavaScript instead of inline HTML attributes. Update your script:
<script>
function update() {
let bill = document.getElementById("yourBill").value;
let tipPercent = document.getElementById("tipInput").value;
let split = document.getElementById("splitInput").value;
console.log({ bill, tipPercent, split });
}
const container = document.getElementById("container");
container.addEventListener("input", update);
</script>
const containercreates a constant to store a reference to the container element.addEventListener("input", update)tells the container to listen for "input" events from any of its children.- When an input event occurs, it will call our
updatefunction. - Note that we pass
updatewithout parentheses, we're passing a reference to the function, not calling it.
This pattern is called "event delegation". Instead of attaching event listeners to each input, we attach one listener to a parent element. This is more efficient and keeps our HTML clean.
Testing the complete interaction
Now test your calculator:
- Open the browser console (F12)
- Type a bill amount
- Move the tip slider
- Move the split slider
You should see the console update with all three values every time you interact with any input. The object format makes it clear which value is which:
{ bill: "100", tipPercent: "15", split: "2" }
Why this approach is better
Using addEventListener instead of inline event handlers has several advantages:
- Separation of concerns: HTML structure is kept separate from JavaScript.
- Easier maintenance: All event handling logic is in one place.
- More flexible: Multiple event listeners can be added to the same element.
Final code
Here's the complete HTML with JavaScript for this lesson:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link
href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,300;0,700;1,400&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="styles.css" />
<title>Quick Tip</title>
</head>
<body>
<main id="container">
<h1>Quick Tip</h1>
<section>
<div class="bill">
<label for="yourBill">Bill</label>
<input type="number" placeholder="Your bill" id="yourBill" />
</div>
<div>
<div class="space-between">
<label for="tipInput">Select tip</label
><span id="tipPercent"></span>
</div>
<input type="range" value="0" id="tipInput" class="range" />
</div>
<div class="space-between">
<span>Tip</span>
<span id="tipValue"></span>
</div>
<hr />
<div class="space-between total">
<span>Total</span>
<span id="totalWithTip"></span>
</div>
</section>
<section>
<div>
<div class="space-between">
<label for="splitInput">Split</label>
<span id="splitValue"></span>
</div>
<input
type="range"
min="1"
max="10"
value="1"
id="splitInput"
class="range"
/>
</div>
<div class="space-between">
<span>Bill each</span>
<span id="billEach"></span>
</div>
<div class="space-between">
<span>Tip each</span>
<span id="tipEach"></span>
</div>
</section>
</main>
<script>
function update() {
let bill = document.getElementById("yourBill").value;
let tipPercent = document.getElementById("tipInput").value;
let split = document.getElementById("splitInput").value;
console.log({ bill, tipPercent, split });
}
const container = document.getElementById("container");
container.addEventListener("input", update);
</script>
</body>
</html>
Summary
In this lesson, you learned how to:
- Add JavaScript to HTML using the
<script>tag. - Create functions that can be called when events occur.
- Use
onclickandoninputevent attributes (and whyoninputis better for continuous updates). - Access form input values using
document.getElementById()and.value. - Log values to the console for debugging, including object shorthand syntax.
- Use
addEventListener()for cleaner and more maintainable event handling. - Apply event delegation to handle multiple inputs with a single event listener.
Now we're successfully capturing all the user input! In the next lesson, we will use these values to calculate the tip amounts and display them in the interface.