chore(curriculum): add fcc authors page workshop to FSD cert (#57054)

This commit is contained in:
Dario-DC
2024-11-06 20:42:36 +01:00
committed by GitHub
parent 2e76ea10da
commit d9997a3e11
34 changed files with 5337 additions and 2 deletions

View File

@@ -2796,7 +2796,13 @@
]
},
"mjbe": { "title": "251", "intro": [] },
"byqx": { "title": "252", "intro": [] },
"workshop-fcc-authors-page": {
"title": "Build an fCC Authors Page",
"intro": [
"One common aspect of web development is learning how to fetch data from an external API, then work with asynchronous JavaScript.",
"In this workshop you will practice how to use the fetch method, dynamically update the DOM to display the fetched data and paginate your data so you can load results in batches."
]
},
"alda": { "title": "253", "intro": [] },
"cvaf": { "title": "254", "intro": [] },
"review-asynchronous-javascript": {

View File

@@ -0,0 +1,11 @@
---
title: Introduction to the Build an fCC Authors Page
block: workshop-fcc-authors-page
superBlock: full-stack-developer
---
## Introduction to the Build an fCC Authors Page
One common aspect of web development is learning how to fetch data from an external API, then work with asynchronous JavaScript.
In this workshop you will practice how to use the fetch method, dynamically update the DOM to display the fetched data and paginate your data so you can load results in batches.

View File

@@ -0,0 +1,129 @@
{
"name": "Build an fCC Authors Page",
"blockType": "workshop",
"blockLayout": "challenge-grid",
"isUpcomingChange": true,
"usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "workshop-fcc-authors-page",
"superBlock": "full-stack-developer",
"challengeOrder": [
{
"id": "641d9a19bff38d34d5a5edb8",
"title": "Step 1"
},
{
"id": "641da3c6b6fbd742bff6ee40",
"title": "Step 2"
},
{
"id": "641da42481d90c4314c99e94",
"title": "Step 3"
},
{
"id": "641da465273051435d332b15",
"title": "Step 4"
},
{
"id": "641da4b16937be43ba24c63d",
"title": "Step 5"
},
{
"id": "641da51a9810e74411262fcc",
"title": "Step 6"
},
{
"id": "641da5462576784453146ec2",
"title": "Step 7"
},
{
"id": "641da5abaac81844a54adb03",
"title": "Step 8"
},
{
"id": "641da5dd6cd6db44f58b7787",
"title": "Step 9"
},
{
"id": "641da615af82bf454215a992",
"title": "Step 10"
},
{
"id": "641da6570acf7545931ce477",
"title": "Step 11"
},
{
"id": "641da6dcb6e4c9463d54c75b",
"title": "Step 12"
},
{
"id": "641da7071d0d45467cd59977",
"title": "Step 13"
},
{
"id": "641da73b09e7f046c758e0ed",
"title": "Step 14"
},
{
"id": "641da791d0c34a472b8d15b6",
"title": "Step 15"
},
{
"id": "641da7bfbc7f0f477438ad8a",
"title": "Step 16"
},
{
"id": "641da803d9892447d059804e",
"title": "Step 17"
},
{
"id": "641da836581c254815f785fe",
"title": "Step 18"
},
{
"id": "641da86294fd9f485d3c2bf0",
"title": "Step 19"
},
{
"id": "641da895fb7ec648a5bdf19c",
"title": "Step 20"
},
{
"id": "641da8db2a036048ebe6999e",
"title": "Step 21"
},
{
"id": "641da97c987a514959ada414",
"title": "Step 22"
},
{
"id": "641da9aceb788e49a73ebcc9",
"title": "Step 23"
},
{
"id": "641da9ea9b847a49fe6ee9b6",
"title": "Step 24"
},
{
"id": "641daa5ea050f24a7cade6e6",
"title": "Step 25"
},
{
"id": "641daa8c2c3e364ac3650b37",
"title": "Step 26"
},
{
"id": "641daabed8d0584b1150c953",
"title": "Step 27"
},
{
"id": "641daae5e18eae4b562633e4",
"title": "Step 28"
},
{
"id": "641dab13c1b6f14b9828e6b1",
"title": "Step 29"
}
],
"helpCategory": "JavaScript"
}

View File

@@ -0,0 +1,149 @@
---
id: 641d9a19bff38d34d5a5edb8
title: Step 1
challengeType: 0
dashedName: step-1
demoType: onLoad
---
# --description--
All the HTML and CSS for this project has been provided for you. You can take a look at the two files to familiarize yourself with them.
Start by getting the `#author-container` and `#load-more-btn` elements with the `.getElementById()` method. Assign them to the variables `authorContainer` and `loadMoreBtn`, respectively.
The variables will not change, so use `const` to declare them.
# --hints--
You should use `document.getElementById()` to get the `#author-container` element.
```js
assert.match(code, /document\.getElementById\(\s*('|"|`)author\-container\1\s*\)/);
```
You should assign the `#author-container` element to the variable `authorContainer`. Don't forget to use `const` to declare the variable.
```js
assert.match(code, /const\s+authorContainer\s*\=\s*document\.getElementById\(\s*('|"|`)author\-container\1\s*\)/);
```
You should use `document.getElementById()` to get the `#load-more-btn` element.
```js
assert.match(code, /document\.getElementById\(\s*('|"|`)load\-more\-btn\1\s*\)/);
```
You should assign the `#load-more-btn` element to the variable `loadMoreBtn`. Don't forget to use `const` to declare the variable.
```js
assert.match(code, /const\s+loadMoreBtn\s*\=\s*document\.getElementById\(\s*('|"|`)load\-more\-btn\1\s*\)/);
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
--fcc-editable-region--
--fcc-editable-region--
```

View File

@@ -0,0 +1,143 @@
---
id: 641da3c6b6fbd742bff6ee40
title: Step 2
challengeType: 0
dashedName: step-2
---
# --description--
The <dfn>Fetch API</dfn> is a built-in JavaScript interface to make network requests to a server. It has a `fetch()` method you can use to make `GET`, `POST`, `PUT`, or `PATCH` requests. In this project, you'll make a `GET` request to a URL for a JSON file with information about authors on freeCodeCamp News.
Here is how you can make a `GET` request with the `fetch()` method:
```js
fetch("url-goes-here")
```
Make a `GET` request to this URL: `"https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json"`. Don't terminate your code with a semicolon yet.
# --hints--
You should use the `fetch()` method to make a `GET` request to `"https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json"`.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)/)
```
You should not have a semicolon after your `fetch` call.
```js
assert.notMatch(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*;/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
--fcc-editable-region--
--fcc-editable-region--
```

View File

