@ -167,7 +167,7 @@ Add those plant images into two columns between the `<body></body>` tags:
With this markup, the plants now show up on the screen. It looks pretty bad, because they aren't yet styled using CSS, and we'll do that in the next lesson.
With this markup, the plants now show up on the screen. It looks pretty bad, because they aren't yet styled using CSS, and we'll do that in the next lesson.
Each image has an alt tag that will appear even if you can't see or render an image. This is an important element to include for accessibility. Learn more about accessibility in future lessons; for now, remember that the alt attribute provides alternative information for an image if a user for some reason cannot view it (because of slow connection, an error in the src attribute, or if the user uses a screen reader).
Each image has alt text that will appear even if you can't see or render an image. This is an important attribute to include for accessibility. Learn more about accessibility in future lessons; for now, remember that the alt attribute provides alternative information for an image if a user for some reason cannot view it (because of slow connection, an error in the src attribute, or if the user uses a screen reader).
✅ Did you notice that each image has the same alt tag? Is this good practice? Why or why not? Can you improve this code?
✅ Did you notice that each image has the same alt tag? Is this good practice? Why or why not? Can you improve this code?
@ -175,7 +175,7 @@ Each image has an alt tag that will appear even if you can't see or render an im
## Semantic markup
## Semantic markup
In general, it's preferable to use 'semantics' when writing HTML. What does that mean? It means that you use HTML tags the way they were designed: to represent its data; so an H1 tag should always be present on a page
In general, it's preferable to use meaningful 'semantics' when writing HTML. What does that mean? It means that you use HTML tags to represent the type of data or interaction they were designed for. For example, the main title text on a page should use an `<h1>` tag.
Add the following line right below your opening `<body>` tag:
Add the following line right below your opening `<body>` tag:
@ -183,9 +183,9 @@ Add the following line right below your opening `<body>` tag:
<h1>My Terrarium</h1>
<h1>My Terrarium</h1>
```
```
Using semantic markup such as having headers be `<h1>` and unordered lists be rendered as `<ul>` helps screen readers navigate through a page. In general, buttons should be written as `<button>` and lists should be `<li>`. While it's _possible_ to use specially styled `<span>` elements with click handlers to mock buttons, it's better for differently-abled users to use technologies to determine where on a page a button resides, and to interact with it, if the element appears as a button. For this reason, try to use semantic markup as much as possible.
Using semantic markup such as having headers be `<h1>` and unordered lists be rendered as `<ul>` helps screen readers navigate through a page. In general, buttons should be written as `<button>` and lists should be `<li>`. While it's _possible_ to use specially styled `<span>` elements with click handlers to mock buttons, it's better for disabled users to use technologies to determine where on a page a button resides, and to interact with it, if the element appears as a button. For this reason, try to use semantic markup as much as possible.
✅ Take a look at a screen reader and [how it interacts with a web page](https://www.youtube.com/watch?v=OUDV1gqs9GA). Can you see why having non semantic markup might confuse the user?
✅ Take a look at a screen reader and [how it interacts with a web page](https://www.youtube.com/watch?v=OUDV1gqs9GA). Can you see why having non semantic markup might frustrate the user?
@ -46,12 +46,12 @@ Browser extensions are fun to develop, too. They tend to manage a finite number
Before you start building, take a look at the process of building and deploying a browser extension. While each browser varies a bit in how they manage this task, the process is similar on Chrome and Firefox to this example on Edge:
Before you start building, take a look at the process of building and deploying a browser extension. While each browser varies a bit in how they manage this task, the process is similar on Chrome and Firefox to this example on Edge:
![install a browser extension](images/install-on-edge.png)
![screenshot of the Edge browser showing the open edge://extensions page and open settings menu](images/install-on-edge.png)
In essence, the process will be:
In essence, the process will be:
- build your extension using `npm build`
- build your extension using `npm build`
- navigate in the browser to the extensions pane using the `...` icon on the top right
- navigate in the browser to the extensions pane using the "Settings and more" button (the `...` icon) on the top right
- if it's a new installation, choose `load unpacked` to upload a fresh extension from its build folder (in our case it is `/dist`)
- if it's a new installation, choose `load unpacked` to upload a fresh extension from its build folder (in our case it is `/dist`)
- or, click `reload` if you are reloading the already-installed extension
- or, click `reload` if you are reloading the already-installed extension
@ -87,11 +87,11 @@ src
This extension has two views. One to gather the API key and region code:
This extension has two views. One to gather the API key and region code:
![extension form](images/1.png)
![screenshot of the completed extension open in a browser, displaying a form with inputs for region name and API key.](images/1.png)
And the second to display the region's carbon usage:
And the second to display the region's carbon usage:
![carbon usage](images/2.png)
![screenshot of the completed extension displaying values for carbon usage and fossil fuel percentage for the US-NEISO region.](images/2.png)
Let's start by building the HTML for the form and styling it with CSS.
Let's start by building the HTML for the form and styling it with CSS.
@ -103,12 +103,12 @@ In the `/dist` folder, you will build a form and a result area. In the `index.ht
@ -16,7 +16,7 @@ It remains to manage some background tasks, including refreshing the color of th
The topic of how to make your web sites blazingly fast on all kinds of devices, for all kinds of users, in all kinds of situations, is unsurprisingly vast. Here are some points to keep in mind as you build either a standard web project or a browser extension.
The topic of how to make your web sites blazingly fast on all kinds of devices, for all kinds of users, in all kinds of situations, is unsurprisingly vast. Here are some points to keep in mind as you build either a standard web project or a browser extension.
The first thing you need to do to ensure that your site is running efficiently is to gather data about its performance. The first place to do this is in the developer tools of your web browser. In Edge, you can select the three dots on the top right of the browser, then navigate to More Tools > Developer Tools and open the Performance tab.
The first thing you need to do to ensure that your site is running efficiently is to gather data about its performance. The first place to do this is in the developer tools of your web browser. In Edge, you can select the "Settings and more" button (the three dots icon on the top right of the browser), then navigate to More Tools > Developer Tools and open the Performance tab. You can also use the keyboard shortcuts `Ctrl` + `Shift` + `I` on Windows, or `Option` + `Command` + `I` on Mac to open developer tools.
The Performance tab contains a Profiling tool. Open a web site (try, for example, https://www.microsoft.com) and click the 'Record' button, then refresh the site. Stop the recording at any time, and you will be able to see the routines that are generated to 'script', 'render', and 'paint' the site:
The Performance tab contains a Profiling tool. Open a web site (try, for example, https://www.microsoft.com) and click the 'Record' button, then refresh the site. Stop the recording at any time, and you will be able to see the routines that are generated to 'script', 'render', and 'paint' the site:
@ -55,20 +55,20 @@ We're giving it an `id` to make it easier to locate it with JavaScript later.
> Tip: since the content of this element will be replaced, we can put in a loading message or indicator that will be shown while the app is loading.
> Tip: since the content of this element will be replaced, we can put in a loading message or indicator that will be shown while the app is loading.
Next, let's add below the HTML template for the login page. For now we'll only put in there a title and a section containing a button that we'll use to perform the navigation.
Next, let's add below the HTML template for the login page. For now we'll only put in there a title and a section containing a link that we'll use to perform the navigation.
```html
```html
<templateid="login">
<templateid="login">
<h1>Bank App</h1>
<h1>Bank App</h1>
<section>
<section>
<button>Login</button>
<a href="/dashboard">Login</a>
</section>
</section>
</template>
</template>
```
```
Then we'll add another HTML template for the dashboard page. This page will contain different sections:
Then we'll add another HTML template for the dashboard page. This page will contain different sections:
- A header with a title and a logout button
- A header with a title and a logout link
- The current balance of the bank account
- The current balance of the bank account
- A list of transactions, displayed in a table
- A list of transactions, displayed in a table
@ -76,7 +76,7 @@ Then we'll add another HTML template for the dashboard page. This page will cont
<templateid="dashboard">
<templateid="dashboard">
<header>
<header>
<h1>Bank App</h1>
<h1>Bank App</h1>
<button>Logout</button>
<a href="/login">Logout</a>
</header>
</header>
<section>
<section>
Balance: 100$
Balance: 100$
@ -189,22 +189,22 @@ function updateRoute() {
}
}
```
```
Here we mapped the routes we declared to the corresponding template. You can try it that it works correctlt by changing the URL manually in your browser.
Here we mapped the routes we declared to the corresponding template. You can try it that it works correctly by changing the URL manually in your browser.
✅ What happens if you enter an unknown path in the URL? How could we solve this?
✅ What happens if you enter an unknown path in the URL? How could we solve this?
## Adding navigation
## Adding navigation
The next step for ou app is to add the possibility to navigate between pages without having to change the URL manually. This implies two things:
The next step for our app is to add the possibility to navigate between pages without having to change the URL manually. This implies two things:
1. Updating the current URL
1. Updating the current URL
2. Updating the displayed template based on the new URL
2. Updating the displayed template based on the new URL
We already took care of the second part with the `updateRoute` function, so we have to figure out how to update the current URL.
We already took care of the second part with the `updateRoute` function, so we have to figure out how to update the current URL.
While the HTML anchor element [`<a>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) can be used to create hyperlinks to different URLs, we can't use that here as it will make the browser reload the HTML.
We'll have to use JavaScript and more specifically the [`history.pushState`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState) that allows to update the URL and create a new entry in the browsing history, without reloading the HTML.
Instead we'll have to use JavaScript and more specifically the [`history.pushState`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState) that allows to update the URL and create a new entry in the browsing history, without reloading the HTML.
> Note: While the HTML anchor element [`<a href>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) can be used on its own to create hyperlinks to different URLs, it will make the browser reload the HTML by default. It is necessary to prevent this behavior when handling routing with custom javascript, using the preventDefault() function on the click event.
### Task
### Task
@ -235,17 +235,26 @@ function updateRoute() {
If a route cannot be found, we'll now redirect to the `login` page.
If a route cannot be found, we'll now redirect to the `login` page.
Let's complete the navigation system by adding bindings to our *Login* and *Logout* buttons in the HTML.
Now let's create a function to get the URL when a link is clicked, and to prevent the browser's default link behavior:
```js
function onLinkClick(event) {
event.preventDefault();
navigate(event.target.href);
}
```
Let's complete the navigation system by adding bindings to our *Login* and *Logout* links in the HTML.
Using the [`onclick`](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onclick) attribute bind the `click` event to JavaScript code, here the call to the `navigate()` function.
Using the [`onclick`](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onclick) attribute bind the `click` event to JavaScript code, here the call to the `navigate()` function.
Try clicking on these buttons, you should be now able to navigate between the different screens of your app.
Try clicking on these links, you should be now able to navigate between the different screens of your app.
✅ The `history.pushState` method is part of the HTML5 standard and implemented in [all modern browsers](https://caniuse.com/?search=pushState). If you're building a web app for older browsers, there's a trick you can use in place of this API: using a [hash (`#`)](https://en.wikipedia.org/wiki/URI_fragment) before the path you can implement routing that works with regular anchor navigation and does not reload the page, as it's purpose was to create internal links within a page.
✅ The `history.pushState` method is part of the HTML5 standard and implemented in [all modern browsers](https://caniuse.com/?search=pushState). If you're building a web app for older browsers, there's a trick you can use in place of this API: using a [hash (`#`)](https://en.wikipedia.org/wiki/URI_fragment) before the path you can implement routing that works with regular anchor navigation and does not reload the page, as it's purpose was to create internal links within a page.
@ -274,7 +283,7 @@ Here's a refresher video on arrow functions:
@ -30,10 +30,10 @@ The `<form>` element encapsulates a section of an HTML document where the user c
There are a lot of different [types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) of `<input>`, for example to create a field where the user can enter its username you can use:
There are a lot of different [types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) of `<input>`, for example to create a field where the user can enter its username you can use:
```html
```html
<input name="username"type="text">
<inputid="username"name="username"type="text">
```
```
The `name` attribute is used to identify the control and will be used as the property name when the form data will be sent over.
The `name` attribute will be used as the property name when the form data will be sent over. The `id` attribute is used to associate a `<label>` with the form control.
> Take a look at the whole list of [`<input>` types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) and [other form controls](https://developer.mozilla.org/en-US/docs/Learn/Forms/Other_form_controls) to get an idea of all the native UI elements you can use when building your UI.
> Take a look at the whole list of [`<input>` types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) and [other form controls](https://developer.mozilla.org/en-US/docs/Learn/Forms/Other_form_controls) to get an idea of all the native UI elements you can use when building your UI.
@ -55,20 +55,20 @@ Let's start by adding a form to the `login` template. We'll need a *username* fi
<section>
<section>
<h2>Login</h2>
<h2>Login</h2>
<formid="loginForm">
<formid="loginForm">
<labelfor="user">Username</label>
<labelfor="username">Username</label>
<input name="user"type="text">
<inputid="username"name="user"type="text">
<button>Login</button>
<button>Login</button>
</form>
</form>
</section>
</section>
</template>
</template>
```
```
If you take a closer look, you can notice that we also added a `<label>` element here. `<label>`are used to add a caption for UI controls, such as our username field. Labels are important for the readbility of your forms, but also comes with additional benefits:
If you take a closer look, you can notice that we also added a `<label>` element here. `<label>`elements are used to add a name to UI controls, such as our username field. Labels are important for the readability of your forms, but also comes with additional benefits:
- By associating a label to a form control, it helps users using assistive technologies (like a screen reader) to understand what data they're expected to provide.
- By associating a label to a form control, it helps users using assistive technologies (like a screen reader) to understand what data they're expected to provide.
- You can click on the label to directly put focus on the associated input, making it easier to reach on touch-screen based devices.
- You can click on the label to directly put focus on the associated input, making it easier to reach on touch-screen based devices.
> [Accessibility](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/What_is_accessibility) on the web is a very important topic that's often overlooked. Thanks to [HTML5 semantic elements](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML) it's not difficult to create accessible content if you use them properly. You can [read more about accessibility](https://developer.mozilla.org/en-US/docs/Web/Accessibility) to avoid common mistakes and become a responsible developer.
> [Accessibility](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/What_is_accessibility) on the web is a very important topic that's often overlooked. Thanks to [semantic HTML elements](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML) it's not difficult to create accessible content if you use them properly. You can [read more about accessibility](https://developer.mozilla.org/en-US/docs/Web/Accessibility) to avoid common mistakes and become a responsible developer.
Now we'll add a second form for the registration, just below the previous one:
Now we'll add a second form for the registration, just below the previous one:
@ -77,13 +77,13 @@ Now we'll add a second form for the registration, just below the previous one:
@ -121,7 +121,7 @@ Add `action` and `method` properties to the registration form:
Now try to register a new account with your name. After clicking on the *Register* button you should see something like this:
Now try to register a new account with your name. After clicking on the *Register* button you should see something like this:
![](./images/form-post.png)
![A browser window at the address localhost:5000/api/accounts, showing a JSON string with user data](./images/form-post.png)
If everything goes well, the server should answer your request with a [JSON](https://www.json.org/json-en.html) response containing the account data that was created.
If everything goes well, the server should answer your request with a [JSON](https://www.json.org/json-en.html) response containing the account data that was created.
@ -239,12 +239,14 @@ Before sending data to a server it's a good practice to [validate the form data]
### Task
### Task
There are 2 required fields to create a valid new account, the username and currency, the other fields being optional. Update the form in the HTML to reflect that:
There are 2 required fields to create a valid new account, the username and currency, the other fields being optional. Update the form's HTML, using both the `required` attribute and text in the field's label to that:
While this particular server implementation does not enforce specific limits on the fields maximum length, it's always a good practice to define reasonable limits for any user text entry.
While this particular server implementation does not enforce specific limits on the fields maximum length, it's always a good practice to define reasonable limits for any user text entry.
@ -252,11 +254,11 @@ While this particular server implementation does not enforce specific limits on
Now that we have the user data, we have to update the existing HTML to display it. We already now how to retrieve element from the DOM using for example `document.getElementById()`. After you have a base element, here are some APIs you can use to modify it or add child elements to it:
Now that we have the user data, we have to update the existing HTML to display it. We already know how to retrieve element from the DOM using for example `document.getElementById()`. After you have a base element, here are some APIs you can use to modify it or add child elements to it:
- Using the [`textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent) property you can change the text of an element. Note that changing this values removes all the element's children (if there's any) and replace it with the text provided. As such, it's also an efficient method to remove all children of a given element by assigning an empty string `''` to it.
- Using the [`textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent) property you can change the text of an element. Note that changing this values removes all the element's children (if there's any) and replace it with the text provided. As such, it's also an efficient method to remove all children of a given element by assigning an empty string `''` to it.
@ -157,6 +157,12 @@ Now if you try to login with an invalid account, you should see something like t
![Screenshot showing the error message displayed during login](./images/login-error.png)
![Screenshot showing the error message displayed during login](./images/login-error.png)
Now we have error text that shows up visually, but if you try it with a screen reader you'll notice that nothing is announced. In order for text that is dynamically added to a page to be announced by screen readers, it will need to use something called a [Live Region](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions). Here we're going to use a specific type of live region called an alert:
```html
<divid="loginError"role="alert"></div>
```
Implement the same behavior for the `register` function errors (don't forget to update the HTML).
Implement the same behavior for the `register` function errors (don't forget to update the HTML).
## Display information on the dashboard
## Display information on the dashboard
@ -194,10 +200,10 @@ Let's start by replacing the "Balance" section in the HTML to add placeholder el
We'll also add a new section just below to display the account description:
We'll also add a new section just below to display the account description:
✅ As there is no text label here to explain what this section is about, we use the `aria-label` attribute to give an accessibility hint. Learn more about [ARIA attributes](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) to make sure your web apps are accessible to everyone.
✅ Since the account description functions as a title for the content underneath it, it is marked up semantically as a heading. Learn more about how [heading structure](https://www.nomensa.com/blog/2017/how-structure-headings-web-accessibility) is important for accessibility, and take a critical look at the page to determine what else could be a heading.
Next, we'll create a new function in `app.js` to fill in the placeholder:
Next, we'll create a new function in `app.js` to fill in the placeholder:
@ -213,7 +219,7 @@ function updateDashboard() {
}
}
```
```
First, we check that we have the account data we need before going futher. Then we use the `updateElement()` function we created earlier to update the HTML.
First, we check that we have the account data we need before going further. Then we use the `updateElement()` function we created earlier to update the HTML.
> To make the balance display prettier, we use the method [`toFixed(2)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) to force displaying the value with 2 digits after the decimal point.
> To make the balance display prettier, we use the method [`toFixed(2)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) to force displaying the value with 2 digits after the decimal point.
@ -7,6 +7,7 @@ Using everything that you've learnt in the four previous lessons, implement an "
- Add an "Add transaction" button in the dashboard page
- Add an "Add transaction" button in the dashboard page
- Either create a new page with an HTML template, or use JavaScript to show/hide the dialog HTML without leaving the dashboard page (you can use [`hidden`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden) property for that, or CSS classes)
- Either create a new page with an HTML template, or use JavaScript to show/hide the dialog HTML without leaving the dashboard page (you can use [`hidden`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden) property for that, or CSS classes)
- Make sure you handle [keyboard and screen reader accessibility](https://developer.paciellogroup.com/blog/2018/06/the-current-state-of-modal-dialog-accessibility/) for the dialog
- Implement an HTML form to receive input data
- Implement an HTML form to receive input data
- Create JSON data from the form data and send it to the API
- Create JSON data from the form data and send it to the API