Week 6 - Events, Forms, & Navigation
Events
Section titled “Events”In JavaScript, an event
is something that happens to an object.
Objects have property values, which are values that can be used to describe an object. Property values can be things like size, speed, colour, name, id, etc. String.prototype.length
is an example of what are generally referred to as a property.
Objects have methods, which are functions that belong to the object that make something happen. It could be something that the Object can do or something that the object does to another Object. Methods are also properties but are just called methods. Array.prototype.sort()
is an example of a method. You can usually tell the difference between them in your code by the parentheses that
appear after the name of the method. Properties don’t have parentheses.
Event
s are things that happen to an object. A webpage loads. An anchor tag is clicked. A form is submitted. load
, click
, and submit
are all examples of events.
Back in the early days of the web events were usually added through the HTML, like this:
WARNING
<p onclick="someFunction()">click me</p>
In the first few years of JavaScript, there were ways of adding events through JavaScript but they varied by browser.
In the early 2000s a standardized way of adding and removing events was created that worked in all browsers.
Adding and Removing Events
Section titled “Adding and Removing Events”All HTML DOM Element Nodes have built-in methods called addEventListener
and removeEventListener
. The standard way to add an event to a DOM element is like this:
//get a reference to the <h1> in the mastheadlet h1 = document.querySelector('.masthead h1');//add a click event listener to the <h1> elementh1.addEventListener('click', doSomething);// OBJECT.addEventListener( EVENT_TYPE, FUNCTION_NAME )
After these two lines of code run, the user can click on the <h1>
element as many times as they want. Each time the function called doSomething
will run.
Note the lack of parentheses after the function name. If you add the parentheses then the function runs immediately, without waiting for the
click
event.
Intro to event listeners
You can add as many event listeners to an element as you want. Just pass in an event-type and the name of a function.
If you want to remove an event listener from an object then use the removeEventListener
method. Pass to it the combination of event-type and name of function that you want to remove.
h1.removeEventListener('click', doSomething);
An alternative way of adding event listeners is to use an anonymous function.
h1.addEventListener('click', function (ev) { //this is an anonymous function that will run when the user clicks the h1});
The downside of doing this is that you cannot reuse the function somewhere else in your code. So, only use an anonymous function when your function is one or two lines of code and you DEFINITELY won’t use it again somewhere else.
removing event listeners
The Event Argument
Section titled “The Event Argument”If you look at the example with the anonymous function above, you will see a variable called ev
that is a function argument which holds the actual Event Object that JavaScript was listening for.
Listening for a click event? The function will be passed a click Event Object
. Listening for a load
event? The function will be passed a load Event Object
. Listening for a submit
event? The function will be passed a submit Event Object
.
Every Event Object
will contain a few standard properties and a few properties that will vary depending on the type of event.
The standard properties include target
and type
. The type
will be the name of event that happened - click
, load
, submit
, etc.
Event Target
Section titled “Event Target”The target
is the element that reported the Event to the Event Listener.
//find a ul with the className userListlet ul = document.querySelector('.user-list');//add a click listener to the ul.ul.addEventListener('click', highlightLI);
//the function that will add a CSS class to the LI that was clicked inside the ULfunction highlightLI(ev) { //ev is a standard variable name to represent the event object //ask for the target of the click event let li = ev.target; //add the CSS class `highlight` to the clicked li li.classList.add('highlight');}
Target and CurrentTarget
Section titled “Target and CurrentTarget”There are actually two similar properties on the Event object. One is the target
and the other is currentTarget
.
The target
is the actual triggering point of the event. The currentTarget
can be used to track the event as it bubbles up through the DOM.
target vs currenttarget
We can also check to see if it was actually an li
that was clicked. Here is a modified version of the highlightLI
function that checks.
function highlightLI(ev) { let li = ev.target; //only add the class if the target was an <li> // the tagName property of every DOM element is in uppercase if (li.tagName === 'LI') { li.classList.add('highlight'); }}
We can also locate a specific element based on the target of a click. Let’s start with this HTML
<ul class="movie-list"> <li class="movie" data-ref="76759"><span class="title">Star Wars</span><span class="year">1976</span></li> <li class="movie" data-ref="1136608"><span class="title">District 9</span><span class="year">2009</span></li> <li class="movie" data-ref="130827"><span class="title">Run Lola Run</span><span class="year">1998</span></li></ul>
When a user clicks inside the ul
, depending on the padding and margins, they could be clicking on the ul
or an li
or any one of the span
elements. We have class names on all the elements that will help us to identify which element was clicked. We also have a data-ref
attribute in the li.movie
elements.
As a best practice we do NOT want to add click listeners to every one of those items. It would be bad for performance. Instead, we will add ONE listener to the ul
. When it’s listener function is triggered we will get the ev.target
and then from that find our li
element.
let ul = document.querySelector('.movie-list');ul.addEventListener('click', pickMovie);
function pickMovie(ev) { //get a reference to whatever was clicked inside the ul let target = ev.target; //use the `closest` method to find the closest element with the class `movie` let li = target.closest('.movie'); //it works just like querySelector() except it walks up through the parent elements looking //for the specific CSS selector match console.log(`You clicked on movie ${li.getAttribute('data-ref')}`);}
Planning your event targets
closest and matches
Self Removing Event Listeners
Section titled “Self Removing Event Listeners”There is a third argument for the addEventListener
method. It is an optional Object with a few properties. You can use the once
property set to true
to prevent the event listener from running
more than once. It will actually remove the event listener automatically the first time it runs the function.
myElement.addEventListener('click', doSomething, { once: true });
event listener options
Event Bubbling and Propagation
Section titled “Event Bubbling and Propagation”Events in the DOM will bubble
by default.
Does this mean that they are happy? No. Does this mean that they are filled with CO2? No.
It means that when an event happens to a DOM element, it will travel up the HTML source through it’s parent elements until it reaches the <html>
element.
Let’s use this basic HTML as a reference:
<html> <head> <title>My Webpage</title> </head> <body> <header> <h1>Some Site Title</h1> </header> <main> <h2>Page Title</h2> <p>Lorem Ipsum</p> <p>Lala Palooza <span>Click Me!!!!</span></p> <p>Lorem Ipsum</p> <p>Lorem Ipsum</p> </main> </body></html>
We want our user to click on the span with the text Click Me!!!!
. Forgetting any JavaScript that might be attached to the page for a moment, Let’s look at what happens with a click event.
The user clicks on the <span>
. The click event will then bubble
to the parent element of the span
. This is the <p>
with the text Lala Palooza
. It will then bubble
to the next parent element - the <main>
element. The <h2>
and the first <p>
are not parent elements, they are siblings of the Lala Palooza paragraph.
From main
it will bubble through body
and then to html
.
If you want to STOP the event from bubbling up to it’s parent element then, inside the event listener function, we call the stopPropagation()
method on the Event Object that was passed to the function.
function doSomething(ev) { //ev is the event object passed to this function ev.stopPropagation();}
Sometimes, you might have a DOM element that has more than one listener for the same event. Eg: a button that has multiple click listeners.
Now we are not looking at bubbling
of the event. The event is on the same element and just triggering multiple functions. If we want to stop subsequent listeners on the same element, as well as, stopping the bubbling, we can use stopImmediatePropagation
.
let btn = document.getElementById('myButton');//add 4 listeners to the button so clicking it will run four functions//the functions will run in the order that they were added.btn.addEventListener('click', doTheFirstThing);btn.addEventListener('click', doTheSecondThing);btn.addEventListener('click', doTheThirdThing);btn.addEventListener('click', doTheFourthThing);
//In the 2nd function we are going to check for a setting//if the setting is false then we do NOT want the 3rd or 4th functions to run.function doSecondThing(ev) { if (setting === false) { ev.stopImmediatePropagation(); }}
Bubbling and Propagation
Related Target
Prevent Default
Section titled “Prevent Default”One last event object method that you will frequently use is preventDefault()
.
Some Objects have built-in default behaviours. These are things that will happen without any JavaScript even being loaded on the page.
If you click an anchor - the browser will navigate to a new page.
If you click a submit button - the browser will bundle the data in a form and send it to the server.
If you right-click on a page - the browser will show you a context menu with options.
There will absolutely be times when you want to stop the browser doing the default activity. You will want to use JavaScript to decide what happens next.
//find an anchor, form, and submit button//give them listeners to call the same sample functionlet a = document.querySelector('a#linkHome');a.addEventListener('click', doSomething);
//forms have a submit eventlet myForm = document.querySelector('form#myForm');myForm.addEventListener('submit', doSomething);
let btnSubmit = document.querySelector('button#btnSubmit');btnSubmit.addEventListener('click', doSomething);
function doSomething(ev) { console.log(ev.type); //could be click or submit //stop the browser from doing it's default activity ev.preventDefault(); //now we can do whatever we want...}
Page, File, and Navigation Events
Section titled “Page, File, and Navigation Events”There are different events that help us keep track of the progress of loading files and web pages, knowing when scripts are ready to run, when the user is starting to navigate, when the browser is switching between tabs, if storage has been updated, if the page orientation or device orientation changes, when the browser goes online or offline, scrolling and resizing.
Most of these events occur on the global window
object. In the list below the objects that the event happens to are shown.
window.addEventListener('load', (ev) => { //the window has loaded all html, css, js, fonts, images, etc //fires after DOMContentLoaded});img.addEventListener('load', (ev) => { //load also fires for images, scripts, audio, video, css files.});window.addEventListener('DOMContentLoaded', (ev) => { //The HTML has been loaded and parse. The DOM is ready to be used. //first event that happens when loading a page});window.addEventListener('pageshow', (ev) => { //user has navigated to a page and the load event has fired});window.addEventListener('beforeunload', (ev) => { //about to leave the page. last chance to do something with the page});window.addEventListener('unload', (ev) => { //browser is leaving this page. You can't stop it now...});window.addEventListener('hashchange', (ev) => { //the hash value in the URL has changed. navigating within the page. //happens after popstate});window.addEventListener('popstate', (ev) => { //navigated to a new page or within the history array //happens after pageshow but before hashchange});
- window | img | script
load
- window
DOMContentLoaded
- window
pageshow
/pagehide
- window
beforeunload
- window
unload
- window
hashchange
- window
popstate
- window
online
/offline
: fully looses connection or regains even a weak one - window
orientationchange
: landscape vs portrait - window
deviceorientation
: mobile device rotates along any of the 3 axis - window
error
: when any error happens in your script - window
storage
: web storage updated on another tab - window
beforeprint
/afterprint
- document
visibilitychange
: active tab changes - document
scroll
- document
resize
- document
cut
/copy
/paste
load vs DOMContentLoaded Events
online and offline Events
copy paste Events
Mouse Events
Section titled “Mouse Events”When using a mouse there are a number of methods that we can use to track how the user is using the mouse to interact with the webpage. The pointer
and touch
events will become mouse events on
non-touch devices.
Remember that click events all bubble.
Every mouse event will give you useful information about the mouse event like x
and y
position.
click
: left clickdblclick
: clicked twice in rapid successionmouseover
/mouseout
: mouse cursor moves over edge of element or leaves content box of elementmouseenter
/mouseleave
: same as over/out except checks parent-child relationshipmousemove
: mouse cursor moves while over the element in questioncontextmenu
: right click on mouse
mouseover vs mouseenter
right-click Menus
Finding Elements from Mouse Position
Pointer and Touch Events
Section titled “Pointer and Touch Events”In addition to the mouse events, there are touch events and pointer events which were added to the HTML5 APIs to handle things like stylus pointers, and multi-touch with fingers.
The built-in touch
events are quite basic. There are no built-in events for things like swipe
, pin
, pinch
, zoom
, or rotate
. If you want those things you will have to use a library or write
your own code to calculate changes that happen with touchmove
between a touchstart
and a touchend
event.
- document
pointerup
/pointerdown
- document
pointerenter
/pointerleave
- document
pointerover
/pointerout
- document
pointermove
- document
pointercancel
- document
touchstart
- document
touchend
- document
touchmove
- document
touchcancel
JS Touch events
pointer-events and touch-action css
Keyboard Events
Section titled “Keyboard Events”There are three keyboard events. keydown
is the process of pushing a key down. keyup
is releasing the key. keypress
says that both have happened to the same key.
You can attach these events to an <input>
or <textarea>
but it makes more sense to use the appropriate form events for that. The Keyboard events are intended for things like left and right arrows
to move through a slideshow or an escape key to close a modal window.
- document | input
keydown
- document | input
keyup
- document | input
keypress
Keyboard Events
Mobile Keyboards and Forms
inputmode for Mobile Keyboards
enterkeyhint for Mobile Keyboards
Form Events
Section titled “Form Events”These are the events to use with the form, input, select, and textarea elements.
- input | select | textarea
change
- input | textarea
input
- input | select | textarea
focus
- input | select | textarea
blur
- button
click
- form
submit
Form Events
CSS Events
Section titled “CSS Events”If you have animations or transitions in your CSS that get triggered when you add CSS class names to your elements. We can tap into the animationstart
, animationend
, transitionstart
and
transitionend
events to use as triggers to run something else.
NOTE: these events are not 100% guaranteed to fire. If the browser is busy doing many things it is possible that it could miss the end of a transition or animation.
- document
animationstart
- document
animationend
- document
animationiteration
- document
transitionend
transition and animation events
HTTP 203 animation gotchas
Drag Events
Section titled “Drag Events”HTML5 has a drag and drop API that lets the user move things around the page. This is a list of the associated events that you can use to build your own drag and drop interactions on your pages.
You will be able to customize the visual item dragged as well as carry data from one place to another.
dragstart
drag
dragend
dragenter
dragleave
dragover
drop
Everything drag and drop
Working with Forms
Section titled “Working with Forms”In your Web Design Course you have learned how to create forms with HTML and style them with CSS. We have talked about JSON and FormData objects in this course. FormData objects are a JavaScript representation of what you would be sending to the web server if you submitted the HTML form, without JS.
A quick way to create a JavaScript FormData
object from a form is to pass a reference to the form to the FormData
constructor.
Effective use of FormData
Here is an overview of how to interact with HTML forms using JavaScript. It includes discussions on submitting and resetting forms, checkboxes and radio buttons, the input
and change
and select
events, and more.
HTML Forms and JavaScript
Here are some key points to be aware of when working with forms.
- Forms want to submit and load a new page into the browser. They are like clicking a link. They do this without any JavaScript.
- A form will submit if the user clicks a button inside the form, or if they hit
<enter>
inside of an<input>
element. - If you want to stop a form submitting then you need to add an event listener for the
submit
event and call thepreventDefault()
method. - All
<input>
elements need to have anid
attribute AND aname
attribute. - The
id
attribute is for JavaScript and CSS to reference the element. - The
name
attribute will be used by the browser when submitting the form. - The
action
attribute of a<form>
element, is like thehref
attribute of an<a>
element. It tells the browser what page to load.
A select
element will have a value
property as well as a selectedIndex
property. The value
is the value of the <option>
picked by the user. The selectedIndex
is the numeric index of the chosen <option>
element inside the select
.
Take this simple example form.
<form id="myform" action="somepage.html"> <select id="numbers" name="numbers"> <option value="1">One</option> <option value="2">Two</option> <option value="3" selected>Three</option> <!-- default selected option --> <option value="4">Four</option> </select></form>
We can reference the select element from that form in any of these ways.
let selectA = document.getElementById('numbers');let selectB = document.getElementById('myform')['numbers'];let selectC = document.forms[0]['numbers'];let selectD = document.querySelector('#myform')['numbers'];let selectE = document.getElementById('myform').querySelector('#numbers');
Once you have the reference to the select, then you can access the value or position of the currently selected option element like this:
selectA.value;selectA.selectedIndex;selectA.options[selectA.selectedIndex];
It is possible to allow users to pick more than one option. We just add the multiple
attribute to the <select>
<select id="numbers" multiple></select>
If you really need to loop through the options and compare all the values against a variable or check if each one has been selected, then we can loop through the options like this:
let selectedOptions = [];let selectedValues = [];selectA.options.forEach(function (option) { if (option.selected) { selectedOptions.push(option); //save a reference to the option in an array selectedValues.push(option.value); }});
working with Select, options, optgroup
If you would rather or need to work with JSON
instead of a FormData
object, this video talks about building a JavaScript Object from the data in your form.
Once you have a JS object you can then call JSON.stringify()
on that object to convert it to a JSON
string that can be uploaded with a fetch
call.
Put JS form values into JS Objects
See the notes in Data Fetch Module for more details on uploading data with fetch
.
Here is a refresher on Keyboard
Events.
Keyboard Events
A quick video talking about how you can properly test for an empty input field.
Testing for Empty fields
These next two videos explain modern web form validation in depth and in detail.
Form Validation part I
Form Validation part 2
And finally, how to enable and disable autocomplete features in web forms.
Helping Users with Autocomplete
Dispatching Events
Section titled “Dispatching Events”We have discussed and used many events. Did you know that some events also have matching methods that you can call?
//submit a formdocument.querySelector('#myForm').submit();//reset a formdocument.querySelector('#myForm').reset();//click a linkdocument.querySelector('a.someLink').click();
Not all events have a matching method and not all elements are able to run the methods.
There is also a way that you dispatch
and event. Basically, you can make an element think that an event has happened to it. And if that element has an event listener then it will be triggered. The syntax to do this is actually very simple. It is a method that you can call on any DOM Element to dispatch the event. You just need to create an Event
object to pass to the method.
someElement.dispatchEvent(someEventObject);
The Event
object will have a type
property that indicates which type of event is happening to the referenced element.
//target the 3rd li in the sidebarlet someElement = document.querySelectorAll('.sideNav li')[2];someElement.addEventListener('click', (ev) => { //this will run when our event gets dispatched console.log('I was just clicked... I think.');});//create a click eventlet ev = new Event('click', { bubbles: false, cancelable: false, composed: false,});//the options object is optional. This one shows the default values for all the properties
//now make the click happen to the <li>someElement.dispatchEvent(ev);
Creating and Dispatching Custom Events
Custom Events
Section titled “Custom Events”What is a custom event? It is an event that does not already exist in the DOM or other HTML5 APIs. You might want an event that fires on your document
object telling you that the next set of items have been fetched from an API. Maybe you aren’t adding data to the screen as soon as it comes back from the server, but you want the document
object to know that data is ready to be loaded. Maybe you want to create custom events that fire during css animations or transitions. Whatever the event, you can create it. You really just create an Event
and give it a name.
As long as your object is able to call addEventListener
then it can know that the event was triggered.
There are a few ways that you can create your own custom events too. Here are the first two.
//create a basic event with a namelet ev1 = new Event('beerOpened');//create a custom event that can contain data that will be part of the event objectlet ev2 = new CustomEvent('beerOpened', { detail: { today: new Date(), brand: 'Corona' },});
If you use the CustomEvent
constructor, the second parameter is an object that must have a detail
property. The detail property can contain whatever you want. This is the custom data that will be available through the event when addEventListener
calls the listener function.
let p = document.querySelector('main p');p.addEventListener('beerOpened', displayBeer);//listen for the string name (type) of the eventp.dispatchEvent(ev2);//use the event object to dispatch, not the string type name of the event
function displayBeer(ev) { let span = document.createElement('span'); span.textContent = `A ${ev.detail.brand} was opened at ${ev.detail.today}`; p.append(span);}
All the properties inside the Event
must be put inside detail
.
Custom Events with Class Syntax
Section titled “Custom Events with Class Syntax”Another way of creating a custom event is to use the class
syntax and extend
the base Error object.
class ScreamEvent extends Event { constructor(prop) { super('scream'); //create a basic Event object that sets the type as 'scream' this.detail = prop; //create a custom property for our Event }}
And then we can add our ScreamEvent
to objects that have the addEventListener
method.
//create a ScreamEventlet sev = new ScreamEvent({ msg: 'hi' });//add a listener for the ScreamEvent on the document objectdocument.addEventListener('scream', (ev) => { console.log('screamed', ev.detail.msg);});//dispatch the ScreamEventdocument.dispatchEvent(sev);
When you build your own Objects it is also possible for you to make them listen for events.
It is also possible for you to create your own custom events and then attach those events to existing objects or objects that you created.
If you want to use one of the built-in events like a Mouse Event, then you can use new Event()
for a generic one or new MouseEvent('click')
for a specific type. Just use the exact constructor that you want.
If you want to invent a new kind of event then use new CustomEvent()
. With the custom event you need to pass in a name for the event, and then pass an options object as the second parameter. If you want to add custom properties that can be accessed through the event object, then add a detail
property to the options object. Inside that you can add whatever you need.
let mouseEv = new MouseEvent('click', { screenX: 200, screenY: 200 });//create a click MouseEvent at the position (200,200) on the screen
let myEv = new Event('finished');// create a generic event with the type property set as 'finished'
let awakeEv = new CustomEvent('awakened', { detail: { awake: true, message: 'good morning sunshine', },});// create a custom event object with custom properties.
With ANY of these events created as an event object we can make the browser think that they have happened by dispatching the event. See more about dispatching events in the above section.
const main = document.querySelector('main');main.dispatchEvent(mouseEv);main.dispatchEvent(myEv);main.dispatchEvent(awakeEv);
Any listener that is attached to the HTML element for the correct type of event will trigger its event handler function. The handler function will be passed an Event object which is of the type that was dispatched. From that event object, we can access the properties of the event like screenX, screenY, or type. We can also access the detail
properties of any CustomEvent that we created.
main.addEventListener('click', handlerFunc);main.addEventListener('finished', handlerFunc);main.addEventListener('awakened', handlerFunc);
function handlerFunc(ev) { //ev is the event object //all three events will trigger and run this function console.log(ev.type); // click or finished or awakened if (ev.type === 'awakened') { console.log(ev.detail.message); //'good morning sunshine' }}
Custom Events
Handle Event Property
View Transition API
Section titled “View Transition API”The new View Transition API is currently supported in Chrome, Edge, and Opera. Hopefully, in the near future Firefox and Safari will also start to support it too. In the meantime, you can use these as a progressive enhancement.
The initial version of the API works only on View Transitions between states on the current page. In other words, this is intended to be used for Single Page Applications. As the user clicks on buttons and links and your script changes what is being displayed on the page, this API will be able to create transitions between the starting and ending state of the page.
Checking for Feature Support
Section titled “Checking for Feature Support”Whenever you use something as a progressive enhancement you need to test for browser support first. Call a function that will start the process of updating the interface of your SPA. Check if the browser supports the method document.startViewTransition
. Whether it does or not you will end up calling your function to make the actual changes to the DOM.
If the browser does support the startViewTransition
method then you call that and pass it your function for updating the DOM.
function doSomeNavigation() { // check for support for the startViewTransition method if (!document.startViewTransition) { //this code runs for browsers that do NOT support it yet //call your method that will make changes to the page updateTheDOMSomehow(); //exit the function if there was no support return; }
// With a transition: document.startViewTransition(() => updateTheDOMSomehow());}
What the startViewTransition
method does is take a snapshot of the page just before your DOM update function is called. It monitors all the changes that would be made by your method and then creates the visual transition between the starting state and the ending state.
CSS Pseudo Elements
Section titled “CSS Pseudo Elements”To help with the visual changes, in addition to the snapshot, the startViewTransition
method will create the following pseudo-elements.
::view-transition└─ ::view-transition-group(root) └─ ::view-transition-image-pair(root) ├─ ::view-transition-old(root) └─ ::view-transition-new(root)
The ::view-transition
sits in an overlay, over everything else on the page. This is useful if you want to set a background color for the transition.
::view-transition-old(root)
is a screenshot of the old view, and ::view-transition-new(root)
is a live representation of the new view. Both render as CSS ‘replaced content’ (like an <img>
element).
The old view animates by default from opacity: 1
to opacity: 0
, while the new view animates from opacity: 0
to opacity: 1
, creating a cross-fade. All of the animation is performed using CSS animations, so they can be customized with CSS.
You can use the pseudo classes in your own CSS file to control the transition from the old state to the new state. See below for an example.
@keyframes fade-in { from { opacity: 0; }}
@keyframes fade-out { to { opacity: 0; }}
@keyframes slide-from-right { from { transform: translateX(30px); }}
@keyframes slide-to-left { to { transform: translateX(-30px); }}
::view-transition-old(root) { animation: 90ms linear both fade-out, 300ms linear both slide-to-left;}
::view-transition-new(root) { animation: 210ms linear 90ms both fade-in, 300ms linear both slide-from-right;}
Transitioning Multiple Elements
Section titled “Transitioning Multiple Elements”If you want to animate different parts of the interface with different transitions then we can add a view-transition-name
property for each DOM area that will be animated. Each part needs to have a unique name.
.masthead { view-transition-name: heading;}.content { view-transition-name: content;}
This will create a pseudo element tree for the DOM like this.
::view-transition├─ ::view-transition-group(root)│ └─ ::view-transition-image-pair(root)│ ├─ ::view-transition-old(root)│ └─ ::view-transition-new(root)└─ ::view-transition-group(heading)│ └─ ::view-transition-image-pair(heading)│ ├─ ::view-transition-old(heading)│ └─ ::view-transition-new(heading)└─::view-transition-group(content) └─ ::view-transition-image-pair(content) ├─ ::view-transition-old(content) └─ ::view-transition-new(content)
The heading
and content
view-transition-name
values will target specific parts of the page. And the ::view-transition-image-pair(root)
will cover everything else on the page outside of the two named areas.
The names can also be added through JavaScript, if needed.
const header = document.querySelector('.heading');header.style.viewTransitionName = 'heading';
Asynchronous Transitions
Section titled “Asynchronous Transitions”There will sometimes be transitions where you have an element that exists on the screen in one state but not in the other. Sometimes the element will only exist on the new state and sometimes only on the old state.
To handle situations like this we can add the ::only-child
pseudo-class to the element. This will target the element when it only exists in one of the two states in the pair.
::view-transition-new(heading):only-child { animation: 300ms linear both fade-in; /* entry transition */}::view-transition-old(heading):only-child { animation: 300ms linear both fade-out; /* exit transition */}
JavaScript Control
Section titled “JavaScript Control”So, moving beyond the CSS part, there are a few JavaScript Promise-based properties which resolve and a method that can be accessed on the ViewTransition
object created by calling the startViewTransition
method.
function someMethod() { if (!document.startViewTransition) { updateTheDOM(); return; } const viewTransition = document.startViewTransition(updateTheDOM); //viewTransition.ready.then() //viewTransition.finished.then() //viewTransition.updateCallbackDone.then() //viewTransition.skipTransition()}
The viewTransition.updateCallbackDone
Promise resolves when the function called by startViewTransition()
is complete and its Promise resolves and creates the viewTransition
object.
The viewTransition.ready
Promise resolves after startViewTransition
has taken the snapshot and finished building the pseudo-element tree for the transition.
The viewTransition.finished
Promise resolves when the transitions created by the call to updateTheDOM
has completed.
The viewTransition.skipTransition()
method, if called, will skip over any transition animations that are referenced by the pseudo-element styles - ::view-transition-new()
, ::view-transition-old()
, and ::view-transition-group()
. The updateTheDOM
method still gets called and changes are still made to the DOM. It’s just those transitions that are skipped.