@@ -0,0 +1,173 @@
---
id: 641da42481d90c4314c99e94
title: Step 3
challengeType: 0
dashedName: step-3
---
# --description--
The `fetch()` method returns a `Promise`, which is a placeholder object that will either be fulfilled if your request is successful, or rejected if your request is unsuccessful.
If the `Promise` is fulfilled, it resolves to a `Response` object, and you can use the `.then()` method to access the `Response`.
Here's how you can chain `.then()` to the `fetch()` method:
```js
fetch("sample-url-goes-here")
.then((res) => res)
```
Chain the `.then()` method to your `fetch` call. Inside the `.then()` method, add a callback function with `res` as a parameter, then log the `res` to the console so you can see the `Response` object. Open your browser console and expand the `Response` object to see what it contains.
Again, don't terminate the code with a semicolon yet.
# --hints--
You should use the `fetch()` method to make a `GET` request to `"https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json"`.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)/)
```
You should chain the `.then()` method to your `fetch()` syntax
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(/)
```
You should add a `res` parameter to your `.then()` method.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)/)
```
Your `.then()` method should use arrow function syntax.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*/)
```
You should log the `res` parameter to the console so you can see the `Response` object.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*console\.log\(\s*res\s*\)\s*\)/)
```
You should not have a semicolon after your `.then()`.
```js
assert.notMatch(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*console\.log\(\s*res\s*\)\s*\)\s*;/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
--fcc-editable-region--
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
--fcc-editable-region--
```

View File

@@ -0,0 +1,146 @@
---
id: 641da465273051435d332b15
title: Step 4
challengeType: 0
dashedName: step-4
---
# --description--
The data you get from a `GET` request is not usable at first. To make the data usable, you can use the `.json()` method on the `Response` object to parse it into JSON. If you expand the `Prototype` of the `Response` object in the browser console, you will see the `.json()` method there.
Remove `console.log(res)` and implicitly return `res.json()` instead.
# --hints--
You should remove the `console.log(res)`.
```js
assert.notMatch(code, /\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*console\.log\(\s*res\s*\)\s*\)/)
```
You should implicitly return `res.json()` in your `.then()`.
```js
assert.match(code, /\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)/)
```
You should not have a semicolon after your `.then()`.
```js
assert.notMatch(code, /\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)\s*;/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
--fcc-editable-region--
.then((res) => console.log(res))
--fcc-editable-region--
```

View File

@@ -0,0 +1,176 @@
---
id: 641da4b16937be43ba24c63d
title: Step 5
challengeType: 0
dashedName: step-5
---
# --description--
In order to start working with the data, you will need to use another `.then()` method.
Chain another `.then()` to the existing `.then()` method. This time, pass in `data` as the parameter for the callback function. For the callback, use a curly brace because you will have more than one expression. Within your callback function, log `data` to the console to see what it looks like.
# --hints--
You should use the `fetch()` method to make a `GET` request to `"https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json"`.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)/)
```
Your first `.then()` should not have a semicolon.
```js
assert.notMatch(code, /\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)\s*;/)
```
You should chain another `.then()` to the existing `.then()`.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)\s*\.then\(/)
```
You should add a `data` parameter to your second `.then()` method.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)\s*\.then\(\s*(\(\s*data\s*\)|data)/)
```
Your second `.then()` method should use arrow function syntax.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)\s*\.then\(\s*(\(\s*data\s*\)|data)\s*=>\s*/)
```
Your second `.then()` method should have curly braces.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)\s*\.then\(\s*(\(\s*data\s*\)|data)\s*=>\s*\{\s*\n?\s*/)
```
Your second `.then()` method should log `data` to the console.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)\s*\.then\(\s*(\(\s*data\s*\)|data)\s*=>\s*\{\s*\n?\s*?console\.log\(\s*data\s*\)\s*;?\s*\}\s*\)/)
```
Your second `.then()` should not have semicolon after it.
```js
assert.notMatch(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)\s*\.then\(\s*(\(\s*data\s*\)|data)\s*=>\s*\{\s*\n?\s*?console\.log\(\s*data\s*\)\s*;?\s*\}\s*\)\s*;/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
--fcc-editable-region--
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
--fcc-editable-region--
```

View File

@@ -0,0 +1,198 @@
---
id: 641da51a9810e74411262fcc
title: Step 6
challengeType: 0
dashedName: step-6
---
# --description--
The `.catch()` method is another asynchronous JavaScript method you can use to handle errors. This is useful in case the `Promise` gets rejected.
Chain `.catch()` to the last `.then()`. Pass in a callback function with `err` as the parameter. Inside the callback, use `console.error()` to log possible errors to the console with the text `There was an error: ${err}`. Since you're using `err` in the text, don't forget to use a template literal string with backticks (\`\`) instead of single or double quotes.
**Note**: Now you can terminate your code with a semicolon. You couldn't do that in the previous steps because you'll signal to JavaScript to stop parsing your code, which will affect the `fetch()` syntax.
# --hints--
You should use the `fetch()` method to make a `GET` request to `"https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json"`.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)/)
```
You should chain another `.then()` to the existing `.then()`.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)\s*\.then\(/)
```
You should add a `data` parameter to your second `.then()` method.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)\s*\.then\(\s*(\(\s*data\s*\)|data)/)
```
Your second `.then()` method should use arrow function syntax.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)\s*\.then\(\s*(\(\s*data\s*\)|data)\s*=>\s*/)
```
Your second `.then()` method should have curly braces.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)\s*\.then\(\s*(\(\s*data\s*\)|data)\s*=>\s*\{\s*\n?\s*/)
```
Your second `.then()` method should log `data` to the console.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)\s*\.then\(\s*(\(\s*data\s*\)|data)\s*=>\s*\{\s*\n?\s*?console\.log\(\s*data\s*\)\s*;?\n?\s*\}\s*\)/)
```
Your second `.then()` should not have a semicolon.
```js
assert.notMatch(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)\s*\.then\(\s*(\(\s*data\s*\)|data)\s*=>\s*\{\s*\n?\s*?console\.log\(\s*data\s*\)\s*;?\n?\s*\}\s*\)\s*;/)
```
You should chain the `.catch()` method to the second `.then()` you already have.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)\s*\.then\(\s*(\(\s*data\s*\)|data)\s*=>\s*\{\s*\n?\s*?console\.log\(\s*data\s*\)\s*;?\n?\s*\}\s*\)\n?\s*\.catch\(/)
```
You should add an `err` parameter to your `.catch()` method.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)\s*\.then\(\s*(\(\s*data\s*\)|data)\s*=>\s*\{\s*\n?\s*?console\.log\(\s*data\s*\)\s*;?\n?\s*\}\s*\)\n?\s*\.catch\(\s*(\(\s*err\s*\)|err)/)
```
Your `.catch()` method should have an arrow function syntax.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)\s*\.then\(\s*(\(\s*data\s*\)|data)\s*=>\s*\{\s*\n?\s*?console\.log\(\s*data\s*\)\s*;?\n?\s*\}\s*\)\n?\s*\.catch\(\s*(\(\s*err\s*\)|err)\s*=>\s*\{?/)
```
Your `.catch()` method should use `console.error()` to log `err` to the console with the text `` `There was an error: ${err}` ``. Don't forget to use backticks.
```js
assert.match(code, /fetch\(\s*('|"|`)https:\/\/cdn\.freecodecamp\.org\/curriculum\/news\-author\-page\/authors\.json\1\s*\)\s*\.then\(\s*(\(\s*res\s*\)|res)\s*=>\s*res\.json\(\s*\)\s*\)\s*\.then\(\s*(\(\s*data\s*\)|data)\s*=>\s*\{\s*\n?\s*?console\.log\(\s*data\s*\)\s*;?\n?\s*\}\s*\)\n?\s*\.catch\(\s*(\(\s*err\s*\)|err)\s*=>\s*\{?\n?(\s*)?console\.error\(\s*`There\swas\san\serror:\s?\$\{\s*err\s*\}`\s*\)\s*;?\n?(\s*)?\}?\s*\)\s*;?/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
console.log(data);
})
--fcc-editable-region--
--fcc-editable-region--
```

View File

@@ -0,0 +1,172 @@
---
id: 641da5462576784453146ec2
title: Step 7
challengeType: 0
dashedName: step-7
---
# --description--
Now that you have the data you want, you can use it to populate the UI. But the fetched data contains an array of 26 authors, and if you add them all to the page at the same time, it could lead to poor performance.
Instead, you should add 8 authors at a time, and have a button to add 8 more until there's no more data to display.
Use `let` to create 2 variables named `startingIndex` and `endingIndex`, and assign them the number values `0` and `8`, respectively. Also, create an `authorDataArr` variable with `let` and set it to an empty array.
# --hints--
You should use `let` to declare a variable named `startingIndex`.
```js
assert.match(code, /let\s+startingIndex/)
```
You should set your `startingIndex` variable to `0`.
```js
assert.match(code, /let\s+startingIndex\s*=\s*0\s*;?/)
```
You should use `let` to declare a variable named `endingIndex`.
```js
assert.match(code, /let\s+endingIndex/)
```
You should set your `endingIndex` variable to `8`.
```js
assert.match(code, /let\s+endingIndex\s*=\s*8\s*;?/)
```
You should use `let` to declare a variable named `authorDataArr`.
```js
assert.match(code, /let\s+authorDataArr/)
```
You should set your `authorDataArr` variable to an empty array (`[]`).
```js
assert.match(code, /let\s+authorDataArr\s*=\s*\[\s*\]\s*;?/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
--fcc-editable-region--
--fcc-editable-region--
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
console.log(data);
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
```

View File

@@ -0,0 +1,168 @@
---
id: 641da5abaac81844a54adb03
title: Step 8
challengeType: 0
dashedName: step-8
---
# --description--
Now you'll create a function to populate the UI with the author data. You will call this function inside the second `.then()` method.
Create an empty arrow function named `displayAuthors` that takes `authors` as a parameter.
# --hints--
You should use `const` to create an empty function named `displayAuthors`.
```js
assert.match(code, /const\s+displayAuthors\s*=\s*/)
```
`displayAuthors` should be a function.
```js
assert.isFunction(displayAuthors)
```
Your `displayAuthors` function should use an arrow syntax.
```js
assert.match(code, /const\s+displayAuthors\s*=\s*(\(.*\)|[^\s()]+)\s*=>\s*/)
```
Your `displayAuthors` function should take an `authors` parameter.
```js
assert.match(code, /const\s+displayAuthors\s*=\s*\(?\s*authors/)
```
Your `displayAuthors` function should be empty.
```js
assert.match(code, /const\s+displayAuthors\s*=\s*(\(\s*authors\s*\)|authors)\s*=>\s*\{\n?\s*?\}\s*;?/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
console.log(data);
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
--fcc-editable-region--
--fcc-editable-region--
```

View File

@@ -0,0 +1,144 @@
---
id: 641da5dd6cd6db44f58b7787
title: Step 9
challengeType: 0
dashedName: step-9
---
# --description--
Inside your `displayAuthors` function, chain `.forEach()` to `authors`.
# --hints--
You should chain `.forEach()` to `authors`.
```js
assert.match(code, /authors\.forEach\(\s*\)/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
console.log(data);
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
const displayAuthors = (authors) => {
--fcc-editable-region--
--fcc-editable-region--
};
```

View File

@@ -0,0 +1,164 @@
---
id: 641da615af82bf454215a992
title: Step 10
challengeType: 0
dashedName: step-10
---
# --description--
Pass an empty callback function to the `.forEach()` method. For the first parameter of the callback, destructure the `author`, `image`, `url`, and `bio` items.
For the second parameter, pass in `index`. This will represent the position of each author, and will be useful for pagination later.
# --hints--
You should destructure `author`, `image`, `url`, and `bio` as the first parameter of your callback function. Don't forget to put them inside curly braces.
```js
assert.match(code, /authors\.forEach\(\s*\(\s*\{\s*(?:author\s*,\s*image\s*,\s*url\s*,\s*bio)|(?:image\s*,\s*author\s*,\s*url\s*,\s*bio)|(?:url\s*,\s*author\s*,\s*image\s*,\s*bio)|(?:author\s*,\s*url\s*,\s*image\s*,\s*bio)|(?:image\s*,\s*url\s*,\s*author\s*,\s*bio)|(?:url\s*,\s*image\s*,\s*author\s*,\s*bio)|(?:url\s*,\s*image\s*,\s*bio\s*,\s*author)|(?:image\s*,\s*url\s*,\s*bio\s*,\s*author)|(?:bio\s*,\s*url\s*,\s*image\s*,\s*author)|(?:url\s*,\s*bio\s*,\s*image\s*,\s*author)|(?:image\s*,\s*bio\s*,\s*url\s*,\s*author)|(?:bio\s*,\s*image\s*,\s*url\s*,\s*author)|(?:bio\s*,\s*author\s*,\s*url\s*,\s*image)|(?:author\s*,\s*bio\s*,\s*url\s*,\s*image)|(?:url\s*,\s*bio\s*,\s*author\s*,\s*image)|(?:bio\s*,\s*url\s*,\s*author\s*,\s*image)|(?:author\s*,\s*url\s*,\s*bio\s*,\s*image)|(?:url\s*,\s*author\s*,\s*bio\s*,\s*image)|(?:image\s*,\s*author\s*,\s*bio\s*,\s*url)|(?:author\s*,\s*image\s*,\s*bio\s*,\s*url)|(?:bio\s*,\s*image\s*,\s*author\s*,\s*url)|(?:image\s*,\s*bio\s*,\s*author\s*,\s*url)|(?:author\s*,\s*bio\s*,\s*image\s*,\s*url)|(?:bio\s*,\s*author\s*,\s*image\s*,\s*url)\s*\}/)
```
You should destructure `index` as the second parameter of your callback function. It has to be outside of the curly braces you put the others in.
```js
assert.match(code, /authors\.forEach\(\s*\(\s*\{.*\}\s*,\s*index\s*\)/)
```
Your callback function should use an arrow syntax.
```js
assert.match(code, /authors\.forEach\(\s*\(\s*\{.*\}\s*,\s*index\s*\)\s*=>\s*/)
```
Your callback function should be empty.
```js
assert.match(code, /authors\.forEach\(\s*\(\s*\{.*\}\s*,\s*index\s*\)\s*=>\s*\{\s*\}\s*\)\s*;?/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
console.log(data);
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
const displayAuthors = (authors) => {
--fcc-editable-region--
authors.forEach();
--fcc-editable-region--
};
```

View File

@@ -0,0 +1,155 @@
---
id: 641da6570acf7545931ce477
title: Step 11
challengeType: 0
dashedName: step-11
---
# --description--
Now it's time to start building the HTML for the page with your destructured data. You can do this with a combination of the compound assignment operator (`+=`) and the `innerHTML` property.
Inside your callback function, use the compound assignment operator to append an empty template literal to the `innerHTML` of `authorContainer`.
# --hints--
You should access the `innerHTML` of `authorContainer`.
```js
assert.match(code, /authorContainer\.innerHTML\s*/)
```
You should use compound assignment to append an empty template literal to the `innerHTML` of `authorContainer`.
```js
assert.match(code, /authorContainer\.innerHTML\s*\+=\s*`\s*\n?\s*`\s*;?/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
console.log(data);
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
--fcc-editable-region--
--fcc-editable-region--
});
};
```

View File

@@ -0,0 +1,162 @@
---
id: 641da6dcb6e4c9463d54c75b
title: Step 12
challengeType: 0
dashedName: step-12
---
# --description--
Inside the template literal, create a `div` element with the `id` set to the `index` from the `.forEach()` array method. Remember to use string interpolation to do this.
Also, add a `class` of `"user-card"` to the `div`.
# --hints--
You should create a `div` element with the class `"user-card"`.
```js
displayAuthors([{author: "Kolade"}]);
assert.include(document.querySelector('#author-container > div')?.className, 'user-card');
```
Your `div` element should have the `id` of `index`. Remember to use string interpolation to set the `id`.
```js
displayAuthors([{author: "Naomi"}]);
assert.equal(document.querySelector('#author-container > div')?.id, '0');
```
Your `div` element should be empty.
```js
assert.equal(document.querySelector('#author-container > div')?.innerText, '');
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
console.log(data);
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
--fcc-editable-region--
authorContainer.innerHTML += ``;
--fcc-editable-region--
});
};
```

View File

@@ -0,0 +1,168 @@
---
id: 641da7071d0d45467cd59977
title: Step 13
challengeType: 0
dashedName: step-13
---
# --description--
Now you need to show some information about the author. First, show the author's name.
Create an `h2` tag with the `class` `"author-name"`. Then, interpolate `author` inside the `h2` tag. This is the author's name.
# --hints--
You should create an `h2` element within your `div` element.
```js
displayAuthors([{author: "Kolade"}]);
assert.exists(document.querySelector('#author-container > div > h2'));
```
Your `h2` element should have a class set to `"author-name"`
```js
displayAuthors([{name: "Jane", height: 12}])
assert.equal(document.querySelector('#author-container > div > h2')?.className, 'author-name');
```
You should interpolate the `author` in your `h2` tag to show the authors' names
```js
displayAuthors([{author: "Kolade"}]);
assert.equal(document.querySelector('#author-container > div > h2')?.innerText, 'Kolade');
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
console.log(data);
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
authorContainer.innerHTML += `
<div id="${index}" class="user-card">
--fcc-editable-region--
--fcc-editable-region--
</div>
`;
});
};
```

View File

@@ -0,0 +1,159 @@
---
id: 641da73b09e7f046c758e0ed
title: Step 14
challengeType: 0
dashedName: step-14
---
# --description--
To see the authors' names on the page, you need to call the `displayAuthors` function inside the second `.then()` method. But before that, you need to assign the author data to the empty `authorDataArr` array.
First, remove your `console.log()` statement. Then, assign `data` to the `authorDataArr` variable.
# --hints--
You should remove the console log showing the `data`.
```js
assert.notMatch(code, /console\.log\(\s*data\s*\)\s*;?/)
```
You should assign `data` to the `authorDataArr` variable
```js
assert.match(code, /authorDataArr\s*=\s*data\s*;?/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
--fcc-editable-region--
console.log(data);
--fcc-editable-region--
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
authorContainer.innerHTML += `
<div id="${index}" class="user-card">
<h2 class="author-name">${author}</h2>
</div>
`;
});
};
```

View File

@@ -0,0 +1,171 @@
---
id: 641da791d0c34a472b8d15b6
title: Step 15
challengeType: 0
dashedName: step-15
---
# --description--
Now `authorDataArr` is the same as the `data` you logged to the console a while ago. Log `authorDataArr` to the console to confirm this.
Inside your `console.log()` statement, add the text `"Author Data Array:"` as the first argument and `authorDataArr` as the second argument. Use comma to separate the text from `authorDataArr`.
# --hints--
You should assign `data` to the `authorDataArr` variable
```js
assert.match(code, /authorDataArr\s*=\s*data\s*;?/)
```
You should have a console log with the text `"Author Data Array:"`.
```js
assert.match(code, /console\.log\(\s*("|'|`)Author\s+Data\s+Array:\s*\1/)
```
You should use comma to separate your `"Author Data Array:"` text and `authorDataArr`.
```js
assert.match(code, /console\.log\(\s*("|'|`)Author\s+Data\s+Array:\s*\1\s*,/)
```
`authorDataArr` should be the second argument of your console log statement.
```js
assert.match(code, /console\.log\(\s*("|'|`)Author\s+Data\s+Array:\s*\1\s*,\s*authorDataArr\s*\)\s*;?/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
--fcc-editable-region--
authorDataArr = data;
--fcc-editable-region--
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
authorContainer.innerHTML += `
<div id="${index}" class="user-card">
<h2 class="author-name">${author}</h2>
</div>
`;
});
};
```

View File

@@ -0,0 +1,184 @@
---
id: 641da7bfbc7f0f477438ad8a
title: Step 16
challengeType: 0
dashedName: step-16
---
# --description--
Now it's time to call the `displayAuthors` function. But again, you don't want to populate the page with all the authors at once. Instead, you can extract a portion of the authors with the `startingIndex` and `endingIndex` variables. The best method to do this is the `.slice()` array method.
First, remove the console log statement showing `authorDataArr`. Then, call the `displayAuthors` function with the `authorDataArr` array and `.slice()`. Use the `startingIndex` variable for the starting point and the `endingIndex` variable for the ending point.
# --hints--
You should assign `data` to the `authorDataArr` variable
```js
assert.match(code, /authorDataArr\s*=\s*data\s*;?/)
```
You should remove the console log statement showing `authorDataArr`
```js
assert.notMatch(code, /console\.log\(\s*("|'|`)Author\s+Data\s+Array:\s*\1\s*,\s*authorDataArr\s*\)\s*;?/)
```
You should call your `displayAuthors` function.
```js
assert.match(code, /displayAuthors\(/)
```
You should call your `displayAuthors` function and pass in an argument of `authorDataArr.slice()`.
```js
assert.match(code, /displayAuthors\(\s*authorDataArr\.slice\(/)
```
The first parameter of your `slice()` method should be `startingIndex`.
```js
assert.match(code, /displayAuthors\(\s*authorDataArr\.slice\(\s*startingIndex/)
```
The second parameter of your `slice()` method should be `endingIndex`.
```js
assert.match(code, /displayAuthors\(\s*authorDataArr\.slice\(\s*startingIndex\s*,\s*endingIndex\s*\)\s*\)\s*;?/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
--fcc-editable-region--
authorDataArr = data;
console.log("Author Data Array:", authorDataArr);
--fcc-editable-region--
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
authorContainer.innerHTML += `
<div id="${index}" class="user-card">
<h2 class="author-name">${author}</h2>
</div>
`;
});
};
```

View File

@@ -0,0 +1,182 @@
---
id: 641da803d9892447d059804e
title: Step 17
challengeType: 0
dashedName: step-17
---
# --description--
Now create an image tag and give it the `class` `"user-img"`. Use string interpolation to set the `src` attribute to `image` you destructured earlier. Set the `alt` attribute to `author` followed by the text `"avatar"`. Make sure there is a space between the `author` variable and the word `"avatar"`, for example, `"Quincy Larson avatar"`.
# --hints--
You should create an `img` element.
```js
const retryingTest = (test, message, tries = 20) => {
if (tries < 1) return Promise.reject(message);
if (test()) return Promise.resolve();
return new Promise((resolve, reject) => {
setTimeout(() => {
retryingTest(test, message, tries - 1)
.then(resolve)
.catch(reject);
}, 100);
});
};
() => retryingTest(() => document.querySelector('img'), "'img' element not found");
```
Your `img` element should have the class `"user-img"`.
```js
assert.include(document.querySelector('img')?.className, "user-img");
```
You should set the `src` attribute of your `img` element to `${image}`.
```js
assert.equal(document.querySelector('img')?.getAttribute('src'), authorDataArr[0].image);
```
You should set the `alt` attribute of your `img` element to `${author} avatar`.
```js
assert.equal(document.querySelector('img')?.getAttribute('alt'), `${authorDataArr[0].author} avatar`);
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
authorDataArr = data;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
authorContainer.innerHTML += `
<div id="${index}" class="user-card">
<h2 class="author-name">${author}</h2>
--fcc-editable-region--
--fcc-editable-region--
</div>
`;
});
};
```

View File

@@ -0,0 +1,180 @@
---
id: 641da836581c254815f785fe
title: Step 18
challengeType: 0
dashedName: step-18
---
# --description--
The next thing you'll show are biographical details about the author. You can do this with `bio` that you destructured earlier.
Add a paragraph element with the `class` `"bio"`, then interpolate `bio` inside the paragraph element.
# --hints--
You should create a `p` element.
```js
const retryingTest = (test, message, tries = 20) => {
if (tries < 1) return Promise.reject(message);
if (test()) return Promise.resolve();
return new Promise((resolve, reject) => {
setTimeout(() => {
retryingTest(test, message, tries - 1)
.then(resolve)
.catch(reject);
}, 100);
});
};
() => retryingTest(() => document.querySelector('p'), "'p' element not found");
```
Your `p` element should have the class `"bio"`
```js
assert.include(document.querySelector('p')?.className, 'bio');
```
You should interpolate `bio` in your `p` element.
```js
assert.equal(document.querySelector('p')?.innerText, authorDataArr[0].bio);
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
authorDataArr = data;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
authorContainer.innerHTML += `
<div id="${index}" class="user-card">
<h2 class="author-name">${author}</h2>
<img class="user-img" src="${image}" alt="${author} avatar" />
--fcc-editable-region--
--fcc-editable-region--
</div>
`;
});
};
```

View File

@@ -0,0 +1,193 @@
---
id: 641da86294fd9f485d3c2bf0
title: Step 19
challengeType: 0
dashedName: step-19
---
# --description--
Next, add a link to the author's page on freeCodeCamp News.
Add an anchor element with the `class` `"author-link"`, interpolate `url` as the value for the `href` attribute, and set `target` to `"_blank"`. For the text of the anchor element, interpolate `author` followed by the text `"'s author page"`. For example, `"Quincy Larson's author page"`.
# --hints--
You should create an anchor element.
```js
const retryingTest = (test, message, tries = 20) => {
if (tries < 1) return Promise.reject(message);
if (test()) return Promise.resolve();
return new Promise((resolve, reject) => {
setTimeout(() => {
retryingTest(test, message, tries - 1)
.then(resolve)
.catch(reject);
}, 100);
});
};
() => retryingTest(() => document.querySelector('a'), "'a' element not found");
```
Your anchor element should have the class `"author-link"`.
```js
assert.include(document.querySelector('a')?.className, "author-link");
```
You should interpolate `url` as the value of your anchor's `href` attribute.
```js
assert.equal(document.querySelector('a')?.getAttribute('href'), authorDataArr[0].url);
```
You should set the `target` attribute of your anchor element to `"_blank"`.
```js
assert.equal(document.querySelector('a')?.getAttribute('target'), '_blank');
```
You should interpolate `author` followed by the text `"'s author page"` inside your anchor element.
```js
assert.equal(document.querySelector('a')?.textContent.trim(), `${authorDataArr[0].author}'s author page`);
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
authorDataArr = data;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
const displayAuthors = (authors) => {
authors.forEach( ({ author, image, url, bio }, index) =>
(authorContainer.innerHTML += `
<div id="${index}" class="user-card">
<h2 class="author-name">${author}</h2>
<img class="user-img" src="${image}" alt="${author} avatar" />
<p class="bio">${bio}</p>
--fcc-editable-region--
--fcc-editable-region--
</div>
`)
)
};
```

View File

@@ -0,0 +1,182 @@
---
id: 641da895fb7ec648a5bdf19c
title: Step 20
challengeType: 0
dashedName: step-20
---
# --description--
Now you have everything you want to include in the UI. The next step is to make the `Load More Authors` button fetch more authors whenever it's clicked. You can do this by adding a `click` event to the button and carefully incrementing the `startingIndex` and `endingIndex` variables.
Create a `fetchMoreAuthors` function with the arrow function syntax. Don't put anything in it yet. Make sure you use curly braces because you'll have more than one expression inside the function.
# --hints--
You should use `const` to create a `fetchMoreAuthors` function.
```js
assert.match(code, /const\s+fetchMoreAuthors\s*=\s*/)
```
`fetchMoreAuthors` should be a function.
```js
assert.isFunction(fetchMoreAuthors)
```
Your `fetchMoreAuthors` function should not take any parameter.
```js
assert.match(code, /const\s+fetchMoreAuthors\s*=\s*\(\s*\)\s*/)
```
Your `fetchMoreAuthors` function should use arrow syntax.
```js
assert.match(code, /const\s+fetchMoreAuthors\s*=\s*\(\s*\)\s*=>\s*/)
```
Your `fetchMoreAuthors` function should be empty.
```js
assert.match(code, /const\s+fetchMoreAuthors\s*=\s*\(\s*\)\s*=>\s*\{\s*\}/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
authorDataArr = data;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
--fcc-editable-region--
--fcc-editable-region--
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
authorContainer.innerHTML += `
<div id="${index}" class="user-card">
<h2 class="author-name">${author}</h2>
<img class="user-img" src="${image}" alt="${author} avatar" />
<p class="bio">${bio}</p>
<a class="author-link" href="${url}" target="_blank">${author}'s author page</a>
</div>
`;
});
};
```

View File

@@ -0,0 +1,167 @@
---
id: 641da8db2a036048ebe6999e
title: Step 21
challengeType: 0
dashedName: step-21
---
# --description--
Inside the `fetchMoreAuthors` function, set the `startingIndex` and `endingIndex` variables to `+= 8` each.
# --hints--
You should set the `startingIndex` variable to `+=8`.
```js
assert.match(code, /startingIndex\s*\+=\s*8\s*;?/)
```
You should set the `endingIndex` variable to `+=8`.
```js
assert.match(code, /endingIndex\s*\+=\s*8\s*;?/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
authorDataArr = data;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
const fetchMoreAuthors = () => {
--fcc-editable-region--
--fcc-editable-region--
};
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
authorContainer.innerHTML += `
<div id="${index}" class="user-card">
<h2 class="author-name">${author}</h2>
<img class="user-img" src="${image}" alt="${author} avatar" />
<p class="bio">${bio}</p>
<a class="author-link" href="${url}" target="_blank">${author}'s author page</a>
</div>
`;
});
};
```

View File

@@ -0,0 +1,187 @@
---
id: 641da97c987a514959ada414
title: Step 22
challengeType: 0
dashedName: step-22
---
# --description--
Now call the `displayAuthors` function with a portion of the author data just like you did before.
If you click the `Load More Authors` button after calling the function, it won't work. That's because you still have to add the `click` event listener to the button. You'll do that next.
# --hints--
You should call your `displayAuthors` function.
```js
const afterAdd = code.split("endingIndex += 8;")[1];
assert.match(afterAdd, /displayAuthors\(/)
```
You should call your `displayAuthors` function and pass in an argument of `authorDataArr.slice()`.
```js
const afterAdd = code.split("endingIndex += 8;")[1];
assert.match(afterAdd, /displayAuthors\(\s*authorDataArr\.slice\(/)
```
The first argument of your `slice()` method should be `startingIndex`.
```js
const afterAdd = code.split("endingIndex += 8;")[1];
assert.match(afterAdd, /displayAuthors\(\s*authorDataArr\.slice\(\s*startingIndex/)
```
The second argument of your `slice()` method should be `endingIndex`.
```js
const afterAdd = code.split("endingIndex += 8;")[1];
assert.match(afterAdd, /displayAuthors\(\s*authorDataArr\.slice\(\s*startingIndex\s*,\s*endingIndex\s*\)\s*\)/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
authorDataArr = data;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
const fetchMoreAuthors = () => {
startingIndex += 8;
endingIndex += 8;
--fcc-editable-region--
--fcc-editable-region--
};
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
authorContainer.innerHTML += `
<div id="${index}" class="user-card">
<h2 class="author-name">${author}</h2>
<img class="user-img" src="${image}" alt="${author} avatar" />
<p class="bio">${bio}</p>
<a class="author-link" href="${url}" target="_blank">${author}'s author page</a>
</div>
`;
});
};
```

View File

@@ -0,0 +1,180 @@
---
id: 641da9aceb788e49a73ebcc9
title: Step 23
challengeType: 0
dashedName: step-23
---
# --description--
Remember that in step 1 you selected the `Load More Authors` button and assigned it to `loadMoreBtn`.
Use `addEventListener` to add a `"click"` event listener to `loadMoreBtn`. Also, pass in a reference to the `fetchMoreAuthors` function to run whenever the button is clicked.
After that, when you click the button you should see 8 more authors.
# --hints--
You should call the `addEventListener()` method on your `loadMoreBtn` variable.
```js
assert.match(code, /loadMoreBtn\.addEventListener\(/)
```
Your event listener should listen for a `"click"` event.
```js
assert.match(code, /loadMoreBtn\.addEventListener\(\s*('|"|`)click\1/)
```
Your event listener should take `fetchMoreAuthors` as the function to run.
```js
assert.match(code, /loadMoreBtn\.addEventListener\(\s*('|"|`)click\1\s*,\s*fetchMoreAuthors\s*\)\s*;?/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
authorDataArr = data;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
const fetchMoreAuthors = () => {
startingIndex += 8;
endingIndex += 8;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
};
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
authorContainer.innerHTML += `
<div id="${index}" class="user-card">
<h2 class="author-name">${author}</h2>
<img class="user-img" src="${image}" alt="${author} avatar" />
<p class="bio">${bio}</p>
<a class="author-link" href="${url}" target="_blank">${author}'s author page</a>
</div>
`;
});
};
--fcc-editable-region--
--fcc-editable-region--
```

View File

@@ -0,0 +1,180 @@
---
id: 641da9ea9b847a49fe6ee9b6
title: Step 24
challengeType: 0
dashedName: step-24
---
# --description--
Your fCC Authors Page is now complete. But you could improve on a few things.
First, if you click the `Load More Authors` button a couple of times, you'll see that it won't add more authors to the page. That's because you've reached the end of the authors list. For a better user experience, you should make it clear when there's no more data to display by disabling the button and changing its text. An `if` statement is the perfect tool for this.
Inside the `fetchMoreAuthors` function, write an `if` statement and set the condition to `authorDataArr.length <= endingIndex` meaning there's no more data to load.
# --hints--
You should have an `if` statement.
```js
assert.match(code, /if\s*\(/)
```
Your `if` statement should take in `authorDataArr.length <= endingIndex` as its condition.
```js
assert.match(code, /if\s*\(\s*authorDataArr\.length\s*<=\s*endingIndex\s*\)\s*/)
```
Your `if` statement should be empty.
```js
assert.match(code, /if\s*\(\s*authorDataArr\.length\s*<=\s*endingIndex\s*\)\s*\{\s*\}/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
authorDataArr = data;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
const fetchMoreAuthors = () => {
startingIndex += 8;
endingIndex += 8;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
--fcc-editable-region--
--fcc-editable-region--
};
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
authorContainer.innerHTML += `
<div id="${index}" class="user-card">
<h2 class="author-name">${author}</h2>
<img class="user-img" src="${image}" alt="${author} avatar" />
<p class="bio">${bio}</p>
<a class="author-link" href="${url}" target="_blank">${author}'s author page</a>
</div>
`;
});
};
loadMoreBtn.addEventListener('click', fetchMoreAuthors);
```

View File

@@ -0,0 +1,172 @@
---
id: 641daa5ea050f24a7cade6e6
title: Step 25
challengeType: 0
dashedName: step-25
---
# --description--
If this condition is met, disable the button by setting its `disabled` property to `true`. Also, set the `textContent` of the button to `"No more data to load"`.
# --hints--
You should set the `disabled` property of `loadMoreBtn` to `true`.
```js
assert.match(code, /loadMoreBtn\.disabled\s*=\s*true\s*;?/)
```
You should set the `textContent` of `loadMoreBtn` to `"No more data to load"`.
```js
assert.match(code, /loadMoreBtn\.textContent\s*=\s*('|"|`)No\s+more\s+data\s+to\s+load\1\s*;?/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
authorDataArr = data;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
const fetchMoreAuthors = () => {
startingIndex += 8;
endingIndex += 8;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
if (authorDataArr.length <= endingIndex) {
--fcc-editable-region--
--fcc-editable-region--
}
};
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
authorContainer.innerHTML += `
<div id="${index}" class="user-card">
<h2 class="author-name">${author}</h2>
<img class="user-img" src="${image}" alt="${author} avatar" />
<p class="bio">${bio}</p>
<a class="author-link" href="${url}" target="_blank">${author}'s author page</a>
</div>
`;
});
};
loadMoreBtn.addEventListener('click', fetchMoreAuthors);
```

View File

@@ -0,0 +1,188 @@
---
id: 641daa8c2c3e364ac3650b37
title: Step 26
challengeType: 0
dashedName: step-26
---
# --description--
Next, there's not a lot of separation between each author's name and image, and the rest of the details on the card. A divider will give the author cards a clear visual hierarchy.
Add a `div` element above the author's bio and give it the `class` `"purple-divider"`.
# --hints--
You should create a `div` element before your `p` element.
```js
const retryingTest = (test, message, tries = 20) => {
if (tries < 1) return Promise.reject(message);
if (test()) return Promise.resolve();
return new Promise((resolve, reject) => {
setTimeout(() => {
retryingTest(test, message, tries - 1)
.then(resolve)
.catch(reject);
}, 100);
});
};
() => retryingTest(() => document.querySelector('p')?.previousElementSibling?.tagName === 'DIV', "'div' element not found");
```
Your `div` element should have the `class` set to `"purple-divider"`.
```js
assert.include(document.querySelector('p')?.previousElementSibling?.className, 'purple-divider');
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
authorDataArr = data;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
const fetchMoreAuthors = () => {
startingIndex += 8;
endingIndex += 8;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
if (authorDataArr.length <= endingIndex) {
loadMoreBtn.disabled = true;
loadMoreBtn.textContent = 'No more data to load';
}
};
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
authorContainer.innerHTML += `
<div id="${index}" class="user-card">
<h2 class="author-name">${author}</h2>
<img class="user-img" src="${image}" alt="${author} avatar" />
--fcc-editable-region--
--fcc-editable-region--
<p class="bio">${bio}</p>
<a class="author-link" href="${url}" target="_blank">${author}'s author page</a>
</div>
`;
});
};
loadMoreBtn.addEventListener('click', fetchMoreAuthors);
```

View File

@@ -0,0 +1,194 @@
---
id: 641daabed8d0584b1150c953
title: Step 27
challengeType: 0
dashedName: step-27
---
# --description--
Some of the author bios are much longer than others. To give the cards a uniform look, you can extract the first 50 characters of each one and replace the rest with an ellipsis `("...")`. Otherwise, you can show the entire bio.
Within the paragraph element, replace `bio` with a ternary operator. For the condition, check if the length of `bio` is greater than 50. If it is, use the `.slice()` method to extract the first 50 characters of `bio` and add an ellipsis at the end. Otherwise, show the full `bio`.
# --hints--
You should create a `p` element.
```js
assert.match(code, /<p\s*/)
```
Your `p` element should have the class `bio`
```js
assert.match(code, /<p\s*class=("|')bio\1>/);
```
You should check if the length of the `bio` text is greater than `50`.
```js
assert.match(code, /<p\s*class=("|')bio\1>\s*\$\{\s*bio\.length\s*>\s*50/)
```
If the `bio` text is greater than `50` characters, you should extract the first 50 characters with `slice()` and replace the rest with `"..."`. Don't forget that indexes are zero-based.
```js
assert.match(code, /<p\s*class\s*=\s*("|')bio\1\s*>\s*\$\{\s*bio\.length\s*>\s*50\s*\?\s*(?:bio\.slice\(\s*0\s*,\s*50\s*\)\s*\+\s*("|')\.\.\.\2|`\$\{\s*bio\.slice\(\s*0\s*,\s*50\s*\)\s*\}\.\.\.`)\s*:/);
```
If the `bio` text is less than 50 characters, use the `bio` text directly.
```js
assert.match(code, /<p\s*class\s*=\s*("|')bio\1\s*>\s*\$\{\s*bio\.length\s*>\s*50\s*\?\s*(?:bio\.slice\(\s*0\s*,\s*50\s*\)\s*\+\s*("|')\.\.\.\2|`\$\{\s*bio\.slice\(\s*0\s*,\s*50\s*\)\s*\}\.\.\.`)\s*:\s*bio\s*\}\s*<\/p>/);
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
authorDataArr = data;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
})
.catch((err) => {
console.error(`There was an error: ${err}`);
});
const fetchMoreAuthors = () => {
startingIndex += 8;
endingIndex += 8;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
if (authorDataArr.length <= endingIndex) {
loadMoreBtn.disabled = true;
loadMoreBtn.textContent = 'No more data to load';
}
};
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
authorContainer.innerHTML += `
<div id="${index}" class="user-card">
<h2 class="author-name">${author}</h2>
<img class="user-img" src="${image}" alt="${author} avatar" />
<div class="purple-divider"></div>
--fcc-editable-region--
<p class="bio">${bio}</p>
--fcc-editable-region--
<a class="author-link" href="${url}" target="_blank">${author}'s author page</a>
</div>
`;
});
};
loadMoreBtn.addEventListener('click', fetchMoreAuthors);
```

View File

@@ -0,0 +1,191 @@
---
id: 641daae5e18eae4b562633e4
title: Step 28
challengeType: 0
dashedName: step-28
---
# --description--
Finally, what if there's an error and the author data fail to load? Then we need to show an error in the UI. That's exactly what the `.catch()` method is for handling errors.
Inside the `.catch()`, remove the `console.error()` and set the `innerHTML` of the `authorContainer` to a `p` element with the `class` `"error-msg"` and text `"There was an error loading the authors"`.
# --hints--
You should remove your `console.error` and its text.
```js
assert.notMatch(code, /console\.error\(\s*`There\s+was\s+an\s+error:\s+\$\{err\}`\s*\)\s*;?/)
```
You should access the `innerHTML` of `authorContainer` and set it to a `p` element. Don't forget to surround the `p` element with a template literal.
```js
assert.match(code, /authorContainer\.innerHTML\s*=\s*(`|"|')\s*<p.*>.*<\/p>\s*\1/)
```
Your `p` element should have the class `"error-msg"`.
```js
assert.match(code, /(`|"|')\s*<p\s+class\s*=\s*("|')error-msg\2>/)
```
Your `p` element should have the text `"There was an error loading the authors"`.
```js
assert.match(code, /(`|"|')<p\s+class\s*=\s*("|')error-msg\2>There\s+was\s+an\s+error\s+loading\s+the\s+authors<\/p>\s*\1\s*;?/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
authorDataArr = data;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
})
.catch((err) => {
--fcc-editable-region--
console.error(`There was an error: ${err}`);
--fcc-editable-region--
});
const fetchMoreAuthors = () => {
startingIndex += 8;
endingIndex += 8;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
if (authorDataArr.length <= endingIndex) {
loadMoreBtn.disabled = true;
loadMoreBtn.textContent = 'No more data to load';
}
};
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
authorContainer.innerHTML += `
<div id="${index}" class="user-card">
<h2 class="author-name">${author}</h2>
<img class="user-img" src="${image}" alt="${author} avatar" />
<div class="purple-divider"></div>
<p class="bio">${bio.length > 50 ? bio.slice(0, 50) + '...' : bio}</p>
<a class="author-link" href="${url}" target="_blank">${author} author page</a>
</div>
`;
});
};
loadMoreBtn.addEventListener('click', fetchMoreAuthors);
```

View File

@@ -0,0 +1,332 @@
---
id: 641dab13c1b6f14b9828e6b1
title: Step 29
challengeType: 0
dashedName: step-29
---
# --description--
One more thing. If you keep clicking the `Load More Authors` button until there's no more data to load and the text changes to `"No more data to load"`, the cursor value is still `pointer`. Why not change the cursor value to `not-allowed` instead?
Access the `style` property of the `Load More Authors` button and set `cursor` to `"not-allowed"`.
With that, your author page is complete!
# --hints--
You should access the `style` property of `loadMoreBtn` with a dot notation.
```js
assert.match(code, /loadMoreBtn\.style/)
```
You should access `cursor` from the `style` property with dot notation.
```js
assert.match(code, /loadMoreBtn\.style\.cursor/)
```
You should set the value of the `cursor` property to `"not-allowed"`.
```js
assert.match(code, /loadMoreBtn\.style\.cursor\s*=\s*('|"|`)not\-allowed\1\s*;?/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
authorDataArr = data;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
})
.catch((err) => {
authorContainer.innerHTML = '<p class="error-msg">There was an error loading the authors</p>';
});
const fetchMoreAuthors = () => {
startingIndex += 8;
endingIndex += 8;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
if (authorDataArr.length <= endingIndex) {
loadMoreBtn.disabled = true;
--fcc-editable-region--
--fcc-editable-region--
loadMoreBtn.textContent = 'No more data to load';
}
};
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
authorContainer.innerHTML += `
<div id="${index}" class="user-card">
<h2 class="author-name">${author}</h2>
<img class="user-img" src="${image}" alt="${author} avatar">
<div class="purple-divider"></div>
<p class="bio">${bio.length > 50 ? bio.slice(0, 50) + '...' : bio}</p>
<a class="author-link" href="${url}" target="_blank">${author} author page</a>
</div>
`;
});
};
loadMoreBtn.addEventListener('click', fetchMoreAuthors);
```
# --solutions--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>freeCodeCamp News Author Page</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<h1 class="title">freeCodeCamp News Author Page</h1>
<main>
<div id="author-container"></div>
<button class="btn" id="load-more-btn">Load More Authors</button>
</main>
<script src="./script.js"></script>
</body>
</html>
```
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--main-bg-color: #1b1b32;
--light-grey: #f5f6f7;
--dark-purple: #5a01a7;
--golden-yellow: #feac32;
}
body {
background-color: var(--main-bg-color);
text-align: center;
}
.title {
color: var(--light-grey);
margin: 20px 0;
}
#author-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.user-card {
border-radius: 15px;
width: 300px;
height: 350px;
background-color: var(--light-grey);
margin: 20px;
}
.user-img {
width: 150px;
height: 150px;
object-fit: cover;
}
.purple-divider {
background-color: var(--dark-purple);
width: 100%;
height: 15px;
}
.author-name {
margin: 10px;
}
.bio {
margin: 20px;
}
.error-msg {
color: var(--light-grey);
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px;
color: var(--main-bg-color);
font-size: 14px;
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
```
```js
const authorContainer = document.getElementById('author-container');
const loadMoreBtn = document.getElementById('load-more-btn');
let startingIndex = 0;
let endingIndex = 8;
let authorDataArr = [];
fetch('https://cdn.freecodecamp.org/curriculum/news-author-page/authors.json')
.then((res) => res.json())
.then((data) => {
authorDataArr = data;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
})
.catch((err) => {
authorContainer.innerHTML = '<p class="error-msg">There was an error loading the authors</p>';
});
const fetchMoreAuthors = () => {
startingIndex += 8;
endingIndex += 8;
displayAuthors(authorDataArr.slice(startingIndex, endingIndex));
if (authorDataArr.length <= endingIndex) {
loadMoreBtn.disabled = true;
loadMoreBtn.style.cursor = "not-allowed"
loadMoreBtn.textContent = 'No more data to load';
}
};
const displayAuthors = (authors) => {
authors.forEach(({ author, image, url, bio }, index) => {
authorContainer.innerHTML += `
<div id="${index}" class="user-card">
<h2 class="author-name">${author}</h2>
<img class="user-img" src="${image}" alt="${author} avatar" />
<div class="purple-divider"></div>
<p class="bio">${bio.length > 50 ? bio.slice(0, 50) + '...' : bio}</p>
<a class="author-link" href="${url}" target="_blank">${author} author page</a>
</div>
`;
});
};
loadMoreBtn.addEventListener('click', fetchMoreAuthors);
```

View File

@@ -395,6 +395,7 @@
{
"dashedName": "asynchronous-javascript",
"blocks": [
{ "dashedName": "workshop-fcc-authors-page" },
{ "dashedName": "quiz-javascript-object-oriented-programming" },
{ "dashedName": "lab-stack-class" },
{ "dashedName": "lab-linked-list-class" },

View File

@@ -1049,7 +1049,35 @@ const duplicatedProjectIds = [
// Nth Fibonacci Number
// FCC Authors Page
'641d9a19bff38d34d5a5edb8',
'641da3c6b6fbd742bff6ee40',
'641da42481d90c4314c99e94',
'641da465273051435d332b15',
'641da4b16937be43ba24c63d',
'641da51a9810e74411262fcc',
'641da5462576784453146ec2',
'641da5abaac81844a54adb03',
'641da5dd6cd6db44f58b7787',
'641da615af82bf454215a992',
'641da6570acf7545931ce477',
'641da6dcb6e4c9463d54c75b',
'641da7071d0d45467cd59977',
'641da73b09e7f046c758e0ed',
'641da791d0c34a472b8d15b6',
'641da7bfbc7f0f477438ad8a',
'641da803d9892447d059804e',
'641da836581c254815f785fe',
'641da86294fd9f485d3c2bf0',
'641da895fb7ec648a5bdf19c',
'641da8db2a036048ebe6999e',
'641da97c987a514959ada414',
'641da9aceb788e49a73ebcc9',
'641da9ea9b847a49fe6ee9b6',
'641daa5ea050f24a7cade6e6',
'641daa8c2c3e364ac3650b37',
'641daabed8d0584b1150c953',
'641daae5e18eae4b562633e4',
'641dab13c1b6f14b9828e6b1',
// Weather App
// Forum Leaderboard