- Why events?
- What are events and event listeners
- When and why to use event listeners
- Event types
- Synthesize knowledge of forms with event listeners
-
"In the case of the Web, events are fired inside the browser window, and tend to be attached to a specific item that resides in it — this might be a single HTML element, set of HTML elements, the HTML document loaded in the current tab, or the entire browser window. There are a lot of different types of events that can occur, for example:
- The user clicking the mouse over a certain element or hovering the cursor over a certain element.
- The user pressing a key on the keyboard.
- The user resizing or closing the browser window.
- A web page finishing loading.
- A form being submitted.
- A video being played, or paused, or finishing play.
- An error occurring.
-
You will gather from this (and from glancing at the MDN Event reference) that there are a lot of events that can be responded to.
-
"Each available event has an event handler, which is a block of code (usually a user-defined JavaScript function) that will be run when the event fires. When such a block of code is defined to be run in response to an event firing, we say we are registering an event handler. Note that event handlers are sometimes called event listeners — they are pretty much interchangeable for our purposes, although strictly speaking, they work together. The listener listens out for the event happening, and the handler is the code that is run in response to it happening." - MDN Introduction to Events.
- As it pertains to what we've seen so far, we can tell JavaScript to listen for certain events and invoke a callback function when that event occurs:
const firstBtnOnPage = document.querySelector('button')
firstBtnOnPage.addEventListener('click', function() {
console.log('BUTTON WAS CLICKED')
})
- We are telling
addEventListener
to invoke the anonymous function passed as the second argument when the event fires; we're waiting for something to happen then responding to this event.
-
JavaScript allows us to traverse the DOM and find elements. Let's see how we can target a particular element and listen for events:
-
Assuming our HTML looks like this:
<div id="comments">
<h3>Comments</h3>
<form id="comment-form">
<div class="field">
<input id="new-comment" type="text" placeholder="New Comment" />
<input type="submit" class="btn" value="Submit" />
</div>
</form>
<div id="commentsContainer">
</div>
</div>
- We can grab the
comment-form
and listen for events on it:
const commentForm = document.getElementById('comment-form')
// OR querySelector
// const commentForm = document.querySelector('#comment-form')
- Something to look out for. If we are loading our js files in the
head
tag of our HTML, there is a chance that the JavaScript code we have written will start executing before our HTML has been loaded and parsed by the browser. This might cause some element selectors to returnnull
. As a precaution, we can listen for theDOMContentLoaded
event.- "The
DOMContentLoaded
event is fired when the initial HTML document has been completely loaded and parsed" - MDN DOMContentLoaded Reference. Let's add this to our code:
- "The
document.addEventListener('DOMContentLoaded', function() {
const commentForm = document.getElementById('comment-form')
})
-
In the snippet above, we are adding an event listener to the document and listening for the
DOMContentLoaded
event. When that event is fired, the anonymous function passed toaddEventListener
will be invoked.- "
addEventListener()
sets up a function that will be called whenever the specifiedevent
is delivered to the target. Common targets are HTMLElement
,Document
, andWindow
" - MDNaddEventListener
Reference
- "
-
Now that we're waiting for our DOM content to load, let's listen for a
submit
event on our form:- "The
submit
event is fired when a form is submitted. - Note that
submit
is fired only on the form element, not the button or submit input. (Forms are submitted, not buttons.)" - MDN Article on thesubmit
event
- "The
document.addEventListener('DOMContentLoaded', function() {
const commentForm = document.getElementById('comment-form')
commentForm.addEventListener('submit', function(event) {
console.log(event)
})
})
- If we try adding something to the form and clicking submit, we'll see our
console.log
for a second then it will disappear.
- Forms will attempt to make an HTTP
POST
request on submission. Recall from Mod2 that our forms would send aPOST
request that was then handled by our controller (remember HTTP and the request/response cycle?). If we give ourform
anaction
attribute, it will try toPOST
to the endpoint specified by theaction
attribute:
<form id="comment-form" action="/hotdog">
<div class="field">
<input id="new-comment" type="text" placeholder="New Comment" />
<input type="submit" class="btn" value="Submit" />
</div>
</form>
- This form ⬆️ will try to send a
POST
request to/hotdog
- If our form does not have an action attribute it will attempt to
POST
to the URL we are currently on, making it appear as though our page is being refreshed. Even though it looks like the page is being refreshed, that is not technically what is happening. The form is sending a POST request out into the void
- Our current JS app is not currently sending data to a server, so we'll need some way to prevent this default action of submitting the form 🤔
- Let's tell our event handler––our callback function––to
preventDefault
:
document.addEventListener('DOMContentLoaded', function() {
const commentForm = document.getElementById('comment-form')
commentForm.addEventListener('submit', function(event) {
event.preventDefault() //stop form from POSTING
console.log(event.target) //form
})
})
- Some key points about the
event
object that is passed to ourformSubmitEventHandler
function as a parameter:- "The Event interface represents any event which takes place in the DOM; some are user-generated (such as mouse or keyboard events), while others are generated by APIs (such as events that indicate an animation has finished running, a video has been paused, and so forth). There are many types of events, some of which use other interfaces based on the main Event interface. Event itself contains the properties and methods which are common to all events." - MDN Article on
Event
event.target
refers to the HTML Element that dispatched the event. For example, if I tell my app to listen forclick
events and the user clicks on ap
tag,event.target
will be thep
tag. If the user clicks on abutton
theevent.target
will be the button. In our case, theform
is causing thesubmit
event to fire.- This is something I'd strongly recommend burning into your memory: HTML forms will attempt to send a POST request. Any child of an HTML form such as an
<input/>
or<button></button>
will cause the form to submit. YOU DO NOT WANT TO LISTEN FOR A CLICK EVENT IN A FORM; YOU SHOULD BE LISTENING FOR THEsubmit
EVENT!!! super important lol
- "The Event interface represents any event which takes place in the DOM; some are user-generated (such as mouse or keyboard events), while others are generated by APIs (such as events that indicate an animation has finished running, a video has been paused, and so forth). There are many types of events, some of which use other interfaces based on the main Event interface. Event itself contains the properties and methods which are common to all events." - MDN Article on
- We need to grab the user's input from the form. That information is stored in the
input
tag inside the form. If we refer back to ourform
we can see thatinput
is a child of theform
itself:
<form id="comment-form" action="/hotdog">
<div class="field">
<input id="new-comment" type="text" placeholder="New Comment" />
<input type="submit" class="btn" value="Submit" />
</div>
</form>
- Since our
event.target
is thecomment-form
itself, we can grab the input usingquerySelector
:
document.addEventListener('DOMContentLoaded', function() {
const commentForm = document.getElementById('comment-form')
commentForm.addEventListener('submit', function(event) {
event.preventDefault() //stop form from POSTING
const userInputField = event.target.querySelector('#new-comment')
//OR document.querySelector('#new-comment')
const userInputString = userInputField.value
})
})
-
userInputField
will give us the wholeinput
element. Since we only care about the user's new comment, we can grab thevalue
attribute which will be whatever comment the user typed into the field. Refer to the MDN Docs forinput
tags if you're unfamiliar. -
Let's use the information the user typed into the form to add their comment to the page:
document.addEventListener('DOMContentLoaded', function () {
const commentsContainer = document.getElementById('commentsContainer')
const commentForm = document.getElementById('comment-form')
commentForm.addEventListener('submit', function(event) {
event.preventDefault() //stop form from POSTING
const userInputField = event.target.querySelector('#new-comment')
const userInputString = userInputField.value
const commentPTag = document.createElement('p')
commentPTag.textContent = userInputString
commentsContainer.appendChild(commentPTag)
})
})
- "In an HTML document, the
document.createElement()
method creates the HTML element specified bytagName
." - MDN Article oncreateElement
- In other words, calling
document.createElement('p')
will create ap
tag. - This tag is an HTML object that we can manipulate using JavaScript. We can change the style, or give it a particular
textContent
as a string.
- In other words, calling
- We can then
append
thatp
tag to the DOM by callingcommentsContainer.appendChild(commentPTag)
. Also note thatcommentsContainer
is declared at the top of our function.
- Given a series of buttons deeply nested in some
<div></div>
tags on our page:
<div id="helicopter-parent">
<div>
<br>
<div>
<br>
<div>
<p>HI</p>
<div>
<button class="btn" data-name="alert">Alert ME</button>
<button class="btn" data-name="log">Console Log something</button>
<button class="btn" data-name="error">Console Error</button>
</div>
</div>
</div>
</div>
</div>
- How might we listen for events on those buttons?
- We could find all the buttons, loop over that collection and attach several event listeners for our buttons 😔. There must be a better way!™
-
The diagram above outlines the flow of JS events from the target all the way up the DOM (tree) to the topmost node, the
Document
- In other words, every HTML element will know about everything that happens to its children
-
Instead of iterating over the buttons and attaching duplicate event handlers, we can create one event handler to Rule Them All™:
const helicopterNode = document.getElementById('helicopter-parent')
helicopterNode.addEventListener('click', function(event) {
console.log(event.target) //event target will be whatever node was clicked
})
- Now we can introduce some control flow to our click handler and decide what to do based on which button was clicked:
helicopterNode.addEventListener('click', function(event) {
// i do not need to prevent the click default action
//event.target is the node that was clicked
// our buttons have a key of dataset -> { name: 'alert' }
// i am checking the value of button.dataset.name and deciding what to do based on what i find
if (event.target.dataset.name === 'alert') {
window.alert('HI')
} else if (event.target.dataset.name === 'log') {
console.log('HI')
} else if (event.target.dataset.name === 'error') {
console.error('HI')
}
})
- nice
- MDN Introduction to Events
- MDN Event reference
- MDN DOMContentLoaded Reference
- MDN
addEventListener
Reference - MDN Article on Sending Form Data
- MDN Article on
Event
- MDN Article on the
submit
event - MDN Article on
input
tags) - MDN Article on
createElement
- MDN Article on
textContent
- MDN Article on Dataset
- JavaScript.info Article on Event Bubbling