Web Apps

14.1 Forms and Page Transitions

# 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 webserver 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.

Remember

All values that come from any web form are going to be strings. It doesn't matter if the value is numeric or a boolean. If you are using a value property then you get a String. If you need a number then you have to do the conversion with Number() or parseInt() or parseFloat().

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.

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.

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.

See the notes in Module 11.2 for more details on uploading data with fetch.

Here is a refresher on Keyboard Events.

A quick video talking about how you can properly test for an empty input field.

These next two videos explain modern web form validation in depth and in detail.

And finally, how to enable and disable autocomplete features in web forms.

MDN Guide to Web Forms (opens new window)

# 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 form
document.querySelector('#myForm').submit();
//reset a form
document.querySelector('#myForm').reset();
//click a link
document.querySelector('a.someLink').click();
1
2
3
4
5
6

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);
1

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 sidebar
let 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 event
let 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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 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 name
let ev1 = new Event('beerOpened');
//create a custom event that can contain data that will be part of the event object
let ev2 = new CustomEvent('beerOpened', {
  detail: { today: new Date(), brand: 'Corona' },
});
1
2
3
4
5
6

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 event
p.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);
}
1
2
3
4
5
6
7
8
9
10
11

All the properties inside the Event must be put inside detail.

# 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
  }
}
1
2
3
4
5
6

And then we can add our ScreamEvent to objects that have the addEventListener method.

//create a ScreamEvent
let sev = new ScreamEvent({ msg: 'hi' });
//add a listener for the ScreamEvent on the document object
document.addEventListener('scream', (ev) => {
  console.log('screamed', ev.detail.msg);
});
//dispatch the ScreamEvent
document.dispatchEvent(sev);
1
2
3
4
5
6
7
8

# SPA Page Transitions

If you are building a SPA and you want to add transitions to your pages you can do this with a mixture of JavaScript and CSS.

Let's say that you are going to keep your <header>, main <nav>, and <footer> the same regardless of the "page" that your users are viewing. You will be updating the content inside of <main> with each navigation. If you use position: fixed for the header and footer with a high z-index, then we can animate the <main> element on to the screen behind them.

<body>
  <header>
    <!-- fixed at top of screen -->
    <h1>Site Header</h1>
    <nav>Nav links go here</nav>
  </header>

  <main>
    <!-- The main content for each page gets loaded here -->
  </main>

  <footer>
    <!-- fixed at bottom of screen -->
  </footer>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Let's give the main element 3 states:

  • We have gone to a new URL but not shown the main element yet (default)
  • We are on the URL and want the main element to be shown
  • We are leaving the current URL

Between each of these states there will be a transition. When the transition to the last state is complete is when you will remove the old content, change the state to the first one, and add the new content.

The first state is the default one so we will not create a className for it.

The second state we will call .active.

The final state we will call .leaving. These are not special names, just terms that give a sense of what they represent in the interface.

Since you can apply multiple classNames to HTML elements we will have main elements in the HTML that look like one of these three:

<main></main>
<main class="active"></main>
<main class="active leaving"></main>
1
2
3

In the CSS we will define what those look like.

body {
  overflow-x: hidden; /* when things are wider than that screen do not expand the scrollbar */
}
main {
  transform: translateX(-200vw); /* 200% the width of the screen to the left */
  opacity: 0; /* optionally make it fade in from invisible */
}
main.active {
  transform: translateX(0); /* fully on the screen */
  opacity: 1;
}
main.active.leaving {
  transform: translateX(200vw); /* 200% to the right */
  opacity: 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Once you have the three states defined, test them by manually adding and removing the classNames in the HTML. Now, we will pick a direction for the transitions. Let's say left to right. And a duration for the transition, say between 0.2s and 0.8s. If you are transitioning both the translateX and the opacity you can give them different durations.

main {
  transform: translateX(-200vw); /* 200% the width of the screen to the left */
  opacity: 0; /* optionally make it fade in from invisible */
  transition: transform 0.5s linear, opacity 0.3s linear;
}
1
2
3
4
5

In your JavaScript adding the .active class will trigger the transition between the first two states. Adding .leaving will trigger the second transition.

let main = document.querySelector('main');
main.classList.add('active');
main.classList.add('leaving');
1
2
3

We still need one last thing - being able to remove both the active and leaving classes and change the content. We can use the transitionend event listener.

main.addEventListener('transitionend', resetMain);

function resetMain(ev) {
  //this will fire after every transition so check that it has both classes.
  if (main.classList.contains('active') && main.classList.contains('leaving')) {
    main.innerHTML = ''; //remove the old content
    //call a function to fetch and/or load the new content into main
    main.className = ''; //remove both classes
  }
}
1
2
3
4
5
6
7
8
9
10

It doesn't have 100% effectiveness. There is always a slight possibility that the event does not fire. So we should also set a timer that fires just after the transition should have ended.

let timmy = setTimeout(resetMain, 600);
//call the same function as the transitionend event
// use time that is 100ms longer than the longest transition.
1
2
3

And inside the resetMain function we should clear out the timer too.

function resetMain(ev) {
  clearTimeout(timmy);
}
1
2
3

You can invent your own page transition using any of the transition methods or possible properties. Try opacity, scale, rotation, colours, etc.

# What to do this week

TODO

Things to do before next week.

  • Read all the content from Modules 14.1, 14.2.
  • Work on Final Project
Last Updated: 6/28/2022, 9:38:05 AM