mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
feat(curriculum): adding JS A11y note taking app (#61449)
Co-authored-by: Huyen Nguyen <25715018+huyenltnguyen@users.noreply.github.com>
This commit is contained in:
@@ -3468,6 +3468,13 @@
|
||||
"In this workshop, you will build a dynamic tabbed interface that showcases facts about the planets in the solar system."
|
||||
]
|
||||
},
|
||||
"workshop-note-taking-app": {
|
||||
"title": "Build a Note Taking App",
|
||||
"intro": [
|
||||
"In this workshop, you are going to build an accessible note taking app.",
|
||||
"This will provide you with the opportunity to practice working with <code>aria-live</code> attribute."
|
||||
]
|
||||
},
|
||||
"review-js-a11y": {
|
||||
"title": "JavaScript and Accessibility Review",
|
||||
"intro": [
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: Introduction to Build a Note Taking App
|
||||
block: workshop-note-taking-app
|
||||
superBlock: full-stack-developer
|
||||
---
|
||||
|
||||
## Introduction to Build a Note Taking App
|
||||
|
||||
In this workshop, you are going to build an accessible note taking app. This will provide you with the opportunity to practice working with `aria-live` attribute.
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "Build a Note Taking App",
|
||||
"blockType": "workshop",
|
||||
"blockLayout": "challenge-grid",
|
||||
"isUpcomingChange": true,
|
||||
"dashedName": "workshop-note-taking-app",
|
||||
"superBlock": "full-stack-developer",
|
||||
"helpCategory": "JavaScript",
|
||||
"challengeOrder": [
|
||||
{
|
||||
"id": "68813c1b28ec3fd41838daf6",
|
||||
"title": "Step 1"
|
||||
},
|
||||
{
|
||||
"id": "68813f8b1b11bbdda98c6214",
|
||||
"title": "Step 2"
|
||||
},
|
||||
{
|
||||
"id": "68813fbfc9f56dde3f21da08",
|
||||
"title": "Step 3"
|
||||
},
|
||||
{
|
||||
"id": "688140eac18479dee90553e5",
|
||||
"title": "Step 4"
|
||||
},
|
||||
{
|
||||
"id": "6881417fdb4032df98134a89",
|
||||
"title": "Step 5"
|
||||
},
|
||||
{
|
||||
"id": "688141a8be6e6be03af945ea",
|
||||
"title": "Step 6"
|
||||
},
|
||||
{
|
||||
"id": "688141e05e367de0e3ef7635",
|
||||
"title": "Step 7"
|
||||
},
|
||||
{
|
||||
"id": "68814221fc4752e17b9696bd",
|
||||
"title": "Step 8"
|
||||
},
|
||||
{
|
||||
"id": "6881424b87c826e21247645b",
|
||||
"title": "Step 9"
|
||||
},
|
||||
{
|
||||
"id": "688142a607d1dce2aa82da77",
|
||||
"title": "Step 10"
|
||||
},
|
||||
{
|
||||
"id": "6881431e481bf6e352a33969",
|
||||
"title": "Step 11"
|
||||
},
|
||||
{
|
||||
"id": "6881436e1e2afae400e8b4fe",
|
||||
"title": "Step 12"
|
||||
},
|
||||
{
|
||||
"id": "688143c66d5665e4b3409977",
|
||||
"title": "Step 13"
|
||||
}
|
||||
],
|
||||
"usesMultifileEditor": true,
|
||||
"hasEditableBoundaries": true
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
---
|
||||
id: 68813c1b28ec3fd41838daf6
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In this workshop, you will practice working with the `aria-live` and `aria-label` attributes by building an accessible note taking app.
|
||||
|
||||
Most of the HTML and all of the CSS has been provided for you. The first few steps will involve adding the remaining markup.
|
||||
|
||||
If you look at the preview, there is text to tell you to click on the card to edit it. But if you try to do that, it will not work. To make that `div` element editable, you can use the `contenteditable` attribute like this:
|
||||
|
||||
```html
|
||||
<div contenteditable="true"></div>
|
||||
```
|
||||
|
||||
In the opening `div` tag, add the `contenteditable` attribute and set its value to `"true"`. Now you should be able to click on the element and edit the text.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your opening `div` tag should have the `contenteditable` attribute.
|
||||
|
||||
```js
|
||||
assert.isTrue(document.getElementById("note").hasAttribute("contenteditable"));
|
||||
```
|
||||
|
||||
Your `div` element should have the `contenteditable` attribute set to `"true"`.
|
||||
|
||||
```js
|
||||
assert.equal(document.getElementById("note").getAttribute("contenteditable"), "true");
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Note taking app</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p class="helper-text">Click or tap on the card to edit your note.</p>
|
||||
|
||||
--fcc-editable-region--
|
||||
<div id="note" class="note">
|
||||
--fcc-editable-region--
|
||||
Many languages have words that carry meanings so specific or culturally rooted that they can't be neatly translated into English.
|
||||
|
||||
One example is the Japanese word "tsundoku", which refers to the habit of acquiring books and letting them pile up unread, something many book lovers can relate to. Another is the Portuguese word "saudade", describing a deep, bittersweet longing for something or someone that is absent. Meanwhile, the French word "Dépaysement" captures the disorienting yet exciting feeling of being in a new place, far from home.
|
||||
|
||||
These unique words remind us that language is more than vocabulary: it's a window into the values, habits, and emotions of the cultures that create it.
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 2em;
|
||||
max-width: 700px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.note {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.5;
|
||||
min-height: 250px;
|
||||
font-size: 16px;
|
||||
/* This is needed to preserve line breaks in the div */
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.note[contenteditable="true"] {
|
||||
caret-color: black;
|
||||
}
|
||||
|
||||
.note:hover {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-top: 0.5em;
|
||||
user-select: none;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#status {
|
||||
color: #00471b;
|
||||
padding: 0 1em;
|
||||
}
|
||||
```
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
---
|
||||
id: 68813f8b1b11bbdda98c6214
|
||||
title: Step 2
|
||||
challengeType: 0
|
||||
dashedName: step-2
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Since a `div` element is being used for the note, screen readers will not understand its purpose. You can fix that by adding an `aria-label` attribute. This will help with accessibility and provide meaning for this `div` element.
|
||||
|
||||
In your opening `div` tag, add an `aria-label` attribute and set its value to `"Note editor"`.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your opening `div` tag should have the `aria-label` attribute.
|
||||
|
||||
```js
|
||||
assert.isTrue(document.getElementById("note").hasAttribute("aria-label"));
|
||||
```
|
||||
|
||||
Your opening `div` tag should have the `aria-label` attribute set to `"Note editor"`.
|
||||
|
||||
```js
|
||||
assert.equal(document.getElementById("note").getAttribute("aria-label"), "Note editor");
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Note taking app</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p class="helper-text">Click or tap on the card to edit your note.</p>
|
||||
|
||||
--fcc-editable-region--
|
||||
<div id="note" class="note" contenteditable="true">
|
||||
--fcc-editable-region--
|
||||
Many languages have words that carry meanings so specific or culturally rooted that they can't be neatly translated into English.
|
||||
|
||||
One example is the Japanese word "tsundoku", which refers to the habit of acquiring books and letting them pile up unread, something many book lovers can relate to. Another is the Portuguese word "saudade", describing a deep, bittersweet longing for something or someone that is absent. Meanwhile, the French word "Dépaysement" captures the disorienting yet exciting feeling of being in a new place, far from home.
|
||||
|
||||
These unique words remind us that language is more than vocabulary: it's a window into the values, habits, and emotions of the cultures that create it.
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 2em;
|
||||
max-width: 700px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.note {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.5;
|
||||
min-height: 250px;
|
||||
font-size: 16px;
|
||||
/* This is needed to preserve line breaks in the div */
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.note[contenteditable="true"] {
|
||||
caret-color: black;
|
||||
}
|
||||
|
||||
.note:hover {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-top: 0.5em;
|
||||
user-select: none;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#status {
|
||||
color: #00471b;
|
||||
padding: 0 1em;
|
||||
}
|
||||
```
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
---
|
||||
id: 68813fbfc9f56dde3f21da08
|
||||
title: Step 3
|
||||
challengeType: 0
|
||||
dashedName: step-3
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
When a user edits a note and either tabs or clicks outside of the card, then a status message should display notifying users that the note was saved successfully.
|
||||
|
||||
To begin, create a `div` element with an `id` attribute set to `"status"`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have a second `div` element.
|
||||
|
||||
```js
|
||||
assert.lengthOf(document.querySelectorAll("div"), 2);
|
||||
```
|
||||
|
||||
Your second `div` element should have an `id` attribute.
|
||||
|
||||
```js
|
||||
assert.isTrue(document.querySelectorAll("div")[1].hasAttribute("id"));
|
||||
```
|
||||
|
||||
Your second `div` element should have an `id` attribute set to `"status"`.
|
||||
|
||||
```js
|
||||
assert.equal(document.querySelectorAll("div")[1].getAttribute("id"), "status");
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Note taking app</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p class="helper-text">Click or tap on the card to edit your note.</p>
|
||||
|
||||
<div id="note" class="note" contenteditable="true" aria-label="Note editor">
|
||||
Many languages have words that carry meanings so specific or culturally rooted that they can't be neatly translated into English.
|
||||
|
||||
One example is the Japanese word "tsundoku", which refers to the habit of acquiring books and letting them pile up unread, something many book lovers can relate to. Another is the Portuguese word "saudade", describing a deep, bittersweet longing for something or someone that is absent. Meanwhile, the French word "Dépaysement" captures the disorienting yet exciting feeling of being in a new place, far from home.
|
||||
|
||||
These unique words remind us that language is more than vocabulary: it's a window into the values, habits, and emotions of the cultures that create it.
|
||||
</div>
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 2em;
|
||||
max-width: 700px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.note {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.5;
|
||||
min-height: 250px;
|
||||
font-size: 16px;
|
||||
/* This is needed to preserve line breaks in the div */
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.note[contenteditable="true"] {
|
||||
caret-color: black;
|
||||
}
|
||||
|
||||
.note:hover {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-top: 0.5em;
|
||||
user-select: none;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#status {
|
||||
color: #00471b;
|
||||
padding: 0 1em;
|
||||
}
|
||||
```
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
---
|
||||
id: 688140eac18479dee90553e5
|
||||
title: Step 4
|
||||
challengeType: 0
|
||||
dashedName: step-4
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Since you are using another `div` element, you will need to make this more accessible for screen readers.
|
||||
|
||||
In a prior lecture, you learned about the `aria-live` attribute which is used to create a live region on a page. This will allow screen reader users to be automatically notified when the content of the live region changes, without needing to manually focus on or interact with it.
|
||||
|
||||
In your `#status` element, add an `aria-live` attribute and set its value to `"polite"`.
|
||||
|
||||
The `polite` value will tell screen readers to wait until any current announcement is finished or until the user stops typing before announcing the update.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `#status` element should have the `aria-live` attribute.
|
||||
|
||||
```js
|
||||
assert.isTrue(document.getElementById("status").hasAttribute("aria-live"));
|
||||
```
|
||||
|
||||
Your `#status` element should have the `aria-live` attribute set to `"polite"`.
|
||||
|
||||
```js
|
||||
assert.equal(document.getElementById("status").getAttribute("aria-live"), "polite");
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Note taking app</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p class="helper-text">Click or tap on the card to edit your note.</p>
|
||||
|
||||
<div id="note" class="note" contenteditable="true" aria-label="Note editor">
|
||||
Many languages have words that carry meanings so specific or culturally rooted that they can't be neatly translated into English.
|
||||
|
||||
One example is the Japanese word "tsundoku", which refers to the habit of acquiring books and letting them pile up unread, something many book lovers can relate to. Another is the Portuguese word "saudade", describing a deep, bittersweet longing for something or someone that is absent. Meanwhile, the French word "Dépaysement" captures the disorienting yet exciting feeling of being in a new place, far from home.
|
||||
|
||||
These unique words remind us that language is more than vocabulary: it's a window into the values, habits, and emotions of the cultures that create it.
|
||||
</div>
|
||||
|
||||
--fcc-editable-region--
|
||||
<div id="status"></div>
|
||||
--fcc-editable-region--
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 2em;
|
||||
max-width: 700px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.note {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.5;
|
||||
min-height: 250px;
|
||||
font-size: 16px;
|
||||
/* This is needed to preserve line breaks in the div */
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.note[contenteditable="true"] {
|
||||
caret-color: black;
|
||||
}
|
||||
|
||||
.note:hover {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-top: 0.5em;
|
||||
user-select: none;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#status {
|
||||
color: #00471b;
|
||||
padding: 0 1em;
|
||||
}
|
||||
```
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
---
|
||||
id: 6881417fdb4032df98134a89
|
||||
title: Step 5
|
||||
challengeType: 0
|
||||
dashedName: step-5
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The remaining steps for the workshop will involve adding the code for dynamically showing the notes saved message and logging the current notes to the console.
|
||||
|
||||
Start by creating a variable called `noteEl` and assigning it the result of querying the document for the element with the `id` of `note`.
|
||||
|
||||
Then create another variable called `statusEl` and assign it the result of querying the document for the element with the `id` of `status`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have a `noteEl` variable and assign it the result of querying the document for the element with the `id` of `note`.
|
||||
|
||||
```js
|
||||
assert.equal(noteEl, document.getElementById("note"));
|
||||
```
|
||||
|
||||
You should have a `statusEl` variable and assign it the result of querying the document for the element with the `id` of `status`.
|
||||
|
||||
```js
|
||||
assert.equal(statusEl, document.getElementById("status"));
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Note taking app</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p class="helper-text">Click or tap on the card to edit your note.</p>
|
||||
|
||||
<div id="note" class="note" contenteditable="true" aria-label="Note editor">
|
||||
Many languages have words that carry meanings so specific or culturally rooted that they can't be neatly translated into English.
|
||||
|
||||
One example is the Japanese word "tsundoku", which refers to the habit of acquiring books and letting them pile up unread, something many book lovers can relate to. Another is the Portuguese word "saudade", describing a deep, bittersweet longing for something or someone that is absent. Meanwhile, the French word "Dépaysement" captures the disorienting yet exciting feeling of being in a new place, far from home.
|
||||
|
||||
These unique words remind us that language is more than vocabulary: it's a window into the values, habits, and emotions of the cultures that create it.
|
||||
</div>
|
||||
|
||||
<div id="status" aria-live="polite"></div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 2em;
|
||||
max-width: 700px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.note {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.5;
|
||||
min-height: 250px;
|
||||
font-size: 16px;
|
||||
/* This is needed to preserve line breaks in the div */
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.note[contenteditable="true"] {
|
||||
caret-color: black;
|
||||
}
|
||||
|
||||
.note:hover {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-top: 0.5em;
|
||||
user-select: none;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#status {
|
||||
color: #00471b;
|
||||
padding: 0 1em;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
---
|
||||
id: 688141a8be6e6be03af945ea
|
||||
title: Step 6
|
||||
challengeType: 0
|
||||
dashedName: step-6
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
When a user edits the note, you want to keep track of the edited version.
|
||||
|
||||
Use `let` to create a variable called `currentContent` and assign it an empty string.
|
||||
|
||||
*NOTE*: In a real world application you would normally save your notes in a database. Or you could even save them in local storage. However, working with databases and local storage is beyond the scope of this workshop and those concepts will be taught later on.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should use `let` to create the `currentContent` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /let\s+currentContent/);
|
||||
```
|
||||
|
||||
Your `currentContent` variable should be a string.
|
||||
|
||||
```js
|
||||
assert.isString(currentContent);
|
||||
```
|
||||
|
||||
Your `currentContent` variable should be initialized with an empty string.
|
||||
|
||||
```js
|
||||
assert.equal(currentContent, "");
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Note taking app</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p class="helper-text">Click or tap on the card to edit your note.</p>
|
||||
|
||||
<div id="note" class="note" contenteditable="true" aria-label="Note editor">
|
||||
Many languages have words that carry meanings so specific or culturally rooted that they can't be neatly translated into English.
|
||||
|
||||
One example is the Japanese word "tsundoku", which refers to the habit of acquiring books and letting them pile up unread, something many book lovers can relate to. Another is the Portuguese word "saudade", describing a deep, bittersweet longing for something or someone that is absent. Meanwhile, the French word "Dépaysement" captures the disorienting yet exciting feeling of being in a new place, far from home.
|
||||
|
||||
These unique words remind us that language is more than vocabulary: it's a window into the values, habits, and emotions of the cultures that create it.
|
||||
</div>
|
||||
|
||||
<div id="status" aria-live="polite"></div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 2em;
|
||||
max-width: 700px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.note {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.5;
|
||||
min-height: 250px;
|
||||
font-size: 16px;
|
||||
/* This is needed to preserve line breaks in the div */
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.note[contenteditable="true"] {
|
||||
caret-color: black;
|
||||
}
|
||||
|
||||
.note:hover {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-top: 0.5em;
|
||||
user-select: none;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#status {
|
||||
color: #00471b;
|
||||
padding: 0 1em;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const noteEl = document.getElementById("note");
|
||||
const statusEl = document.getElementById("status");
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
---
|
||||
id: 688141e05e367de0e3ef7635
|
||||
title: Step 7
|
||||
challengeType: 0
|
||||
dashedName: step-7
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Right now, `currentContent` is initialized with the value of an empty string. But the desired result would be to have `currentContent` hold the value of the current note text.
|
||||
|
||||
In an earlier lecture, you learned how to work with the `DOMContentLoaded` event like this:
|
||||
|
||||
```js
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
// Do stuff here
|
||||
});
|
||||
```
|
||||
|
||||
When everything in the HTML document has been loaded and parsed, you will be able to access the `noteEl` safely.
|
||||
|
||||
Start by attaching an `addEventListener` method to the `window` object. The first argument for the `addEventListener` method should be the `"DOMContentLoaded"` event. The second argument should be an arrow function. Inside the body of the arrow function, reassign `currentContent` to the value of `noteEl.textContent`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should attach an `addEventListener` method to the `window` object.
|
||||
|
||||
```js
|
||||
assert.match(code, /window\.addEventListener/);
|
||||
```
|
||||
|
||||
Your event listener should listen for the `"DOMContentLoaded"` event.
|
||||
|
||||
```js
|
||||
assert.match(code, /window\.addEventListener\(['"]DOMContentLoaded['"]\,/);
|
||||
```
|
||||
|
||||
Inside the body of the arrow function, you should reassign `currentContent` to the value of `noteEl.textContent`.
|
||||
|
||||
```js
|
||||
const event = new Event("DOMContentLoaded");
|
||||
window.dispatchEvent(event);
|
||||
|
||||
assert.equal(currentContent, noteEl.textContent);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Note taking app</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p class="helper-text">Click or tap on the card to edit your note.</p>
|
||||
|
||||
<div id="note" class="note" contenteditable="true" aria-label="Note editor">
|
||||
Many languages have words that carry meanings so specific or culturally rooted that they can't be neatly translated into English.
|
||||
|
||||
One example is the Japanese word "tsundoku", which refers to the habit of acquiring books and letting them pile up unread, something many book lovers can relate to. Another is the Portuguese word "saudade", describing a deep, bittersweet longing for something or someone that is absent. Meanwhile, the French word "Dépaysement" captures the disorienting yet exciting feeling of being in a new place, far from home.
|
||||
|
||||
These unique words remind us that language is more than vocabulary: it's a window into the values, habits, and emotions of the cultures that create it.
|
||||
</div>
|
||||
|
||||
<div id="status" aria-live="polite"></div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 2em;
|
||||
max-width: 700px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.note {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.5;
|
||||
min-height: 250px;
|
||||
font-size: 16px;
|
||||
/* This is needed to preserve line breaks in the div */
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.note[contenteditable="true"] {
|
||||
caret-color: black;
|
||||
}
|
||||
|
||||
.note:hover {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-top: 0.5em;
|
||||
user-select: none;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#status {
|
||||
color: #00471b;
|
||||
padding: 0 1em;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const noteEl = document.getElementById("note");
|
||||
const statusEl = document.getElementById("status");
|
||||
|
||||
let currentContent = "";
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
---
|
||||
id: 68814221fc4752e17b9696bd
|
||||
title: Step 8
|
||||
challengeType: 0
|
||||
dashedName: step-8
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
When the user clicks on the note, makes edits and then clicks outside of the note, you will need a way to get that newly edited version. You can accomplish this by using the `blur` event. This event fires when an element loses focus. Here is an example:
|
||||
|
||||
```js
|
||||
element.addEventListener("blur", () => {
|
||||
// do something here
|
||||
});
|
||||
```
|
||||
|
||||
Attach an `addEventListener` method to the `noteEl` variable. The first argument should be the `"blur"` event and the second argument should an arrow function. Inside of the body of that arrow function, use `const` to create a new variable called `newContent` and assign it the value of `noteEl.innerHTML`.
|
||||
|
||||
`innerHTML` is used here to help preserve spacing and formatting for the note.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should attach an `addEventListener` method to the `noteEl` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /noteEl\.addEventListener/);
|
||||
```
|
||||
|
||||
Your `addEventListener` method should have a first argument of the `"blur"` event.
|
||||
|
||||
```js
|
||||
assert.match(code, /noteEl\.addEventListener\(['"]blur['"],/);
|
||||
```
|
||||
|
||||
Inside of the body of the arrow function, you should use `const` to create a new variable called `newContent` and assign it the value of `noteEl.innerHTML`.
|
||||
|
||||
```js
|
||||
assert.match(code, /noteEl\.addEventListener\(['"]blur['"],\s*\(\)\s*=>\s*{[^}]*const\s+newContent\s*=\s*noteEl\.innerHTML[^}]*}\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Note taking app</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p class="helper-text">Click or tap on the card to edit your note.</p>
|
||||
|
||||
<div id="note" class="note" contenteditable="true" aria-label="Note editor">
|
||||
Many languages have words that carry meanings so specific or culturally rooted that they can't be neatly translated into English.
|
||||
|
||||
One example is the Japanese word "tsundoku", which refers to the habit of acquiring books and letting them pile up unread, something many book lovers can relate to. Another is the Portuguese word "saudade", describing a deep, bittersweet longing for something or someone that is absent. Meanwhile, the French word "Dépaysement" captures the disorienting yet exciting feeling of being in a new place, far from home.
|
||||
|
||||
These unique words remind us that language is more than vocabulary: it's a window into the values, habits, and emotions of the cultures that create it.
|
||||
</div>
|
||||
|
||||
<div id="status" aria-live="polite"></div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 2em;
|
||||
max-width: 700px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.note {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.5;
|
||||
min-height: 250px;
|
||||
font-size: 16px;
|
||||
/* This is needed to preserve line breaks in the div */
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.note[contenteditable="true"] {
|
||||
caret-color: black;
|
||||
}
|
||||
|
||||
.note:hover {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-top: 0.5em;
|
||||
user-select: none;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#status {
|
||||
color: #00471b;
|
||||
padding: 0 1em;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const noteEl = document.getElementById("note");
|
||||
const statusEl = document.getElementById("status");
|
||||
|
||||
let currentContent = "";
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
currentContent = noteEl.textContent;
|
||||
});
|
||||
```
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
---
|
||||
id: 6881424b87c826e21247645b
|
||||
title: Step 9
|
||||
challengeType: 0
|
||||
dashedName: step-9
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Once you get the edited content, that will now be the current content.
|
||||
|
||||
Inside of the event listener, assign `newContent` to `currentContent`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should assign `newContent` to `currentContent` inside of the event listener.
|
||||
|
||||
```js
|
||||
assert.match(code, /noteEl\.addEventListener\(['"]blur['"],\s*\(\)\s*=>\s*{[^}]*const\s+newContent\s*=\s*noteEl\.innerHTML[^}]*currentContent\s*=\s*newContent[^}]*}\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Note taking app</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p class="helper-text">Click or tap on the card to edit your note.</p>
|
||||
|
||||
<div id="note" class="note" contenteditable="true" aria-label="Note editor">
|
||||
Many languages have words that carry meanings so specific or culturally rooted that they can't be neatly translated into English.
|
||||
|
||||
One example is the Japanese word "tsundoku", which refers to the habit of acquiring books and letting them pile up unread, something many book lovers can relate to. Another is the Portuguese word "saudade", describing a deep, bittersweet longing for something or someone that is absent. Meanwhile, the French word "Dépaysement" captures the disorienting yet exciting feeling of being in a new place, far from home.
|
||||
|
||||
These unique words remind us that language is more than vocabulary: it's a window into the values, habits, and emotions of the cultures that create it.
|
||||
</div>
|
||||
|
||||
<div id="status" aria-live="polite"></div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 2em;
|
||||
max-width: 700px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.note {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.5;
|
||||
min-height: 250px;
|
||||
font-size: 16px;
|
||||
/* This is needed to preserve line breaks in the div */
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.note[contenteditable="true"] {
|
||||
caret-color: black;
|
||||
}
|
||||
|
||||
.note:hover {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-top: 0.5em;
|
||||
user-select: none;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#status {
|
||||
color: #00471b;
|
||||
padding: 0 1em;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const noteEl = document.getElementById("note");
|
||||
const statusEl = document.getElementById("status");
|
||||
|
||||
let currentContent = "";
|
||||
|
||||
--fcc-editable-region--
|
||||
noteEl.addEventListener("blur", () => {
|
||||
const newContent = noteEl.innerHTML;
|
||||
|
||||
});
|
||||
--fcc-editable-region--
|
||||
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
currentContent = noteEl.textContent;
|
||||
});
|
||||
```
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
---
|
||||
id: 688142a607d1dce2aa82da77
|
||||
title: Step 10
|
||||
challengeType: 0
|
||||
dashedName: step-10
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
It would be nice to see the edited changes in the console.
|
||||
|
||||
Add a `console.log(currentContent);`.
|
||||
|
||||
To test out your changes, trying editing the note and then clicking out of the note. You should now see the edited note in the console.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should add a `console.log(currentContent);` inside of the event listener, after assigning `newContent` to `currentContent`.
|
||||
|
||||
```js
|
||||
assert.match(code, /noteEl\.addEventListener\(['"]blur['"],\s*\(\)\s*=>\s*{[^}]*const\s+newContent\s*=\s*noteEl\.innerHTML[^}]*currentContent\s*=\s*newContent[^}]*console\.log\(currentContent\)[^}]*}\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Note taking app</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p class="helper-text">Click or tap on the card to edit your note.</p>
|
||||
|
||||
<div id="note" class="note" contenteditable="true" aria-label="Note editor">
|
||||
Many languages have words that carry meanings so specific or culturally rooted that they can't be neatly translated into English.
|
||||
|
||||
One example is the Japanese word "tsundoku", which refers to the habit of acquiring books and letting them pile up unread, something many book lovers can relate to. Another is the Portuguese word "saudade", describing a deep, bittersweet longing for something or someone that is absent. Meanwhile, the French word "Dépaysement" captures the disorienting yet exciting feeling of being in a new place, far from home.
|
||||
|
||||
These unique words remind us that language is more than vocabulary: it's a window into the values, habits, and emotions of the cultures that create it.
|
||||
</div>
|
||||
|
||||
<div id="status" aria-live="polite"></div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 2em;
|
||||
max-width: 700px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.note {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.5;
|
||||
min-height: 250px;
|
||||
font-size: 16px;
|
||||
/* This is needed to preserve line breaks in the div */
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.note[contenteditable="true"] {
|
||||
caret-color: black;
|
||||
}
|
||||
|
||||
.note:hover {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-top: 0.5em;
|
||||
user-select: none;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#status {
|
||||
color: #00471b;
|
||||
padding: 0 1em;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const noteEl = document.getElementById("note");
|
||||
const statusEl = document.getElementById("status");
|
||||
|
||||
let currentContent = "";
|
||||
|
||||
--fcc-editable-region--
|
||||
noteEl.addEventListener("blur", () => {
|
||||
const newContent = noteEl.innerHTML;
|
||||
|
||||
currentContent = newContent;
|
||||
|
||||
});
|
||||
--fcc-editable-region--
|
||||
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
currentContent = noteEl.textContent;
|
||||
});
|
||||
```
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
---
|
||||
id: 6881431e481bf6e352a33969
|
||||
title: Step 11
|
||||
challengeType: 0
|
||||
dashedName: step-11
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
As mentioned earlier, when the user edits the note and then clicks outside of the note, there should be a message to display that the note was saved.
|
||||
|
||||
To accomplish this, set the `statusEl.textContent` to the string `"Note saved successfully!"`.
|
||||
|
||||
Now when you test it out, you should see that message display on the screen.
|
||||
|
||||
# --hints--
|
||||
|
||||
When the user edits a note, the status message should be updated to indicate that the note was saved.
|
||||
|
||||
```js
|
||||
const event = new Event("blur");
|
||||
note.dispatchEvent(event);
|
||||
|
||||
assert.equal(statusEl.textContent, "Note saved successfully!");
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Note taking app</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p class="helper-text">Click or tap on the card to edit your note.</p>
|
||||
|
||||
<div id="note" class="note" contenteditable="true" aria-label="Note editor">
|
||||
Many languages have words that carry meanings so specific or culturally rooted that they can't be neatly translated into English.
|
||||
|
||||
One example is the Japanese word "tsundoku", which refers to the habit of acquiring books and letting them pile up unread, something many book lovers can relate to. Another is the Portuguese word "saudade", describing a deep, bittersweet longing for something or someone that is absent. Meanwhile, the French word "Dépaysement" captures the disorienting yet exciting feeling of being in a new place, far from home.
|
||||
|
||||
These unique words remind us that language is more than vocabulary: it's a window into the values, habits, and emotions of the cultures that create it.
|
||||
</div>
|
||||
|
||||
<div id="status" aria-live="polite"></div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 2em;
|
||||
max-width: 700px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.note {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.5;
|
||||
min-height: 250px;
|
||||
font-size: 16px;
|
||||
/* This is needed to preserve line breaks in the div */
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.note[contenteditable="true"] {
|
||||
caret-color: black;
|
||||
}
|
||||
|
||||
.note:hover {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-top: 0.5em;
|
||||
user-select: none;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#status {
|
||||
color: #00471b;
|
||||
padding: 0 1em;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const noteEl = document.getElementById("note");
|
||||
const statusEl = document.getElementById("status");
|
||||
|
||||
let currentContent = "";
|
||||
|
||||
--fcc-editable-region--
|
||||
noteEl.addEventListener("blur", () => {
|
||||
const newContent = noteEl.innerHTML;
|
||||
|
||||
currentContent = newContent;
|
||||
console.log(currentContent);
|
||||
|
||||
});
|
||||
--fcc-editable-region--
|
||||
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
currentContent = noteEl.textContent;
|
||||
});
|
||||
```
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
---
|
||||
id: 6881436e1e2afae400e8b4fe
|
||||
title: Step 12
|
||||
challengeType: 0
|
||||
dashedName: step-12
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Right now, anytime the user edits a note and clicks outside of the note, the `currentContent` variable is being updated. But what about situations where the user focuses on the note and decides to leave without making any edits?
|
||||
|
||||
Well, in this situation, you can add a condition to handle that use case.
|
||||
|
||||
Above your `currentContent = newContent;` line, add an `if` statement to check if `currentContent` is equal to `newContent`. If so, return.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should add an `if` statement to check if `currentContent` is equal to `newContent`. If so, return.
|
||||
|
||||
```js
|
||||
assert.match(code, /if\s*\(\s*currentContent\s*=={2,3}\s*newContent\s*\)\s*(?:{\s*return;?\s*}|return\s*;?)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Note taking app</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p class="helper-text">Click or tap on the card to edit your note.</p>
|
||||
|
||||
<div id="note" class="note" contenteditable="true" aria-label="Note editor">
|
||||
Many languages have words that carry meanings so specific or culturally rooted that they can't be neatly translated into English.
|
||||
|
||||
One example is the Japanese word "tsundoku", which refers to the habit of acquiring books and letting them pile up unread, something many book lovers can relate to. Another is the Portuguese word "saudade", describing a deep, bittersweet longing for something or someone that is absent. Meanwhile, the French word "Dépaysement" captures the disorienting yet exciting feeling of being in a new place, far from home.
|
||||
|
||||
These unique words remind us that language is more than vocabulary: it's a window into the values, habits, and emotions of the cultures that create it.
|
||||
</div>
|
||||
|
||||
<div id="status" aria-live="polite"></div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 2em;
|
||||
max-width: 700px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.note {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.5;
|
||||
min-height: 250px;
|
||||
font-size: 16px;
|
||||
/* This is needed to preserve line breaks in the div */
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.note[contenteditable="true"] {
|
||||
caret-color: black;
|
||||
}
|
||||
|
||||
.note:hover {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-top: 0.5em;
|
||||
user-select: none;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#status {
|
||||
color: #00471b;
|
||||
padding: 0 1em;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const noteEl = document.getElementById("note");
|
||||
const statusEl = document.getElementById("status");
|
||||
|
||||
let currentContent = "";
|
||||
|
||||
noteEl.addEventListener("blur", () => {
|
||||
const newContent = noteEl.innerHTML;
|
||||
--fcc-editable-region--
|
||||
|
||||
currentContent = newContent;
|
||||
console.log(currentContent);
|
||||
--fcc-editable-region--
|
||||
statusEl.textContent = "Note saved successfully!";
|
||||
});
|
||||
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
currentContent = noteEl.textContent;
|
||||
});
|
||||
```
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
---
|
||||
id: 688143c66d5665e4b3409977
|
||||
title: Step 13
|
||||
challengeType: 0
|
||||
dashedName: step-13
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Things seem to be working except for one small issue. If you edit a note and then leave that note, you should see the `"Note saved successfully!"` message. But if you try to edit the note again, the message is still displaying.
|
||||
|
||||
The desired behavior is for the message to disappear when the user focuses on the element. You can use the `focus` event for this.
|
||||
|
||||
```js
|
||||
element.addEventListener("focus", () => {
|
||||
// do something here
|
||||
});
|
||||
```
|
||||
|
||||
Attach an `addEventListener` to the `noteEl` variable. The first argument should be the `"focus"` event and the second argument should be a callback function. Inside of that callback function, set the `statusEl.textContent` to an empty string. This will reset the live region when the note is focused.
|
||||
|
||||
And with that last change, your workshop is complete!
|
||||
|
||||
# --hints--
|
||||
|
||||
When the user focuses on the note, you should set the `statusEl.textContent` to an empty string.
|
||||
|
||||
```js
|
||||
window.dispatchEvent(new Event("DOMContentLoaded"));
|
||||
|
||||
const temp = console.log;
|
||||
console.log = () => {};
|
||||
|
||||
noteEl.innerHTML = "New content";
|
||||
noteEl.dispatchEvent(new Event("blur"));
|
||||
|
||||
assert.equal(noteEl.innerHTML, "New content");
|
||||
assert.equal(statusEl.textContent, "Note saved successfully!");
|
||||
|
||||
noteEl.dispatchEvent(new Event("focus"));
|
||||
assert.equal(statusEl.textContent, "");
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Note taking app</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p class="helper-text">Click or tap on the card to edit your note.</p>
|
||||
|
||||
<div id="note" class="note" contenteditable="true" aria-label="Note editor">
|
||||
Many languages have words that carry meanings so specific or culturally rooted that they can't be neatly translated into English.
|
||||
|
||||
One example is the Japanese word "tsundoku", which refers to the habit of acquiring books and letting them pile up unread, something many book lovers can relate to. Another is the Portuguese word "saudade", describing a deep, bittersweet longing for something or someone that is absent. Meanwhile, the French word "Dépaysement" captures the disorienting yet exciting feeling of being in a new place, far from home.
|
||||
|
||||
These unique words remind us that language is more than vocabulary: it's a window into the values, habits, and emotions of the cultures that create it.
|
||||
</div>
|
||||
|
||||
<div id="status" aria-live="polite"></div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 2em;
|
||||
max-width: 700px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.note {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.5;
|
||||
min-height: 250px;
|
||||
font-size: 16px;
|
||||
/* This is needed to preserve line breaks in the div */
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.note[contenteditable="true"] {
|
||||
caret-color: black;
|
||||
}
|
||||
|
||||
.note:hover {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-top: 0.5em;
|
||||
user-select: none;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#status {
|
||||
color: #00471b;
|
||||
padding: 0 1em;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const noteEl = document.getElementById("note");
|
||||
const statusEl = document.getElementById("status");
|
||||
|
||||
let currentContent = "";
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
noteEl.addEventListener("blur", () => {
|
||||
const newContent = noteEl.innerHTML;
|
||||
|
||||
if (currentContent === newContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentContent = newContent;
|
||||
console.log(currentContent);
|
||||
|
||||
statusEl.textContent = "Note saved successfully!";
|
||||
});
|
||||
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
currentContent = noteEl.textContent;
|
||||
});
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Note taking app</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p class="helper-text">Click or tap on the card to edit your note.</p>
|
||||
|
||||
<div id="note" class="note" contenteditable="true" aria-label="Note editor">
|
||||
Many languages have words that carry meanings so specific or culturally rooted that they can't be neatly translated into English.
|
||||
|
||||
One example is the Japanese word "tsundoku", which refers to the habit of acquiring books and letting them pile up unread, something many book lovers can relate to. Another is the Portuguese word "saudade", describing a deep, bittersweet longing for something or someone that is absent. Meanwhile, the French word "Dépaysement" captures the disorienting yet exciting feeling of being in a new place, far from home.
|
||||
|
||||
These unique words remind us that language is more than vocabulary: it's a window into the values, habits, and emotions of the cultures that create it.
|
||||
</div>
|
||||
|
||||
<div id="status" aria-live="polite"></div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 2em;
|
||||
max-width: 700px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.note {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.5;
|
||||
min-height: 250px;
|
||||
font-size: 16px;
|
||||
/* This is needed to preserve line breaks in the div */
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.note[contenteditable="true"] {
|
||||
caret-color: black;
|
||||
}
|
||||
|
||||
.note:hover {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-top: 0.5em;
|
||||
user-select: none;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#status {
|
||||
color: #00471b;
|
||||
padding: 0 1em;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const noteEl = document.getElementById("note");
|
||||
const statusEl = document.getElementById("status");
|
||||
|
||||
let currentContent = "";
|
||||
|
||||
noteEl.addEventListener("focus", () => {
|
||||
statusEl.textContent = '';
|
||||
})
|
||||
|
||||
noteEl.addEventListener("blur", () => {
|
||||
const newContent = noteEl.innerHTML;
|
||||
|
||||
if (currentContent === newContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentContent = newContent;
|
||||
console.log(currentContent);
|
||||
|
||||
statusEl.textContent = "Note saved successfully!";
|
||||
});
|
||||
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
currentContent = noteEl.textContent;
|
||||
});
|
||||
```
|
||||
@@ -834,6 +834,9 @@
|
||||
{
|
||||
"dashedName": "workshop-planets-tablist"
|
||||
},
|
||||
{
|
||||
"dashedName": "workshop-note-taking-app"
|
||||
},
|
||||
{
|
||||
"dashedName": "review-js-a11y"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user