Content updated
By the end, you can…
- Pass a function as a callback without calling it during setup.
- Find an element using its HTML id.
- Attach a function that runs when a button is clicked.
- Keep a JavaScript value and the displayed text in sync.
- Read an input and handle a form submission without navigating away.
What to remember first
An event listener registers work for later. Read current input inside its callback, then update the page.
The parts of an interaction
addEventListener(type, callback)Register a response- Pass a function without calling it. The browser invokes it when the named event occurs.
clickRespond to activation- A real button supports mouse, touch, and keyboard activation. You do not need a separate touch handler for this example.
inputRead edits as they happen- The input event runs as the user edits a field. Read its value property inside the callback.
submitHandle the whole form- Listen on the form so its button and Enter both work. event.preventDefault stops default navigation here.
eventInspect what happened- The callback receives an event object. currentTarget is the element whose listener is running; target is where the event originated.
value / textContentRead input and show output- value reads a form field; textContent changes visible text without interpreting it as HTML.
Connect a familiar function to an event
You already used functions to return values and the DOM to change text. Here the new step is deciding when that work runs. A callback is a function you pass to other code. An event listener keeps it ready until an interaction occurs.
function announce() {
console.log("You ran the function!");
}
const task = announce; // Keep the function value.
task(); // Call it now.Compare announce with announce(): the first is a function value; the second runs it immediately. When attaching a listener, pass the value. The browser will call it later. The button example uses an unnamed arrow function for the same purpose.
A click is an event
A button can look ready to use without doing anything yet. An event is something that happens, such as a click. An event listener connects that event to a function: when the event happens, the browser runs the function. You decide what that function should do.
The browser represents your HTML as a document containing elements. JavaScript can find one of those elements and change it. We will start with two steps: find a button, then tell it what to do when someone clicks. Native buttons also support keyboard activation, so use a real button instead of making a paragraph act like one.
Change a message with one click
This example deliberately uses an inline script below the elements so you can copy one complete file. Do not also run a duplicate copy from script.js. Save it as index.html in a plain-text code editor and open it in your browser. The script comes after the visible elements, so they already exist when JavaScript looks for them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>A friendly button</title>
</head>
<body>
<p id="message" aria-live="polite">Ready to begin?</p>
<button id="greet" type="button">Say hello</button>
<script>
const message = document.querySelector("#message");
const greetButton = document.querySelector("#greet");
greetButton.addEventListener("click", () => {
message.textContent = "Hello! You made something happen.";
});
</script>
</body>
</html>document.querySelector("#message") finds the first element whose id is message. The # means we are selecting an id; it is not part of the HTML id itself. We keep a reference to that element in a variable so we can use it later. The second query finds the button.
addEventListener takes an event name and a function. The event name here is "click". The expression () => { ... } creates an arrow function. Its braces contain the work that runs later, when the click happens. Attaching the listener does not run that work immediately.
textContent changes the text inside the selected element. It treats the value as text, not as HTML markup. The aria-live attribute helps assistive technology announce the updated message without moving keyboard focus. Click the button, then reload the page: the original message returns because this example does not save anything.
See it happen: try a small change
Click the button, then switch to the form and type a name. Each version starts with fresh state.
Code to notice
const button = document.querySelector("#hello");
const message = document.querySelector("#message");
button.addEventListener("click", () => {
message.textContent = "Hello, gardener!";
});Live result
What changed: Registering the listener does not change the message. Clicking the button runs the callback.
A stored value is different from displayed text
Suppose a button adds a star to a score. A variable holds the number, while the page shows a text version of that number. Changing the variable alone does not update the page. Changing the page alone does not update the variable. Keep both steps together in the listener.
let stars = 0;
starButton.addEventListener("click", () => {
stars = stars + 1;
starDisplay.textContent = stars;
});This is a small fragment, not a complete page: starButton and starDisplay must first refer to existing elements. Define stars outside the listener so it keeps its value between clicks. If you create it with zero inside the listener, every click starts over. Use let because the number changes; const remains suitable for an element reference that you do not reassign.
Read an input when a form is submitted
Buttons are only one way people interact with a page. A text input lets someone type a value. Read that text with the input's value property. textContent is for text inside an element such as a paragraph; it does not read what someone typed into an input.
A form groups inputs and a submit button. Its submit event can happen when someone clicks the button or presses Enter in a single text input. Listen on the form so both interactions work. A form normally sends its data and navigates to another page. For this local example, event.preventDefault() stops that default action.
<form id="topic-form" novalidate>
<label for="topic">What would you like to learn?</label>
<input id="topic" name="topic" type="text">
<button type="submit">Choose topic</button>
</form>
<p id="topic-message" role="status"></p>
<script>
const form = document.querySelector("#topic-form");
const input = document.querySelector("#topic");
const message = document.querySelector("#topic-message");
form.addEventListener("submit", (event) => {
event.preventDefault();
const topic = input.value.trim();
if (topic === "") {
message.textContent = "Please choose a topic.";
return;
}
message.textContent = "Let us explore " + topic + ".";
});
</script>Use this form as a separate example, replacing the earlier greeting page’s body so declarations such as message do not collide. Keep its script below the form. Alternatively, move just the JavaScript into your deferred script.js and remove the inline script. The listener's event parameter receives information about this submission. Calling its preventDefault method keeps the current page open. It does not prevent your listener from running.
Read input.value inside the listener to get the latest text each time. trim() returns a new string without spaces at the beginning or end; it does not change the input itself. An input always gives you text, even when it looks like a number. Here we want text, so no conversion is needed.
An if statement runs its block only when its condition is true. The === operator checks equality, while "" means an empty string. A spaces-only input becomes empty after trimming. return exits this callback after showing the reminder, so the success message cannot overwrite it. Otherwise, the final line displays the chosen topic using textContent, which treats the input as plain text.
This example deliberately leaves out required and uses novalidate so you can see your own empty-input check. HTML can also validate forms, and a real service must validate submitted data on its server. This example stays in the browser and does not send the input anywhere.
Try an empty submission, then type CSS and press Enter. Next enter spaces around HTML and submit again. The message should use the current topic without the extra surrounding spaces. You will apply the same steps to a person's name in the greeting-form practice.
Try feedback before submission
The input event reports edits while someone types; submit reports their decision to send the form. Use input for a live draft preview and submit for the final action. change is another event: for a text field it normally reports a committed change when focus leaves the field. Do not replace a form's submit handler with a click-only handler.
<label for="draft">Draft greeting</label>
<input id="draft" name="draft">
<p id="draft-preview" role="status">Start typing.</p>Predict the result, choose a version, then compare the code and output. Try the controls when the example has them. Each version starts fresh.
Code to notice
const input = document.querySelector("#draft");
const preview = document.querySelector("#draft-preview");
input.addEventListener("input", () => {
preview.textContent = input.value || "Start typing.";
});Live result
What changed: Typing updates the draft immediately. Nothing is submitted or stored; clearing the field restores the prompt.
A common wrong turn
A selector that does not match any element returns null. Check the spelling and capitalization of the id, and check that the script runs after the element exists. Calling addEventListener on null cannot attach a listener. Your browser console will usually show an error that helps locate the problem.
Pass the function to the listener instead of calling it while setting up the page. With a named function, use addEventListener("click", sayHello), not addEventListener("click", sayHello()). The parentheses call a function immediately. Also avoid attaching another listener inside the click handler: repeated clicks could add more handlers than you intended.
Change one thing
In the complete example, change the message inside the listener. Save, reload, and predict what appears before and after the click. Next, use Tab to focus Say hello and press Enter. The same click behavior should run. Finally, click several times: the text stays the same because this listener assigns the same message each time.
Your turn: make a button respond
Extend the friendly-button example with a button labeled Start again. When clicked, it should restore Ready to begin? without reloading the page. Keep Say hello working too.
Related practice in the course path
This practice appears in JavaScript, step 11. Follow Continue below to reach it after its prerequisites.
For related guided practice, Build a click counter gives you starter code, a live preview, hints, and checks. The task above is a separate variation to try in your own project.
- Create the elements and connect the event listener in this task.
- Test the first click, repeated clicks, and the reset behavior.
- Use the related counter or greeting practice to apply the same event skills with checks and hints.
Go to the source
- MDN: Form submit event ↗
- MDN: Input value ↗
- MDN: Introduction to events ↗
- MDN: querySelector ↗
- MDN: textContent ↗
Examples are authored for Tuklas and checked with code and reference documentation. This is not a claim of independent human review. Suggest a correction.