feat: add 'back/front end' in curriculum (#42596)

* chore: rename APIs and Microservices to include "Backend" (#42515)

* fix typo

* fix typo

* undo change

* Corrected grammar mistake

Corrected a grammar mistake by removing a comma.

* change APIs and Microservices cert title

* update title

* Change APIs and Microservices certi title

* Update translations.json

* update title

* feat(curriculum): rename apis and microservices cert

* rename folder structure

* rename certificate

* rename learn Markdown

* apis-and-microservices -> back-end-development-and-apis

* update backend meta

* update i18n langs and cypress test

Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>

* fix: add development to front-end libraries (#42512)

* fix: added-the-word-Development-to-front-end-libraries

* fix/added-the-word-Development-to-front-end-libraries

* fix/added-word-development-to-front-end-libraries-in-other-related-files

* fix/added-the-word-Development-to-front-end-and-all-related-files

* fix/removed-typos-from-last-commit-in-index.md

* fix/reverted-changes-that-i-made-to-dependecies

* fix/removed xvfg

* fix/reverted changes that i made to package.json

* remove unwanted changes

* front-end-development-libraries changes

* rename backend certSlug and README

* update i18n folder names and keys

* test: add legacy path redirect tests

This uses serve.json from the client-config repo, since we currently use
that in production

* fix: create public dir before moving serve.json

* fix: add missing script

* refactor: collect redirect tests

* test: convert to cy.location for stricter tests

* rename certificate folder to 00-certificates

* change crowdin config to recognise new certificates location

* allow translations to be used

Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com>

* add forwards slashes to path redirects

* fix cypress path tests again

* plese cypress

* fix: test different challenge

Okay so I literally have no idea why this one particular challenge
fails in Cypress Firefox ONLY. Tom and I paired and spun a full build
instance and confirmed in Firefox the page loads and redirects as
expected. Changing to another bootstrap challenge passes Cypress firefox
locally. Absolutely boggled by this.

AAAAAAAAAAAAAAA

* fix: separate the test

Okay apparently the test does not work unless we separate it into
a different `it` statement.

>:( >:( >:( >:(

Co-authored-by: Sujal Gupta <55016909+heysujal@users.noreply.github.com>
Co-authored-by: Noor Fakhry <65724923+NoorFakhry@users.noreply.github.com>
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com>
This commit is contained in:
Shaun Hamilton
2021-08-14 03:57:13 +01:00
committed by GitHub
parent 4df2a0c542
commit c2a11ad00d
1215 changed files with 790 additions and 449 deletions

View File

@@ -0,0 +1,90 @@
---
id: bad87fee1348bd9aec908849
title: 在 Bootstrap Wells 中添加元素
challengeType: 0
forumTopicId: 16636
dashedName: add-elements-within-your-bootstrap-wells
---
# --description--
現在的每一行的列都已經有了 `div` 元素。 這已經足夠了, 現在讓我們添加 `button` 元素吧。
每一個 class 屬性爲 `well``div` 元素內都應該放三個 `button` 元素。
# --hints--
每一個 class 屬性爲 `well``div` 元素內都應該放三個 `button` 元素。
```js
assert(
$('div.well:eq(0)').children('button').length === 3 &&
$('div.well:eq(1)').children('button').length === 3
);
```
總共有 6 個 `button` 元素。
```js
assert($('button') && $('button').length > 5);
```
確保所有 `button` 元素都有閉合標籤。
```js
assert(
code.match(/<\/button>/g) &&
code.match(/<button/g) &&
code.match(/<\/button>/g).length === code.match(/<button/g).length
);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well">
</div>
</div>
<div class="col-xs-6">
<div class="well">
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well">
<button></button>
<button></button>
<button></button>
</div>
</div>
<div class="col-xs-6">
<div class="well">
<button></button>
<button></button>
<button></button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,178 @@
---
id: bad87fee1348bd9aedc08845
title: 將字體圖標添加到所有的按鈕上
challengeType: 0
forumTopicId: 16637
required:
-
link: 'https://use.fontawesome.com/releases/v5.8.1/css/all.css'
raw: true
dashedName: add-font-awesome-icons-to-all-of-our-buttons
---
# --description--
Font Awesome 是一個非常便利的圖標庫。 這些圖標可以是網絡字體,也可以是一張矢量圖。 這些圖標就和字體一樣, 不僅能通過像素單位指定它們的大小,它們也同樣會繼承父級 HTML 元素的字號。
# --instructions--
爲 info 按鈕添加 Font Awesome `info-circle` 圖標delete 按鈕添加 `trash` 圖標。
**注意:** 下述要求中的 `i` 元素也可以用 `span` 元素代替。
# --hints--
應在 `info` 按鈕元素中添加一個 `<i class="fas fa-info-circle"></i>`
```js
assert(
$('.btn-info > i').is('.fas.fa-info-circle') ||
$('.btn-info > span').is('.fas.fa-info-circle')
);
```
應在 `delete` 按鈕元素中添加一個 `<i class="fas fa-trash"></i>`
```js
assert(
$('.btn-danger > i').is('.fas.fa-trash') ||
$('.btn-danger > span').is('.fas.fa-trash')
);
```
每個 `i` 元素都應有一個閉合標籤,`<i class="fas fa-thumbs-up"></i>``like` 標籤元素中。
```js
assert(
code.match(/<\/i>|<\/span/g) &&
code.match(/<\/i|<\/span>/g).length > 2 &&
($('.btn-primary > i').is('.fas.fa-thumbs-up') ||
$('.btn-primary > span').is('.fas.fa-thumbs-up'))
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fas fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fas fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info"><i class="fas fa-info-circle"></i> Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger"><i class="fas fa-trash"></i> Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@@ -0,0 +1,195 @@
---
id: bad87fee1348bd9aedd08845
title: 在按鈕中添加字體圖標
challengeType: 0
forumTopicId: 16638
required:
-
link: 'https://use.fontawesome.com/releases/v5.8.1/css/all.css'
raw: true
dashedName: add-font-awesome-icons-to-our-buttons
---
# --description--
Font Awesome 是一個非常便利的圖標庫。 我們可以通過 webfont 或矢量圖的方式來使用這些圖標。 這些圖標就和字體一樣, 不僅能通過像素單位指定它們的大小,它們也同樣會繼承父級 HTML 元素的字體大小。
可以將 Font Awesome 圖標庫添加至任何一個 web app 中,方法很簡單,只需要在 HTML head 標籤中添加以下代碼即可:
```html
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">
```
在這個挑戰中,我們已經爲你在頁面上添加好了 Font Awesome 圖標庫。
`i` 元素原本是用來表示斜體文字內容的;不過現在,用它來表示圖標也很常見。 只需要爲 `i` 元素添加相應的 class 就可以讓它展示圖標,比如:
```html
<i class="fas fa-info-circle"></i>
```
同時,我們也可以使用 `span` 元素來展示圖標。
# --instructions--
請使用 Font Awesome 來爲我們的點贊按鈕添加一個 `thumbs-up` 圖標,只需添加一個 class 包含 `fas``fa-thumbs-up``i` 元素即可。 同時,請記得保留圖標之後的文字內容 `Like`
# --hints--
應添加一個 class 包含 `fas``fa-thumbs-up``i` 元素。
```js
assert($('i').is('.fas.fa-thumbs-up') || $('span').is('.fas.fa-thumbs-up'));
```
Like 按鈕中應包含 `fa-thumbs-up` 圖標。
```js
assert(
($('i.fa-thumbs-up').parent().text().match(/Like/gi) &&
$('.btn-primary > i').is('.fas.fa-thumbs-up')) ||
($('span.fa-thumbs-up').parent().text().match(/Like/gi) &&
$('.btn-primary > span').is('.fas.fa-thumbs-up'))
);
```
`i` 元素應出現在 `button` 元素內。
```js
assert(
$('button').children('i').length > 0 ||
$('button').children('span').length > 0
);
```
表示圖標的元素應有閉合標籤。
```js
assert(code.match(/<\/i>|<\/span>/g));
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary">Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fas fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@@ -0,0 +1,93 @@
---
id: bad87fee1348bd9aec908853
title: 給 Bootstrap 元素添加 id 屬性
challengeType: 0
forumTopicId: 16639
dashedName: add-id-attributes-to-bootstrap-elements
---
# --description--
回憶一下,除了可以給元素添加 class 屬性,我們還可以給元素設置 `id` 屬性。
每個元素的 id 都必須是唯一的,並且在一個頁面中只能使用一次。
讓我們爲每個 class 爲 `well``div` 元素添加一個唯一的 id。
記住,可以這樣給一個元素設置 id
```html
<div class="well" id="center-well">
```
給左邊的塊設置 id 爲 `left-well`。 給右邊的塊設置 id 爲 `right-well`
# --hints--
左邊的 `well` 的 id 應爲 `left-well`
```js
assert(
$('.col-xs-6').children('#left-well') &&
$('.col-xs-6').children('#left-well').length > 0
);
```
右邊的 `well` 的 id 應爲 `right-well`
```js
assert(
$('.col-xs-6').children('#right-well') &&
$('.col-xs-6').children('#right-well').length > 0
);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
<div class="col-xs-6">
<div class="well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well" id="left-well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
<div class="col-xs-6">
<div class="well" id="right-well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,77 @@
---
id: bad87fee1348bd9aec908850
title: 給 Bootstrap 按鈕添加默認樣式
challengeType: 0
forumTopicId: 16657
dashedName: apply-the-default-bootstrap-button-style
---
# --description--
Bootstrap 還有另外一種按鈕 class 屬性叫做 `btn-default`
爲所有的 `button` 元素增加兩個 class `btn``btn-default`
# --hints--
應該將 `btn` class 添加到所有的 `button` 元素中。
```js
assert($('.btn').length > 5);
```
應該將 `btn-default` class 添加到每一個 `button` 元素中。
```js
assert($('.btn-default').length > 5);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well">
<button></button>
<button></button>
<button></button>
</div>
</div>
<div class="col-xs-6">
<div class="well">
<button></button>
<button></button>
<button></button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well">
<button class="btn btn-default"></button>
<button class="btn btn-default"></button>
<button class="btn btn-default"></button>
</div>
</div>
<div class="col-xs-6">
<div class="well">
<button class="btn btn-default"></button>
<button class="btn btn-default"></button>
<button class="btn btn-default"></button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,175 @@
---
id: bad87fee1348cd8acef08813
title: 可選操作樣式 btn-info
challengeType: 0
forumTopicId: 16770
dashedName: call-out-optional-actions-with-btn-info
---
# --description--
Bootstrap 有着豐富的預定義按鈕顏色。 淺藍色的 `btn-info` class 通常用在備選操作上。
`Like` 按鈕下方創建包含文本 `Info` 的塊級 Bootstrap 按鈕,然後爲該按鈕添加 Bootstrap 的 `btn-info``btn-block` classes。
注意:這些按鈕仍然需要 `btn``btn-block` classes。
# --hints--
應創建一個新的包含 `Info` 文本的 `button` 元素。
```js
assert(new RegExp('info', 'gi').test($('button').text()));
```
兩個按鈕的 class 屬性應該包含 `btn``btn-block`
```js
assert($('button.btn-block.btn').length > 1);
```
新按鈕的 class 屬性應該含有 `btn-info`
```js
assert($('button').hasClass('btn-info'));
```
確保所有的 `button` 元素都有一個閉合標籤。
```js
assert(
code.match(/<\/button>/g) &&
code.match(/<button/g) &&
code.match(/<\/button>/g).length === code.match(/<button/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<button class="btn btn-block btn-primary">Like</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<button class="btn btn-block btn-primary">Like</button>
<button class="btn btn-block btn-info">Info</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@@ -0,0 +1,157 @@
---
id: bad87fee1348bd8acde08812
title: 用 Bootstrap 居中文本
challengeType: 0
forumTopicId: 16771
dashedName: center-text-with-bootstrap
---
# --description--
可以使用 Bootstrap 將頂部的元素居中來美化頁面。 只需要將 `h2` 元素的 class 屬性設置爲 `text-center` 就可以實現。
記住:可以爲一個元素添加多個 classes class 間通過空格分隔,就像這樣:
```html
<h2 class="red-text text-center">your text</h2>
```
# --hints--
應使用 `text-center` class 將 `h2` 元素居中。
```js
assert($('h2').hasClass('text-center'));
```
`h2` 元素應具有 `red-text` class。
```js
assert($('h2').hasClass('red-text'));
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@@ -0,0 +1,185 @@
---
id: bad87fee1348cd8acef08812
title: 創建一個 Bootstrap 塊級元素按鈕
challengeType: 0
forumTopicId: 16810
dashedName: create-a-block-element-bootstrap-button
---
# --description--
一般情況下,`btn``btn-default` 兩個 classes 修飾的 `button` 元素寬度與它包含的文本相同, 舉個例子:
```html
<button class="btn btn-default">Submit</button>
```
這個按鈕的寬度應該和文本 `Submit` 的寬度相同。
<button class='btn btn-default'>提交</button>
通過爲按鈕添加 class 屬性 `btn-block` 使其成爲塊級元素,按鈕會伸展並填滿頁面的整個水平空間,後續的元素會流到這個塊級元素的下方,即 "另起一行"。
```html
<button class="btn btn-default btn-block">Submit</button>
```
這個按鈕會 100% 佔滿所有的可用寬度。
<button class='btn btn-default btn-block'>提交</button>
注意,這些按鈕仍然需要 `btn` 這個 class。
給剛創建的 Bootstrap 按鈕添加 Bootstrap 的 `btn-block` class。
# --hints--
按鈕仍然應該有 `btn``btn-default` class。
```js
assert($('button').hasClass('btn') && $('button').hasClass('btn-default'));
```
按鈕應該有 `btn-block` class。
```js
assert($('button').hasClass('btn-block'));
```
所有 `button` 元素都應該有閉合標籤。
```js
assert(
code.match(/<\/button>/g) &&
code.match(/<button/g) &&
code.match(/<\/button>/g).length === code.match(/<button/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<button class="btn btn-default">Like</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<button class="btn btn-block btn-default">Like</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@@ -0,0 +1,176 @@
---
id: bad87fee1348cd8acdf08812
title: 創建一個 Bootstrap 按鈕
challengeType: 0
forumTopicId: 16811
dashedName: create-a-bootstrap-button
---
# --description--
Bootstrap 的 `button` 元素有着獨有的、比默認 HTML 按鈕更好的樣式風格。
在較大的小貓圖片下方創建新的 `button` 元素。 爲它添加 `btn``btn-default` 兩個 classes 和 `Like` 文本。
# --hints--
應創建一個新的包含 `Like` 文本的 `button` 元素。
```js
assert(
new RegExp('like', 'gi').test($('button').text()) &&
$('img.img-responsive + button.btn').length > 0
);
```
新的按鈕元素應該有兩個 classes: `btn``btn-default`
```js
assert($('button').hasClass('btn') && $('button').hasClass('btn-default'));
```
保證所有 `button` 元素都有一個閉合標籤。
```js
assert(
code.match(/<\/button>/g) &&
code.match(/<button/g) &&
code.match(/<\/button>/g).length === code.match(/<button/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
</head>
<body>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<!-- ADD Bootstrap Styled Button -->
<button class="btn btn-default">Like</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
</html>
```

View File

@@ -0,0 +1,67 @@
---
id: bad87fee1348bd9aec908846
title: 創建一個 Bootstrap 標題
challengeType: 0
forumTopicId: 16812
dashedName: create-a-bootstrap-headline
---
# --description--
現在,來運用 HTML、CSS 和 Bootstrap 從頭開始做點東西。
接下來將會搭建一個 jQuery playground以便在後續的 jQuery 課程中使用它。
首先,創建一個包含 `jQuery Playground` 文本內容的 `h3` 元素。
通過給 `h3` 元素設置 Bootstrap 的 `text-primary` class 屬性來爲其上色,然後添加 Bootstrap 的 `text-center` class 屬性使其文本居中顯示。
# --hints--
爲頁面添加一個 `h3` 元素。
```js
assert($('h3') && $('h3').length > 0);
```
確保 `h3` 元素有一個閉合標籤。
```js
assert(
code.match(/<\/h3>/g) &&
code.match(/<h3/g) &&
code.match(/<\/h3>/g).length === code.match(/<h3/g).length
);
```
爲了確保成功上色,`h3` 元素應該具有 `text-primary` class。
```js
assert($('h3').hasClass('text-primary'));
```
爲了確保文本居中顯示,`h3` 元素應該具有 `text-center` class。
```js
assert($('h3').hasClass('text-center'));
```
`h3` 元素文本內容爲 `jQuery Playground`
```js
assert.isTrue(/jquery(\s)+playground/gi.test($('h3').text()));
```
# --seed--
## --seed-contents--
```html
```
# --solutions--
```html
<h3 class="text-primary text-center">jQuery Playground</h3>
```

View File

@@ -0,0 +1,68 @@
---
id: bad87fee1348bd9bec908846
title: 創建一個 Bootstrap Row
challengeType: 0
forumTopicId: 16813
dashedName: create-a-bootstrap-row
---
# --description--
這次爲內聯元素創建一個 Bootstrap 柵格系統的 Row
`h3` 標籤下方創建一個 class 屬性爲 `row``div` 元素。
# --hints--
`h3` 元素下應該增加一個 `div` 元素。
```js
assert(
$('div').length > 1 &&
$('div.row h3.text-primary').length == 0 &&
$('div.row + h3.text-primary').length == 0 &&
$('h3.text-primary + div.row').length > 0
);
```
`div` 元素的 class 屬性應爲 `row`
```js
assert($('div').hasClass('row'));
```
`row div` 應該內嵌於 `container-fluid div`
```js
assert($('div.container-fluid div.row').length > 0);
```
確保所有 `div` 元素都有一個閉合標籤。
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row"></div>
</div>
```

View File

@@ -0,0 +1,71 @@
---
id: bad87fee1348bd9aec908852
title: 用 jQuery 選擇器給目標元素的添加 class
challengeType: 0
forumTopicId: 16815
dashedName: create-a-class-to-target-with-jquery-selectors
---
# --description--
並不是所有 class 屬性都需要有對應的 CSS 樣式。 有時候我們設置 class 只是爲了更方便地在 jQuery 中選中這些元素。
爲每一個 `button` 元素添加 `target` class。
# --hints--
每一個 `button` 元素都應該設置 `target` class
```js
assert($('.target').length > 5);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well">
<button class="btn btn-default"></button>
<button class="btn btn-default"></button>
<button class="btn btn-default"></button>
</div>
</div>
<div class="col-xs-6">
<div class="well">
<button class="btn btn-default"></button>
<button class="btn btn-default"></button>
<button class="btn btn-default"></button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well">
<button class="target btn btn-default"></button>
<button class="target btn btn-default"></button>
<button class="target btn btn-default"></button>
</div>
</div>
<div class="col-xs-6">
<div class="well">
<button class="target btn btn-default"></button>
<button class="target btn btn-default"></button>
<button class="target btn btn-default"></button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,177 @@
---
id: bad87fee1348bd9aede08845
title: 創建自定義標題
challengeType: 0
forumTopicId: 16816
dashedName: create-a-custom-heading
---
# --description--
爲 Cat Photo App 做一個導航,把標題和愜意的貓咪圖片放在同一行。
記住,由於 Bootstrap 使用了響應式柵格系統,可以很方便的把元素放到一行以及指定元素的相對寬度。 大部分的 Bootstrap 的 class 都能用在 `div` 元素上。
把第一張圖片和 `h2` 元素用一個簡單的 `<div class="row">` 元素包裹起來。 再用 `<div class="col-xs-8">` 包裹 `h2` 元素,用 `<div class="col-xs-4">` 包裹我們的圖片,這樣它們就能位於同一行了。
注意現在圖片是否與文字大小一致呢?
# --hints--
`h2` 元素和最上方的 `img` 元素應該一起內嵌於具有 `row` class 的 `div` 元素內。
```js
assert($('div.row:has(h2)').length > 0 && $('div.row:has(img)').length > 0);
```
最上方的 `img` 元素應該內嵌於含有 `col-xs-4` class 的 `div` 元素中。
```js
assert(
$('div.col-xs-4:has(img)').length > 0 &&
$('div.col-xs-4:has(div)').length === 0
);
```
`h2` 元素應該內嵌於含有 `col-xs-8` class 的 `div` 元素中。
```js
assert(
$('div.col-xs-8:has(h2)').length > 0 &&
$('div.col-xs-8:has(div)').length === 0
);
```
確保每一個 `div` 元素都有一個閉合標籤。
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<h2 class="text-primary text-center">CatPhotoApp</h2>
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary">Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary">Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@@ -0,0 +1,71 @@
---
id: bad87fee1348bd9aec908848
title: 創建 Bootstrap Wells
challengeType: 0
forumTopicId: 16825
dashedName: create-bootstrap-wells
---
# --description--
Bootstrap 有一個叫作 `well` 的 class作用是使界面更具層次感。
在每一個 class 屬性爲 `col-xs-6``div` 元素中都嵌入一個帶有 `well` class 的 `div` 元素。
# --hints--
應在每一個 class 屬性爲 `col-xs-6``div` 元素中添加一個帶有 `well` class 的 `div` 元素。
```js
assert($('div.col-xs-6').not(':has(>div.well)').length < 1);
```
兩個 class 爲 `col-xs-6``div` 元素都應該嵌入一個 class 爲 `row``div` 元素。
```js
assert($('div.row > div.col-xs-6').length > 1);
```
確保所有的 `div` 元素都有一個閉合標籤。
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
</div>
<div class="col-xs-6">
</div>
</div>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well"></div>
</div>
<div class="col-xs-6">
<div class="well"></div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,188 @@
---
id: bad87fee1347bd9aedf08845
title: 用 Bootstrap 來取代我們之前的自定義樣式
challengeType: 0
forumTopicId: 17565
dashedName: ditch-custom-css-for-bootstrap
---
# --description--
現在可以清理一下之前代碼,用 Bootstrap 的內置樣式來替換之前定義的樣式,這樣 Cat Photo App 看起來更簡潔些。
別擔心——以後會有大把時間來自定義 CSS 樣式的。
刪除 `style` 元素裏的 `.red-text``p`,和 `.smaller-image` CSS 定義,使 `style` 元素只留下 `h2``thick-green-border`
刪除包含死鏈接的 `p` 元素。 然後將 `h2``red-text` class 替換爲 Bootstrap 的 `text-primary` class。
最後,從第一個 `img` 元素中刪除 `smaller-image` class並將其替換爲 `img-responsive` class。
# --hints--
`h2` 元素不應該有 `red-text` class。
```js
assert(!$('h2').hasClass('red-text'));
```
`h2` 元素應有 `text-primary` class。
```js
assert($('h2').hasClass('text-primary'));
```
段落元素p不應該再使用 `Monospace` 字體。
```js
assert(
!$('p')
.css('font-family')
.match(/monospace/i)
);
```
移除第一張圖片的 `smaller-image` class 屬性。
```js
assert(!$('img').hasClass('smaller-image'));
```
給第一張圖片添加 `img-responsive` class 屬性。
```js
assert($('.img-responsive').length > 1);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary">Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<h2 class="text-primary text-center">CatPhotoApp</h2>
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary">Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@@ -0,0 +1,125 @@
---
id: bad87fee1348bd9aec908855
title: 給每個元素一個唯一的 id
challengeType: 0
forumTopicId: 18191
dashedName: give-each-element-a-unique-id
---
# --description--
也可以通過 jQuery 根據每個按鈕唯一的 id 來定位出它們。
給每一個按鈕設置唯一的 id`target1` 開始,`target6` 結束。
確保 `target1``target3``#left-well` 之中,`target4``target6``#right-well` 之中。
# --hints--
其中一個 `button` 元素應該有 id `target1`
```js
assert(
$('#left-well').children('#target1') &&
$('#left-well').children('#target1').length > 0
);
```
其中一個 `button` 元素應該有 id `target2`
```js
assert(
$('#left-well').children('#target2') &&
$('#left-well').children('#target2').length > 0
);
```
其中一個 `button` 元素應該有 id `target3`
```js
assert(
$('#left-well').children('#target3') &&
$('#left-well').children('#target3').length > 0
);
```
其中一個 `button` 元素應該有 id `target4`
```js
assert(
$('#right-well').children('#target4') &&
$('#right-well').children('#target4').length > 0
);
```
其中一個 `button` 元素應該有 id `target5`
```js
assert(
$('#right-well').children('#target5') &&
$('#right-well').children('#target5').length > 0
);
```
其中一個 `button` 元素應有 id `target6`
```js
assert(
$('#right-well').children('#target6') &&
$('#right-well').children('#target6').length > 0
);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1"></button>
<button class="btn btn-default target" id="target2"></button>
<button class="btn btn-default target" id="target3"></button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4"></button>
<button class="btn btn-default target" id="target5"></button>
<button class="btn btn-default target" id="target6"></button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,53 @@
---
id: bad87fee1348bd9aec908746
title: 將頁面放在 container-fluid div 中
challengeType: 0
forumTopicId: 18198
dashedName: house-our-page-within-a-bootstrap-container-fluid-div
---
# --description--
現在確保頁面所有內容都可以響應移動端。
將的 `h3` 元素內嵌進一個具有 `container-fluid` class 的`div` 元素中。
# --hints--
`div` 元素 class 屬性應該爲 `container-fluid`
```js
assert($('div').hasClass('container-fluid'));
```
確保每一個 `div` 元素都有一個閉合標籤。
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
`h3` 元素應該內嵌於 `div` 元素。
```js
assert($('div').children('h3').length > 0);
```
# --seed--
## --seed-contents--
```html
<h3 class="text-primary text-center">jQuery Playground</h3>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
</div>
```

View File

@@ -0,0 +1,105 @@
---
id: bad87fee1348bd9aec908856
title: 給 Bootstrap 按鈕設置標籤
challengeType: 0
forumTopicId: 18222
dashedName: label-bootstrap-buttons
---
# --description--
正如標註了每個 wells 一樣,同樣可以標註每一個按鈕。
爲每個 `button` 元素設置與其 id 選擇器對應的文本。
# --hints--
應給 id 爲 `target1``button` 元素設置文本爲 `#target1`
```js
assert(new RegExp('#target1', 'gi').test($('#target1').text()));
```
應給 id 爲 `target2``button` 元素設置文本爲`#target2`
```js
assert(new RegExp('#target2', 'gi').test($('#target2').text()));
```
應給 id 爲 `target3``button` 元素設置文本爲 `#target3`
```js
assert(new RegExp('#target3', 'gi').test($('#target3').text()));
```
應給 id 爲 `target4``button` 元素設置文本爲 `#target4`
```js
assert(new RegExp('#target4', 'gi').test($('#target4').text()));
```
應給 id 爲 `target5``button` 元素設置文本爲`#target5`
```js
assert(new RegExp('#target5', 'gi').test($('#target5').text()));
```
應給 id 爲 `target6``button` 元素設置文本爲`#target6`
```js
assert(new RegExp('#target6', 'gi').test($('#target6').text()));
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1"></button>
<button class="btn btn-default target" id="target2"></button>
<button class="btn btn-default target" id="target3"></button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4"></button>
<button class="btn btn-default target" id="target5"></button>
<button class="btn btn-default target" id="target6"></button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,101 @@
---
id: bad87fee1348bd9aec908854
title: 給 Bootstrap Wells 設置標籤
challengeType: 0
forumTopicId: 18223
dashedName: label-bootstrap-wells
---
# --description--
爲了讓頁面邏輯更清晰,每個 wells 都標上它們的 id 吧。
在 left-well 的上一層class 屬性爲 `col-xs-6``div` 元素裏面,增加一個文本爲 `#left-well``h4` 元素。
在 right-well 的上一層class 屬性爲 `col-xs-6``div` 元素裏面,增加一個文本爲 `#right-well``h4` 元素。
# --hints--
應該爲每個 `<div class="col-xs-6">` 元素添加一個 `h4` 元素。
```js
assert(
$('.col-xs-6').children('h4') && $('.col-xs-6').children('h4').length > 1
);
```
其中一個 `h4` 元素應該含有文本內容 `#left-well`
```js
assert(new RegExp('#left-well', 'gi').test($('h4').text()));
```
其中一個 `h4` 元素應該含有文本內容 `#right-well`
```js
assert(new RegExp('#right-well', 'gi').test($('h4').text()));
```
確保每個 `h4` 元素都有一個閉合標籤。
```js
assert(
code.match(/<\/h4>/g) &&
code.match(/<h4/g) &&
code.match(/<\/h4>/g).length === code.match(/<h4/g).length
);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<div class="well" id="left-well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
<div class="col-xs-6">
<div class="well" id="right-well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
<button class="btn btn-default target"></button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,215 @@
---
id: bad87fee1348bd9aec908845
title: 使用 Bootstrap 響應式排列表單元素
challengeType: 0
forumTopicId: 18225
required:
-
link: >-
https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.css
raw: true
dashedName: line-up-form-elements-responsively-with-bootstrap
---
# --description--
現在把表單裏的 `input` 元素和 submit `button` (提交按鈕)放在同一行。 跟着之前的做法來就行:準備好一個具有 `row` class 的 `div` 元素還有幾個具有 `col-xs-*` class 的 `div` 元素。
先把表單的 text `input` (文本輸入框)和 submit `button` (提交按鈕)放進具有 `row` class 的 `div` 中。 再用 `col-xs-7` class 的 div 包裹表單的 text `input` (文本輸入框), `col-xs-5` class 的 `div` 包裹表單的 submit `button` (提交按鈕)。
這是我們到目前爲止 Cat Photo App 的最後一個挑戰了。 祝你編碼愉快。
# --hints--
確保提交按鈕和文本輸入框都在同一個具有 class 屬性 `row` 的 div 元素中。
```js
assert(
$('div.row:has(input[type="text"])').length > 0 &&
$('div.row:has(button[type="submit"])').length > 0
);
```
表單的文本輸入框應該嵌入到 `col-xs-7` class 屬性的 div 內。
```js
assert($('div.col-xs-7:has(input[type="text"])').length > 0);
```
表單的提交按鈕應該嵌入到 `col-xs-5` class 屬性的 div 內。
```js
assert($('div.col-xs-5:has(button[type="submit"])').length > 0);
```
確保每一個 `div` 元素都有一個閉合標籤。
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fa fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info"><i class="fa fa-info-circle"></i> Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger"><i class="fa fa-trash"></i> Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<div class="row">
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
</div>
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
</div>
</div>
<div class="row">
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Loving</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Lazy</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Crazy</label>
</div>
</div>
<input type="text" class="form-control" placeholder="cat photo URL" required>
<button type="submit" class="btn btn-primary"><i class="fa fa-paper-plane"></i> Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fa fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info"><i class="fa fa-info-circle"></i> Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger"><i class="fa fa-trash"></i> Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<div class="row">
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
</div>
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
</div>
</div>
<div class="row">
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Loving</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Lazy</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Crazy</label>
</div>
</div>
<div class="row">
<div class="col-xs-7">
<input type="text" class="form-control" placeholder="cat photo URL" required>
</div>
<div class="col-xs-5">
<button type="submit" class="btn btn-primary"><i class="fa fa-paper-plane"></i> Submit</button>
</div>
</div>
</form>
</div>
```

View File

@@ -0,0 +1,176 @@
---
id: bad87fee1348bd9acde08812
title: 使圖片自適應移動端
challengeType: 0
forumTopicId: 18232
dashedName: make-images-mobile-responsive
---
# --description--
首先,在已有的圖片下面添加一張新的圖片。 設置其 `src` 屬性爲 `https://bit.ly/fcc-running-cats`
如果圖片的大小恰恰和我們手機屏幕尺寸大小一樣自然是最好的。
幸運的是,現在通過 Bootstrap我們僅僅只需要爲 image 標籤上設置 class 屬性爲 `img-responsive` 就可以讓它完美地適應你的頁面的寬度了。
# --hints--
該頁面共有兩張圖片。
```js
assert($('img').length === 2);
```
新的圖片應該在舊的圖片下面並且有值包含 `img-responsive` 的 class 屬性。
```js
assert($('img:eq(1)').hasClass('img-responsive'));
```
新圖片的 class 不應該包含 `smaller-image`
```js
assert(!$('img:eq(1)').hasClass('smaller-image'));
```
新圖片的 `src` 屬性值應該爲 `https://bit.ly/fcc-running-cats`
```js
assert($('img:eq(1)').attr('src') === 'https://bit.ly/fcc-running-cats');
```
確保新的 `img` 元素有一個閉合的尖括號。
```js
assert(
code.match(/<img/g) &&
code.match(/<img[^<]*>/g).length === 2 &&
code.match(/<img/g).length === 2
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive">
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@@ -0,0 +1,192 @@
---
id: bad87fee1348bd9aeda08845
title: 響應式風格的複選框
challengeType: 0
forumTopicId: 18269
required:
-
link: >-
https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.css
raw: true
dashedName: responsively-style-checkboxes
---
# --description--
Bootstrap 的 `col-xs-*` 可應用於所有的 `form` 元素上,包括複選框! 這樣就可以不必關注屏幕大小,均勻地把複選框放在頁面上了。
# --instructions--
將所有三個複選框都放置於一個 `<div class="row">` 元素中。 然後分別把每個複選框都放置於一個 `<div class="col-xs-4">` 元素中。
# --hints--
將所有的複選框嵌入一個含有 `row` class 的 `div` 元素中。
```js
assert($('div.row:has(input[type="checkbox"])').length > 0);
```
每一個複選框應該嵌套於單獨的具有 `col-xs-4` class 的 `div` 元素中。
```js
assert($('div.col-xs-4:has(input[type="checkbox"])').length > 2);
```
確保所有 `div` 元素都有一個閉合標籤。
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fa fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info"><i class="fa fa-info-circle"></i> Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger"><i class="fa fa-trash"></i> Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<div class="row">
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
</div>
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
</div>
</div>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fa fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info"><i class="fa fa-info-circle"></i> Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger"><i class="fa fa-trash"></i> Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<div class="row">
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
</div>
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
</div>
</div>
<div class="row">
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Loving</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Lazy</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Crazy</label>
</div>
</div>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@@ -0,0 +1,176 @@
---
id: bad87fee1348bd9aedb08845
title: 響應式風格的單選按鈕
challengeType: 0
forumTopicId: 18270
required:
-
link: >-
https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.css
raw: true
dashedName: responsively-style-radio-buttons
---
# --description--
Bootstrap 的 `col-xs-*` class 也可以用在 `form` 元素上! 這樣就可以在不關心屏幕大小的情況下,將的單選按鈕均勻的平鋪在頁面上。
將所有單選按鈕放入 `<div class="row">` 元素中。 再用 `<div class="col-xs-6">` 元素包裹每一個單選按鈕。
**注意:** 提醒一句,單選按鈕是 type 爲 `radio``input` 元素。
# --hints--
所有的單選按鈕應該放置於具有 `row` class 的 `div` 元素中。
```js
assert($('div.row:has(input[type="radio"])').length > 0);
```
每一個單選按鈕應該嵌套於具有 class 屬性爲 `col-xs-6``div` 元素之中。
```js
assert($('div.col-xs-6:has(input[type="radio"])').length > 1);
```
確保所有 `div` 元素都有閉合標籤。
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fa fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info"><i class="fa fa-info-circle"></i> Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger"><i class="fa fa-trash"></i> Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fa fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info"><i class="fa fa-info-circle"></i> Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger"><i class="fa fa-trash"></i> Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<div class="row">
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
</div>
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
</div>
</div>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@@ -0,0 +1,57 @@
---
id: bad87fee1348bd9aec908847
title: 分割 Bootstrap Row
challengeType: 0
forumTopicId: 18306
dashedName: split-your-bootstrap-row
---
# --description--
現在已經有了一個 Bootstrap Row把它分成兩列。
在行內創建兩個 class 屬性爲 `col-xs-6``div` 元素。
# --hints--
兩個 `div class="col-xs-6"` 元素應該內嵌入 `div class="row"` 元素中。
```js
assert($('div.row > div.col-xs-6').length > 1);
```
確保所有的 `div` 元素都有閉合標籤。
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
</div>
</div>
```
# --solutions--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6"></div>
<div class="col-xs-6"></div>
</div>
</div>
```

View File

@@ -0,0 +1,202 @@
---
id: bad87fee1348bd9aed908845
title: 給表單輸入框添加樣式
challengeType: 0
forumTopicId: 18312
required:
-
link: >-
https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.css
raw: true
dashedName: style-text-inputs-as-form-controls
---
# --description--
可以在 submit `button` 元素內加上 `<i class="fa fa-paper-plane"></i>`,來添加 Font Awesome 的 `fa-paper-plane` 圖標。
爲表單的文本輸入框text input設置 `form-control` class。 爲表單的提交submit按鈕設置 `btn btn-primary` class 併爲它加上 Font Awesome 的 `fa-paper-plane` 圖標。
所有文本輸入類的元素如 `<input>``<textarea>``<select>` 只要設置 `.form-control` class 就會佔滿100%的寬度。
# --hints--
給你的 submit 按鈕添加 `btn btn-primary` class。
```js
assert($('button[type="submit"]').hasClass('btn btn-primary'));
```
在你的 submit `button` 元素內添加 `<i class="fa fa-paper-plane"></i>`
```js
assert($('button[type="submit"]:has(i.fa.fa-paper-plane)').length > 0);
```
給表單中的 text `input` 元素添加 `form-control` class。
```js
assert($('input[type="text"]').hasClass('form-control'));
```
確保每一個 `i` 元素都有一個閉合標籤。
```js
assert(code.match(/<\/i>/g) && code.match(/<\/i/g).length > 3);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fa fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info"><i class="fa fa-info-circle"></i> Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger"><i class="fa fa-trash"></i> Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<div class="row">
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
</div>
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
</div>
</div>
<div class="row">
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Loving</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Lazy</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Crazy</label>
</div>
</div>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<div class="row">
<div class="col-xs-8">
<h2 class="text-primary text-center">CatPhotoApp</h2>
</div>
<div class="col-xs-4">
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
</div>
</div>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary"><i class="fa fa-thumbs-up"></i> Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info"><i class="fa fa-info-circle"></i> Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger"><i class="fa fa-trash"></i> Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love:</span></p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<div class="row">
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
</div>
<div class="col-xs-6">
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
</div>
</div>
<div class="row">
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Loving</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Lazy</label>
</div>
<div class="col-xs-4">
<label><input type="checkbox" name="personality"> Crazy</label>
</div>
</div>
<input type="text" class="form-control" placeholder="cat photo URL" required>
<button type="submit" class="btn btn-primary"><i class="fa fa-paper-plane"></i>Submit</button>
</form>
</div>
```

View File

@@ -0,0 +1,167 @@
---
id: bad87fee1348cd8acef08811
title: 體驗 Bootstrap 按鈕的不同主題
challengeType: 0
forumTopicId: 18323
dashedName: taste-the-bootstrap-button-color-rainbow
---
# --description--
`btn-primary` class 的顏色是應用的主要顏色。 這樣 “突出顯示” 是引導用戶按部就班進行操作的有效辦法。
將按鈕的 class 從 Bootstrap 的 `btn-default` 替換爲 `btn-primary`
注意:按鈕仍然需要 `btn``btn-block` classes。
# --hints--
按鈕的 class 屬性應該包含 `btn-primary`
```js
assert($('button').hasClass('btn-primary'));
```
按鈕的 class 屬性應該包含 `btn``btn-block`
```js
assert($('button').hasClass('btn-block') && $('button').hasClass('btn'));
```
確保所有 `button` 元素都有一個閉合標籤。
```js
assert(
code.match(/<\/button>/g) &&
code.match(/<button/g) &&
code.match(/<\/button>/g).length === code.match(/<button/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<button class="btn btn-default btn-block">Like</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<button class="btn btn-primary btn-block">Like</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@@ -0,0 +1,185 @@
---
id: bad87fee1348bd9aedf08845
title: 使用 span 創建行內元素
challengeType: 0
forumTopicId: 18370
dashedName: use-a-span-to-target-inline-elements
---
# --description--
可以使用 span 標籤來創建行內元素。 還記得怎麼使用 `btn-block` class 來創建填滿整行的按鈕嗎?
<button class='btn' style='background-color: rgb(0, 100, 0); color: rgb(255, 255, 255);'>普通按鈕</button>
<button class='btn btn-block' style='background-color: rgb(0, 100, 0); color: rgb(255, 255, 255);'>塊級按鈕</button>
上面的例子就是 "inline" (行內)元素和 "block" (塊級)元素的區別。
通過使用行內元素 `span`,可以把不同的元素放在同一行,甚至能爲一行的不同部分指定不同樣式。
使用一個 `span` 元素,將文本 `love` 嵌入現在包含文本 `Things cats love``p` 元素中。 然後給 `span` 元素設置 class `text-danger`,使文本變成紅色。
對含有文本 `Top 3 things cats hate``p` 元素這樣處理:
```html
<p>Top 3 things cats <span class="text-danger">hate:</span></p>
```
# --hints--
`span` 元素應該在 `p` 元素內。
```js
assert($('p span') && $('p span').length > 0);
```
`span` 元素應該有文本 `love`
```js
assert(
$('p span') &&
$('p span').text().match(/love/i) &&
!$('p span')
.text()
.match(/Things cats/i)
);
```
`span` 元素應該有 `text-danger` class。
```js
assert($('span').hasClass('text-danger'));
```
`span` 元素應該有一個閉合標籤。
```js
assert(
code.match(/<\/span>/g) &&
code.match(/<span/g) &&
code.match(/<\/span>/g).length === code.match(/<span/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<h2 class="text-primary text-center">CatPhotoApp</h2>
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary">Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
h2 {
font-family: Lobster, Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
</style>
<div class="container-fluid">
<h2 class="text-primary text-center">CatPhotoApp</h2>
<a href="#"><img class="img-responsive thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary">Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats <span class="text-danger">love</span>:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@@ -0,0 +1,98 @@
---
id: bad87fee1348bd9aec908857
title: 使用註釋來說明代碼
challengeType: 0
forumTopicId: 18347
dashedName: use-comments-to-clarify-code
---
# --description--
使用 jQuery 時,修改 HTML 元素時並不需要直接修改 HTML 代碼。
必須確保讓每個人都知道不應該直接修改此頁面上的這些代碼。
記住,可以在 `<!--` 爲開始,`-->` 爲結束的地方進行評論註釋。
請你在 HTML 頂部加如下一段註釋: `Code below this line should not be changed`
# --hints--
應該在 HTML 頂部增加此代碼 `<!--` 以開始註釋。
```js
assert(code.match(/^\s*<!--/));
```
註釋內容應該爲 `Code below this line should not be changed`
```js
assert(code.match(/<!--(?!(>|->|.*-->.*this line))\s*.*this line.*\s*-->/gi));
```
註釋應該用 `-->` 進行閉合。
```js
assert(code.match(/-->.*\n+.+/g));
```
注意,註釋的開始標籤和閉合標籤數量應該一一對應,保持數量一致。
```js
assert(code.match(/<!--/g).length === code.match(/-->/g).length);
```
# --seed--
## --seed-contents--
```html
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<!-- Code below this line should not be changed -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,170 @@
---
id: bad87fee1348bd9acde08712
title: 使用 Bootstrap Fluid 容器實現響應式設計
challengeType: 0
forumTopicId: 18362
dashedName: use-responsive-design-with-bootstrap-fluid-containers
---
# --description--
之前,在 freeCodeCamp 的 HTML5 和 CSS 章節中我們構建了一個 Cat Photo App。 我們在此基礎上繼續學習。 這次我們將會使用最受歡迎的響應式 CSS 框架 Bootstrap 來美化它。
Bootstrap 會根據屏幕大小來動態調整 HTML 元素的大小————因此稱爲 <dfn>Responsive Design</dfn>(響應式設計)。
通過響應式設計,我們將無需額外設計一個手機版的網頁, 因爲它在任何尺寸的屏幕上看起來都很棒。
任何 Web 應用,都可以通過添加如下代碼到 HTML 頂部來引入 Bootstrap 。
```html
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"/>
```
不過在這裏,已經預先爲此頁面添加了上述代碼。 注意使用 `>` 或者 `/>` 兩種方式閉合 `link` 標籤都是可行的。
首先,我們應該將所有 HTML `link` 標籤和 `style` 元素除外)嵌套在帶有 `container-fluid` class 的 `div` 元素裏面。
# --hints--
`div` 元素應該有 `container-fluid` class。
```js
assert($('div').hasClass('container-fluid'));
```
`div` 元素應該有結束標籤。
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
`style` 結束標籤後面的所有 HTML 元素都應該被嵌套在 `.container-fluid` 裏面。
```js
assert($('.container-fluid').children().length >= 8 && !$('.container-fluid').has("style").length && !$('.container-fluid').has("link").length);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<h2 class="red-text">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@@ -0,0 +1,196 @@
---
id: bad88fee1348ce8acef08815
title: 使用 Bootstrap Grid 並排放置元素
challengeType: 0
forumTopicId: 18371
dashedName: use-the-bootstrap-grid-to-put-elements-side-by-side
---
# --description--
Bootstrap 具有一套 12 列的響應式柵格系統,可以輕鬆的將多個元素放入一行並指定它們的相對寬度。 Bootstrap 的大部分 class 屬性都可以應用在 `div` 元素上。
Bootstrap 的列寬取決於用戶的屏幕寬度。 比如,手機有着窄屏幕而筆記本電腦有者更大的屏幕.
就拿 Bootstrap 的 `col-md-*` class 來說。 在這裏, `md` 表示 medium (中等的), 而 `*` 是一個數字,說明了這個元素佔有多少個列寬度。 這個例子就是指定了中等大小屏幕(例如筆記本電腦)下元素所佔的列寬度。
在 Cat Photo App 中,將使用 `col-xs-*` 其中 `xs` 是 extra small 的縮寫 (比如窄屏手機屏幕) `*` 是填寫的數字,代表一行中的元素該佔多少列寬。
`Like``Info``Delete` 三個按鈕並排放入一個 `<div class="row">` 元素中,然後每個按鈕都各用一個 `<div class="col-xs-4">` 元素包裹起來。
`div` 元素設置了 `row` class 之後,那幾個按鈕便會嵌入其中了。
# --hints--
所有按鈕都需要嵌入到同一個 `div` 元素中, 並且該元素包含 `row` class 屬性。
```js
assert($('div.row:has(button)').length > 0);
```
每個 Bootstrap 按鈕都需要嵌入各自的 `div` 元素,並且該元素包含 class 屬性 `col-xs-4`
```js
assert($('div.col-xs-4:has(button)').length > 2);
```
確保每一個 `button` 元素都有一個閉合標籤。
```js
assert(
code.match(/<\/button>/g) &&
code.match(/<button/g) &&
code.match(/<\/button>/g).length === code.match(/<button/g).length
);
```
確保每一個 `div` 元素都有一個閉合標籤。
```js
assert(
code.match(/<\/div>/g) &&
code.match(/<div/g) &&
code.match(/<\/div>/g).length === code.match(/<div/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<button class="btn btn-block btn-primary">Like</button>
<button class="btn btn-block btn-info">Info</button>
<button class="btn btn-block btn-danger">Delete</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<div class="row">
<div class="col-xs-4">
<button class="btn btn-block btn-primary">Like</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-info">Info</button>
</div>
<div class="col-xs-4">
<button class="btn btn-block btn-danger">Delete</button>
</div>
</div>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@@ -0,0 +1,176 @@
---
id: bad87fee1348ce8acef08814
title: 使用 btn-danger 提示危險操作
challengeType: 0
forumTopicId: 18375
dashedName: warn-your-users-of-a-dangerous-action-with-btn-danger
---
# --description--
Bootstrap 有着豐富的預定義按鈕顏色。 紅色 `btn-danger` class 用來提醒用戶此行爲具有破壞性,比如刪除一張貓的圖片。
創建一個帶有文本 `Delete` 的按鈕,給它設置 class `btn-danger`
注意:這些按鈕仍然需要 `btn``btn-block` class。
# --hints--
應創建一個新的帶有文本 `Delete``button` 元素。
```js
assert(new RegExp('Delete', 'gi').test($('button').text()));
```
所有 Bootstrap 按鈕的 class 屬性都應該包含 `btn``btn-block`
```js
assert($('button.btn-block.btn').length > 2);
```
新創建按鈕的 class 屬性應該包含 `btn-danger`
```js
assert($('button').hasClass('btn-danger'));
```
確保所有的 `button` 元素都有一個閉合標籤。
```js
assert(
code.match(/<\/button>/g) &&
code.match(/<button/g) &&
code.match(/<\/button>/g).length === code.match(/<button/g).length
);
```
# --seed--
## --seed-contents--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<button class="btn btn-block btn-primary">Like</button>
<button class="btn btn-block btn-info">Info</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```
# --solutions--
```html
<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
.red-text {
color: red;
}
h2 {
font-family: Lobster, Monospace;
}
p {
font-size: 16px;
font-family: Monospace;
}
.thick-green-border {
border-color: green;
border-width: 10px;
border-style: solid;
border-radius: 50%;
}
.smaller-image {
width: 100px;
}
</style>
<div class="container-fluid">
<h2 class="red-text text-center">CatPhotoApp</h2>
<p>Click here for <a href="#">cat photos</a>.</p>
<a href="#"><img class="smaller-image thick-green-border" src="https://bit.ly/fcc-relaxing-cat" alt="A cute orange cat lying on its back."></a>
<img src="https://bit.ly/fcc-running-cats" class="img-responsive" alt="Three kittens running towards the camera.">
<button class="btn btn-block btn-primary">Like</button>
<button class="btn btn-block btn-info">Info</button>
<button class="btn btn-block btn-danger">Delete</button>
<p>Things cats love:</p>
<ul>
<li>cat nip</li>
<li>laser pointers</li>
<li>lasagna</li>
</ul>
<p>Top 3 things cats hate:</p>
<ol>
<li>flea treatment</li>
<li>thunder</li>
<li>other cats</li>
</ol>
<form action="https://freecatphotoapp.com/submit-cat-photo">
<label><input type="radio" name="indoor-outdoor"> Indoor</label>
<label><input type="radio" name="indoor-outdoor"> Outdoor</label>
<label><input type="checkbox" name="personality"> Loving</label>
<label><input type="checkbox" name="personality"> Lazy</label>
<label><input type="checkbox" name="personality"> Crazy</label>
<input type="text" placeholder="cat photo URL" required>
<button type="submit">Submit</button>
</form>
</div>
```

View File

@@ -0,0 +1,81 @@
---
id: bd7158d8c442eddfaeb5bd0f
title: 構建一個番茄時鐘
challengeType: 3
forumTopicId: 301373
dashedName: build-a-25--5-clock
---
# --description--
**目標:** 在 [CodePen.io](https://codepen.io) 上實現一個功能類似 <https://codepen.io/freeCodeCamp/full/XpKrrW> 的 App。
在滿足以下[需求](https://en.wikipedia.org/wiki/User_story)並能通過所有測試的前提下, 可以根據自己的喜好來美化 app。
可以使用 HTML、JavaScript、CSS、Bootstrap、SASS、React、Redux、jQuery 來完成這個挑戰。 但鑑於這個章節的學習內容與前端框架相關,推薦使用一款前端框架(比如 React來完成這個挑戰。 不推薦使用前面沒有提到的技術,否則風險自負。 我們有計劃新增其他前端框架課程,例如 Angular 和 Vue不過目前還沒有這些內容。 如果使用建議的技術棧完成這個項目,我們會接受並嘗試修復相關的問題反饋。 編碼愉快!
**需求 1** 應該能看到一個具有`id="break-label"`屬性的元素,這個元素的內容應該是一個字符串(例如:"Break Length")。
**需求 2** 應該能看到一個具有`id="session-label"`屬性的元素,這個元素的內容應該是一個字符串(例如:"Session Length")。
**需求 3** 應該能看到兩個可以點擊的元素,他們分別具有如下 id`id="break-decrement"``id="session-decrement"`
**需求 4** 應該能看到兩個可以點擊的元素,它們分別具有如下 id`id="break-increment"``id="session-increment"`
**需求 5** 應該能看到一個具有 `id="break-length"` 屬性的元素,這個元素默認展示數值應該爲 5加載後
**需求 6** 應該能看到一個具有 `id="session-length"` 屬性的元素,這個元素默認展示數值應該爲 25加載後
**需求 7** 應該能看到一個具有 `id="timer-label"` 屬性的元素,這個元素包含一個表示當前狀態的字符串(例如:"Session")。
**需求 8** 應該能看到一個具有 `id="time-left"` 屬性的元素。 注意:暫停或者運行時,該元素的內容應始終以 `mm:ss` 格式顯示(如 25:00
**需求 9** 應該能看到一個具有 `id="start_stop"` 屬性的可點擊的元素。
**需求 10** 應該能看到一個具有 `id="reset"` 屬性的可點擊的元素。
**需求 11** 當點擊 id 屬性爲 `reset` 的元素時,運行中的計時器都應該停止,`id="break-length"` 元素中的值應該重新回到 `5``id="session-length"` 元素中的值應該重新回到 25`id="time-left"` 元素應該重置爲默認狀態。
**需求 12** 當點擊 id 屬性爲 `break-decrement` 的元素時,`id="break-length"` 元素的值應該減去 1且我應該能看到更新後的值。
**需求 13** 當點擊 id 屬性爲 `break-increment` 的元素時,`id="break-length"` 元素的值應該增加 1且應該能看到更新後的值。
**需求 14** 當點擊 id 屬性爲 `session-decrement` 的元素時,`id="session-length"` 元素的值應該減去 1且應該能看到更新後的值。
**需求 15** 當點擊 id 屬性爲 `session-increment` 的元素時,`id="session-length"` 元素的值應該增加 1且應該能看到更新後的值。
**需求 16** 工作或者休息長度不應該可以設置爲 &lt;= 0 的值。
**需求 17** 工作或者休息長度比應該可以設置爲 > 60 的值。
**需求 18** 當首次點擊具有 `id="start_stop"` 屬性的元素時,計時器應該根據 `id="session-length"` 元素當前顯示的值開始運行,即使該值已從原始值 25 遞增過或遞減過。
**需求 19** 如果計時器正在運行id 屬性爲 `time-left` 的元素應該以 `mm:ss` 的格式展示剩餘的時間按1遞減並且每秒更新一次顯示的值
**需求 20** 如果計時器正在運行,當點擊 `id="start_stop"` 元素時,倒計時應該暫停。
**需求 21** 如果計時器已經暫停,當點擊 `id="start_stop"` 元素時,倒計時應該從暫停的時間點恢復運行。
**需求 22** 當一個工作倒計時結束(注意:計時器必須達到 00:00並且新的倒計時開始運行時id 屬性爲 `timer-label` 的元素應該顯示一個表示已經開始休息的字符串。
**需求 23** 當一個工作倒計時結束(注意:計時器必須達到 00:00應該開始一個新的休息倒計時時間應該從 `id="break-length"` 元素中當前顯示的值開始計算。
**需求 24** 當一個休息倒計時結束(注意:計時器必須達到 00:00並且新的倒計時開始運行時id 屬性爲 `timer-label` 的元素應該顯示一個表示已經開始工作的字符串。
**需求 25** 當一個休息倒計時結束(注意:計時器必須達到 00:00應該開始一個新的工作倒計時時間應該從 `id="session-length"` 元素中當前顯示的值開始計算。
**需求 26** 當一個倒計時結束(注意:計時器必須達到 00:00應該播放一個表示時間到了的聲音提示。 這個提示音應該使用 HTML5 的 `audio`標籤並有一個 `id="beep"` 屬性。
**需求 27** 具有`id="beep"`屬性的音頻元素時長應該至少有一秒。
**需求 28** 當點擊 id 屬性爲 `reset` 的元素時id 屬性爲 `beep` 的音頻元素必須停止播放並回到開頭。
你可以<a href='https://codepen.io/pen?template=MJjpwO' target='_blank' rel='nofollow'>使用這個 CodePen 模板</a>,點擊 `Save` 即可創建你自己的項目。 或者你可以在任何你喜歡的環境中使用以下 CDN 鏈接來運行測試:`https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
當你完成了本項目,並且該項目所有測試運行通過, 請提交項目的 URL。
# --solutions--
```js
// solution required
```

View File

@@ -0,0 +1,39 @@
---
id: 587d7dbc367417b2b2512bae
title: 構建一臺鼓式機器
challengeType: 3
forumTopicId: 301370
dashedName: build-a-drum-machine
---
# --description--
**目標:** 在 [CodePen.io](https://codepen.io) 上實現一個功能類似 <https://codepen.io/freeCodeCamp/full/MJyNMd> 的 App。
在滿足以下[需求](https://en.wikipedia.org/wiki/User_story)並能通過所有測試的前提下, 可以根據自己的喜好來美化 app。
可以使用 HTML、JavaScript、CSS、Bootstrap、SASS、React、Redux、jQuery 來完成這個挑戰。 但鑑於這個章節的學習內容與前端框架相關,推薦使用一款前端框架(比如 React來完成這個挑戰。 不推薦使用前面沒有提到的技術,否則風險自負。 我們有計劃新增其他前端框架課程,例如 Angular 和 Vue不過目前還沒有這些內容。 我們會接受並嘗試修復使用推薦技術棧遇到的反饋問題。 編碼愉快!
**需求 1** 應該可以看到一個具有 `id="drum-machine"` 屬性的外層容器,該容器包含了其它所有元素。
**需求 2** 在具有 `#drum-machine` 屬性的元素內,應該能看到一個具有 `id="display"` 屬性的元素。
**需求 3** 在具有 `#drum-machine` 屬性的元素內,應該能看到 9 個可以點擊的鼓墊元素,且每個鼓墊元素都應該有一個值爲 `drum-pad` 的 class 屬性, 一個用於描述觸發鼓墊音頻片段的特殊 id以及以下鍵值之一的文本內容`Q``W``E``A``S``D``Z``X``C`。 這些鼓墊必須按照以上順序排列。
**需求 4** 在每一個具有 `.drum-pad` 屬性的元素內,應該有一個具有指向音頻片段地址的 `src` 屬性的 HTML5 `audio` 元素,一個值爲 `clip` 的 class 屬性,以及一個 id 屬性,它的值應該是其父元素 `.drum-pad` 的文本內容(例如 `id="Q"``id="W"``id="E"` 等等)。
**需求 5** 當點擊一個具有 `.drum-pad` 屬性的元素時,應該觸發它的子元素 `audio` 包含的音頻片段。
**需求 6** 當按下每一個 `.drum-pad` 元素的關聯鍵時,應該觸發其子元素 `audio` 包含的音頻片段(例如:按下 `Q` 鍵應該觸發包含字符串 `Q` 的鼓墊,按下 `W` 鍵應該觸發包含字符串 `W` 的鼓墊等等)。
**需求 7** 當觸發一個具有 `.drum-pad` 屬性的元素時,`#display` 元素內應該展示這個觸發元素關聯音頻片段的描述字符串(每一個字符串都應該是獨一無二的)。
你可以<a href='https://codepen.io/pen?template=MJjpwO' target='_blank' rel='nofollow'>使用這個 CodePen 模板</a>,點擊 `Save` 即可創建你自己的項目。 或者可以在任何喜歡的環境中使用以下 CDN 鏈接來運行測試:`https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
當你完成了本項目,並且該項目所有測試運行通過,請提交項目的 URL。
# --solutions--
```js
// solution required
```

View File

@@ -0,0 +1,62 @@
---
id: bd7158d8c442eddfaeb5bd17
title: 構建一個 JavaScript 計算器
challengeType: 3
forumTopicId: 301371
dashedName: build-a-javascript-calculator
---
# --description--
**目標:** 在 [CodePen.io](https://codepen.io) 上實現一個功能類似 <https://codepen.io/freeCodeCamp/full/wgGVVX> 的 App。
在滿足以下[需求](https://en.wikipedia.org/wiki/User_story)並能通過所有測試的前提下, 可以根據自己的喜好來美化 app。
可以使用 HTML、JavaScript、CSS、Bootstrap、SASS、React、Redux、jQuery 來完成這個挑戰。 但鑑於這個章節的學習內容與前端框架相關,推薦使用一款前端框架(比如 React來完成這個挑戰。 不推薦使用前面沒有提到的技術,否則風險自負。 我們有計劃新增其他前端框架課程,例如 Angular 和 Vue不過目前還沒有這些內容。 我們會接受並嘗試修復使用推薦技術棧遇到的反饋問題。 編碼愉快!
**需求 1** 計算器應該包含一個具有 `id="equals"` 屬性的可以點擊的元素,元素的文本內容爲 `=`(等於符號)。
**需求 2** 計算器應該包含 10 個具有如下 id 屬性的可以點擊的元素,每個元素的文本內容對應 0-9 的數字:`id="zero"``id="one"``id="two"``id="three"``id="four"``id="five"``id="six"``id="seven"``id="eight"``id="nine"`
**需求 3** 計算器應該包含四個可以點擊的元素文本內容對應4個主要數學運算符且應具有如下 id 屬性: `id="add"`, `id="subtract"`, `id="multiply"`, `id="divide"`
**需求 4** 計算器應該包含一個可點擊的 `.`(小數點)符號,對應的 `id="decimal"`
**需求 5** 計算器應該包含一個具有 `id="clear"` 屬性的可以點擊的元素。
**需求 6** 我的計算器應該包含一個用於展示數值的元素,這個元素具有 `id="display"` 屬性。
**需求 7** 在任何時候按下 `clear` 鍵,都會清空輸入和輸出的數值並且使計算器回到初始狀態;此時在 id 爲 `display` 的元素中應該顯示數字 0。
**需求 8** 在輸入數字的同時,應該能看到輸入的數字展示在 id 爲 `display` 的元素中。
**需求 9** 應該可以在任意順序下對一串任意長度的數字執行加、減、乘、除操作,並且當按下 `=` 時,正確答案應該顯示在 id 爲 `display` 的元素中。
**需求 10** 在輸入數字的同時,計算器應該不允許一個數字以多個零開頭。
**需求 11** 當點擊小數點元素時,當前展示的數值後面應該添加一個 `.` 符號;數字中不允許出現兩個 `.` 符號。
**需求 12** 我可以對包含小數點的數字執行任何四則運算(`+``-``*``/`)。
**需求 13** 如果連續鍵入了兩個及以上的運算符,應該執行最後一次鍵入的運算符(負數(`-`)運算符除外)。 例如,如果輸入 `5 + * 7 =`,結果應該是 `35` (等同於 `5 * 7`);如果輸入 `5 * - 5 =`,結果應該是 `-25`(等同於 `5 * (-5)`)。
**需求 14** 如果在按下 `=` 符號後繼續按一個運算符,則應該在上一次計算結果的基礎上進行新的計算。
**需求 15** 當需要四捨五入時,計算器可以處理結果的精度(注意:處理到多少位沒有一個準確的標準,但是至少能處理類似 `2 / 7` 這樣的計算,使之至少有 4 位小數的精度)。
**注意計算器的邏輯:** 應當注意的是,計算器輸入邏輯主要有兩種思路:<dfn>立即執行邏輯</dfn><dfn>公式邏輯</dfn>。 我們的示例使用公式邏輯並遵守運算優先順序,而立即執行則不然。 兩者都是可以接受的,但請注意,根據你的選擇,你的計算器對於某些算式可能會得到與示例不同的計算結果(參見下面的示例)。 只要數學計算可以通過其它現實中計算器的驗證,那麼代碼就是合理的。
**示例:** `3 + 5 x 6 - 2 / 4 =`
- **立即執行邏輯:** `11.5`
- **公式/表達式邏輯:** `32.5`
你可以<a href='https://codepen.io/pen?template=MJjpwO' target='_blank' rel='nofollow'>使用這個 CodePen 模板</a>,點擊 `Save` 即可創建你自己的項目。 或者可以在任何喜歡的環境中使用以下 CDN 鏈接來運行測試:`https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
當你完成了本項目,並且該項目所有測試運行通過,請提交項目的 URL。
# --solutions--
```js
// solution required
```

View File

@@ -0,0 +1,39 @@
---
id: bd7157d8c242eddfaeb5bd13
title: 構建一個 Markdown 文件預覽器
challengeType: 3
forumTopicId: 301372
dashedName: build-a-markdown-previewer
---
# --description--
**目標:** 在 [CodePen.io](https://codepen.io) 上實現一個功能類似 <https://codepen.io/freeCodeCamp/full/GrZVVO> 的 App。
在滿足以下[需求](https://en.wikipedia.org/wiki/User_story)並能通過所有測試的前提下, 你可以根據自己的喜好來美化 app。
可以使用 HTML、JavaScript、CSS、Bootstrap、SASS、React、Redux、jQuery 來完成這個挑戰。 但鑑於這個章節的學習內容與前端框架相關,推薦使用一款前端框架(比如 React來完成這個挑戰。 不推薦使用前面沒有提到的技術,否則風險自負。 我們有計劃新增其他前端框架課程,例如 Angular 和 Vue不過目前還沒有這些內容。 我們會接受並嘗試修復使用推薦技術棧遇到的反饋問題。 編碼愉快!
**需求 1** 應該能看到一個具有 `id="editor"` 屬性的 `textarea` 元素。
**需求 2** 應該能看到一個具有 `id="preview"` 屬性的元素。
**需求 3** 當在具有 `#editor` 屬性的元素內輸入文本時,具有 `#preview` 屬性的元素應該同步更新展示鍵入的內容。
**需求 4** 當在具有 `#editor` 屬性的元素內輸入 GitHub 風格的 markdown 內容時,文本應該以 HTML 的形式,把所鍵入的內容渲染在具有 `#preview` 屬性的元素中(提示:不需要自己解析 Markdown——可以引入一個叫做 Marked 的庫來完成這項工作:<https://cdnjs.com/libraries/marked>)。
**需求 5** 當 Markdown 預覽器首次加載時,具有 `#editor` 屬性的元素內的默認內容應該包含以下每個種類的至少一段有效的 Markdown 代碼標題H1 標籤、次級標題H2 標籤)、鏈接、行內代碼、代碼塊、列表、引用塊、圖片、加粗文本。
**需求 6** 當 Markdown 預覽器首次加載時,具有 `#editor` 屬性的元素內容應該以 HTML 的形式渲染在具有 `#preview` 屬性的元素中。
**可選需求(你無需通過這項測試):** Markdown 預覽器能夠解析回車符並且將他們以 `br`(換行)元素的形式渲染出來。
你可以<a href='https://codepen.io/pen?template=MJjpwO' target='_blank' rel='nofollow'>使用這個 CodePen 模板</a>,點擊 `Save` 即可創建你自己的項目。 或者可以在任何喜歡的環境中使用以下 CDN 鏈接來運行測試:`https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
當你完成了本項目,並且該項目所有測試運行通過,請提交項目的 URL。
# --solutions--
```js
// solution required
```

View File

@@ -0,0 +1,49 @@
---
id: bd7158d8c442eddfaeb5bd13
title: 構建一個隨機引語生成器
challengeType: 3
forumTopicId: 301374
dashedName: build-a-random-quote-machine
---
# --description--
**目標:** 在 [CodePen.io](https://codepen.io) 上實現一個功能類似 <https://codepen.io/freeCodeCamp/full/qRZeGZ> 的 App。
在滿足以下[需求](https://en.wikipedia.org/wiki/User_story)並能通過所有測試的前提下, 可以根據自己的喜好來美化 app。
可以使用 HTML、JavaScript、CSS、Bootstrap、SASS、React、Redux、jQuery 來完成這個挑戰。 但鑑於這個章節的學習內容與前端框架相關,推薦使用一款前端框架(比如 React來完成這個挑戰。 不推薦使用前面沒有提到的技術,否則風險自擔。 我們有計劃新增其他前端框架課程,例如 Angular 和 Vue不過目前還沒有這些內容。 我們會接受並嘗試修復使用推薦技術棧遇到的反饋問題。 編碼愉快!
**需求 1** 應該能看到一個具有 `id="quote-box"` 屬性的包裹元素。
**需求 2**`#quote-box` 元素內,應該能看到一個具有 `id="text"` 屬性的元素。
**需求 3**`#quote-box` 元素內,應該能看到一個具有 `id="author"` 屬性的元素。
**需求 4**`#quote-box` 元素內,應該能看到一個具有 `id="new-quote"` 屬性的可點擊元素。
**需求 5**`#quote-box` 元素內,應該能看到一個具有 `id="tweet-quote"` 屬性的可點擊 `a` 元素。
**需求 6** 首次加載時App 應該在具有 `id="text"` 屬性的元素內展示一條隨機引語。
**需求 7** 首次加載時App 應該在具有 `id="author"` 屬性的元素內展示該條隨機引語的作者。
**需求 8** 當點擊具有 `#new-quote` 屬性的按鈕時App 應該得到一條新的引語並在具有 `#text` 屬性的元素內展示出來。
**需求 9** 當點擊具有 `#new-quote` 屬性的按鈕時App 應該得到新引語的作者並在具有 `#author` 屬性的元素內展示出來。
**需求 10** 可以通過點擊具有 `#tweet-quote` 屬性的 `a` 標籤將當前引語發送到推特。 該 `a` 標籤的 `href` 屬性應該是 `"twitter.com/intent/tweet"`,以便成功發送。
**需求 11** 具有 `#quote-box` 屬性的包裹元素應該水平居中。 請在瀏覽器縮放尺寸爲 100% 且頁面窗口最大化時運行測試。
你可以<a href='https://codepen.io/pen?template=MJjpwO' target='_blank' rel='nofollow'>使用這個 CodePen 模板</a>,點擊 `Save` 創建即可你自己的項目 或者可以在任何喜歡的環境中使用以下 CDN 鏈接來運行測試:`https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js`
一旦完成了本項目並且該項目所有測試運行通過,請提交項目的 URL。
**注意:** Twitter 不允許在 iframe 里加載鏈接。 如果 tweet 不能加載,嘗試在 `#tweet-quote` 元素上使用 `target="_blank"` 或者 `target="_top"` 屬性。 `target="_top"` 會替換當前 tab 頁的內容,所以確保當前內容已經保存了。
# --solutions--
```js
// solution required
```

View File

@@ -0,0 +1,131 @@
---
id: 564944c91be2204b269d51e3
title: 使用 jQuery 更改元素內部的文本
challengeType: 6
forumTopicId: 16773
dashedName: change-text-inside-an-element-using-jquery
---
# --description--
可以通過 jQuery 改變元素開始和結束標籤之間的文本。 甚至改變 HTML 標籤。
jQuery 有一個 `.html()` 函數,能用其在標籤裏添加 HTML 標籤和文本, 函數提供的內容將完全替換之前標籤的內容。
下面是重寫並強調標題文本的代碼:
```js
$("h3").html("<em>jQuery Playground</em>");
```
jQuery 還有一個類似的函數 `.text()`,可以在不添加標籤的前提下改變標籤內的文本。 換句話說,這個函數不會評估傳遞給它的任何 HTML 標記,而是將其視爲要替換現有內容的文本。
給 id 爲 `target4` 的按鈕的文本添加強調效果。
查看這篇[關於 &lt;em> 的文章](https://www.freecodecamp.org/news/html-elements-explained-what-are-html-tags/#em-element)來了解更多 `<i>``<em>` 的區別和用法。
注意,`<i>` 標籤雖然傳統上用來強調文本,但此後常用作圖標的標籤。 `<em>` 標籤作爲強調標籤現在已被廣泛接受。 可以使用任意一種完成這個挑戰。
# --hints--
應該通過添加 HTML 標籤強調 `target4` 按鈕中的文本。
```js
assert.isTrue(
/<em>|<i>\s*#target4\s*<\/em>|<\/i>/gi.test($('#target4').html())
);
```
文本應該保持不變。
```js
assert($('#target4') && $('#target4').text().trim() === '#target4');
```
不應該改變其它任何文本內容。
```js
assert.isFalse(/<em>|<i>/gi.test($('h3').html()));
```
應該使用 `.html()` 方法而不是 `.text()` 方法。
```js
assert(code.match(/\.html\(/g));
```
應該使用 jQuery 選取 `button id="target4"`
```js
assert(code.match(/\$\(\s*?(\"|\')#target4(\"|\')\s*?\)\.html\(/));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target4").html('<em>#target4</em>');
});
</script>
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,119 @@
---
id: bad87fee1348bd9aed908826
title: 使用 jQuery 更改元素的 CSS
challengeType: 6
forumTopicId: 16776
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: change-the-css-of-an-element-using-jquery
---
# --description--
也能用 jQuery 直接改變 HTML 標籤的 CSS。
jQuery 有一個 `.css()` 方法,能改變標籤的 CSS。
下面的代碼效果是把顏色變藍:
```js
$("#target1").css("color", "blue");
```
這與通常的 CSS 聲明略有不同,因爲這個 CSS 屬性和它的值在英文引號裏,並且它們用逗號而不是冒號間隔開。
刪除 jQuery 選擇器,留下一個空的 `document ready function`
選擇 `target1`,並將其顏色更改爲紅色。
# --hints--
`target1` 元素應該有紅色文本。
```js
assert($('#target1').css('color') === 'rgb(255, 0, 0)');
```
應該僅用 jQuery 給元素添加這些 class。
```js
assert(!code.match(/class.*animated/g));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
$(".well").addClass("animated shake");
$("#target3").addClass("animated fadeOut");
$("button").removeClass("btn-default");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
$(".well").addClass("animated shake");
$("#target3").addClass("animated fadeOut");
$("button").removeClass("btn-default");
$("#target1").css("color", "red");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,121 @@
---
id: bad87fee1348bd9aed508826
title: 使用 jQuery 克隆元素
challengeType: 6
forumTopicId: 16780
dashedName: clone-an-element-using-jquery
---
# --description--
除了移動標籤,也可以把元素從一個地方複製到另一地方。
jQuery 有一個 `clone()` 方法,可以複製標籤。
例如,如果想把 `target2``left-well` 複製到 `right-well`,可以設置如下:
```js
$("#target2").clone().appendTo("#right-well");
```
是否注意到這兩個 jQuery 函數連在一起了? 這被稱爲<dfn>鏈式調用function chaining</dfn>,是一種用 jQuery 實現效果的簡便方法。
克隆 `target5` 元素,並將其附加到 `left-well`
# --hints--
`target5` 元素應該在 `right-well` 裏面。
```js
assert($('#right-well').children('#target5').length > 0);
```
應該克隆 `target5` 元素,並放在 `left-well` 裏面。
```js
assert($('#left-well').children('#target5').length > 0);
```
應該僅用 jQuery 移動這些元素。
```js
assert(!code.match(/class.*animated/g));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,125 @@
---
id: bad87fee1348bd9aeda08726
title: 刪除 jQuery 函數
challengeType: 6
forumTopicId: 17561
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: delete-your-jquery-functions
---
# --description--
這些動畫開始看起來很酷,但是有時可能會讓用戶分心。
請刪除 `document ready function` 內的三個 jQuery 函數,但保留 `document ready function` 本身。
# --hints--
應該刪除 `document ready function` 中的三個 jQuery 函數。
```js
assert(code.match(/\{\s*\}\);/g));
```
應該保持 `script` 標籤不變。
```js
assert(code.match(/<script>/g));
```
應該保持 `$(document).ready(function() {``script` 標籤的開頭不變。
```js
assert(code.match(/\$\(document\)\.ready\(function\(\)\s?\{/g));
```
應該保持 `document.ready` function 用 `})` 閉合。
```js
assert(code.match(/.*\s*\}\);/g));
```
應該保持 `script` 標籤閉合。
```js
assert(
code.match(/<\/script>/g) &&
code.match(/<script/g) &&
code.match(/<\/script>/g).length === code.match(/<script/g).length
);
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
$(".well").addClass("animated shake");
$("#target3").addClass("animated fadeOut");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,120 @@
---
id: bad87fee1348bd9aed808826
title: 使用 jQuery 禁用元素
challengeType: 6
forumTopicId: 17563
dashedName: disable-an-element-using-jquery
---
# --description--
還能用 jQuery 改變 HTML 標籤的非 CSS 屬性, 例如:禁用按鈕。
當禁用按鈕時,它將變成灰色並無法點擊。
jQuery 有一個 `.prop()` 方法,可以用其調整標籤的屬性。
下面是禁用所有的按鈕的代碼:
```js
$("button").prop("disabled", true);
```
僅禁用 `target1` 按鈕。
# --hints--
`target1` 按鈕應該被禁用。
```js
assert(
$('#target1') &&
$('#target1').prop('disabled') &&
code.match(/["']disabled["'],( true|true)/g)
);
```
不應該禁用其它的按鈕。
```js
assert($('#target2') && !$('#target2').prop('disabled'));
```
應該僅用 jQuery 給元素添加這些 class。
```js
assert(!code.match(/disabled[^<]*>/g));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,118 @@
---
id: bad87fee1348bd9acdd08826
title: 瞭解 script 和 document.ready 是如何工作的
challengeType: 6
forumTopicId: 18224
dashedName: learn-how-script-tags-and-document-ready-work
---
# --description--
馬上就要學習有史以來最受歡迎的 JavaScript 框架——jQuery 了。
在使用 jQuery 之前,需要在 HTML 頁面中添加一些東西。
首先,在頁面頂部添加 `script` 標籤, 記得在後面爲它添加結束標籤。
瀏覽器會運行 `script` 標籤所有的 JavaScript 腳本包括 jQuery。
`script` 標籤中添加代碼 `$(document).ready(function() {`。 然後在後面(仍在該 `script` 標籤內)用 `});` 閉合它。
稍後將詳細介紹 `functions` 現在需要知道的是,只要瀏覽器加載頁面,`function` 中放入的代碼就會運行。
有一點很重要,如果沒有 `document ready function`,代碼將在 HTML 頁面呈現之前運行,這可能會導致錯誤。
# --hints--
應該創建一個 `script` 標籤,確保其有效並具有閉合標籤。
```js
assert(
code.match(/<\/script\s*>/g) &&
code.match(
/<script(\sasync|\sdefer)*(\s(charset|src|type)\s*=\s*["\"]+[^"\"]*["\"]+)*(\sasync|\sdefer)*\s*>/g
) &&
code.match(/<\/script\s*>/g).length ===
code.match(
/<script(\sasync|\sdefer)*(\s(charset|src|type)\s*=\s*["\"]+[^"\"]*["\"]+)*(\sasync|\sdefer)*\s*>/g
).length
);
```
應該在 `script` 的開頭添加 `$(document).ready(function() {`
```js
assert(
code.match(
/\$\s*?\(\s*?document\s*?\)\.ready\s*?\(\s*?function\s*?\(\s*?\)\s*?\{/g
)
);
```
應該用 `});` 閉合 `$(document).ready(function() {` 函數。
```js
assert(code.match(/\n*?\s*?\}\s*?\);/g));
```
# --seed--
## --seed-contents--
```html
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,109 @@
---
id: bad87fee1348bd9aed708826
title: 使用 jQuery 刪除元素
challengeType: 6
forumTopicId: 18262
dashedName: remove-an-element-using-jquery
---
# --description--
現在學習用 jQuery 從頁面移除 HTML 標籤。
jQuery 有一個 `.remove()` 方法,能完全移除 HTML 標籤。
`.remove()` 方法從頁面移除 `#target4` 元素。
# --hints--
應該用 jQuery 從頁面中移除 `target4`標籤。
```js
assert(
$('#target4').length === 0 && code.match(/\$\(["']#target4["']\).remove\(\)/g)
);
```
應該僅用 jQuery 移除該標籤。
```js
assert(
code.match(/id="target4/g) &&
!code.match(/<!--.*id="target4".*-->/g) &&
$('#right-well').length > 0
);
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,122 @@
---
id: bad87fee1348bd9aed918626
title: 使用 jQuery 從元素中移除 class
challengeType: 6
forumTopicId: 18264
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: remove-classes-from-an-element-with-jquery
---
# --description--
和用 jQuery 的 `addClass()` 方法給標籤添加類一樣,也可以利用 jQuery 的 `removeClass()` 方法移除它們。
下面是爲指定按鈕執行上面的操作的代碼:
```js
$("#target2").removeClass("btn-default");
```
請把所有 `button` 元素的 `btn-default` class 移除。
# --hints--
應該移除所有 `button` 元素 `btn-default` class。
```js
assert($('.btn-default').length === 0);
```
應該僅用 jQuery 從元素中移除 class。
```js
assert(code.match(/btn btn-default/g));
```
應該僅移除 `btn-default` class。
```js
assert(
code.match(
/\.[\v\s]*removeClass[\s\v]*\([\s\v]*('|")\s*btn-default\s*('|")[\s\v]*\)/gm
)
);
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
$(".well").addClass("animated shake");
$("#target3").addClass("animated fadeOut");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
$(".well").addClass("animated shake");
$("#target3").addClass("animated fadeOut");
$("button").removeClass("btn-default");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,144 @@
---
id: bad87fee1348bd9aed108826
title: 使用 jQuery 選擇元素的特定子元素
challengeType: 6
forumTopicId: 18315
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: target-a-specific-child-of-an-element-using-jquery
---
# --description--
已經看到了 id 屬性對於 jQuery 選擇器選取標籤的便利, 但這並不適用於所有情景。
幸運的是jQuery 有一些其他的方法可以選取正確的標籤。
jQuery 可以用 CSS 選擇器CSS Selectors選取標籤。 `target:nth-child(n)` CSS 選擇器可以選取指定 class 或者元素類型的的第 n 個標籤。
下面的代碼展示了給每個區域well的第 3 個標籤設置彈跳bounce動畫效果
```js
$(".target:nth-child(3)").addClass("animated bounce");
```
給 well 元素的第二個子元素添加彈跳bounce動畫效果。 你必須選擇具有 `target` class 的元素子項。
# --hints--
`target` 元素中的第二個元素應該有彈跳bounce動畫效果。
```js
assert(
$('.target:nth-child(2)').hasClass('animated') &&
$('.target:nth-child(2)').hasClass('bounce')
);
```
應該僅兩個元素有彈跳bounce動畫效果。
```js
assert($('.animated.bounce').length === 2);
```
你應該使用 `:nth-child()` 選擇器修改這些元素。
```js
assert(code.match(/\:nth-child\(/g));
```
應該僅用 jQuery 給元素添加 class。
```js
assert(
code.match(/\$\(".target:nth-child\(2\)"\)/g) ||
code.match(/\$\('.target:nth-child\(2\)'\)/g) ||
code.match(/\$\(".target"\).filter\(":nth-child\(2\)"\)/g) ||
code.match(/\$\('.target'\).filter\(':nth-child\(2\)'\)/g)
);
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
$("#target1").parent().css("background-color", "red");
$("#right-well").children().css("color", "orange");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
$("#target1").parent().css("background-color", "red");
$("#right-well").children().css("color", "orange");
$(".target:nth-child(2)").addClass("animated bounce");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,113 @@
---
id: bad87fee1348bd9aedc08826
title: 使用 jQuery class 選擇器選擇元素
challengeType: 6
forumTopicId: 18316
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: target-elements-by-class-using-jquery
---
# --description--
如何使所有的 `button` 標籤都有彈跳的動畫效果? 用 `$("button")` 選取所有的 button 標籤,並用 `.addClass("animated bounce");` 給其添加一些 CSS 屬性。
jQuery 的 `.addClass()` 方法用來給標籤添加類。
首先,使用 `$(".well")` 選取類爲 `well``div` 標籤。
值得注意的是,和 CSS 聲明一樣,在類名前需要添加 `.`
然後,用 jQuery 的 `.addClass()` 方法添加 `animated``shake` class。
例如,在 `document ready function` 中添加下面的代碼,使所有類爲 `text-primary` 的標籤抖動:
```js
$(".text-primary").addClass("animated shake");
```
# --hints--
應該用 jQuery 的 `addClass()` 方法給所有 class 爲 `well` 的元素添加 `animated``shake` class。
```js
assert($('.well').hasClass('animated') && $('.well').hasClass('shake'));
```
應該僅用 jQuery 給元素添加這些 class。
```js
assert(!code.match(/class\.\*animated/g));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
$(".well").addClass("animated shake");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,123 @@
---
id: bad87fee1348bd9aeda08826
title: 使用 jQuery id 選擇器選擇元素
challengeType: 6
forumTopicId: 18317
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: target-elements-by-id-using-jquery
---
# --description--
也能通過 id 屬性選取標籤。
首先,用 `$("#target3")` 選擇器選取 id 爲 `target3``button` 標籤。
注意,和 CSS 聲明一樣,在 id 名前需要添加 `#`
然後,用 jQuery 的 `.addClass()` 方法添加 `animated``fadeOut` 類。
下面的代碼的效果是使 id 爲 `target6``button` 標籤淡出:
```js
$("#target6").addClass("animated fadeOut");
```
# --hints--
應該選擇 `id``target3``button` 元素,使用 jQuery 的 `addClass()` 函數給它添加 `animated` class。
```js
assert($('#target3').hasClass('animated'));
```
應該選中 id 爲 `target3` 的元素,使用 jQuery 的 `addClass()` 函數給它添加 `fadeOut` class。
```js
assert(
($('#target3').hasClass('fadeOut') || $('#target3').hasClass('fadeout')) &&
code.match(/\$\(\s*.#target3.\s*\)/g)
);
```
應該僅用 jQuery 給元素添加這些 class。
```js
assert(!code.match(/class.*animated/g));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
$(".well").addClass("animated shake");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
$(".well").addClass("animated shake");
$("#target3").addClass("animated fadeOut");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,139 @@
---
id: bad87fee1348bd9aed008826
title: 使用 jQuery 選擇偶數元素
challengeType: 6
forumTopicId: 18318
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: target-even-elements-using-jquery
---
# --description--
也可以用基於位置的奇 `:odd` 和偶 `:even` 選擇器選取標籤。
注意jQuery 是零索引zero-indexed這意味着第 1 個標籤的位置編號是 0。 這有點混亂和反常——`:odd` 表示選擇第 2 個標籤(位置編號 1第 4 個標籤(位置編號 3……等等以此類推。
下面的代碼展示了選取所有 `target` class 元素的奇數元素並設置 sheke 效果:
```js
$(".target:odd").addClass("animated shake");
```
嘗試選取所有 `target` class 元素的偶數元素並給它們設置 `animated``shake` class。 請記住, **偶數**指的是基於零系統的元素的位置。
# --hints--
所有的偶數位置上的 `target` 元素都應該抖動。
```js
assert(
$('.target:even').hasClass('animated') && $('.target:even').hasClass('shake')
);
```
應該使用 `:even` 選擇器修改這些元素。
```js
assert(code.match(/\:even/g));
```
應該僅用 jQuery 給元素添加 class。
```js
assert(
code.match(/\$\(".target:even"\)/g) ||
code.match(/\$\('.target:even'\)/g) ||
code.match(/\$\(".target"\).filter\(":even"\)/g) ||
code.match(/\$\('.target'\).filter\(':even'\)/g)
);
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
$("#target1").parent().css("background-color", "red");
$("#right-well").children().css("color", "orange");
$("#left-well").children().css("color", "green");
$(".target:nth-child(2)").addClass("animated bounce");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
$("#target1").parent().css("background-color", "red");
$("#right-well").children().css("color", "orange");
$("#left-well").children().css("color", "green");
$(".target:nth-child(2)").addClass("animated bounce");
$(".target:even").addClass("animated shake");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,118 @@
---
id: bad87fee1348bd9bedc08826
title: 使用 jQuery 選擇器選擇元素
challengeType: 6
forumTopicId: 18319
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: target-html-elements-with-selectors-using-jquery
---
# --description--
現在我們有一個 `document ready` 函數。
首先,完成第一個 jQuery 語句。 所有的 jQuery 函數都以 `$` 開頭這個符號通常被稱爲美元符號dollar sign operator或 bling。
jQuery 通常選取並操作帶有<dfn>選擇器selector</dfn>的 HTML 標籤。
比如,想要給 `button` 元素添加跳躍效果。 只需要在 document ready 函數內添加如下代碼:
```js
$("button").addClass("animated bounce");
```
請注意,我們已經在後臺引入了 jQuery 庫和 Animate.css 庫,所以你可以在編輯器裏直接使用它們。 你將使用 jQuery 將 Animate.css `bounce` class 應用於 `button` 元素。
# --hints--
應該用 jQuery 的 `addClass()` 函數給 `button` 元素添加 `animated``bounce` class。
```js
assert($('button').hasClass('animated') && $('button').hasClass('bounce'));
```
應該僅用 jQuery 給元素添加 class。
```js
assert(!code.match(/class.*animated/g));
```
jQuery 代碼應該放在 `$(document).ready();` 函數裏。
```js
assert(
code.replace(/\s/g, '').match(/\$\(document\)\.ready\(function\(\)\{\$/g)
);
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated bounce");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,125 @@
---
id: bad87fee1348bd9aed208826
title: 使用 jQuery 選擇元素的子元素
challengeType: 6
forumTopicId: 18320
dashedName: target-the-children-of-an-element-using-jquery
---
# --description--
把 HTML 標籤放到另一個級別的標籤裏,這些 HTML 標籤被稱爲該標籤的<dfn>子標籤children element</dfn>。 例如,本次挑戰中文本爲 `#target1``#target2``#target3` 的按鈕都是 `<div class="well" id="left-well">` 標籤的子標籤。
jQuery 有一個 `children()` 方法,可以訪問被選取標籤的子標籤。
下面的代碼展示了用 `children()` 方法把 `left-well` 標籤的子標籤的顏色設置成 `blue`(藍色):
```js
$("#left-well").children().css("color", "blue")
```
# --instructions--
`right-well` 元素的所有子元素設置爲橙色orange
# --hints--
`#right-well` 的所有子元素應該有橙色文本。
```js
assert($('#right-well').children().css('color') === 'rgb(255, 165, 0)');
```
應該用 `children()` 函數修改這些元素。
```js
assert(code.match(/\.children\(\)\.css/g));
```
應該僅用 jQuery 給標籤添加 class。
```js
assert(code.match(/<div class="well" id="right-well">/g));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
$("#target1").parent().css("background-color", "red");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
$("#target1").parent().css("background-color", "red");
$("#right-well").children().css("color", "orange");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,140 @@
---
id: bad87fee1348bd9aed308826
title: 使用 jQuery 選擇元素的父元素
challengeType: 6
forumTopicId: 18321
dashedName: target-the-parent-of-an-element-using-jquery
---
# --description--
每個 HTML 標籤都默認 `inherits`(繼承)其 `parent`(父標籤)的 CSS 屬性。
例如,`h3` 標籤 `jQuery Playground` 的父標籤是 `<div class="container-fluid">`,而這個標籤的父標籤是 `body`
jQuery 有一個 `parent()` 方法,可以訪問被選取標籤的父標籤。
下面的代碼展示了使用 `parent()` 方法把 `left-well` 標籤的父標籤背景色設置成藍色blue
```js
$("#left-well").parent().css("background-color", "blue")
```
`#target1` 元素的父元素背景色設置成紅色red
# --hints--
`left-well` 元素應該有紅色的背景。
```js
assert(
$('#left-well').css('background-color') === 'red' ||
$('#left-well').css('background-color') === 'rgb(255, 0, 0)' ||
$('#left-well').css('background-color').toLowerCase() === '#ff0000' ||
$('#left-well').css('background-color').toLowerCase() === '#f00'
);
```
應該用 `.parent()` 函數修改該元素。
```js
assert(code.match(/\.parent\s*\(\s*\)\s*\.css/g));
```
應該在 `#target1` 元素上調用 `.parent()` 方法。
```js
assert(
code.match(/\$\s*?\(\s*?(?:'|")\s*?#target1\s*?(?:'|")\s*?\)\s*?\.parent/gi)
);
```
應該僅用 jQuery 給元素添加 class。
```js
assert(code.match(/<div class="well" id="left-well">/g));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
});
</script>
<!-- Only change code above this line -->
<body>
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
</body>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
$("#target1").parent().css("background-color", "red");
});
</script>
<!-- Only change code above this line -->
<body>
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
</body>
```

View File

@@ -0,0 +1,143 @@
---
id: bad87fee1348bd9aed908626
title: 用多個 jQuery 選擇器選擇同一個元素
challengeType: 6
forumTopicId: 18322
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: target-the-same-element-with-multiple-jquery-selectors
---
# --description--
現在學寫了三種選取標籤的方法:用標籤選擇器: `$("button")`,用類選擇器:`$(".btn")` 以及用 id 選擇器:`$("#target1")`
雖然可以在單個 `.addClass()` 內添加多個類,但是我們可以用*三種不同的方式*給一種標籤添加類。
以三種不同的方式用 `.addClass()` 方法每次只給一種標籤添加一個類:
給所有的 `button` 標籤添加 `animated` 類。
給所有類爲 `.btn` 的 button 標籤添加 `shake` 類。
給所有 id 爲 `#target1` 的 button 標籤添加 `btn-primary` 類。
**注意:**只針對一個元素並且一次只能添加一個 class。 總之,三個選擇器最終將添加三個 class `shake``animated` 以及 `btn-primary``#target1` 上。
# --hints--
應該使用 `$("button")` 選擇器。
```js
assert(code.match(/\$\s*?\(\s*?(?:'|")\s*?button\s*?(?:'|")/gi));
```
應該使用 `$(".btn")` 選擇器。
```js
assert(code.match(/\$\s*?\(\s*?(?:'|")\s*?\.btn\s*?(?:'|")/gi));
```
應該使用 `$("#target1")` 選擇器。
```js
assert(code.match(/\$\s*?\(\s*?(?:'|")\s*?#target1\s*?(?:'|")/gi));
```
三個選擇器每個應該只添加一個類。
```js
assert(
code.match(/addClass/g) &&
code.match(/addClass\s*?\(\s*?('|")\s*?[\w-]+\s*?\1\s*?\)/g).length > 2
);
```
`#target1` 標籤應具有 `animated``shake``btn-primary` 三個類。
```js
assert(
$('#target1').hasClass('animated') &&
$('#target1').hasClass('shake') &&
$('#target1').hasClass('btn-primary')
);
```
應該僅用 jQuery 給標籤添加類。
```js
assert(!code.match(/class.*animated/g));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("button").addClass("animated");
$(".btn").addClass("shake");
$("#target1").addClass("btn-primary");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,117 @@
---
id: bad87fee1348bd9aed608826
title: 使用 jQuery 的 appendTo 方法移動元素
challengeType: 6
forumTopicId: 18340
dashedName: use-appendto-to-move-elements-with-jquery
---
# --description--
現在來把標籤從一個 `div` 移動到另一個裏。
jQuery 有一個 `appendTo()` 方法,可以選取 HTML 標籤並將其添加到另一個標籤裏面。
例如,如果要把 `target4` 從 right well 移到 left well可以設置如下
```js
$("#target4").appendTo("#left-well");
```
`target2` 元素從 `left-well` 移動到 `right-well`
# --hints--
`target2` 元素不應該在 `left-well` 內。
```js
assert($('#left-well').children('#target2').length === 0);
```
`target2` 元素應該在 `right-well` 內。
```js
assert($('#right-well').children('#target2').length > 0);
```
應該僅用 jQuery 移動這些標籤。
```js
assert(!code.match(/class.*animated/g));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,118 @@
---
id: bad87fee1348bd9aecb08826
title: 使用 jQuery 修改整個頁面
challengeType: 6
forumTopicId: 18361
required:
-
link: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.css'
dashedName: use-jquery-to-modify-the-entire-page
---
# --description--
目前,已經完成了 jQuery playground。 把它移除吧。
jQuery 也能選取 `body` 標籤。
這是使整個 body 淡出的代碼:`$("body").addClass("animated fadeOut");`
來做一些更好玩的事。 給 `body` 標籤添加 `animated``hinge` class。
# --hints--
應該給 `body` 標籤添加 `animated``hinge` class。
```js
assert($('body').hasClass('animated') && $('body').hasClass('hinge'));
```
# --seed--
## --seed-contents--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
$("#target1").parent().css("background-color", "red");
$("#right-well").children().css("color", "orange");
$("#left-well").children().css("color", "green");
$(".target:nth-child(2)").addClass("animated bounce");
$(".target:even").addClass("animated shake");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```
# --solutions--
```html
<script>
$(document).ready(function() {
$("#target1").css("color", "red");
$("#target1").prop("disabled", true);
$("#target4").remove();
$("#target2").appendTo("#right-well");
$("#target5").clone().appendTo("#left-well");
$("#target1").parent().css("background-color", "red");
$("#right-well").children().css("color", "orange");
$("#left-well").children().css("color", "green");
$(".target:nth-child(2)").addClass("animated bounce");
$(".target:even").addClass("animated shake");
$("body").addClass("animated hinge");
});
</script>
<!-- Only change code above this line -->
<div class="container-fluid">
<h3 class="text-primary text-center">jQuery Playground</h3>
<div class="row">
<div class="col-xs-6">
<h4>#left-well</h4>
<div class="well" id="left-well">
<button class="btn btn-default target" id="target1">#target1</button>
<button class="btn btn-default target" id="target2">#target2</button>
<button class="btn btn-default target" id="target3">#target3</button>
</div>
</div>
<div class="col-xs-6">
<h4>#right-well</h4>
<div class="well" id="right-well">
<button class="btn btn-default target" id="target4">#target4</button>
<button class="btn btn-default target" id="target5">#target5</button>
<button class="btn btn-default target" id="target6">#target6</button>
</div>
</div>
</div>
</div>
```

View File

@@ -0,0 +1,156 @@
---
id: 5a24c314108439a4d4036147
title: 連接 Redux 和 React
challengeType: 6
forumTopicId: 301426
dashedName: connect-redux-to-react
---
# --description--
你已經寫了`mapStateToProps()``mapDispatchToProps()` 兩個函數,現在可以用它們來把 `state``dispatch` 映射到 React 組件的 `props` 了。 React Redux 的 `connect` 方法可以完成這個任務。 此方法有 `mapStateToProps()``mapDispatchToProps()` 兩個可選參數, 它們是可選的,原因是你的組件可能僅需要訪問 `state` 但不需要分發任何 actions反之亦然。
爲了使用此方法,需要傳入函數參數並在調用時傳入組件。 這種語法有些不尋常,如下所示:
```js
connect(mapStateToProps, mapDispatchToProps)(MyComponent)
```
**注意:**如果要省略 `connect` 方法中的某個參數,則應當用 `null` 替換這個參數。
# --instructions--
代碼編輯器具有 `mapStateToProps()``mapDispatchToProps()` 函數,以及一個名爲 `Presentational` 的新 React 組件。 使用 `ReactRedux` 全局對象中的 `connect` 方法將此組件連接到 Redux並立即在 `Presentational` 組件上調用它。 將結果賦值給名爲 `ConnectedComponent` 的新 `const`,表示連接的組件。 就是這樣,現在你已經連接到 Redux 了! 嘗試將 `connect` 的參數更改爲 `null`,並觀察測試結果。
# --hints--
應渲染 `Presentational` 組件。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
return mockedComponent.find('Presentational').length === 1;
})()
);
```
`Presentational` 組件應通過 `connect` 接收一個 `messages` 屬性。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const props = mockedComponent.find('Presentational').props();
return props.messages === '__INITIAL__STATE__';
})()
);
```
`Presentational` 組件應通過 `connect` 接收一個 `submitNewMessage` 屬性。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const props = mockedComponent.find('Presentational').props();
return typeof props.submitNewMessage === 'function';
})()
);
```
# --seed--
## --after-user-code--
```jsx
const store = Redux.createStore(
(state = '__INITIAL__STATE__', action) => state
);
class AppWrapper extends React.Component {
render() {
return (
<ReactRedux.Provider store = {store}>
<ConnectedComponent/>
</ReactRedux.Provider>
);
}
};
ReactDOM.render(<AppWrapper />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const addMessage = (message) => {
return {
type: 'ADD',
message: message
}
};
const mapStateToProps = (state) => {
return {
messages: state
}
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (message) => {
dispatch(addMessage(message));
}
}
};
class Presentational extends React.Component {
constructor(props) {
super(props);
}
render() {
return <h3>This is a Presentational Component</h3>
}
};
const connect = ReactRedux.connect;
// Change code below this line
```
# --solutions--
```jsx
const addMessage = (message) => {
return {
type: 'ADD',
message: message
}
};
const mapStateToProps = (state) => {
return {
messages: state
}
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (message) => {
dispatch(addMessage(message));
}
}
};
class Presentational extends React.Component {
constructor(props) {
super(props);
}
render() {
return <h3>This is a Presentational Component</h3>
}
};
const connect = ReactRedux.connect;
// Change code below this line
const ConnectedComponent = connect(mapStateToProps, mapDispatchToProps)(Presentational);
```

View File

@@ -0,0 +1,300 @@
---
id: 5a24c314108439a4d4036148
title: 將 Redux 連接到 Messages App
challengeType: 6
forumTopicId: 301427
dashedName: connect-redux-to-the-messages-app
---
# --description--
知道如何使用 `connect` 連接 React 和 Redux 後,我們可以在 React 組件中應用上面學到的內容。
在上一課,連接到 Redux 的組件命名爲 `Presentational`,這個命名不是任意的, 這樣的術語*通常*是指未直接連接到 Redux 的 React 組件, 它們只負責執行接收 props 的函數來實現 UI 的呈現。 相比之下,容器組件用來連接到 Redux 上。 這些組件通常負責把 actions 分派給 store且經常給子組件傳入 store state 屬性。
# --instructions--
到目前爲止,我們的編輯器上已包含了整個章節的代碼, 唯一不同的是React 組件被重新命名爲 `Presentational`,即展示層組件。 創建一個新組件,保存在名爲 `Container` 的常量中。 這個常量用 `connect``Presentational` 組件和 Redux 連接起來。 然後,在`AppWrapper` 中渲染 React Redux 的 `Provider`組件, 給 `Provider` 傳入 Redux `store` 屬性並渲染 `Container` 爲子組件。 設置完所有內容後,將再次看到消息應用程序渲染到頁面上。
# --hints--
`AppWrapper` 應渲染該頁面上。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
return mockedComponent.find('AppWrapper').length === 1;
})()
);
```
`Presentational` 組件應該渲染到頁面上。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
return mockedComponent.find('Presentational').length === 1;
})()
);
```
`Presentational` 組件應渲染 `h2``input``button``ul` 四個元素。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const PresentationalComponent = mockedComponent.find('Presentational');
return (
PresentationalComponent.find('div').length === 1 &&
PresentationalComponent.find('h2').length === 1 &&
PresentationalComponent.find('button').length === 1 &&
PresentationalComponent.find('ul').length === 1
);
})()
);
```
`Presentational` 組件應接收 Redux store 的 `messages` 屬性。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const PresentationalComponent = mockedComponent.find('Presentational');
const props = PresentationalComponent.props();
return Array.isArray(props.messages);
})()
);
```
`Presentational` 組件應接收創建 action 的函數 `submitMessage` 屬性。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const PresentationalComponent = mockedComponent.find('Presentational');
const props = PresentationalComponent.props();
return typeof props.submitNewMessage === 'function';
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<AppWrapper />, document.getElementById('root'))
```
## --seed-contents--
```jsx
// Redux:
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message: message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
// React:
class Presentational extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.setState((state) => {
const currentMessage = state.input;
return {
input: '',
messages: state.messages.concat(currentMessage)
};
});
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.state.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
// React-Redux:
const mapStateToProps = (state) => {
return { messages: state }
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (newMessage) => {
dispatch(addMessage(newMessage))
}
}
};
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;
// Define the Container component here:
class AppWrapper extends React.Component {
constructor(props) {
super(props);
}
render() {
// Complete the return statement:
return (null);
}
};
```
# --solutions--
```jsx
// Redux:
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message: message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
// React:
class Presentational extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.setState((state) => {
const currentMessage = state.input;
return {
input: '',
messages: state.messages.concat(currentMessage)
};
});
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.state.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
// React-Redux:
const mapStateToProps = (state) => {
return { messages: state }
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (newMessage) => {
dispatch(addMessage(newMessage))
}
}
};
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;
const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);
class AppWrapper extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Provider store={store}>
<Container/>
</Provider>
);
}
};
```

View File

@@ -0,0 +1,397 @@
---
id: 5a24c314108439a4d4036149
title: 將局部狀態提取到 Redux 中
challengeType: 6
forumTopicId: 301428
dashedName: extract-local-state-into-redux
---
# --description--
馬上就完成了! 請回顧一下爲管理 React messages app 的狀態寫的 Redux 代碼。 現在有了連接好的 Redux還要從`Presentational`組件中提取狀態管理到 Redux 目前,已連接 Redux但正在 `Presentational` 組件中本地處理狀態。
# --instructions--
`Presentational` 組件中,先刪除本地 `state` 中的 `messages` 屬性, 被刪的 messages 將由 Redux 管理。 接着,修改 `submitMessage()` 方法,使該方法從 `this.props` 那裏分發 `submitNewMessage()`;從本地 `state` 中傳入當前消息輸入作爲參數。 因本地狀態刪除了 `messages` 屬性,所以在調用 `this.setState()` 時也要刪除 `messages` 屬性。 最後,修改 `render()` 方法,使其所映射的消息是從 `props` 接收的,而不是 `state`
完成這些更改後,我們的應用會實現 Redux 管理應用的狀態,但它繼續運行着相同的功能。 此示例還闡明瞭組件獲得本地 `state` 的方式,即在自己的 `state` 中繼續跟蹤用戶本地輸入。 由此可見Redux 爲 React 提供了很有用的狀態管理框架。 先前,僅使用 React 的本地狀態也實現了相同的結果,這在應付簡單的應用時通常是可行的。 但是隨着應用變得越來越大越來越複雜應用的狀態管理也變得非常困難Redux 就是爲解決這樣的問題而誕生的。
# --hints--
`AppWrapper` 應該渲染該到頁面上。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
return mockedComponent.find('AppWrapper').length === 1;
})()
);
```
`Presentational` 應該渲染到頁面上.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
return mockedComponent.find('Presentational').length === 1;
})()
);
```
`Presentational` 組件應渲染 `h2``input``button``ul` 四個元素。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const PresentationalComponent = mockedComponent.find('Presentational');
return (
PresentationalComponent.find('div').length === 1 &&
PresentationalComponent.find('h2').length === 1 &&
PresentationalComponent.find('button').length === 1 &&
PresentationalComponent.find('ul').length === 1
);
})()
);
```
`Presentational` 組件應接收 Redux store 的 `messages` 屬性。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const PresentationalComponent = mockedComponent.find('Presentational');
const props = PresentationalComponent.props();
return Array.isArray(props.messages);
})()
);
```
`Presentational` 組件應接收創建 action 的函數的 `submitMessage` 屬性。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const PresentationalComponent = mockedComponent.find('Presentational');
const props = PresentationalComponent.props();
return typeof props.submitNewMessage === 'function';
})()
);
```
`Presentational` 組件的狀態應包含一個初始化爲空字符串的 `input` 屬性。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const PresentationalState = mockedComponent
.find('Presentational')
.instance().state;
return (
typeof PresentationalState.input === 'string' &&
Object.keys(PresentationalState).length === 1
);
})()
);
```
鍵入 `input` 元素應更新 `Presentational` 組件的狀態。
```js
async () => {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const testValue = '__MOCK__INPUT__';
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
const causeChange = (c, v) =>
c.find('input').simulate('change', { target: { value: v } });
let initialInput = mockedComponent.find('Presentational').find('input');
const changed = () => {
causeChange(mockedComponent, testValue);
return waitForIt(() => mockedComponent);
};
const updated = await changed();
const updatedInput = updated.find('Presentational').find('input');
assert(
initialInput.props().value === '' &&
updatedInput.props().value === '__MOCK__INPUT__'
);
};
```
`Presentational` 組件上 dispatch `submitMessage` 應更新 Redux store 並清除本地狀態中的輸入。
```js
async () => {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
let beforeProps = mockedComponent.find('Presentational').props();
const testValue = '__TEST__EVENT__INPUT__';
const causeChange = (c, v) =>
c.find('input').simulate('change', { target: { value: v } });
const changed = () => {
causeChange(mockedComponent, testValue);
return waitForIt(() => mockedComponent);
};
const clickButton = () => {
mockedComponent.find('button').simulate('click');
return waitForIt(() => mockedComponent);
};
const afterChange = await changed();
const afterChangeInput = afterChange.find('input').props().value;
const afterClick = await clickButton();
const afterProps = mockedComponent.find('Presentational').props();
assert(
beforeProps.messages.length === 0 &&
afterChangeInput === testValue &&
afterProps.messages.pop() === testValue &&
afterClick.find('input').props().value === ''
);
};
```
`Presentational` 組件應渲染 Redux store 中的 `messages`
```js
async () => {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
let beforeProps = mockedComponent.find('Presentational').props();
const testValue = '__TEST__EVENT__INPUT__';
const causeChange = (c, v) =>
c.find('input').simulate('change', { target: { value: v } });
const changed = () => {
causeChange(mockedComponent, testValue);
return waitForIt(() => mockedComponent);
};
const clickButton = () => {
mockedComponent.find('button').simulate('click');
return waitForIt(() => mockedComponent);
};
const afterChange = await changed();
const afterChangeInput = afterChange.find('input').props().value;
const afterClick = await clickButton();
const afterProps = mockedComponent.find('Presentational').props();
assert(
beforeProps.messages.length === 0 &&
afterChangeInput === testValue &&
afterProps.messages.pop() === testValue &&
afterClick.find('input').props().value === '' &&
afterClick.find('ul').childAt(0).text() === testValue
);
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<AppWrapper />, document.getElementById('root'))
```
## --seed-contents--
```jsx
// Redux:
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message: message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
// React:
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;
// Change code below this line
class Presentational extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.setState((state) => ({
input: '',
messages: state.messages.concat(state.input)
}));
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.state.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
// Change code above this line
const mapStateToProps = (state) => {
return {messages: state}
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (message) => {
dispatch(addMessage(message))
}
}
};
const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);
class AppWrapper extends React.Component {
render() {
return (
<Provider store={store}>
<Container/>
</Provider>
);
}
};
```
# --solutions--
```jsx
// Redux:
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message: message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
// React:
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;
// Change code below this line
class Presentational extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.props.submitNewMessage(this.state.input);
this.setState({
input: ''
});
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.props.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
// Change code above this line
const mapStateToProps = (state) => {
return {messages: state}
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (message) => {
dispatch(addMessage(message))
}
}
};
const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);
class AppWrapper extends React.Component {
render() {
return (
<Provider store={store}>
<Container/>
</Provider>
);
}
};
```

View File

@@ -0,0 +1,115 @@
---
id: 5a24c314108439a4d4036143
title: 提取狀態邏輯給 Redux
challengeType: 6
forumTopicId: 301429
dashedName: extract-state-logic-to-redux
---
# --description--
完成 React 組件後,我們需要把在本地 `state` 執行的邏輯移到 Redux 中, 這是爲小規模 React 應用添加 Redux 的第一步。 該應用的唯一功能是把用戶的新消息添加到無序列表中。 下面我們用簡單的示例來演示 React 和 Redux 之間的配合。
# --instructions--
首先,定義 action 的類型 `ADD`,將其設置爲常量 `ADD`。 接着,定義創建 action 的函數`addMessage()`,用該函數創建添加消息的 action`message` 傳給創建 action 的函數並返回包含該消息的 `action`
接着,創建名爲 `messageReducer()` 的 reducer 方法,爲這些消息處理狀態。 初始狀態應爲空數組。 reducer 向狀態中的消息數組添加消息,或返回當前狀態。 最後,創建 Redux store 並傳給 reducer。
# --hints--
應存在一個值爲字符串 `ADD` 的常量 `ADD`
```js
assert(ADD === 'ADD');
```
創建 action 的函數 `addMessage` 應返回 `type` 等於 `ADD` 的對象,其返回的 `message` 即被傳入的消息。
```js
assert(
(function () {
const addAction = addMessage('__TEST__MESSAGE__');
return addAction.type === ADD && addAction.message === '__TEST__MESSAGE__';
})()
);
```
`messageReducer` 應是一個函數。
```js
assert(typeof messageReducer === 'function');
```
存在一個 store 且其初始狀態爲空數組。
```js
assert(
(function () {
const initialState = store.getState();
return typeof store === 'object' && initialState.length === 0;
})()
);
```
分發 `addMessage` 到 store 應添加新消息到狀態中消息數組。
```js
assert(
(function () {
const initialState = store.getState();
const isFrozen = DeepFreeze(initialState);
store.dispatch(addMessage('__A__TEST__MESSAGE'));
const addState = store.getState();
return isFrozen && addState[0] === '__A__TEST__MESSAGE';
})()
);
```
`messageReducer` 被其它任何 actions 調用時應返回當前狀態。
```js
assert(
(function () {
const addState = store.getState();
store.dispatch({ type: 'FAKE_ACTION' });
const testState = store.getState();
return addState === testState;
})()
);
```
# --seed--
## --seed-contents--
```jsx
// Define ADD, addMessage(), messageReducer(), and store here:
```
# --solutions--
```jsx
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
```

View File

@@ -0,0 +1,102 @@
---
id: 5a24c314108439a4d4036141
title: React 和 Redux 入門
challengeType: 6
forumTopicId: 301430
dashedName: getting-started-with-react-redux
---
# --description--
這一系列挑戰介紹的是 Redux 和 React 的配合, 我們先來回顧一下這兩種技術的關鍵原則是什麼。 React 是提供數據的視圖庫,能以高效、可預測的方式渲染視圖。 Redux 是狀態管理框架,可用於簡化 APP 應用狀態的管理。 在 React Redux app 應用中,通常可創建單一的 Redux store 來管理整個應用的狀態。 React 組件僅訂閱 store 中與其角色相關的數據, 可直接從 React 組件中分發 actions 以觸發 store 對象的更新。
React 組件可以在本地管理自己的狀態,但是對於複雜的應用來說,它的狀態最好是用 Redux 保存在單一位置,有特定本地狀態的獨立組件例外。 當單個組件可能僅具有特定於其的本地狀態時,算是例外。 最後一點是Redux 沒有內置的 React 支持,需要安裝 `react-redux`包, 通過這個方式把 Redux 的 `state``dispatch` 作爲 `props` 傳給組件。
在接下來的挑戰中,先要創建一個可輸入新文本消息的 React 組件, 添加這些消息到數組裏,在視圖上顯示數組。 這應該是 React 課程中的一個很好的回顧。 接着,創建 Redux store 和 actions 來管理消息數組的狀態。 最後,使用 `react-redux` 連接 Redux store 和組件,從而將本地狀態提取到 Redux store 中。
# --instructions--
`DisplayMessages` 組件開始。 把構造函數添加到此組件中,使用含兩個屬性的狀態初始化該組件,這兩個屬性爲:`input`(設置爲空字符串),`messages`(設置爲空數組)。
# --hints--
`DisplayMessages` 組件應渲染空的 `div` 元素。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
return mockedComponent.find('div').text() === '';
})()
);
```
`DisplayMessages` 組件的構造函數應調用 `super`,傳入 `props`
```js
(getUserInput) =>
assert(
(function () {
const noWhiteSpace = __helpers.removeWhiteSpace(getUserInput('index'));
return (
noWhiteSpace.includes('constructor(props)') &&
noWhiteSpace.includes('super(props')
);
})()
);
```
`DisplayMessages` 組件的初始狀態應是 `{input: "", messages: []}`
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
const initialState = mockedComponent.state();
return (
typeof initialState === 'object' &&
initialState.input === '' &&
Array.isArray(initialState.messages) &&
initialState.messages.length === 0
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<DisplayMessages />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class DisplayMessages extends React.Component {
// Change code below this line
// Change code above this line
render() {
return <div />
}
};
```
# --solutions--
```jsx
class DisplayMessages extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
}
render() {
return <div/>
}
};
```

View File

@@ -0,0 +1,260 @@
---
id: 5a24c314108439a4d4036142
title: 首先在本地管理狀態
challengeType: 6
forumTopicId: 301431
dashedName: manage-state-locally-first
---
# --description--
這一關的任務是完成 `DisplayMessages` 組件的創建。
# --instructions--
首先,在 `render()` 方法中,讓組件渲染 `input``button``ul` 三個元素。 `input` 元素的改變會觸發 `handleChange()` 方法。 此外,`input` 元素會渲染組件狀態中 `input` 的值。 點擊按鈕 `button` 需觸發 `submitMessage()` 方法。
接着,寫出這兩種方法。 `handleChange()` 方法會更新 `input` 爲用戶正在輸入的內容。 `submitMessage()` 方法把當前存儲在 `input` 的消息與本地狀態的 `messages` 數組連接起來,並清除 `input` 的值。
最後,在 `ul` 中展示 `messages` 數組,其中每個元素內容需放到 `li` 元素內。
# --hints--
`DisplayMessages` 組件的初始狀態應是 `{ input: "", messages: [] }`
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
const initialState = mockedComponent.state();
return (
typeof initialState === 'object' &&
initialState.input === '' &&
initialState.messages.length === 0
);
})()
);
```
`DisplayMessages` 組件應渲染含 `h2``button``ul``li` 四個子元素的`div`
```js
async () => {
const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
const state = () => {
mockedComponent.setState({ messages: ['__TEST__MESSAGE'] });
return waitForIt(() => mockedComponent);
};
const updated = await state();
assert(
updated.find('div').length === 1 &&
updated.find('h2').length === 1 &&
updated.find('button').length === 1 &&
updated.find('ul').length === 1 &&
updated.find('li').length > 0
);
};
```
`.map` 應該用於 `messages` 數組。
```js
assert(code.match(/this\.state\.messages\.map/g));
```
`input` 元素應渲染本地狀態中的 `input` 值。
```js
async () => {
const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
const causeChange = (c, v) =>
c.find('input').simulate('change', { target: { value: v } });
const testValue = '__TEST__EVENT__INPUT';
const changed = () => {
causeChange(mockedComponent, testValue);
return waitForIt(() => mockedComponent);
};
const updated = await changed();
assert(updated.find('input').props().value === testValue);
};
```
調用 `handleChange` 方法時應更新狀態中的 `input` 值爲當前輸入。
```js
async () => {
const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
const causeChange = (c, v) =>
c.find('input').simulate('change', { target: { value: v } });
const initialState = mockedComponent.state();
const testMessage = '__TEST__EVENT__MESSAGE__';
const changed = () => {
causeChange(mockedComponent, testMessage);
return waitForIt(() => mockedComponent);
};
const afterInput = await changed();
assert(
initialState.input === '' &&
afterInput.state().input === '__TEST__EVENT__MESSAGE__'
);
};
```
單擊 `Add message` 按鈕應調用 `submitMessage` 方法,添加當前 `input` 到狀態中的 `messages` 數組。
```js
async () => {
const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
const causeChange = (c, v) =>
c.find('input').simulate('change', { target: { value: v } });
const initialState = mockedComponent.state();
const testMessage_1 = '__FIRST__MESSAGE__';
const firstChange = () => {
causeChange(mockedComponent, testMessage_1);
return waitForIt(() => mockedComponent);
};
const firstResult = await firstChange();
const firstSubmit = () => {
mockedComponent.find('button').simulate('click');
return waitForIt(() => mockedComponent);
};
const afterSubmit_1 = await firstSubmit();
const submitState_1 = afterSubmit_1.state();
const testMessage_2 = '__SECOND__MESSAGE__';
const secondChange = () => {
causeChange(mockedComponent, testMessage_2);
return waitForIt(() => mockedComponent);
};
const secondResult = await secondChange();
const secondSubmit = () => {
mockedComponent.find('button').simulate('click');
return waitForIt(() => mockedComponent);
};
const afterSubmit_2 = await secondSubmit();
const submitState_2 = afterSubmit_2.state();
assert(
initialState.messages.length === 0 &&
submitState_1.messages.length === 1 &&
submitState_2.messages.length === 2 &&
submitState_2.messages[1] === testMessage_2
);
};
```
`submitMessage` 方法應清除當前輸入。
```js
async () => {
const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages));
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
const causeChange = (c, v) =>
c.find('input').simulate('change', { target: { value: v } });
const initialState = mockedComponent.state();
const testMessage = '__FIRST__MESSAGE__';
const firstChange = () => {
causeChange(mockedComponent, testMessage);
return waitForIt(() => mockedComponent);
};
const firstResult = await firstChange();
const firstState = firstResult.state();
const firstSubmit = () => {
mockedComponent.find('button').simulate('click');
return waitForIt(() => mockedComponent);
};
const afterSubmit = await firstSubmit();
const submitState = afterSubmit.state();
assert(firstState.input === testMessage && submitState.input === '');
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<DisplayMessages />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class DisplayMessages extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
}
// Add handleChange() and submitMessage() methods here
render() {
return (
<div>
<h2>Type in a new Message:</h2>
{ /* Render an input, button, and ul below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
class DisplayMessages extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.setState((state) => {
const currentMessage = state.input;
return {
input: '',
messages: state.messages.concat(currentMessage)
};
});
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.state.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
```

View File

@@ -0,0 +1,107 @@
---
id: 5a24c314108439a4d4036146
title: 映射 Dispatch 到 Props
challengeType: 6
forumTopicId: 301432
dashedName: map-dispatch-to-props
---
# --description--
`mapDispatchToProps()` 函數可爲 React 組件提供特定的創建 action 的函數,以便組件可 dispatch actions從而更改 Redux store 中的數據。 該函數的結構跟上一挑戰中的`mapStateToProps()`函數相似, 它返回一個對象,把 dispatch actions 映射到屬性名上,該屬性名成爲`props`。 然而,每個屬性都返回一個用 action creator 及與 action 相關的所有數據調用 `dispatch` 的函數,而不是返回 `state` 的一部分。 可以訪問 `dispatch`,因爲在定義函數時,我們以參數形式把它傳入 `mapDispatchToProps()` 了,這跟 `state` 傳入 `mapStateToProps()` 是一樣的。 在幕後React Redux 用 Redux 的 `store.dispatch()` 來管理這些含 `mapDispatchToProps()` 的dispatches 這跟它使用 `store.subscribe()` 來訂閱映射到 `state` 的組件的方式類似。
例如,創建 action 的函數 `loginUser()``username` 作爲 action payload `mapDispatchToProps()` 返回給創建 action 的函數的對象如下:
```jsx
{
submitLoginUser: function(username) {
dispatch(loginUser(username));
}
}
```
# --instructions--
編輯器上提供的是創建 action 的函數 `addMessage()`。 寫出接收 `dispatch` 爲參數的函數 `mapDispatchToProps()`,返回一個 dispatch 函數對象, 其屬性爲 `submitNewMessage`。該函數在 dispatch `addMessage()` 時爲新消息提供一個參數。
# --hints--
`addMessage` 應返回含 `type``message` 兩個鍵的對象。
```js
assert(
(function () {
const addMessageTest = addMessage();
return (
addMessageTest.hasOwnProperty('type') &&
addMessageTest.hasOwnProperty('message')
);
})()
);
```
`mapDispatchToProps` 應爲函數。
```js
assert(typeof mapDispatchToProps === 'function');
```
`mapDispatchToProps` 應返回一個對象。
```js
assert(typeof mapDispatchToProps() === 'object');
```
`mapDispatchToProps` 通過 `submitNewMessage` 分發 `addMessage`,應向 dispatch 函數返回一條消息。
```js
assert(
(function () {
let testAction;
const dispatch = (fn) => {
testAction = fn;
};
let dispatchFn = mapDispatchToProps(dispatch);
dispatchFn.submitNewMessage('__TEST__MESSAGE__');
return (
testAction.type === 'ADD' && testAction.message === '__TEST__MESSAGE__'
);
})()
);
```
# --seed--
## --seed-contents--
```jsx
const addMessage = (message) => {
return {
type: 'ADD',
message: message
}
};
// Change code below this line
```
# --solutions--
```jsx
const addMessage = (message) => {
return {
type: 'ADD',
message: message
}
};
// Change code below this line
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: function(message) {
dispatch(addMessage(message));
}
}
};
```

View File

@@ -0,0 +1,69 @@
---
id: 5a24c314108439a4d4036145
title: 映射 State 到 Props
challengeType: 6
forumTopicId: 301433
dashedName: map-state-to-props
---
# --description--
`Provider`可向 React 組件提供 `state``dispatch` ,但必須確切地指定所需要的 state 和 actions 以確保每個組件只能訪問所需的 state。 完成這個任務,需要創建兩個函數:`mapStateToProps()``mapDispatchToProps()`
在這兩個函數中,聲明 state 中函數所要訪問的部分及需要 dispatch 的創建 action 的函數。 完成這些,我們就可以迎接下一個挑戰,學習如何使用 React Redux 的 `connect` 方法來把函數連接到組件了。
**注意:** 在幕後React Redux 用 `store.subscribe()` 方法來實現 `mapStateToProps()`
# --instructions--
創建 `mapStateToProps()` 函數, 以 `state` 爲參數,然後返回一個對象,該對象把 state 映射到特定屬性名上, 這些屬性能通過 `props` 訪問組件。 由於此示例把 app 應用的整個狀態保存在單一數組中,可把整個狀態傳給組件。 在返回的對象中創建 `messages` 屬性,並設置爲 `state`
# --hints--
常量 `state` 應爲空數組。
```js
assert(Array.isArray(state) && state.length === 0);
```
`mapStateToProps` 應爲函數。
```js
assert(typeof mapStateToProps === 'function');
```
`mapStateToProps` 應返回一個對象。
```js
assert(typeof mapStateToProps() === 'object');
```
把 state 數組傳入 `mapStateToProps` 後應返回賦值給 `messages` 鍵的數組。
```js
assert(mapStateToProps(['messages']).messages.pop() === 'messages');
```
# --seed--
## --seed-contents--
```jsx
const state = [];
// Change code below this line
```
# --solutions--
```jsx
const state = [];
// Change code below this line
const mapStateToProps = (state) => {
return {
messages: state
}
};
```

View File

@@ -0,0 +1,69 @@
---
id: 5a24c314108439a4d403614a
title: 從這裏前進
challengeType: 6
forumTopicId: 301434
dashedName: moving-forward-from-here
---
# --description--
恭喜! 你完成了 React 和 Redux 的所有課程! 結束之前,還要再提一點。 通常,我們不會在這樣的編輯器中編寫 React 應用代碼。 如果你在自己的計算機上使用 npm 和文件系統,這個挑戰可讓你一瞥 React 應用的語法之貌。 除了使用 `import` 語句(這些語句引入了各挑戰中提供的所有依賴關係),其代碼看起來類似。 “管理包(含 npm”這一節更詳細地介紹了 npm。
最後,寫 React 和 Redux 的代碼通常需要一些配置, 且很快會變得複雜起來。 如果你有興趣在自己的電腦上嘗試,<a href="https://github.com/facebookincubator/create-react-app" target="_blank" rel="nofollow">Create React App</a> 已配置好,並準備就緒。
或者,你可以在 CodePen 中啓用 Babel 作爲 JavaScript 預處理器,將 React 和 ReactDOM 添加爲外部 JavaScript 資源,這樣編寫應用。
# --instructions--
在控制檯輸出消息 `'Now I know React and Redux!'`
# --hints--
應該將 `Now I know React and Redux!` 這一消息應輸出到控制檯。
```js
(getUserInput) =>
assert(
/console\s*\.\s*log\s*\(\s*('|"|`)Now I know React and Redux!\1\s*\)/.test(
getUserInput('index')
)
);
```
# --seed--
## --seed-contents--
```jsx
/*
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider, connect } from 'react-redux'
import { createStore, combineReducers, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import rootReducer from './redux/reducers'
import App from './components/App'
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>,
document.getElementById('root')
);
*/
// Only change code below this line
```
# --solutions--
```jsx
console.log('Now I know React and Redux!');
```

View File

@@ -0,0 +1,263 @@
---
id: 5a24c314108439a4d4036144
title: 使用 Provider 連接 Redux 和 React
challengeType: 6
forumTopicId: 301435
dashedName: use-provider-to-connect-redux-to-react
---
# --description--
在上一挑戰中,創建了 Redux store 和 action分別用於處理消息數組和添加新消息。 下一步要爲 React 提供訪問 Redux store 及發起更新所需的 actions。 `react-redux` 包可幫助我們完成這些任務。
React Redux 提供的 API 有兩個關鍵的功能:`Provider``connect`。 會在另一個挑戰會介紹 `connect``Provider`是 React Redux 包裝 React 應用的 wrapper 組件, 它允許訪問整個組件樹中的 Redux `store``dispatch`(分發)方法。 `Provider` 需要兩個 propsRedux store 和 App 應用的子組件。 用於 App 組件的 `Provider` 可這樣定義:
```jsx
<Provider store={store}>
<App/>
</Provider>
```
# --instructions--
此時,編輯器上顯示的是過去幾個挑戰中所有代碼, 包括 Redux store、actions、`DisplayMessages` 組件。 新出現的代碼是底部的`AppWrapper`組件, 這個頂級組件可用於渲染 `ReactRedux``Provider`,並把 Redux 的 store 作爲 props 傳入。 接着,渲染 `DisplayMessages` 爲子組件。 完成這些任務後,會看到 React 組件渲染到頁面上。
**注意:** React Redux 在此可作全局變量,因此可通過點號表示法訪問 Provider。 利用這一點,編輯器上的代碼把 `Provider` 設置爲常量,便於你在 `AppWrapper` 渲染方法中使用。
# --hints--
`AppWrapper` 應渲染。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
return mockedComponent.find('AppWrapper').length === 1;
})()
);
```
`Provider` 組件應傳入相當於 Redux store 的 `store` 參數。
```js
(getUserInput) =>
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
return __helpers
.removeWhiteSpace(getUserInput('index'))
.includes('<Providerstore={store}>');
})()
);
```
`DisplayMessages` 應渲染爲 `AppWrapper` 的子組件。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
return (
mockedComponent.find('AppWrapper').find('DisplayMessages').length === 1
);
})()
);
```
`DisplayMessages` 組件應渲染 `h2``input``button``ul` 四個元素。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
return (
mockedComponent.find('div').length === 1 &&
mockedComponent.find('h2').length === 1 &&
mockedComponent.find('button').length === 1 &&
mockedComponent.find('ul').length === 1
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<AppWrapper />, document.getElementById('root'))
```
## --seed-contents--
```jsx
// Redux:
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
// React:
class DisplayMessages extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.setState((state) => {
const currentMessage = state.input;
return {
input: '',
messages: state.messages.concat(currentMessage)
};
});
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.state.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
const Provider = ReactRedux.Provider;
class AppWrapper extends React.Component {
// Render the Provider below this line
// Change code above this line
};
```
# --solutions--
```jsx
// Redux:
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
// React:
class DisplayMessages extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.setState((state) => {
const currentMessage = state.input;
return {
input: '',
messages: state.messages.concat(currentMessage)
};
});
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.state.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
const Provider = ReactRedux.Provider;
class AppWrapper extends React.Component {
// Change code below this line
render() {
return (
<Provider store = {store}>
<DisplayMessages/>
</Provider>
);
}
// Change code above this line
};
```

View File

@@ -0,0 +1,165 @@
---
id: 5a24c314108439a4d403616e
title: 使用 this.props 訪問 Props
challengeType: 6
forumTopicId: 301375
dashedName: access-props-using-this-props
---
# --description--
前幾項挑戰涵蓋了將 props 傳遞給子組件的基本方法。 但是,倘若接收 prop 的子組件不是無狀態函數組件,而是一個 ES6 類組件又當如何呢? ES6 類組件訪問 props 的方法略有不同。
任何時候,如果要引用類組件本身,可以使用 `this` 關鍵字。 要訪問類組件中的 props需要在在訪問它的代碼前面添加 `this`。 例如,如果 ES6 類組件有一個名爲 `data` 的 prop可以在 JSX 中這樣寫:`{this.props.data}`
# --instructions--
在父組件 `ResetPassword` 中渲染 `ReturnTempPassword` 組件的一個實例。 在這裏,爲 `ReturnTempPassword` 提供一個 `tempPassword` prop並賦值一個長度至少爲 8 個字符的字符串。 在子組件 `ReturnTempPassword` 中,訪問 `strong` 標籤中的 `tempPassword` prop以確保用戶看到臨時密碼。
# --hints--
`ResetPassword` 組件應該返回單個 `div` 元素。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ResetPassword));
return mockedComponent.children().type() === 'div';
})()
);
```
`ResetPassword` 的第四個子組件應該是 `ReturnTempPassword` 組件。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ResetPassword));
return (
mockedComponent.children().childAt(3).name() === 'ReturnTempPassword'
);
})()
);
```
`ReturnTempPassword` 組件應該有一個名爲 `tempPassword` 的屬性。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ResetPassword));
return mockedComponent.find('ReturnTempPassword').props().tempPassword;
})()
);
```
`ReturnTempPassword` 組件的 `tempPassword` prop 值應該是一個字符串,至少爲 8 個字符。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ResetPassword));
const temp = mockedComponent.find('ReturnTempPassword').props()
.tempPassword;
return typeof temp === 'string' && temp.length >= 8;
})()
);
```
`ReturnTempPassword` 組件應該顯示作爲 `tempPassword` prop 創建的密碼,並且密碼被包裹在 `strong` 標籤中。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ResetPassword));
return (
mockedComponent.find('strong').text() ===
mockedComponent.find('ReturnTempPassword').props().tempPassword
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<ResetPassword />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class ReturnTempPassword extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{ /* Change code below this line */ }
<p>Your temporary password is: <strong></strong></p>
{ /* Change code above this line */ }
</div>
);
}
};
class ResetPassword extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h2>Reset Password</h2>
<h3>We've generated a new temporary password for you.</h3>
<h3>Please reset this password from your account settings ASAP.</h3>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
class ReturnTempPassword extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<p>Your temporary password is: <strong>{this.props.tempPassword}</strong></p>
</div>
);
}
};
class ResetPassword extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h2>Reset Password</h2>
<h3>We've generated a new temporary password for you.</h3>
<h3>Please reset this password from your account settings ASAP.</h3>
{ /* Change code below this line */ }
<ReturnTempPassword tempPassword="serrPbqrPnzc" />
{ /* Change code above this line */ }
</div>
);
}
};
```

View File

@@ -0,0 +1,82 @@
---
id: 5a24bbe0dba28a8d3cbd4c5e
title: 在 JSX 中添加註釋
challengeType: 6
forumTopicId: 301376
dashedName: add-comments-in-jsx
---
# --description--
JSX 是一種可以編譯成 JavaScript 的語法。 有時,爲了便於閱讀,可能需要在代碼中添加註釋。 像大多數編程語言一樣JSX 也有自己的方法來實現這一點。
要將註釋放在 JSX 中,可以使用 `{/* */}` 語法來包裹註釋文本。
# --instructions--
代碼編輯器中的 JSX 元素與在上一個挑戰中創建的元素類似。 在提供的 `div` 元素裏添加註釋,不修改現有的 `h1``p` 元素。
# --hints--
常量 `JSX` 應該返回一個 `div` 元素。
```js
assert(JSX.type === 'div');
```
`div` 應該包含一個 `h1` 標籤作爲第一個元素。
```js
assert(JSX.props.children[0].type === 'h1');
```
`div` 應該包含一個 `p` 標籤作爲第二個元素。
```js
assert(JSX.props.children[1].type === 'p');
```
當前的 `h1``p` 元素不能被修改。
```js
assert(
JSX.props.children[0].props.children === 'This is a block of JSX' &&
JSX.props.children[1].props.children === "Here's a subtitle"
);
```
`JSX` 應該包含一個註釋。
```js
assert(/<div>[\s\S]*{\s*\/\*[\s\S]*\*\/\s*}[\s\S]*<\/div>/.test(code));
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(JSX, document.getElementById('root'))
```
## --seed-contents--
```jsx
const JSX = (
<div>
<h1>This is a block of JSX</h1>
<p>Here's a subtitle</p>
</div>
);
```
# --solutions--
```jsx
const JSX = (
<div>
<h1>This is a block of JSX</h1>
{ /* this is a JSX comment */ }
<p>Here's a subtitle</p>
</div>);
```

View File

@@ -0,0 +1,179 @@
---
id: 5a24c314108439a4d403617e
title: 添加事件偵聽器
challengeType: 6
forumTopicId: 301377
dashedName: add-event-listeners
---
# --description--
`componentDidMount()` 方法也是添加特定功能所需的任何事件監聽器的最佳位置。 React 提供了一個合成事件系統,它封裝了瀏覽器中的事件系統。 這意味着,不管用戶用的是什麼瀏覽器,合成事件系統的行爲都完全相同 -- 即使不同瀏覽器之間的本地事件的行爲可能不同。
之前已經接觸了一些合成事件處理程序,如`onClick()`。 React 的合成事件系統非常適合用於在 DOM 元素上管理的大多數交互。 但是,如果要將事件處理程序附加到 document 或 window 對象,則必須直接執行此操作。
# --instructions--
`componentDidMount()` 方法中爲 `keydown` 事件添加事件監聽器,並讓這些事件觸發回調 `handleKeyPress()`。 可以使用 `document.addEventListener()`,它將事件(用引號括起來)作爲第一個參數,將回調作爲第二個參數。
然後,在 `componentWillUnmount()` 中移除相同的事件監聽器。 可以把相同的參數傳遞給 `document.removeEventListener()`。 在卸載和銷燬 React 組件之前,最好在這個生命週期方法中對它們進行清理。 移除事件監聽器就是這樣一個清理操作的例子。
# --hints--
`MyComponent` 應該渲染一個包含 `h1` 標籤的 `div` 元素。
```js
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.find('div').children().find('h1').length === 1;
})()
);
```
應該在 `componentDidMount` 中將 `keydown` 事件監聽添加到到 document 上。
```js
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const didMountString = mockedComponent
.instance()
.componentDidMount.toString();
return new RegExp(
'document.addEventListener(.|\n|\r)+keydown(.|\n|\r)+this.handleKeyPress'
).test(didMountString);
})()
);
```
應該在 `componentWillUnmount` 中將 document 上的 `keydown` 事件監聽移除。
```js
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const willUnmountString = mockedComponent
.instance()
.componentWillUnmount.toString();
return new RegExp(
'document.removeEventListener(.|\n|\r)+keydown(.|\n|\r)+this.handleKeyPress'
).test(willUnmountString);
})()
);
```
當組件裝載完畢,按 `enter` 鍵應該會更新其 state ,並渲染到 `h1` 標籤。
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const beforeState = mockedComponent.state('message');
const beforeText = mockedComponent.find('h1').text();
const pressEnterKey = () => {
mockedComponent.instance().handleKeyPress({ keyCode: 13 });
return waitForIt(() => {
mockedComponent.update();
return {
state: mockedComponent.state('message'),
text: mockedComponent.find('h1').text()
};
});
};
const afterKeyPress = await pressEnterKey();
assert(
beforeState !== afterKeyPress.state && beforeText !== afterKeyPress.text
);
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
message: ''
};
this.handleEnter = this.handleEnter.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
}
// Change code below this line
componentDidMount() {
}
componentWillUnmount() {
}
// Change code above this line
handleEnter() {
this.setState((state) => ({
message: state.message + 'You pressed the enter key! '
}));
}
handleKeyPress(event) {
if (event.keyCode === 13) {
this.handleEnter();
}
}
render() {
return (
<div>
<h1>{this.state.message}</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
message: ''
};
this.handleKeyPress = this.handleKeyPress.bind(this);
this.handleEnter = this.handleEnter.bind(this); }
componentDidMount() {
// Change code below this line
document.addEventListener('keydown', this.handleKeyPress);
// Change code above this line
}
componentWillUnmount() {
// Change code below this line
document.removeEventListener('keydown', this.handleKeyPress);
// Change code above this line
}
handleEnter() {
this.setState((state) => ({
message: state.message + 'You pressed the enter key! '
}));
}
handleKeyPress(event) {
if (event.keyCode === 13) {
this.handleEnter();
}
}
render() {
return (
<div>
<h1>{this.state.message}</h1>
</div>
);
}
};
```

View File

@@ -0,0 +1,112 @@
---
id: 5a24c314108439a4d4036182
title: 在 React 中添加內聯樣式
challengeType: 6
forumTopicId: 301378
dashedName: add-inline-styles-in-react
---
# --description--
在上一次挑戰中,你可能已經注意到,除了設置爲 JavaScript 對象的 `style` 屬性之外,與 HTML 內聯樣式相比React 的內聯樣式還有其他幾個語法差異。 首先,某些 CSS 樣式屬性的名稱使用駝峯式命名。 例如,最後一個挑戰用 `fontSize` 而不是 `font-size` 來設置字體的大小。 對於 JavaScript 對象屬性來說,像 `font-size` 這樣的連字符命名是無效的語法,所以 React 使用駝峯式命名。 通常,任何連字符的 style 屬性在 JSX 中都是使用駝峯式命名的。
除非另有規定,否則所有屬性值的 length`height``width``fontSize`)其單位都假定爲 `px`。 例如,如果要使用 `em`,可以用引號將值和單位括起來,例如 `{fontSize: "4em"}`。 除了默認爲 `px` 的 length 值之外,所有其他屬性值都應該用引號括起來。
# --instructions--
如果你有大量樣式,你可以將樣式 `object`(對象)分配給一個常量,以保持代碼的組織有序。 在文件頂部將你的樣式聲明爲全局變量。 定義一個 `styles` 常量,並將其聲明爲具有三個樣式屬性及對應值的 `object`(對象)。 使 `div` 的文字顏色爲 `purple`、字號爲 `40`、邊框爲 `2px solid purple`。 然後設置 `style` 屬性,使其等於 `styles` 常量。
# --hints--
`styles` 變量應該是具有三個屬性的 `object`(對象)。
```js
assert(Object.keys(styles).length === 3);
```
`styles` 變量的 `color` 屬性應該設置爲 `purple`
```js
assert(styles.color === 'purple');
```
`styles` 變量應該將 `fontSize` 屬性設置爲 `40`
```js
assert(styles.fontSize === 40);
```
`styles` 變量的 `border` 屬性應該設置爲 `2px solid purple`
```js
assert(styles.border === '2px solid purple');
```
組件應該渲染一個 `div` 元素。
```js
assert(
(function () {
const mockedComponent = Enzyme.shallow(React.createElement(Colorful));
return mockedComponent.type() === 'div';
})()
);
```
`div` 元素的樣式應該由 `styles` 對象定義。
```js
assert(
(function () {
const mockedComponent = Enzyme.shallow(React.createElement(Colorful));
return (
mockedComponent.props().style.color === 'purple' &&
mockedComponent.props().style.fontSize === 40 &&
mockedComponent.props().style.border === '2px solid purple'
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<Colorful />, document.getElementById('root'))
```
## --seed-contents--
```jsx
// Change code above this line
class Colorful extends React.Component {
render() {
// Change code below this line
return (
<div style={{color: "yellow", fontSize: 24}}>Style Me!</div>
);
// Change code above this line
}
};
```
# --solutions--
```jsx
const styles = {
color: "purple",
fontSize: 40,
border: "2px solid purple"
};
// Change code above this line
class Colorful extends React.Component {
render() {
// Change code below this line
return (
<div style={styles}>Style Me!</div>
);
// Change code above this line
}
};
```

View File

@@ -0,0 +1,136 @@
---
id: 5a24c314108439a4d4036174
title: 將 this 綁定到 Class 方法上
challengeType: 6
forumTopicId: 301379
dashedName: bind-this-to-a-class-method
---
# --description--
除了設置和更新 `state` 之外,還可以爲組件類定義方法。 類方法通常需要使用 `this` 關鍵字,以便它可以訪問方法中類的屬性(例如 `state``props`)。 有幾種方法可以讓類方法訪問 `this`
一種常見的方法是在構造函數中顯式地綁定 `this`,這樣當組件初始化時,`this` 就會綁定到類方法。 你可能已經注意到上一個挑戰在構造函數中的 `handleClick` 方法使用了 `this.handleClick = this.handleClick.bind(this)`。 然後,當在類方法中調用像 `this.setState()` 這樣的函數時,`this` 指的是這個類,而不是 `undefined`
**注意:** `this`關鍵字是 JavaScript 中最令人困惑的方面之一,但它在 React 中扮演着重要的角色。 雖然它的行爲在這裏是完全正常的,但是這些課程並不深入研究`this`,所以如果以上內容令你感到困惑,請參考其他課程!
# --instructions--
代碼編輯器有一個帶有 `state` 的組件,用於跟蹤項目計數。 它還有一個方法,允許設置文本爲 `You clicked!`。 但是,該方法不起作用,因爲它使用了未定義的 `this` 關鍵字。 可以通過將 `this` 顯式綁定到組件構造函數中的 `handleClick()`方法來修復它。
接下來,向 render 方法中的 `button` 元素添加一個單擊處理程序。 當按鈕接收到單擊事件時,它應該觸發 `handleClick()` 方法。 記住,傳遞給 `onClick` 處理程序的方法需要使用花括號,因爲它應該直接被解釋爲 JavaScript。
完成上述步驟後,可以單擊按鈕並看到 `You clicked!`
# --hints--
`MyComponent` 應返回 `div` 元素,該元素按順序包含兩個元素,一個按鈕和一個 `h1` 元素。
```js
assert(
Enzyme.mount(React.createElement(MyComponent)).find('div').length === 1 &&
Enzyme.mount(React.createElement(MyComponent))
.find('div')
.childAt(0)
.type() === 'button' &&
Enzyme.mount(React.createElement(MyComponent))
.find('div')
.childAt(1)
.type() === 'h1'
);
```
`MyComponent` 的 state 應該使用鍵值對 `{ text: "Hello" }`,進行初始化。
```js
assert(
Enzyme.mount(React.createElement(MyComponent)).state('text') === 'Hello'
);
```
單擊 `button` 元素應該運行 `handleClick` 方法,並使 state `text``You clicked!`
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const first = () => {
mockedComponent.setState({ text: 'Hello' });
return waitForIt(() => mockedComponent.state('text'));
};
const second = () => {
mockedComponent.find('button').simulate('click');
return waitForIt(() => mockedComponent.state('text'));
};
const firstValue = await first();
const secondValue = await second();
assert(firstValue === 'Hello' && secondValue === 'You clicked!');
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "Hello"
};
// Change code below this line
// Change code above this line
}
handleClick() {
this.setState({
text: "You clicked!"
});
}
render() {
return (
<div>
{ /* Change code below this line */ }
<button>Click Me</button>
{ /* Change code above this line */ }
<h1>{this.state.text}</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "Hello"
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
text: "You clicked!"
});
}
render() {
return (
<div>
<button onClick = {this.handleClick}>Click Me</button>
<h1>{this.state.text}</h1>
</div>
);
}
};
```

View File

@@ -0,0 +1,176 @@
---
id: 5a24c314108439a4d4036189
title: 根據組件狀態有條件地更改內聯 CSS
challengeType: 6
forumTopicId: 301380
dashedName: change-inline-css-conditionally-based-on-component-state
---
# --description--
此時,已經看到了一些條件渲染的應用程序和內聯樣式的使用。 這裏還有一個將這兩個主題結合在一起的例子。 你也可以根據 React 組件的 state 有條件地渲染 CSS。 要執行此操作,請檢查條件,如果滿足該條件,則修改在 render 方法中分配給 JSX 元素的樣式對象。
理解這個模式很重要,因爲相比傳統的方式(這在 jQuery 中非常常見),直接修改 DOM 元素來應用樣式的方法是一個戲劇性的轉變。 在該方法中,必須跟蹤元素何時更改並直接處理實際操作。 跟蹤更改可能變得很困難,可能會使 UI無法預測。 當根據一個條件設置一個樣式對象時,描述了 UI 作爲應用程序的狀態函數應當如何展現。 如此便有一個清晰的單向流動的信息流。 這是使用 React 編寫應用程序時的首選方法。
# --instructions--
代碼編輯器有一個簡單的帶有邊框樣式的受控 input 組件。 如果用戶在輸入框中鍵入超過 15 個字符的文本,希望將此邊框變成紅色。 添加一個條件來檢查這一點,如果條件有效,則將 input 的邊框樣式設置爲`3px solid red`。 可以通過在 input 中輸入文本來檢測它。
# --hints--
`GateKeeper` 組件應該渲染一個 `div` 元素。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
return mockedComponent.find('div').length === 1;
})()
);
```
`GateKeeper` 組件應使用初始爲空字符串的 `input` 進行初始化 state。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
return mockedComponent.state().input === '';
})()
);
```
`GateKeeper` 組件應該渲染一個 `h3` 標籤和一個 `input` 標籤。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
return (
mockedComponent.find('h3').length === 1 &&
mockedComponent.find('input').length === 1
);
})()
);
```
`input` 標籤 `border` 屬性的樣式應該初始化爲 `1px solid black`
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
return (
mockedComponent.find('input').props().style.border === '1px solid black'
);
})()
);
```
如果 state 中 input 的值超過 15 個字符,則 `input` 標籤的 border 樣式應爲 `3px solid red`
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
const simulateChange = (el, value) =>
el.simulate('change', { target: { value } });
let initialStyle = mockedComponent.find('input').props().style.border;
const state_1 = () => {
mockedComponent.setState({ input: 'this is 15 char' });
return waitForIt(() => mockedComponent.find('input').props().style.border);
};
const state_2 = () => {
mockedComponent.setState({
input: 'A very long string longer than 15 characters.'
});
return waitForIt(() => mockedComponent.find('input').props().style.border);
};
const style_1 = await state_1();
const style_2 = await state_2();
assert(
initialStyle === '1px solid black' &&
style_1 === '1px solid black' &&
style_2 === '3px solid red'
);
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<GateKeeper />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class GateKeeper extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({ input: event.target.value })
}
render() {
let inputStyle = {
border: '1px solid black'
};
// Change code below this line
// Change code above this line
return (
<div>
<h3>Don't Type Too Much:</h3>
<input
type="text"
style={inputStyle}
value={this.state.input}
onChange={this.handleChange} />
</div>
);
}
};
```
# --solutions--
```jsx
class GateKeeper extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({ input: event.target.value })
}
render() {
let inputStyle = {
border: '1px solid black'
};
if (this.state.input.length > 15) {
inputStyle.border = '3px solid red';
};
return (
<div>
<h3>Don't Type Too Much:</h3>
<input
type="text"
style={inputStyle}
value={this.state.input}
onChange={this.handleChange} />
</div>
);
}
};
```

View File

@@ -0,0 +1,199 @@
---
id: 5a24c314108439a4d4036166
title: 組合 React 組件
challengeType: 6
forumTopicId: 301381
dashedName: compose-react-components
---
# --description--
隨着挑戰繼續,將組合使用更復雜的 React 組件和 JSX有一點需要注意。 在其它組件中渲染 ES6 風格的類組件和渲染在過去幾個挑戰中使用的簡單組件沒有什麼不同。 可以在其它組件中渲染 JSX 元素、無狀態功能組件和 ES6 類組件。
# --instructions--
在代碼編輯器中,`TypesOfFood` 組件已經渲染了一個名爲 `Vegetables` 的組件。 此外,還有上次挑戰中的 `Fruits` 組件。
`Fruits` 中嵌套兩個組件,首先 `NonCitrus`,然後是 `Citrus` 這兩個組件都已經引入。 接下來,將 `Fruits` 類組件嵌到 `TypesOfFood` 組件中,位於 `h1` 標題下方和 `Vegetables` 上方。 結果應該是一系列嵌套的組件,它們使用兩種不同的組件類型。
# --hints--
`TypesOfFood` 組件應該返回單個 `div` 元素。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return mockedComponent.children().type() === 'div';
})()
);
```
`TypesOfFood` 組件應該返回 `Fruits` 組件。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return mockedComponent.children().childAt(1).name() === 'Fruits';
})()
);
```
`Fruits` 組件應該返回 `NonCitrus` 組件和 `Citrus` 組件。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return (
mockedComponent.find('Fruits').children().find('NonCitrus').length ===
1 &&
mockedComponent.find('Fruits').children().find('Citrus').length === 1
);
})()
);
```
`TypesOfFood` 組件應該返回 `Vegetables` 組件,且其位於 `Fruits` 組件之下。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return mockedComponent.children().childAt(2).name() === 'Vegetables';
})()
);
```
# --seed--
## --before-user-code--
```jsx
class NonCitrus extends React.Component {
render() {
return (
<div>
<h4>Non-Citrus:</h4>
<ul>
<li>Apples</li>
<li>Blueberries</li>
<li>Strawberries</li>
<li>Bananas</li>
</ul>
</div>
);
}
};
class Citrus extends React.Component {
render() {
return (
<div>
<h4>Citrus:</h4>
<ul>
<li>Lemon</li>
<li>Lime</li>
<li>Orange</li>
<li>Grapefruit</li>
</ul>
</div>
);
}
};
class Vegetables extends React.Component {
render() {
return (
<div>
<h2>Vegetables:</h2>
<ul>
<li>Brussel Sprouts</li>
<li>Broccoli</li>
<li>Squash</li>
</ul>
</div>
);
}
};
```
## --after-user-code--
```jsx
ReactDOM.render(<TypesOfFood />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class Fruits extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h2>Fruits:</h2>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
class TypesOfFood extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Types of Food:</h1>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
<Vegetables />
</div>
);
}
};
```
# --solutions--
```jsx
class Fruits extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h2>Fruits:</h2>
{ /* Change code below this line */ }
<NonCitrus />
<Citrus />
{ /* Change code above this line */ }
</div>
)
}
}
class TypesOfFood extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Types of Food:</h1>
{ /* Change code below this line */ }
<Fruits />
{ /* Change code above this line */ }
<Vegetables />
</div>
);
}
};
```

View File

@@ -0,0 +1,110 @@
---
id: 5a24bbe0dba28a8d3cbd4c5d
title: 創建一個複雜的 JSX 元素
challengeType: 6
forumTopicId: 301382
dashedName: create-a-complex-jsx-element
---
# --description--
上一個挑戰是 JSX 的一個簡單示例,但 JSX 也可以表示更復雜的 HTML。
關於嵌套的 JSX需要知道的一件重要的事情那就是它必須返回單個元素。
這個父元素將包裹所有其他級別的嵌套元素。
例如,幾個作爲兄弟元素編寫的 JSX 元素而沒有父元素包裹將不會被轉換。
這裏是一個示例:
**有效的 JSX**
```jsx
<div>
<p>Paragraph One</p>
<p>Paragraph Two</p>
<p>Paragraph Three</p>
</div>
```
**無效的 JSX**
```jsx
<p>Paragraph One</p>
<p>Paragraph Two</p>
<p>Paragraph Three</p>
```
# --instructions--
定義一個新的常量 `JSX`,渲染一個 `div`,其中依次包含以下元素:
一個 `h1`,一個 `p`,一個包含三個 `li` 項的無序列表。 可以在每個元素中包含任意文本。
**注意:** 當像這樣渲染多個元素時,可以把它們都用圓括號括起來,但是這並不是必須的。 另外,此挑戰使用 `div` 標籤把所有子元素包裹在裏面。 如果刪除 `div`JSX 將不會編譯這些元素。 請記住這一點,因爲在 React 組件中返回 JSX 元素時也適用。
# --hints--
常量 `JSX` 應該返回一個 `div` 元素。
```js
assert(JSX.type === 'div');
```
`div` 應該包含一個 `h1` 標籤作爲第一個元素。
```js
assert(JSX.props.children[0].type === 'h1');
```
`div`應該包含一個`p`標籤作爲第二個元素。
```js
assert(JSX.props.children[1].type === 'p');
```
`div` 應該包含一個 `ul` 標籤作爲第三個元素。
```js
assert(JSX.props.children[2].type === 'ul');
```
`ul` 應該包含三個 `li` 元素。
```js
assert(
JSX.props.children
.filter((ele) => ele.type === 'ul')[0]
.props.children.filter((ele) => ele.type === 'li').length === 3
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(JSX, document.getElementById('root'))
```
## --seed-contents--
```jsx
```
# --solutions--
```jsx
const JSX = (
<div>
<h1>Hello JSX!</h1>
<p>Some info</p>
<ul>
<li>An item</li>
<li>Another item</li>
<li>A third item</li>
</ul>
</div>);
```

View File

@@ -0,0 +1,134 @@
---
id: 5a24c314108439a4d4036164
title: 用組合的方式創建一個 React 組件
challengeType: 6
forumTopicId: 301383
dashedName: create-a-component-with-composition
---
# --description--
現在來看看如何組合多個 React 組件。 想象一下,現在正在構建一個應用程序,並創建了三個組件:`Navbar``Dashboard``Footer`
要將這些組件組合在一起,可以創建一個 `App` *父組件*,將這三個組件分別渲染成爲*子組件*。 要在 React 組件中渲染一個子組件,需要在 JSX 中包含作爲自定義 HTML 標籤編寫的組件名稱。 例如,在 `render` 方法中,可以這樣編寫:
```jsx
return (
<App>
<Navbar />
<Dashboard />
<Footer />
</App>
)
```
當 React 遇到一個自定義 HTML 標籤引用另一個組件的時(如本例所示,組件名稱包含在 `< />` 中),它在自定義標籤的位置渲染該組件的標籤。 這可以說明 `App` 組件和 `Navbar``Dashboard` 以及 `Footer` 之間的父子關係。
# --instructions--
在代碼編輯器中,有一個名爲 `ChildComponent` 的簡單功能組件和一個名爲 `ParentComponent` 的 React 組件。 通過在 `ParentComponent` 中渲染 `ChildComponent` 來將兩者組合在一起。 確保使用正斜槓關閉 `ChildComponent` 標籤。
**注意:** `ChildComponent` 是使用 ES6 的箭頭函數定義的,這是使用 React 時非常常見的做法。 但是,要知道這只是一個函數。 如果你不熟悉箭頭函數語法,請參閱 JavaScript 部分。
# --hints--
React 組件應該返回單個 `div` 元素。
```js
assert(
(function () {
var shallowRender = Enzyme.shallow(React.createElement(ParentComponent));
return shallowRender.type() === 'div';
})()
);
```
組件應該返回兩個嵌套的元素。
```js
assert(
(function () {
var shallowRender = Enzyme.shallow(React.createElement(ParentComponent));
return shallowRender.children().length === 2;
})()
);
```
組件的第二個子元素應該是 `ChildComponent`
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ParentComponent));
return (
mockedComponent.find('ParentComponent').find('ChildComponent').length ===
1
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<ParentComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const ChildComponent = () => {
return (
<div>
<p>I am the child</p>
</div>
);
};
class ParentComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>I am the parent</h1>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
const ChildComponent = () => {
return (
<div>
<p>I am the child</p>
</div>
);
};
class ParentComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>I am the parent</h1>
{ /* Change code below this line */ }
<ChildComponent />
{ /* Change code above this line */ }
</div>
);
}
};
```

View File

@@ -0,0 +1,220 @@
---
id: 5a24c314108439a4d4036179
title: 創建一個可以控制的表單
challengeType: 6
forumTopicId: 301384
dashedName: create-a-controlled-form
---
# --description--
上一個挑戰展示了 React 能控制某些元素的內部 state比如 `input``textarea`,這使得這些元素成爲受控組件。 這也適用於其他表單元素,包括常規的 HTML 表單 `form` 元素。
# --instructions--
`MyForm` 組件中是一個帶有提交處理程序的空 `form` 元素, 提交處理程序將在提交表單時被調用。
我們增加了一個提交表單的按鈕。 可以看到它的 `type` 被設置爲 `submit`,表明它是控制表單提交的按鈕。 在 `form` 中添加 `input` 元素,並像上個挑戰一樣設置其 `value``onChange()` 屬性。 然後,應該完成 `handleSubmit` 方法,以便將組件 state 屬性 `submit` 設置爲本地 `state` 下的當前輸入值。
**注意:** 還必須在提交處理程序中調用 `event.preventDefault()`,以防止將會刷新網頁的默認的表單提交行爲。 爲了便於學員操作,默認行爲在這裏被禁用,以防止重置挑戰的代碼。
最後,在 `form` 元素之後創建一個 `h1` 標籤,該標籤從組件的 `state` 渲染 `submit` 的值。 然後,可以在表單中鍵入任何內容,然後單擊按鈕(或按 enter 鍵),輸入會渲染到頁面上。
# --hints--
`MyForm` 應該返回一個包含 `form``h1` 標籤的 `div` 元素, 其中,表單中應該包括一個 `input` 和一個 `button`
```js
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
return (
mockedComponent.find('div').children().find('form').length === 1 &&
mockedComponent.find('div').children().find('h1').length === 1 &&
mockedComponent.find('form').children().find('input').length === 1 &&
mockedComponent.find('form').children().find('button').length === 1
);
})()
);
```
`MyForm` 的 state 應該用 `input``submit` 屬性初始化,且兩者都爲空字符串。
```js
assert(
Enzyme.mount(React.createElement(MyForm)).state('input') === '' &&
Enzyme.mount(React.createElement(MyForm)).state('submit') === ''
);
```
`input` 元素中的輸入應該會更新組件中 state 的 `input` 屬性。
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
const _1 = () => {
mockedComponent.setState({ input: '' });
return mockedComponent.state('input');
};
const _2 = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: 'TestInput' } });
return {
state: mockedComponent.state('input'),
inputVal: mockedComponent.find('input').props().value
};
};
const before = _1();
const after = _2();
assert(
before === '' &&
after.state === 'TestInput' &&
after.inputVal === 'TestInput'
);
})();
```
提交表單應該運行 `handleSubmit`,它應該將 state 中的 `submit` 屬性設置爲當前輸入。
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
const _1 = () => {
mockedComponent.setState({ input: '' });
mockedComponent.setState({ submit: '' });
mockedComponent
.find('input')
.simulate('change', { target: { value: 'SubmitInput' } });
return mockedComponent.state('submit');
};
const _2 = () => {
mockedComponent.find('form').simulate('submit');
return mockedComponent.state('submit');
};
const before = _1();
const after = _2();
assert(before === '' && after === 'SubmitInput');
})();
```
`handleSubmit` 應該調用 `event.preventDefault`
```js
assert(
__helpers.isCalledWithNoArgs(
'event.preventDefault',
MyForm.prototype.handleSubmit.toString()
)
);
```
`h1` 標頭應該從組件的 state 渲染 `submit` 字段的值。
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
const _1 = () => {
mockedComponent.setState({ input: '' });
mockedComponent.setState({ submit: '' });
mockedComponent
.find('input')
.simulate('change', { target: { value: 'TestInput' } });
return mockedComponent.find('h1').text();
};
const _2 = () => {
mockedComponent.find('form').simulate('submit');
return mockedComponent.find('h1').text();
};
const before = _1();
const after = _2();
assert(before === '' && after === 'TestInput');
})();
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyForm />, document.getElementById('root'));
```
## --seed-contents--
```jsx
class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
submit: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
handleSubmit(event) {
// Change code below this line
// Change code above this line
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
{/* Change code below this line */}
{/* Change code above this line */}
<button type='submit'>Submit!</button>
</form>
{/* Change code below this line */}
{/* Change code above this line */}
</div>
);
}
}
```
# --solutions--
```jsx
class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
submit: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
handleSubmit(event) {
event.preventDefault();
this.setState(state => ({
submit: state.input
}));
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input value={this.state.input} onChange={this.handleChange} />
<button type='submit'>Submit!</button>
</form>
<h1>{this.state.submit}</h1>
</div>
);
}
}
```

View File

@@ -0,0 +1,150 @@
---
id: 5a24c314108439a4d4036178
title: 創建一個可以控制的輸入框
challengeType: 6
forumTopicId: 301385
dashedName: create-a-controlled-input
---
# --description--
應用程序可能在 `state` 和渲染的 UI 之間有更復雜的交互。 例如,用於文本輸入的表單控件元素(如 `input``textarea`)在用戶鍵入時在 DOM 中維護自己的 state。 通過 React可以將這種可變 state 轉移到 React 組件的 `state` 中。 用戶的輸入變成了應用程序 `state` 的一部分,因此 React 控制該輸入字段的值。 通常,如果 React 組件具有用戶可以鍵入的輸入字段,那麼它將是一個受控的輸入表單。
# --instructions--
代碼編輯器具有一個名爲 `ControlledInput` 的組件框架,用於創建受控的 `input` 元素。 組件的 `state` 已經被包含空字符串的 `input` 屬性初始化。 此值表示用戶在 `input` 字段中鍵入的文本。
首先,創建一個名爲 `handleChange()` 的方法,該方法具有一個名爲 `event` 的參數。 方法被調用時,它接收一個 `event` 對象,該對象包含一個來自 `input` 元素的字符串文本。 可以使用方法內的 `event.target.value` 來訪問這個字符串。 用這個新字符串更新組件的`state``input`屬性。
`render` 方法中的 `h4` 標籤之上創建 `input` 元素。 添加一個 `value` 屬性,使其等於組件 `state``input` 屬性。 然後將 `onChange()` 事件處理程序設置到 `handleChange()` 方法中。
在輸入框中鍵入時,文本由 `handleChange()` 方法處理,文本被設置爲本地 `state` 中的 `input` 屬性,並渲染在頁面上的 `input` 框中。 組件 `state` 是輸入數據的唯一真實來源。
最後,不要忘記在構造函數中添加必要的綁定。
# --hints--
`ControlledInput` 應該返回包含一個 `input` 標籤和 `p` 標籤的 `div` 元素。
```js
assert(
Enzyme.mount(React.createElement(ControlledInput))
.find('div')
.children()
.find('input').length === 1 &&
Enzyme.mount(React.createElement(ControlledInput))
.find('div')
.children()
.find('p').length === 1
);
```
`ControlledInput` 的 state 應該使用設置爲空字符串的 `input` 屬性初始化。
```js
assert.strictEqual(
Enzyme.mount(React.createElement(ControlledInput)).state('input'),
''
);
```
Input 元素中的鍵入值應該更新 input 的 state 和值,並且 `p` 元素應該在輸入時呈現 state。
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(ControlledInput));
const _1 = () => {
mockedComponent.setState({ input: '' });
return waitForIt(() => mockedComponent.state('input'));
};
const _2 = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: 'TestInput' } });
return waitForIt(() => ({
state: mockedComponent.state('input'),
text: mockedComponent.find('p').text(),
inputVal: mockedComponent.find('input').props().value
}));
};
const before = await _1();
const after = await _2();
assert(
before === '' &&
after.state === 'TestInput' &&
after.text === 'TestInput' &&
after.inputVal === 'TestInput'
);
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<ControlledInput />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class ControlledInput extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
};
// Change code below this line
// Change code above this line
}
// Change code below this line
// Change code above this line
render() {
return (
<div>
{ /* Change code below this line */}
{ /* Change code above this line */}
<h4>Controlled Input:</h4>
<p>{this.state.input}</p>
</div>
);
}
};
```
# --solutions--
```jsx
class ControlledInput extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
render() {
return (
<div>
<input
value={this.state.input}
onChange={this.handleChange} />
<h4>Controlled Input:</h4>
<p>{this.state.input}</p>
</div>
);
}
};
```

View File

@@ -0,0 +1,102 @@
---
id: 5a24c314108439a4d4036163
title: 創建一個 React 組件
challengeType: 6
forumTopicId: 301386
dashedName: create-a-react-component
---
# --description--
定義 React 組件的另一種方法是使用 ES6 的 `class`語法。 在以下示例中,`Kitten` 擴展了`React.Component`
```jsx
class Kitten extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<h1>Hi</h1>
);
}
}
```
這將創建一個 ES6 類 `Kitten`,它擴展了 `React.Component` 類。 因此,`Kitten` 類現在可以訪問許多有用的 React 功能,例如本地狀態和生命週期鉤子。 如果還不熟悉這些術語,請不要擔心,在以後的挑戰中我們將更詳細地介紹它們。 另請注意,`Kitten` 類中定義了一個調用 `super()` 方法的 `constructor`。 它使用 `super()` 調用父類的構造函數,即本例中的 `React.Component`。 構造函數是使用 `class` 關鍵字創建的特殊方法,它在實例初始化之前調用。 最佳做法是在組件的 `constructor` 裏調用 `super`,並將 `props` 傳遞給它們, 這樣可以保證組件能夠正確地初始化。 目前爲止 ,需要知道這些代碼是必要的。 很快會了解到到構造函數的其他用途以及 `props`
# --instructions--
`MyComponent` 是使用類語法在代碼編輯器中定義的。 完成 `render` 方法的編寫,使其返回 `div` 元素,其中包含文本內容爲 `Hello React!``h1` 元素。
# --hints--
該 React 組件應該返回一個 `div` 元素。
```js
assert(Enzyme.shallow(React.createElement(MyComponent)).type() === 'div');
```
返回的 `div` 中應該渲染一個 `h1` 標題。
```js
assert(
/<div><h1>.*<\/h1><\/div>/.test(
Enzyme.shallow(React.createElement(MyComponent)).html()
)
);
```
`h1` 標題中應該包含字符串 `Hello React!`
```js
assert(
Enzyme.shallow(React.createElement(MyComponent)).html() ===
'<div><h1>Hello React!</h1></div>'
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
// Change code below this line
// Change code above this line
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
// Change code below this line
return (
<div>
<h1>Hello React!</h1>
</div>
);
// Change code above this line
}
};
```

View File

@@ -0,0 +1,57 @@
---
id: 587d7dbc367417b2b2512bb1
title: 創建一個簡單的 JSX 元素
challengeType: 6
forumTopicId: 301390
dashedName: create-a-simple-jsx-element
---
# --description--
簡介React 是由 Facebook 創建和維護的開源視圖庫。 它是渲染現代 Web 應用程序用戶界面UI的好工具。
React 使用名爲 JSX 的 JavaScript 語法擴展,可以直接在 JavaScript 中編寫 HTML。 這有幾個好處。 可以在 HTML 中使用 JavaScript 的完整程序功能,並有助於保持代碼的可讀性。 在大多數情況下JSX 類似於已經學過的 HTML但是在這些挑戰中將會涉及一些關鍵差異。
例如,因爲 JSX 是 JavaScript 的語法擴展,所以實際上可以直接在 JSX 中編寫 JavaScript。 要做到這一點,只需在花括號中包含希望被視爲 JavaScript 的代碼:`{ 'this is treated as JavaScript code' }`(這被視爲 JavaScript 代碼)。 請牢記這個寫法,將會在接下來的挑戰中使用。
但是,由於瀏覽器不能解析 JSX因此必須將 JSX 代碼編譯爲 JavaScript。 在這個過程中,轉換器 Babel 是一個很受歡迎的工具。 後續挑戰已經在後臺引入了 Babel可以直接寫 JSX 代碼。 如果代碼不符合 JSX 語法,那麼挑戰中的第一個測試就不會通過。
值得注意的是,這些挑戰在底層調用 `ReactDOM.render(JSX, document.getElementById('root'))`。 這個函數調用將 JSX 置於 React 自己的輕量級 DOM 中。 然後React 使用自己的 DOM 快照來實現增量更新。
# --instructions--
當前代碼使用 JSX 將 `div` 元素賦值給常量 `JSX`。 將 `div` 替換爲 `h1` 元素,並在其中添加文本 `Hello JSX!`
# --hints--
常量 `JSX` 應該返回一個 `h1` 元素。
```js
assert(JSX.type === 'h1');
```
`h1` 標籤應該包含文本 `Hello JSX!`
```js
assert(Enzyme.shallow(JSX).contains('Hello JSX!'));
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(JSX, document.getElementById('root'))
```
## --seed-contents--
```jsx
const JSX = <div></div>;
```
# --solutions--
```jsx
const JSX = <h1>Hello JSX!</h1>;
```

View File

@@ -0,0 +1,134 @@
---
id: 5a24c314108439a4d4036170
title: 創建一個有狀態的組件
challengeType: 6
forumTopicId: 301391
dashedName: create-a-stateful-component
---
# --description--
React 中最重要的主題之一是 `state`。 state 包含應用程序需要了解的任何數據,這些數據可能會隨時間而變化。 應用程序能夠響應 state 的變更,並在必要時顯示更新後的 UI。 React 爲現代 Web 應用程序的狀態管理提供了一個很好的解決方案。
可以在類組件的 `constructor` 上聲明 `state` 屬性來在 React 組件中創建 state 它在創建時使用 `state` 初始化組件。 `state` 屬性必須設置爲 JavaScript `object`(對象)。 聲明如下:
```jsx
this.state = {
}
```
可以在組件的整個生命週期內訪問 `state` 對象, 可以更新它、在 UI 中渲染它,也可以將其作爲 props 傳遞給子組件。 `state` 對象的使用可以很簡單,亦可以很複雜,就看你怎麼用了。 請注意,必須通過擴展 `React.Component` 來創建類組件,以便像這樣創建 `state`
# --instructions--
代碼編輯器中有一個組件試圖從其 `state` 中渲染一個 `name` 屬性, 但是 `state` 還沒有定義。 在 `constructor` 中使用 `state` 初始化組件,並將你的名字賦給 `name` 屬性。
# --hints--
`StatefulComponent` 應該存在並被渲染。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(
React.createElement(StatefulComponent)
);
return mockedComponent.find('StatefulComponent').length === 1;
})()
);
```
`StatefulComponent` 應該渲染一個 `div` 元素和一個 `h1` 元素。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(
React.createElement(StatefulComponent)
);
return (
mockedComponent.find('div').length === 1 &&
mockedComponent.find('h1').length === 1
);
})()
);
```
應使用被設置爲字符串的 `name` 屬性來初始化 `StatefulComponent` 的 state。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(
React.createElement(StatefulComponent)
);
const initialState = mockedComponent.state();
return (
typeof initialState === 'object' && typeof initialState.name === 'string'
);
})()
);
```
`StatefulComponent` 中 state 的 `name` 屬性應該渲染在 `h1` 元素裏。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(
React.createElement(StatefulComponent)
);
const initialState = mockedComponent.state();
return mockedComponent.find('h1').text() === initialState.name;
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<StatefulComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class StatefulComponent extends React.Component {
constructor(props) {
super(props);
// Only change code below this line
// Only change code above this line
}
render() {
return (
<div>
<h1>{this.state.name}</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class StatefulComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'freeCodeCamp!'
}
}
render() {
return (
<div>
<h1>{this.state.name}</h1>
</div>
);
}
};
```

View File

@@ -0,0 +1,102 @@
---
id: 5a24c314108439a4d4036162
title: 創建一個無狀態的函數組件
challengeType: 6
forumTopicId: 301392
dashedName: create-a-stateless-functional-component
---
# --description--
組件是 React 的核心。 React 中的所有內容都是一個組件,在這裏將學習如何創建一個組件。
有兩種方法可以創建 React 組件。 第一種方法是使用 JavaScript 函數。 以這種方式定義組件會創建*無狀態功能組件*。 應用程序中的狀態概念將在以後的挑戰中介紹。 目前爲止,可以將無狀態組件視爲能接收數據並對其進行渲染,但不管理或跟蹤該數據的更改的組件。 (我們將下一個挑戰使用中第二種方式創建 React 組件。)
要用函數創建組件,只需編寫一個返回 JSX 或 `null` 的 JavaScript 函數。 需要注意的一點是React 要求你的函數名以大寫字母開頭。 下面是一個無狀態功能組件的示例,該組件在 JSX 中分配一個 HTML 的 class
```jsx
const DemoComponent = function() {
return (
<div className='customClass' />
);
};
```
翻譯完成後, `<div>` 將有一個 `customClass` 的 CSS class。
因爲 JSX 組件代表 HTML所以你可以將幾個組件放在一起以創建更復雜的 HTML 頁面。 這是 React 提供的組件架構的關鍵優勢之一。 它允許用許多獨立的組件組合成 UI。 這使得構建和維護複雜的用戶界面變得更加容易。
# --instructions--
代碼編輯器中有一個名爲 `MyComponent` 的函數。 完成此函數,使其返回包含一些文本字符串的單個`div`元素。
**注意:** 文本被視爲是 `div` 的子元素,因此不能使用自閉合標籤。
# --hints--
`MyComponent` 應該返回 JSX。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.length === 1;
})()
);
```
`MyComponent` 應該返回一個 `div` 元素。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.children().type() === 'div';
})()
);
```
`div` 元素應該包含一個文本字符串。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.find('div').text() !== '';
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const MyComponent = function() {
// Change code below this line
// Change code above this line
}
```
# --solutions--
```jsx
const MyComponent = function() {
// Change code below this line
return (
<div>
Demo Solution
</div>
);
// Change code above this line
}
```

View File

@@ -0,0 +1,62 @@
---
id: 5a24c314108439a4d4036160
title: 在 JSX 中定義一個 HTML Class
challengeType: 6
forumTopicId: 301393
dashedName: define-an-html-class-in-jsx
---
# --description--
現在已經習慣了編寫 JSX可能想知道它與 HTML 有什麼不同。
到目前爲止HTML 和 JSX 似乎完全相同。
JSX 的一個關鍵區別是你不能再使用 `class` 這個單詞來做爲 HTML 的 class 名。 這是因爲 `class` 是 JavaScript 中的關鍵字。 而 JSX 使用 `className` 來代替。
事實上JSX 中所有 HTML 屬性和事件引用的命名約定都變成了駝峯式。 例如JSX 中的單擊事件是 `onClick`,而不是 `onclick`。 同樣,`onchange` 變成了`onChange`。 雖然這是一個微小的差異,但請你一定要記住。
# --instructions--
將 class `myDiv` 應用於 JSX 提供的 `div`上。
# --hints--
常量`JSX`應該返回一個`div`元素。
```js
assert.strictEqual(JSX.type, 'div');
```
`div` 應該有一個 `myDiv` class。
```js
assert.strictEqual(JSX.props.className, 'myDiv');
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(JSX, document.getElementById('root'))
```
## --seed-contents--
```jsx
const JSX = (
<div>
<h1>Add a class to this div</h1>
</div>
);
```
# --solutions--
```jsx
const JSX = (
<div className = 'myDiv'>
<h1>Add a class to this div</h1>
</div>);
```

View File

@@ -0,0 +1,147 @@
---
id: 5a24c314108439a4d403618b
title: 給同級元素一個唯一的鍵屬性
challengeType: 6
forumTopicId: 301394
dashedName: give-sibling-elements-a-unique-key-attribute
---
# --description--
上一個挑戰展示瞭如何使用 `map` 方法根據用戶輸入動態渲染多個元素。 然而,這個例子中缺少一個重要的部分。 創建元素數組時,每個元素都需要一個設置爲唯一值的 `key` 屬性。 React 使用這些鍵來跟蹤哪些項目被添加、更改或刪除。 這有助於在以任何方式修改列表時提高重新渲染過程的效率。
**注意:** 鍵只需要在兄弟元素之間是唯一的,它們不需要在應用程序中是全局唯一的。
# --instructions--
代碼編輯器有一個數組,它包含一些前端框架和一個名爲 `Frameworks()` 的無狀態函數組件。 `Frameworks()` 需要將數組映射到無序列表,就像上一個挑戰一樣。 完成 `map` 回調,爲 `frontEndFrameworks` 數組中的每個框架返回一個 `li` 元素。 這次,確保給每個 `li``key` 屬性設置一個唯一的值。 `li` 元素還應該包含來自 `frontEndFrameworks` 的文本。
通常,希望使 key 能唯一標識要渲染的元素。 數組索引可以是最後的選擇,但通常你應該嘗試使用唯一標識。
# --hints--
`Frameworks` 組件應該存在並渲染到頁面。
```js
assert(
Enzyme.mount(React.createElement(Frameworks)).find('Frameworks').length === 1
);
```
`Frameworks` 應該渲染一個 `h1` 元素。
```js
assert(Enzyme.mount(React.createElement(Frameworks)).find('h1').length === 1);
```
`Frameworks` 應該渲染一個 `ul` 元素。
```js
assert(Enzyme.mount(React.createElement(Frameworks)).find('ul').length === 1);
```
`ul` 標籤應該渲染 6 個子 `li` 元素。
```js
assert(
Enzyme.mount(React.createElement(Frameworks)).find('ul').children().length ===
6 &&
Enzyme.mount(React.createElement(Frameworks))
.find('ul')
.childAt(0)
.name() === 'li' &&
Enzyme.mount(React.createElement(Frameworks)).find('li').length === 6
);
```
每個列表項元素應該具有唯一的 `key` 屬性。
```js
assert(
(() => {
const ul = Enzyme.mount(React.createElement(Frameworks)).find('ul');
const keys = new Set([
ul.childAt(0).key(),
ul.childAt(1).key(),
ul.childAt(2).key(),
ul.childAt(3).key(),
ul.childAt(4).key(),
ul.childAt(5).key()
]);
return keys.size === 6;
})()
);
```
每個列表項元素應該包含來自 `frontEndFrameworks` 的文本。
```js
assert(
(() => {
const li = Enzyme.mount(React.createElement(Frameworks))
.find('ul')
.children();
return [...Array(5)].every((_, i) =>
frontEndFrameworks.includes(li.at(i).text())
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<Frameworks />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const frontEndFrameworks = [
'React',
'Angular',
'Ember',
'Knockout',
'Backbone',
'Vue'
];
function Frameworks() {
const renderFrameworks = null; // Change this line
return (
<div>
<h1>Popular Front End JavaScript Frameworks</h1>
<ul>
{renderFrameworks}
</ul>
</div>
);
};
```
# --solutions--
```jsx
const frontEndFrameworks = [
'React',
'Angular',
'Ember',
'Knockout',
'Backbone',
'Vue'
];
function Frameworks() {
const renderFrameworks = frontEndFrameworks.map((fw, i) => <li key={i}>{fw}</li>);
return (
<div>
<h1>Popular Front End JavaScript Frameworks</h1>
<ul>
{renderFrameworks}
</ul>
</div>
);
};
```

View File

@@ -0,0 +1,104 @@
---
id: 5a24c314108439a4d4036181
title: 介紹內聯樣式
challengeType: 6
forumTopicId: 301395
dashedName: introducing-inline-styles
---
# --description--
還有其他複雜的概念可以爲 React 代碼增加強大的功能。 但是,你可能會想知道更簡單的問題,比如:如何對在 React 中創建的 JSX 元素添加樣式。 你可能知道,由於[將 class 應用於 JSX 元素的方式](/learn/front-end-development-libraries/react/define-an-html-class-in-jsx)與 HTML 中的使用並不完全相同。
如果從樣式表導入樣式,它就沒有太大的不同。 使用 `className` 屬性將 class 應用於 JSX 元素,並將樣式應用於樣式表中的 class。 另一種選擇是使用內聯樣式,這在 ReactJS 開發中非常常見。
將內聯樣式應用於 JSX 元素,類似於在 HTML 中的操作方式,但有一些 JSX 差異。 以下是 HTML 中內聯樣式的示例:
```jsx
<div style="color: yellow; font-size: 16px">Mellow Yellow</div>
```
JSX 元素使用 `style` 屬性,但是鑑於 JSX 的編譯方式,不能將值設置爲 `string`(字符串)。 相反你應該將其設置爲等於JavaScript `object` 。 如下所示:
```jsx
<div style={{color: "yellow", fontSize: 16}}>Mellow Yellow</div>
```
注意到如何駝峯拼寫 `fontSize` 屬性了嗎? 這是因爲 React 不接受樣式對象中的 kebab-case 鍵。 React 將在 HTML 中爲應用正確的屬性名稱。
# --instructions--
在代碼編輯器中給 `div` 添加一個 `style` 屬性,將文本顏色設置爲紅色,字體大小設置爲 `72px`
請注意,可以選擇將字體大小設置爲數字,省略單位 `px`,或者將其寫爲 `72px`
# --hints--
組件應該渲染一個 `div` 元素。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Colorful));
return mockedComponent.children().type() === 'div';
})()
);
```
`div` 元素的顏色應該是 `red`(紅色)。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Colorful));
return mockedComponent.children().props().style.color === 'red';
})()
);
```
`div` 元素的字體大小應爲 `72px`
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Colorful));
return (
mockedComponent.children().props().style.fontSize === 72 ||
mockedComponent.children().props().style.fontSize === '72' ||
mockedComponent.children().props().style.fontSize === '72px'
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<Colorful />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class Colorful extends React.Component {
render() {
return (
<div>Big Red</div>
);
}
};
```
# --solutions--
```jsx
class Colorful extends React.Component {
render() {
return (
<div style={{color: "red", fontSize: 72}}>Big Red</div>
);
}
};
```

View File

@@ -0,0 +1,75 @@
---
id: 5a24c314108439a4d4036161
title: 瞭解 JSX 的自動閉合
challengeType: 6
forumTopicId: 301396
dashedName: learn-about-self-closing-jsx-tags
---
# --description--
到目前爲止,已經看到 JSX 與 HTML 的關鍵不同在於使用 `className` 還是 `class` 來定義 HTML 的 class。
JSX 不同於 HTML 的另一個重要方面是自閉合標籤。
在HTML中幾乎所有的標籤都有一個開始和結束標籤`<div></div>`,結束標籤在你要關閉的標籤名之前始終具有正斜槓。 但是HTML 中有一些稱爲 “自閉合標籤” 的特殊實例,它們在另一個標籤開始之前,不需要開始和結束標籤都存在。
例如,換行標籤可以寫成 `<br>` 或者 `<br />`,但是不應該寫成 `<br></br>`,因爲它不包含任何內容。
在 JSX 中,規則略有不同。 任何 JSX 元素都可以使用自閉合標籤編寫,並且每個元素都必須關閉。 例如,爲了通過編譯換行標籤必須始終編寫爲 `<br />`。 另一方面 `<div>` 可以寫成 `<div />` 或者 `<div></div>`。 不同之處在於,在第一個語法版本中,無法在 `<div />` 中包含任何內容。 在後面的挑戰中你會發現,這種語法在渲染 React 組件時非常有用。
# --instructions--
修復代碼編輯器中的錯誤,使其成爲有效的 JSX 併成功編譯。 確保不更改任何內容 -- 只需要在需要的地方關閉標籤。
# --hints--
常量 `JSX` 應該返回一個 `div` 元素。
```js
assert.strictEqual(JSX.type, 'div');
```
`div` 應該包含一個 `br` 標籤。
```js
assert(Enzyme.shallow(JSX).find('br').length === 1);
```
`div` 應該包含一個 `hr` 標籤。
```js
assert(Enzyme.shallow(JSX).find('hr').length === 1);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(JSX, document.getElementById('root'))
```
## --seed-contents--
```jsx
const JSX = (
<div>
<h2>Welcome to React!</h2> <br >
<p>Be sure to close all tags!</p>
<hr >
</div>
);
```
# --solutions--
```jsx
const JSX = (
<div>
<h2>Welcome to React!</h2> <br />
<p>Be sure to close all tags!</p>
<hr />
</div>
);
```

View File

@@ -0,0 +1,187 @@
---
id: 5a24c314108439a4d4036180
title: 使用 shouldComponentUpdate 優化重新渲染
challengeType: 6
forumTopicId: 301398
dashedName: optimize-re-renders-with-shouldcomponentupdate
---
# --description--
到目前爲止,如果任何組件接收到新的 `state` 或新的 `props`,它會重新渲染自己及其所有子組件。 這通常是好的。 但是 React 提供了一種生命週期方法,當子組件接收到新的 `state``props` 時,可以調用該方法,並特別聲明組件是否應該更新。 這個方法就是 `shouldComponentUpdate()`,它將 `nextProps``nextState` 作爲參數。
這種方法是優化性能的有效方法。 例如,默認行爲是,當組件接收到新的 `props` 時,即使 `props` 沒有改變,它也會重新渲染。 可以通過使用 `shouldComponentUpdate()` 比較 `props` 來防止這種情況發生。 該方法必須返回一個 `boolean`(布爾值),該值告訴 React 是否更新組件。 可以比較當前的 props`this.props`)和下一個 props`nextProps`),以確定你是否需要更新,並相應地返回 `true``false`
# --instructions--
`shouldComponentUpdate()` 方法添加到名爲 `OnlyEvens` 的組件中。 目前,該方法返回 `true`,因此每次收到新的 `props` 時,`OnlyEvens` 都會重新渲染。 修改該方法,以便 `OnlyEvens` 僅在其新 props 的 `value` 爲偶數時更新。 單擊 `Add` 按鈕,在觸發其他生命週期鉤子時,在瀏覽器控制檯中查看事件的順序。
# --hints--
`Controller` 組件應該將 `OnlyEvens` 組件渲染爲子組件。
```js
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(Controller));
return (
mockedComponent.find('Controller').length === 1 &&
mockedComponent.find('OnlyEvens').length === 1
);
})()
);
```
應該在 `OnlyEvens` 組件上定義 `shouldComponentUpdate` 方法。
```js
assert(
(() => {
const child = React.createElement(OnlyEvens)
.type.prototype.shouldComponentUpdate.toString()
.replace(/s/g, '');
return child !== 'undefined';
})()
);
```
`OnlyEvens` 組件應該返回一個 `h1` 標籤,該標籤渲染 `this.props.value` 的值。
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(Controller));
const first = () => {
mockedComponent.setState({ value: 1000 });
return mockedComponent.find('h1').html();
};
const second = () => {
mockedComponent.setState({ value: 10 });
return mockedComponent.find('h1').html();
};
const firstValue = first();
const secondValue = second();
assert(firstValue === '<h1>1000</h1>' && secondValue === '<h1>10</h1>');
})();
```
只有在 `nextProps.value` 爲偶數時,`OnlyEvens` 纔會重新渲染。
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(Controller));
const first = () => {
mockedComponent.setState({ value: 8 });
return mockedComponent.find('h1').text();
};
const second = () => {
mockedComponent.setState({ value: 7 });
return mockedComponent.find('h1').text();
};
const third = () => {
mockedComponent.setState({ value: 42 });
return mockedComponent.find('h1').text();
};
const firstValue = first();
const secondValue = second();
const thirdValue = third();
assert(firstValue === '8' && secondValue === '8' && thirdValue === '42');
})();
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<Controller />, document.getElementById('root'));
```
## --seed-contents--
```jsx
class OnlyEvens extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate(nextProps, nextState) {
console.log('Should I update?');
// Change code below this line
return true;
// Change code above this line
}
componentDidUpdate() {
console.log('Component re-rendered.');
}
render() {
return <h1>{this.props.value}</h1>;
}
}
class Controller extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0
};
this.addValue = this.addValue.bind(this);
}
addValue() {
this.setState(state => ({
value: state.value + 1
}));
}
render() {
return (
<div>
<button onClick={this.addValue}>Add</button>
<OnlyEvens value={this.state.value} />
</div>
);
}
}
```
# --solutions--
```jsx
class OnlyEvens extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate(nextProps, nextState) {
console.log('Should I update?');
// Change code below this line
return nextProps.value % 2 === 0;
// Change code above this line
}
componentDidUpdate() {
console.log('Component re-rendered.');
}
render() {
return <h1>{this.props.value}</h1>;
}
}
class Controller extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0
};
this.addValue = this.addValue.bind(this);
}
addValue() {
this.setState(state => ({
value: state.value + 1
}));
}
render() {
return (
<div>
<button onClick={this.addValue}>Add</button>
<OnlyEvens value={this.state.value} />
</div>
);
}
}
```

View File

@@ -0,0 +1,112 @@
---
id: 5a24c314108439a4d403616c
title: 覆蓋默認的 Props
challengeType: 6
forumTopicId: 301399
dashedName: override-default-props
---
# --description--
在 React 中,設置默認的 props 是一個很有用的特性, 顯式設置組件的 prop 值即可覆蓋默認 props。
# --instructions--
`ShoppingCart` 組件現在渲染了一個子組件 `Items`。 該 `Items` 組件有一個默認 `quantity` prop其值被設置爲整數 `0`。 通過傳入數值 `10` 來覆蓋 `quantity` 的默認 prop。
**注意:** 請記住,向組件添加 prop 的語法與添加 HTML 屬性類似。 但是,由於 `quantity` 的值是整數,所以它不會加引號,但應該用花括號括起來, 例如`{100}`。 這個語法告訴 JSX 直接將花括號中的值解釋爲 JavaScript。
# --hints--
應該渲染 `ShoppingCart` 組件。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
return mockedComponent.find('ShoppingCart').length === 1;
})()
);
```
應該渲染 `Items` 組件。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
return mockedComponent.find('Items').length === 1;
})()
);
```
`Items` 組件應該有一個 `{ quantity: 10 }` 的 prop該 prop 是從 `ShoppingCart` 組件傳遞過去的。
```js
(getUserInput) =>
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
return (
mockedComponent.find('Items').props().quantity == 10 &&
getUserInput('index')
.replace(/ /g, '')
.includes('<Itemsquantity={10}/>')
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<ShoppingCart />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const Items = (props) => {
return <h1>Current Quantity of Items in Cart: {props.quantity}</h1>
}
Items.defaultProps = {
quantity: 0
}
class ShoppingCart extends React.Component {
constructor(props) {
super(props);
}
render() {
{ /* Change code below this line */ }
return <Items />
{ /* Change code above this line */ }
}
};
```
# --solutions--
```jsx
const Items = (props) => {
return <h1>Current Quantity of Items in Cart: {props.quantity}</h1>
}
Items.defaultProps = {
quantity: 0
}
class ShoppingCart extends React.Component {
constructor(props) {
super(props);
}
render() {
{ /* Change code below this line */ }
return <Items quantity = {10} />
{ /* Change code above this line */ }
}
};
```

View File

@@ -0,0 +1,217 @@
---
id: 5a24c314108439a4d403617b
title: 傳遞迴調作爲 Props
challengeType: 6
forumTopicId: 301400
dashedName: pass-a-callback-as-props
---
# --description--
可以將 `state` 作爲 props 傳遞給子組件,但不僅限於傳遞數據。 也可以將函數或在 React 組件中定義的任何方法傳遞給子組件。 這就是子組件與父組件交互的方式。 可以把方法像普通 prop 一樣傳遞給子組件, 它會被分配一個名字,可以在子組件中的 `this.props` 下訪問該方法的名字。
# --instructions--
代碼編輯器中列出了三個組件。 `MyApp` 是父組件,`GetInput``RenderInput` 是它將要渲染的子組件。 將 `GetInput` 組件添加到 `MyApp` 的 render 方法,然後將 `MyApp``state` 中的 `inputValue` 傳入名爲 `input` 的 prop。 還要創建一個名爲 `handleChange` 的 prop並將輸入處理程序 `handleChange` 傳遞給它。
接下來,將 `RenderInput` 添加到 `MyApp` 中的 render 方法中,然後創建一個名爲 `input` 的 prop並將 `state` 中的 `inputValue` 傳遞給它。 完成後,可以在 `GetInput` 組件中的 `input` 字段中鍵入內容,然後該組件通過 props 調用其父組件中的處理函數方法。 這將更新處於父組件 `state` 中的 input該 input 將作爲 props 傳遞給兩個子組件。 觀察數據如何在組件之間流動,以及單一數據源如何保持父組件`state`。 誠然,這個示例有點做作,但是應該能用來說明數據和回調是如何在 React 組件之間傳遞的。
# --hints--
應該渲染 `MyApp` 組件。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
return mockedComponent.find('MyApp').length === 1;
})()
);
```
應該渲染 `GetInput` 組件。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
return mockedComponent.find('GetInput').length === 1;
})()
);
```
應該渲染 `RenderInput` 組件。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
return mockedComponent.find('RenderInput').length === 1;
})()
);
```
`GetInput` 組件應該接收 `MyApp` 的 state 屬性 `inputValue` 作爲 props幷包含一個修改 `MyApp` state 的 `input` 元素。
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
const state_1 = () => {
mockedComponent.setState({ inputValue: '' });
return waitForIt(() => mockedComponent.state());
};
const state_2 = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: 'TestInput' } });
return waitForIt(() => mockedComponent.state());
};
const updated_1 = await state_1();
const updated_2 = await state_2();
assert(updated_1.inputValue === '' && updated_2.inputValue === 'TestInput');
};
```
`RenderInput` 組件應該接收 `MyApp` state 屬性 `inputValue` 作爲 props。
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
const state_1 = () => {
mockedComponent.setState({ inputValue: 'TestName' });
return waitForIt(() => mockedComponent);
};
const updated_1 = await state_1();
assert(updated_1.find('p').text().includes('TestName'));
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyApp />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
inputValue: ''
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
inputValue: event.target.value
});
}
render() {
return (
<div>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
class GetInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>Get Input:</h3>
<input
value={this.props.input}
onChange={this.props.handleChange}/>
</div>
);
}
};
class RenderInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>Input Render:</h3>
<p>{this.props.input}</p>
</div>
);
}
};
```
# --solutions--
```jsx
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
inputValue: ''
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
inputValue: event.target.value
});
}
render() {
return (
<div>
<GetInput
input={this.state.inputValue}
handleChange={this.handleChange}/>
<RenderInput
input={this.state.inputValue}/>
</div>
);
}
};
class GetInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>Get Input:</h3>
<input
value={this.props.input}
onChange={this.props.handleChange}/>
</div>
);
}
};
class RenderInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>Input Render:</h3>
<p>{this.props.input}</p>
</div>
);
}
};
```

View File

@@ -0,0 +1,186 @@
---
id: 5a24c314108439a4d403616a
title: 傳遞一個數組作爲 Props
challengeType: 6
forumTopicId: 301401
dashedName: pass-an-array-as-props
---
# --description--
上一個挑戰演示瞭如何將來自父組件的信息作爲 `props` 傳遞給子組件。 這個挑戰着眼於如何將數組作爲 `props` 傳遞。 要將數組傳遞給 JSX 元素,必須將其視爲 JavaScript 並用花括號括起來。
```jsx
<ParentComponent>
<ChildComponent colors={["green", "blue", "red"]} />
</ParentComponent>
```
這樣,子組件就可以訪問數組屬性 `colors`。 訪問屬性時可以使用 `join()` 等數組方法。 `const ChildComponent = (props) => <p>{props.colors.join(', ')}</p>` 這將把所有 `colors` 數組項連接成一個逗號分隔的字符串並生成: `<p>green, blue, red</p>` 稍後,我們將瞭解在 React 中渲染數組數據的其他常用方法。
# --instructions--
代碼編輯器中有 `List``ToDo` 組件。 在 `ToDo` 組件中渲染每個 `List` 時,傳入 `tasks` 屬性並將其分配給待辦任務數組,例如 `["walk dog", "workout"]`。 然後訪問 `List` 組件中的 `tasks` 數組,在`p`元素中顯示其值。 使用 `join(", ")``props.tasks` 數組作爲逗號分隔列表顯示在 `p` 元素中。 今天的列表應該至少有 2 個任務,明天的列表應該至少有 3 個任務。
# --hints--
`ToDo` 組件應該返回單個外部 `div`
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return mockedComponent.children().first().type() === 'div';
})()
);
```
`ToDo` 組件的第三個子元素應該是 `List` 組件的一個實例。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return mockedComponent.children().first().childAt(2).name() === 'List';
})()
);
```
`ToDo` 組件的第五個子元素應該是 `List` 組件的一個實例。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return mockedComponent.children().first().childAt(4).name() === 'List';
})()
);
```
`List` 組件的兩個實例都應該具有一個名爲 `tasks` 的屬性,並且 `tasks` 的類型應該是數組。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return (
Array.isArray(mockedComponent.find('List').get(0).props.tasks) &&
Array.isArray(mockedComponent.find('List').get(1).props.tasks)
);
})()
);
```
表示今天任務的第一個 `List` 組件應該有 2 個或更多項。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return mockedComponent.find('List').get(0).props.tasks.length >= 2;
})()
);
```
表示明天任務的第二個 `List` 組件應該有 3 個或更多項。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return mockedComponent.find('List').get(1).props.tasks.length >= 3;
})()
);
```
`List` 組件應在 `p` 標籤中渲染 `tasks` 屬性的值。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return (
mockedComponent
.find('p')
.get(0)
.props.children.replace(/\s*,\s*/g, ',') ===
mockedComponent
.find('List')
.get(0)
.props.tasks.join(',')
.replace(/\s*,\s*/g, ',') &&
mockedComponent
.find('p')
.get(1)
.props.children.replace(/\s*,\s*/g, ',') ===
mockedComponent
.find('List')
.get(1)
.props.tasks.join(',')
.replace(/\s*,\s*/g, ',')
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<ToDo />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const List = (props) => {
{ /* Change code below this line */ }
return <p>{}</p>
{ /* Change code above this line */ }
};
class ToDo extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>To Do Lists</h1>
<h2>Today</h2>
{ /* Change code below this line */ }
<List/>
<h2>Tomorrow</h2>
<List/>
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
const List= (props) => {
return <p>{props.tasks.join(', ')}</p>
};
class ToDo extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>To Do Lists</h1>
<h2>Today</h2>
<List tasks={['study', 'exercise']} />
<h2>Tomorrow</h2>
<List tasks={['call Sam', 'grocery shopping', 'order tickets']} />
</div>
);
}
};
```

View File

@@ -0,0 +1,164 @@
---
id: 5a24c314108439a4d4036169
title: 將 Props 傳遞給無狀態函數組件
challengeType: 6
forumTopicId: 301402
dashedName: pass-props-to-a-stateless-functional-component
---
# --description--
之前的挑戰涵蓋了關於在 React 中創建和組合 JSX 元素、函數組件和 ES6 風格的類組件的很多內容。 有了這個基礎,現在是時候看看 React 中的另一個常見特性 **props** 了。 在 React 中,可以將屬性傳遞給子組件。 假設有一個 `App` 組件,該組件渲染了一個名爲 `Welcome` 的子組件,它是一個無狀態函數組件。 可以通過以下方式給 `Welcome` 傳遞一個 `user` 屬性:
```jsx
<App>
<Welcome user='Mark' />
</App>
```
可以把創建的 React 支持的**自定義 HTML 屬性**傳遞給組件, 在上面的例子裏,將創建的屬性 `user` 傳遞給組件 `Welcome`。 由於 `Welcome` 是一個無狀態函數組件,它可以像這樣訪問該值:
```jsx
const Welcome = (props) => <h1>Hello, {props.user}!</h1>
```
調用 `props` 這個值是常見做法,當處理無狀態函數組件時,基本上可以將其視爲返回 JSX 的函數的參數。 這樣,你就可以在函數體中訪問該值。 但對於類組件,訪問方式會略有不同。
# --instructions--
代碼編輯器中有 `Calendar``CurrentDate` 組件。 從 `Calendar` 組件渲染 `CurrentDate` 時,從 JavaScript 的 `Date` 對象分配當前日期,並將其作爲 `date` 屬性傳入。 然後訪問 `CurrentDate` 組件的 `prop`,並在 `p` 標籤中顯示其值。 請注意,要將 `prop` 的值視爲 JavaScript必須將它們括在花括號中例如`date={Date()}`
# --hints--
`Calendar` 組件應該返回單個 `div` 元素。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Calendar));
return mockedComponent.children().type() === 'div';
})()
);
```
`Calendar` 組件的第二個子元素應該是 `CurrentDate` 組件。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Calendar));
return mockedComponent.children().childAt(1).name() === 'CurrentDate';
})()
);
```
`CurrentDate` 組件應該有一個名爲 `date` 的屬性。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Calendar));
return mockedComponent.children().childAt(1).props().date;
})()
);
```
`CurrentDate``date` 屬性應該包含一段文本字符串。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Calendar));
const prop = mockedComponent.children().childAt(1).props().date;
return typeof prop === 'string' && prop.length > 0;
})()
);
```
`date` 屬性應該通過調用 `Date()` 生成。
```js
assert(/<CurrentDatedate={Date\(\)}\/>/.test(__helpers.removeWhiteSpace(code)));
```
`CurrentDate` 組件應該把 `date` 屬性渲染在 `p` 標籤內。
```js
let date = 'dummy date';
assert(
(function () {
const mockedComponent = Enzyme.mount(
React.createElement(CurrentDate, { date })
);
return mockedComponent.find('p').html().includes(date);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<Calendar />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const CurrentDate = (props) => {
return (
<div>
{ /* Change code below this line */ }
<p>The current date is: </p>
{ /* Change code above this line */ }
</div>
);
};
class Calendar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>What date is it?</h3>
{ /* Change code below this line */ }
<CurrentDate />
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
const CurrentDate = (props) => {
return (
<div>
{ /* Change code below this line */ }
<p>The current date is: {props.date}</p>
{ /* Change code above this line */ }
</div>
);
};
class Calendar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>What date is it?</h3>
{ /* Change code below this line */ }
<CurrentDate date={Date()} />
{ /* Change code above this line */ }
</div>
);
}
};
```

View File

@@ -0,0 +1,145 @@
---
id: 5a24c314108439a4d403617a
title: 將 State 作爲 Props 傳遞給子組件
challengeType: 6
forumTopicId: 301403
dashedName: pass-state-as-props-to-child-components
---
# --description--
在之前的挑戰中,看到了很多將 props 傳遞給子 JSX 元素和子 React 組件的例子。 你可能想知道那些 props 是從哪裏來的。 一個常見的模式是:有狀態組件中包含對應用程序很重要的 `state`,然後用它渲染子組件。 如果想讓這些組件能夠訪問該 `state` 的某些部分,就把這些部分作爲 props 傳入。
例如,有一個 `App` 組件可以渲染 `Navbar` 以及其他組件。 `App` 裏的 `state` 包含大量用戶信息,但 `Navbar` 只需要訪問用戶的用戶名,以便顯示它。 將該 `state` 作爲 prop 傳遞給`Navbar`組件。
這個模式說明了 React 中的一些重要範例。 第一個是*單向數據流* state 沿着應用程序組件樹的一個方向流動,從有狀態父組件到子組件, 子組件只接收它們需要的 state 數據。 第二,複雜的有狀態應用程序可以分解成幾個,或者可能是一個單一的有狀態組件。 其餘組件只是從父組件簡單的接收 state 作爲 props並從該 state 渲染 UI。 它開始創建一種分離在這種分離中state 管理在代碼的一部分中處理,而 UI 渲染在另一部分中處理。 將 state 邏輯與 UI 邏輯分離是 React 的關鍵原則之一。 當它被正確使用時,它使得複雜的、有狀態的應用程序的設計變得更容易管理。
# --instructions--
`MyApp` 組件是有狀態的,並將 `Navbar` 組件渲染爲子組件。 將 `state``name` 屬性向下傳遞給子組件,然後在 `h1` 中顯示該 `name` h1 是 `Navbar` render方法的一部分。 `name` 應該顯示在文本 `Hello, my name is:` 後面。
# --hints--
`MyApp` 組件應該在內部渲染一個 `Navbar` 組件。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
return (
mockedComponent.find('MyApp').length === 1 &&
mockedComponent.find('Navbar').length === 1
);
})()
);
```
`Navbar` 組件應該接收 `MyApp` 的 state 中的 `name` 屬性作爲 props。
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
const setState = () => {
mockedComponent.setState({ name: 'TestName' });
return waitForIt(() => mockedComponent.find('Navbar').props());
};
const navProps = await setState();
assert(navProps.name === 'TestName');
};
```
`Navbar` 中的 `h1`元素應該渲染 prop `name`
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
const navH1Before = mockedComponent.find('Navbar').find('h1').text();
const setState = () => {
mockedComponent.setState({ name: 'TestName' });
return waitForIt(() => mockedComponent.find('Navbar').find('h1').text());
};
const navH1After = await setState();
assert(new RegExp('TestName').test(navH1After) && navH1After !== navH1Before);
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyApp />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'CamperBot'
}
}
render() {
return (
<div>
{/* Change code below this line */}
<Navbar />
{/* Change code above this line */}
</div>
);
}
};
class Navbar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{/* Change code below this line */}
<h1>Hello, my name is: </h1>
{/* Change code above this line */}
</div>
);
}
};
```
# --solutions--
```jsx
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'CamperBot'
}
}
render() {
return (
<div>
<Navbar name={this.state.name}/>
</div>
);
}
};
class Navbar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Hello, my name is: {this.props.name}</h1>
</div>
);
}
};
```

View File

@@ -0,0 +1,159 @@
---
id: 5a24c314108439a4d4036167
title: 將 class 組件渲染到 DOM 樹
challengeType: 6
forumTopicId: 301404
dashedName: render-a-class-component-to-the-dom
---
# --description--
還記不記得在之前的挑戰中使用 ReactDOM API 將 JSX 元素渲染到 DOM 這與渲染 React 組件的過程十分相似。 過去的幾個挑戰主要針對組件和組合,因此渲染是在幕後完成的。 但是,如果不調用 ReactDOM API編寫的任何 React 代碼都不會渲染到 DOM。
複習一下語法: `ReactDOM.render(componentToRender, targetNode)`。 第一個參數是要渲染的 React 組件。 第二個參數是要在其中渲染該組件的 DOM 節點。
傳遞到`ReactDOM.render()` 的React 組件與 JSX 元素略有不同。 對於 JSX 元素,傳入的是要渲染的元素的名稱。 但是,對於 React 組件,需要使用與渲染嵌套組件相同的語法,例如`ReactDOM.render(<ComponentToRender />, targetNode)`。 此語法用於 ES6 class 組件和函數組件都可以。
# --instructions--
在後臺引入了 `Fruits``Vegetables` 組件。 將兩個組件渲染爲 `TypesOfFood` 組件的子組件,然後將 `TypesOfFood` 渲染到 DOM 節點, 在這個挑戰中,請渲染到 `id='challenge-node'``div` 中。
# --hints--
`TypesOfFood` 組件應該返回單個 `div` 元素。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return mockedComponent.children().type() === 'div';
})()
);
```
`TypesOfFood` 組件應該在 `h1` 元素之後渲染 `Fruits` 組件。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return mockedComponent.children().childAt(1).name() === 'Fruits';
})()
);
```
`TypesOfFood` 組件應該在 `Fruits` 組件之後渲染 `Vegetables` 組件。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return mockedComponent.children().childAt(2).name() === 'Vegetables';
})()
);
```
`TypesOfFood` 組件應該渲染到 id 爲 `challenge-node``div`中。
```js
assert(
(function () {
const html = document.getElementById('challenge-node').childNodes[0]
.innerHTML;
return (
html.includes(
'<div><h2>Fruits:</h2><h4>Non-Citrus:</h4><ul><li>Apples</li><li>Blueberries</li><li>Strawberries</li><li>Bananas</li></ul><h4>Citrus:</h4><ul><li>Lemon</li><li>Lime</li><li>Orange</li><li>Grapefruit</li></ul></div>'
) &&
html.includes(
'<div><h2>Vegetables:</h2><ul><li>Brussel Sprouts</li><li>Broccoli</li><li>Squash</li></ul></div>'
)
);
})()
);
```
# --seed--
## --before-user-code--
```jsx
const Fruits = () => {
return (
<div>
<h2>Fruits:</h2>
<h4>Non-Citrus:</h4>
<ul>
<li>Apples</li>
<li>Blueberries</li>
<li>Strawberries</li>
<li>Bananas</li>
</ul>
<h4>Citrus:</h4>
<ul>
<li>Lemon</li>
<li>Lime</li>
<li>Orange</li>
<li>Grapefruit</li>
</ul>
</div>
);
};
const Vegetables = () => {
return (
<div>
<h2>Vegetables:</h2>
<ul>
<li>Brussel Sprouts</li>
<li>Broccoli</li>
<li>Squash</li>
</ul>
</div>
);
};
```
## --seed-contents--
```jsx
class TypesOfFood extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Types of Food:</h1>
{/* Change code below this line */}
{/* Change code above this line */}
</div>
);
}
};
// Change code below this line
```
# --solutions--
```jsx
class TypesOfFood extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Types of Food:</h1>
{/* Change code below this line */}
<Fruits />
<Vegetables />
{/* Change code above this line */}
</div>
);
}
};
// Change code below this line
ReactDOM.render(<TypesOfFood />, document.getElementById('challenge-node'));
```

View File

@@ -0,0 +1,303 @@
---
id: 5a24c314108439a4d4036188
title: 根據 Props 有條件地渲染
challengeType: 6
forumTopicId: 301405
dashedName: render-conditionally-from-props
---
# --description--
到目前爲止,你已經看到如何使用 `if/else``&&` 以及三元運算符(`condition ? expressionIfTrue : expressionIfFalse`)在不同條件下運行不同的代碼。 然而,還有一個重要的話題需要討論,將這些概念中的任何一個或所有概念與另一個強大的 React 功能 props 結合起來。 使用 props 有條件地渲染代碼在 React 開發人員中很常見——也就是說:他們使用給定 prop 的值來自動決定渲染什麼。
在這個挑戰中,將設置一個子組件來根據 props 做出渲染決定。 可以使用三元運算符,但是可以看到過去幾個挑戰中涵蓋的其他幾個概念在這種情況下可能同樣有用。
# --instructions--
代碼編輯器有兩個部分爲你定義的組件:一個名爲 `GameOfChance` 的父組件和一個名爲 `Results` 的子組件。 它們被用來創建一個簡單的遊戲,用戶按下按鈕來看它們是贏還是輸。
首先,需要一個簡單的表達式,每次運行時都會隨機返回一個不同的值。 可以使用 `Math.random()`。 每次調用此方法時,此方法返回 `0`(包括)和 `1`(不包括)之間的值。 因此對於50/50的機率請在表達式中使用 `Math.random() >= .5`。 從統計學上講,這個表達式有 50 的機率返回 `true`,另外 50 返回 `false`。 在第 render 方法裏,用此表達式替換 `null` 以完成變量聲明。
現在了一個表達式,可以使用該表達式在代碼中做出隨機決策。 接下來,需要實現此功能。 將 `Results` 組件渲染爲 `GameOfChance` 的子 組件,並將 `expression` 作爲名爲 `fiftyFifty` 的 prop 傳入 。 在 `Results` 組件中,編寫一個三元表達式來渲染 `h1` 元素的文本。`GameOfChance` 傳來的 prop `fiftyFifty` 來決定渲染文本 `You Win!` 還是 `You Lose!`。 最後,確保 `handleClick()` 方法正確計算每個回合,以便用戶知道他們玩過多少次。 這也可以讓用戶知道組件實際上已經更新,以防他們連續贏兩次或輸兩次時自己不知道。
# --hints--
`GameOfChance` 組件應該存在並渲染到頁面。
```js
assert.strictEqual(
Enzyme.mount(React.createElement(GameOfChance)).find('GameOfChance').length,
1
);
```
`GameOfChance` 應該返回單個 `button` 元素。
```js
assert.strictEqual(
Enzyme.mount(React.createElement(GameOfChance)).find('button').length,
1
);
```
`GameOfChance` 應該返回 `Results` 組件的一個實例,它有一個名爲 `fiftyFifty` 的 prop。
```js
assert(
Enzyme.mount(React.createElement(GameOfChance)).find('Results').length ===
1 &&
Enzyme.mount(React.createElement(GameOfChance))
.find('Results')
.props()
.hasOwnProperty('fiftyFifty') === true
);
```
`GameOfChance` 的 state 應該使用值爲 `1``counter` 屬性來初始化。
```js
assert.strictEqual(
Enzyme.mount(React.createElement(GameOfChance)).state().counter,
1
);
```
`GameOfChance` 組件第一次渲染到 DOM 時,應該返回一個 `p` 元素,其內部文本爲 `Turn: 1`
```js
assert.strictEqual(
Enzyme.mount(React.createElement(GameOfChance)).find('p').text(),
'Turn: 1'
);
```
每次點擊按鈕counter 應該增加 1並且一個包含文本 `Turn: N``p` 元素應該渲染到 DOM其中 `N` 是 counter 的值。
```js
(() => {
const comp = Enzyme.mount(React.createElement(GameOfChance));
const simulate = () => {
comp.find('button').simulate('click');
};
const result = () => ({
count: comp.state('counter'),
text: comp.find('p').text()
});
const _1 = () => {
simulate();
return result();
};
const _2 = () => {
simulate();
return result();
};
const _3 = () => {
simulate();
return result();
};
const _4 = () => {
simulate();
return result();
};
const _5 = () => {
simulate();
return result();
};
const _1_val = _1();
const _2_val = _2();
const _3_val = _3();
const _4_val = _4();
const _5_val = _5();
assert(
_1_val.count === 2 &&
_1_val.text === 'Turn: 2' &&
_2_val.count === 3 &&
_2_val.text === 'Turn: 3' &&
_3_val.count === 4 &&
_3_val.text === 'Turn: 4' &&
_4_val.count === 5 &&
_4_val.text === 'Turn: 5' &&
_5_val.count === 6 &&
_5_val.text === 'Turn: 6'
);
})();
```
`GameOfChance` 組件第一次掛載到 DOM 上時,每次按鈕被點擊,都應該返回一個 `h1` 元素,元素中隨機渲染 `You Win!` 或者 `You Lose!`
```js
(() => {
const comp = Enzyme.mount(React.createElement(GameOfChance));
const simulate = () => {
comp.find('button').simulate('click');
};
const result = () => ({
h1: comp.find('h1').length,
text: comp.find('h1').text()
});
const _1 = result();
const _2 = () => {
simulate();
return result();
};
const _3 = () => {
simulate();
return result();
};
const _4 = () => {
simulate();
return result();
};
const _5 = () => {
simulate();
return result();
};
const _6 = () => {
simulate();
return result();
};
const _7 = () => {
simulate();
return result();
};
const _8 = () => {
simulate();
return result();
};
const _9 = () => {
simulate();
return result();
};
const _10 = () => {
simulate();
return result();
};
const _2_val = _2();
const _3_val = _3();
const _4_val = _4();
const _5_val = _5();
const _6_val = _6();
const _7_val = _7();
const _8_val = _8();
const _9_val = _9();
const _10_val = _10();
const __text = new Set([
_1.text,
_2_val.text,
_3_val.text,
_4_val.text,
_5_val.text,
_6_val.text,
_7_val.text,
_8_val.text,
_9_val.text,
_10_val.text
]);
const __h1 = new Set([
_1.h1,
_2_val.h1,
_3_val.h1,
_4_val.h1,
_5_val.h1,
_6_val.h1,
_7_val.h1,
_8_val.h1,
_9_val.h1,
_10_val.h1
]);
assert(__text.size === 2 && __h1.size === 1);
})();
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<GameOfChance />, document.getElementById('root'));
```
## --seed-contents--
```jsx
class Results extends React.Component {
constructor(props) {
super(props);
}
render() {
{/* Change code below this line */}
return <h1></h1>;
{/* Change code above this line */}
}
}
class GameOfChance extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 1
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => {
// Complete the return statement:
return {
counter: prevState
}
});
}
render() {
const expression = null; // Change this line
return (
<div>
<button onClick={this.handleClick}>Play Again</button>
{/* Change code below this line */}
{/* Change code above this line */}
<p>{'Turn: ' + this.state.counter}</p>
</div>
);
}
}
```
# --solutions--
```jsx
class Results extends React.Component {
constructor(props) {
super(props);
}
render() {
return <h1>{this.props.fiftyFifty ? 'You Win!' : 'You Lose!'}</h1>;
}
}
class GameOfChance extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 1
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => {
return {
counter: prevState.counter + 1
}
});
}
render() {
const expression = Math.random() >= 0.5;
return (
<div>
<button onClick={this.handleClick}>Play Again</button>
<Results fiftyFifty={expression} />
<p>{'Turn: ' + this.state.counter}</p>
</div>
);
}
}
```

View File

@@ -0,0 +1,75 @@
---
id: 5a24bbe0dba28a8d3cbd4c5f
title: 渲染 HTML 元素爲 DOM 樹
challengeType: 6
forumTopicId: 301406
dashedName: render-html-elements-to-the-dom
---
# --description--
到目前爲止,已經瞭解到 JSX 是一種在 JavaScript 中編寫可讀 HTML 的便捷工具。 在 React 中,可以使用它的的渲染 APIReactDOM將此 JSX 直接渲染到 HTML DOM。
ReactDOM 提供了一個簡單的方法來將 React 元素呈現給 DOM如下所示`ReactDOM.render(componentToRender, targetNode)`,其中第一個參數是要渲染的 React 元素或組件,第二個參數是組件將要渲染到的 DOM 節點。
如你所料,必須在 JSX 元素聲明之後調用 `ReactDOM.render()`,就像在使用變量之前必須聲明它一樣。
# --instructions--
代碼編輯器有一個簡單的 JSX 組件。 使用 `ReactDOM.render()` 方法將該組件渲染到頁面。 可以將定義好的 JSX 元素直接作爲第一個參數傳入,然後使用 `document.getElementById()` 來選擇要渲染到的 DOM 節點, 在這個挑戰中,請渲染到 `id='challenge-node'``div` 中。 確保沒有修改 `JSX` 常量。
# --hints--
常量 `JSX` 應該返回一個 `div` 元素。
```js
assert(JSX.type === 'div');
```
`div` 應該包含一個 `h1` 標籤作爲第一個元素。
```js
assert(JSX.props.children[0].type === 'h1');
```
`div` 應該包含一個 `p` 標籤作爲第二個元素。
```js
assert(JSX.props.children[1].type === 'p');
```
提供的 JSX 元素應該渲染到 id 爲 `challenge-node` 的 DOM 節點。
```js
assert(
document.getElementById('challenge-node').childNodes[0].innerHTML ===
'<h1>Hello World</h1><p>Lets render this to the DOM</p>'
);
```
# --seed--
## --seed-contents--
```jsx
const JSX = (
<div>
<h1>Hello World</h1>
<p>Lets render this to the DOM</p>
</div>
);
// Change code below this line
```
# --solutions--
```jsx
const JSX = (
<div>
<h1>Hello World</h1>
<p>Lets render this to the DOM</p>
</div>
);
// Change code below this line
ReactDOM.render(JSX, document.getElementById('challenge-node'));
```

View File

@@ -0,0 +1,76 @@
---
id: 5a24c314108439a4d403618d
title: 用 renderToString 在服務器上渲染 React
challengeType: 6
forumTopicId: 301407
dashedName: render-react-on-the-server-with-rendertostring
---
# --description--
到目前爲止,已經能夠在客戶端上渲染 React 組件, 一般來說我們都是這麼做的。 然而,在一些用例中,需要在服務器上渲染一個 React 組件。 由於 React 是一個 JavaScript 視圖庫,所以通常使用 Node 讓 JavaScript 運行在服務器上。 事實上React 提供了一個可用於此目的的 `renderToString()` 方法。
有兩個關鍵原因可以解釋爲什麼服務器上的渲染可能會在真實世界的應用程序中使用。 首先,如果不這樣做,當 React 應用程序最初加載到瀏覽器時,它將包含一個代碼量很少的 HTML 文件和一大堆 JavaScript。 這對於搜索引擎來說可能不太理想,因爲它們試圖爲網頁內容生成索引,以便人們可以找到這個應用。 如果在服務器上渲染初始 HTML 標記並將其發送到客戶端,則初始頁面加載的內容包含搜索引擎可以抓取的所有頁面標記。 其次,這創造了更快的初始頁面加載體驗,因爲渲染的 HTML 代碼量要比整個應用程序的 JavaScript 代碼小。 React 仍然能夠識別你的應用並在初始加載後進行管理。
# --instructions--
`renderToString()` 方法由 `ReactDOMServer` 提供,在這裏已爲你定義成全局變量。 這個方法接收一個 React 元素作爲參數。 用它來把 `App` 渲染成字符串。
# --hints--
`App` 組件應該使用 `ReactDOMServer.renderToString` 渲染一個字符串。
```js
(getUserInput) =>
assert(
getUserInput('index')
.replace(/ /g, '')
.includes('ReactDOMServer.renderToString(<App/>)') &&
Enzyme.mount(React.createElement(App)).children().name() === 'div'
);
```
# --seed--
## --before-user-code--
```jsx
var ReactDOMServer = { renderToString(x) { return null; } };
```
## --after-user-code--
```jsx
ReactDOM.render(<App />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div/>
}
};
// Change code below this line
```
# --solutions--
```jsx
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div/>
}
};
// Change code below this line
ReactDOMServer.renderToString(<App/>);
```

View File

@@ -0,0 +1,119 @@
---
id: 5a24c314108439a4d4036172
title: 以另一種方式在用戶界面中渲染狀態
challengeType: 6
forumTopicId: 301408
dashedName: render-state-in-the-user-interface-another-way
---
# --description--
還有另一種方法可以訪問組件中的 `state`。 在 `render()` 方法中,在 `return` 語句之前,可以直接編寫 JavaScript。 例如,可以聲明函數、從 `state``props` 中訪問數據、對此數據執行計算等。 然後,可以將任何數據賦值給 `return` 語句中可以訪問的變量。
# --instructions--
`MyComponent` 的 render 方法中,定義一個名爲 `name``const`(常量),並將其設置爲組件 `state` 中的 name 值。 因爲可以直接在代碼部分編寫 JavaScript所以不需要用大括號括起來。
接下來,在 return 語句中,在 `h1` 標籤中渲染變量 `name` 的值。 記住,在 return 語句中需要使用 JSX 語法(用到 JavaScript 的花括號)。
# --hints--
`MyComponent` 應該有一個鍵 `name`,其值 `freeCodeCamp` 存儲在其 state 中。
```js
assert(
Enzyme.mount(React.createElement(MyComponent)).state('name') ===
'freeCodeCamp'
);
```
`MyComponent` 應該在 `div` 中渲染一個 `h1` 標題。
```js
assert(
/<div><h1>.*<\/h1><\/div>/.test(
Enzyme.mount(React.createElement(MyComponent)).html()
)
);
```
渲染的 `h1` 標籤應該包含 `{name}` 的引用。
```js
(getUserInput) =>
assert(/<h1>\n*\s*\{\s*name\s*\}\s*\n*<\/h1>/.test(getUserInput('index')));
```
渲染的 `h1` 標題中應該包含一段文本,這段文本是從組件的 state 中渲染出來的。
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const first = () => {
mockedComponent.setState({ name: 'TestName' });
return waitForIt(() => mockedComponent.html());
};
const firstValue = await first();
assert(firstValue === '<div><h1>TestName</h1></div>');
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'freeCodeCamp'
}
}
render() {
// Change code below this line
// Change code above this line
return (
<div>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'freeCodeCamp'
}
}
render() {
// Change code below this line
const name = this.state.name;
// Change code above this line
return (
<div>
{ /* Change code below this line */ }
<h1>{name}</h1>
{ /* Change code above this line */ }
</div>
);
}
};
```

View File

@@ -0,0 +1,113 @@
---
id: 5a24c314108439a4d4036171
title: 在用戶界面中渲染狀態
challengeType: 6
forumTopicId: 301409
dashedName: render-state-in-the-user-interface
---
# --description--
定義了組件的初始 state 之後,就可以在要渲染的 UI 中顯示它。 如果組件是有狀態的,它將始終可以訪問 `render()` 方法中 `state` 的數據。 就可以使用 `this.state` 訪問數據。
如果想在 render 方法的 `return` 中訪問 state 值,必須把這個值用花括號括起來。
`state` 是 React 組件中最強大的特性之一, 它可以跟蹤應用程序中的重要數據,並根據數據的變化渲染 UI。 如果數據發生變化UI 也會隨之改變。 React 使用所謂的虛擬 DOM 來跟蹤幕後的變化。 當 state 數據更新時,它會使用該數據觸發組件的重新渲染 -- 包括接收 prop 數據的子組件。 React 只在必要的時候更新實際的 DOM 這意味着你不必擔心 DOM 的變更, 只需聲明 UI 的外觀即可。
注意,如果組件是有狀態的,其它組件並不知道它的 `state`。 它的 `state` 是完全封裝的,或者是侷限於組件本身的,除非你將 state 數據作爲 `props` 傳遞給子組件。 封裝 `state` 的概念非常重要,因爲它允許編寫特定的邏輯,然後將該邏輯包含並隔離在代碼中的某個位置。
# --instructions--
在代碼編輯器中,`MyComponent` 是一個有狀態組件, 在組件的 render 方法中定義一個`h1`標籤,該方法從組件的 state 渲染 `name` 的值。
**注意:** `h1` 應該只渲染來自 `state` 的值。 在 JSX 中,使用花括號 `{ }` 編寫的任何代碼都將被視爲 JavaScript。 因此,要訪問 `state` 中的值,只需將引用括在花括號中即可。
# --hints--
`MyComponent` 應該有一個鍵 `name`,其值 `freeCodeCamp` 存儲在其 state 中。
```js
assert(
Enzyme.mount(React.createElement(MyComponent)).state('name') ===
'freeCodeCamp'
);
```
`MyComponent` 應該在 `div` 中渲染一個 `h1` 標題。
```js
assert(
/<div><h1>.*<\/h1><\/div>/.test(
Enzyme.mount(React.createElement(MyComponent)).html()
)
);
```
渲染的 `h1` 標題中應該只包含一段文本,這段文本是從組件的 state 中渲染出來的。
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const first = () => {
mockedComponent.setState({ name: 'TestName' });
return waitForIt(() => mockedComponent.html());
};
const firstValue = await first();
const getValue = firstValue.replace(/\s/g, '');
assert(getValue === '<div><h1>TestName</h1></div>');
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'freeCodeCamp'
}
}
render() {
return (
<div>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'freeCodeCamp'
}
}
render() {
return (
<div>
{ /* Change code below this line */ }
<h1>{this.state.name}</h1>
{ /* Change code above this line */ }
</div>
);
}
};
```

View File

@@ -0,0 +1,155 @@
---
id: 5a24c314108439a4d4036184
title: 使用 If-Else 條件進行渲染
challengeType: 6
forumTopicId: 301410
dashedName: render-with-an-if-else-condition
---
# --description--
使用 JavaScript 控制渲染視圖的另一個應用是按條件渲染元素。 當條件爲真時,將呈現一個視圖, 反之,則呈現另一種視圖。 可以在 React 組件的 `render()` 方法中使用的標準 `if/else` 語句來實現這一點。
# --instructions--
MyComponent 的 state 中包含一個 `boolean`(布爾值),用於跟蹤是否要在 UI 中顯示某個元素。 `button` 切換此值的狀態。 目前,它每次都呈現相同的 UI。 用 `if/else` 語句重寫 `render()` 方法,如果 `display``true` 則返回當前標記。 否則,返回不帶 `h1` 元素的標記。
**注意:**`if/else` 語句才能通過測試, 使用三元運算符是不會通過的。
# --hints--
`MyComponent` 應該存在並被渲染。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.find('MyComponent').length === 1;
})()
);
```
`display` 被設置爲 `true` 時,`div``button``h1` 標籤應該被渲染。
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const state_1 = () => {
mockedComponent.setState({ display: true });
return waitForIt(() => mockedComponent);
};
const updated = await state_1();
assert(
mockedComponent.find('div').length === 1 &&
mockedComponent.find('div').children().length === 2 &&
mockedComponent.find('button').length === 1 &&
mockedComponent.find('h1').length === 1
);
};
```
`display` 被設置爲 `false` 時,只有 `div``button` 應該被渲染。
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const state_1 = () => {
mockedComponent.setState({ display: false });
return waitForIt(() => mockedComponent);
};
const updated = await state_1();
assert(
mockedComponent.find('div').length === 1 &&
mockedComponent.find('div').children().length === 1 &&
mockedComponent.find('button').length === 1 &&
mockedComponent.find('h1').length === 0
);
};
```
Render 方法中應該使用 `if/else` 語句來檢查 `this.state.display` 的條件。
```js
(getUserInput) =>
assert(
getUserInput('index').includes('if') &&
getUserInput('index').includes('else')
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
display: true
}
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
this.setState((state) => ({
display: !state.display
}));
}
render() {
// Change code below this line
return (
<div>
<button onClick={this.toggleDisplay}>Toggle Display</button>
<h1>Displayed!</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
display: true
}
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
this.setState((state) => ({
display: !state.display
}));
}
render() {
// Change code below this line
if (this.state.display) {
return (
<div>
<button onClick={this.toggleDisplay}>Toggle Display</button>
<h1>Displayed!</h1>
</div>
);
} else {
return (
<div>
<button onClick={this.toggleDisplay}>Toggle Display</button>
</div>
);
}
}
};
```

View File

@@ -0,0 +1,145 @@
---
id: 5a24c314108439a4d403616f
title: 複習使用無狀態函數組件的 Props
challengeType: 6
forumTopicId: 301411
dashedName: review-using-props-with-stateless-functional-components
---
# --description--
除了上一個挑戰,一直在將 props 傳遞給無狀態的函數組件。 這些組件就像純函數, 它們接收 props 作爲輸入,並在每次傳遞相同 props 時返回相同的視圖。 你可能好奇什麼是狀態,下一個挑戰將會更詳細地講述它。 在此之前,我們先來回顧一下組件的術語。
*無狀態函數組件*是一個函數,它接收 props 作爲輸入並返回 JSX。 另一方面,*無狀態組件*是一個類,它擴展了`React.Component`,但是不使用內部狀態(下一個挑戰中討論)。 最後,*狀態組件*是指維護其自身內部狀態的組件, 它簡稱組件或 React 組件。
一種常見的應用模式是儘可能減少狀態組件並創建無狀態的函數組件。 這有助於將狀態管理包含到應用程序的特定區域。 反過來,通過更容易地跟蹤狀態變化如何影響其行爲,可以改善應用程序的開發和維護。
# --instructions--
在代碼編輯器中有一個 `CampSite` 組件,它把 `Camper` 組件渲染爲自己的子組件。 定義 `Camper` 組件,併爲其分配默認 props `{ name: 'CamperBot' }`。 可以在 `Camper` 組件內部渲染任何你想要的代碼,但是要確保有一個 `p` 元素,它只包含作爲 `prop` 傳遞的 `name` 值。 最後,在 `Camper` 組件上定義 `propTypes`,要求提供 `name` 作爲 prop並驗證它是 `string` 類型。
# --hints--
應該渲染 `CampSite` 組件。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(CampSite));
return mockedComponent.find('CampSite').length === 1;
})()
);
```
應該渲染 `Camper` 組件。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(CampSite));
return mockedComponent.find('Camper').length === 1;
})()
);
```
`Camper` 組件應該包含默認 props它將字符串 `CamperBot` 賦值給關鍵字 `name`
```js
assert(
/Camper.defaultProps={name:(['"`])CamperBot\1,?}/.test(
__helpers.removeWhiteSpace(code)
)
);
```
`Camper` 組件應該包含 `string` 類型的 `name` prop。
```js
assert(
/Camper.propTypes={name:PropTypes.string.isRequired,?}/.test(
__helpers.removeWhiteSpace(code)
)
);
```
`Camper` 組件應該包含 `p` 元素,元素的文本是 prop 的 `name`
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(CampSite));
return (
mockedComponent.find('p').text() ===
mockedComponent.find('Camper').props().name
);
})()
);
```
# --seed--
## --before-user-code--
```jsx
var PropTypes = {
string: { isRequired: true }
};
```
## --after-user-code--
```jsx
ReactDOM.render(<CampSite />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class CampSite extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Camper/>
</div>
);
}
};
// Change code below this line
```
# --solutions--
```jsx
class CampSite extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Camper/>
</div>
);
}
};
// Change code below this line
const Camper = (props) => {
return (
<div>
<p>{props.name}</p>
</div>
);
};
Camper.propTypes = {
name: PropTypes.string.isRequired
};
Camper.defaultProps = {
name: 'CamperBot'
};
```

View File

@@ -0,0 +1,143 @@
---
id: 5a24c314108439a4d4036173
title: 用 this.setState 設置狀態
challengeType: 6
forumTopicId: 301412
dashedName: set-state-with-this-setstate
---
# --description--
前面的挑戰涵蓋了組件的 `state` 以及如何在 `constructor` 中初始化 state。 還有一種方法可以更改組件的 `state` React 提供了 `setState` 方法來更新組件的 `state`。 在組件類中調用 `setState` 方法如下所示:`this.setState()`,傳入鍵值對的對象, 其中鍵是 state 屬性,值是更新後的 state 數據。 例如,如果我們在 state 中存儲 `username`,並想要更新它,代碼如下所示:
```jsx
this.setState({
username: 'Lewis'
});
```
React 要求永遠不要直接修改 `state`,而是在 state 發生改變時始終使用 `this.setState()`。 此外應該注意React 可以批量處理多個 state 更新以提高性能。 這意味着通過 `setState` 方法進行的 state 更新可以是異步的。 `setState` 方法有一種替代語法可以解決異步問題, 雖然這很少用到,但是最好還是記住它! 有關詳細信息,請參閱[React 文檔](https://facebook.github.io/react/docs/state-and-lifecycle.html)。
# --instructions--
代碼編輯器中有一個 `button` 元素,它有一個 `onClick()` 處理程序。 當 `button` 在瀏覽器中接收到單擊事件時觸發此處理程序,並運行 `MyComponent` 中定義的 `handleClick` 方法。 在 `handleClick` 方法中,使用 `this.setState()` 更新組件的 `state`。 設置 `state` 中的 `name` 屬性爲字符串 `React Rocks!`
單擊按鈕查看渲染的 state 的更新。 如果不完全理解單擊處理程序代碼在此時的工作方式,請不要擔心。 在接下來的挑戰中會有講述。
# --hints--
`MyComponent` 的 state 應該使用鍵值對 `{ name: Initial State }` 來初始化。
```js
assert(
Enzyme.mount(React.createElement(MyComponent)).state('name') ===
'Initial State'
);
```
`MyComponent` 應該渲染一個 `h1` 標題。
```js
assert(Enzyme.mount(React.createElement(MyComponent)).find('h1').length === 1);
```
渲染的 `h1` 標題中應該包含一段文本,這段文本是從組件的 state 中渲染出來的。
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const first = () => {
mockedComponent.setState({ name: 'TestName' });
return waitForIt(() => mockedComponent.html());
};
const firstValue = await first();
assert(/<h1>TestName<\/h1>/.test(firstValue));
};
```
調用 `MyComponent``handleClick` 方法應該將 state 的 name 屬性設置爲 `React Rocks!`
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const first = () => {
mockedComponent.setState({ name: 'Before' });
return waitForIt(() => mockedComponent.state('name'));
};
const second = () => {
mockedComponent.instance().handleClick();
return waitForIt(() => mockedComponent.state('name'));
};
const firstValue = await first();
const secondValue = await second();
assert(firstValue === 'Before' && secondValue === 'React Rocks!');
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'Initial State'
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// Change code below this line
// Change code above this line
}
render() {
return (
<div>
<button onClick={this.handleClick}>Click Me</button>
<h1>{this.state.name}</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'Initial State'
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// Change code below this line
this.setState({
name: 'React Rocks!'
});
// Change code above this line
}
render() {
return (
<div>
<button onClick = {this.handleClick}>Click Me</button>
<h1>{this.state.name}</h1>
</div>
);
}
};
```

View File

@@ -0,0 +1,146 @@
---
id: 5a24c314108439a4d4036185
title: Use && for a More Concise Conditional
challengeType: 6
forumTopicId: 301413
dashedName: use--for-a-more-concise-conditional
---
# --description--
`if/else` 語句在上一次挑戰中是有效的,但是有一種更簡潔的方法可以達到同樣的結果。 假設正在跟蹤組件中的幾個條件,並且希望根據這些條件中的每一個來渲染不同的元素。 如果你寫了很多 `else if` 語句來返回稍微不同的 UI你可能會寫很多重複代碼這就留下了出錯的空間。 相反,你可以使用 `&&` 邏輯運算符以更簡潔的方式執行條件邏輯。 這是完全可行的,因爲你希望檢查條件是否爲 `true`。如果是,則返回一些標記。 這裏有一個例子:
```jsx
{condition && <p>markup</p>}
```
如果 `condition``true`,則返回標記。 如果條件爲 `false` ,則在評估 `condition` 後操作將立即返回 `false`,並且不返回任何內容。 可以將這些語句直接包含在 JSX 中,並通過在每個條件後面寫 `&&` 來將多個條件串在一起。 這允許你在 `render()` 方法中處理更復雜的條件邏輯,而無需重複大量代碼。
# --instructions--
再來看看前面的示例,`h1` 還是在 `display``true` 時渲染,但使用 `&&` 邏輯運算符代替 `if/else` 語句。
# --hints--
`MyComponent` 應該存在並渲染。
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.find('MyComponent').length;
})()
);
```
`display` 被設置爲 `true` 時,`div``button``h1` 標籤應該渲染。
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const state_1 = () => {
mockedComponent.setState({ display: true });
return waitForIt(() => mockedComponent);
};
const updated = await state_1();
assert(
updated.find('div').length === 1 &&
updated.find('div').children().length === 2 &&
updated.find('button').length === 1 &&
updated.find('h1').length === 1
);
};
```
`display` 被設置爲 `false` 時,只有 `div``button` 應該渲染。
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const state_1 = () => {
mockedComponent.setState({ display: false });
return waitForIt(() => mockedComponent);
};
const updated = await state_1();
assert(
updated.find('div').length === 1 &&
updated.find('div').children().length === 1 &&
updated.find('button').length === 1 &&
updated.find('h1').length === 0
);
};
```
render 方法應該使用 `&&` 邏輯運算符來檢查 `this.state.display` 的條件。
```js
(getUserInput) => assert(getUserInput('index').includes('&&'));
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
display: true
}
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
this.setState(state => ({
display: !state.display
}));
}
render() {
// Change code below this line
return (
<div>
<button onClick={this.toggleDisplay}>Toggle Display</button>
<h1>Displayed!</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
display: true
}
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
this.setState(state => ({
display: !state.display
}));
}
render() {
// Change code below this line
return (
<div>
<button onClick={this.toggleDisplay}>Toggle Display</button>
{this.state.display && <h1>Displayed!</h1>}
</div>
);
}
};
```

View File

@@ -0,0 +1,280 @@
---
id: 5a24c314108439a4d4036187
title: 使用三元表達式進行條件渲染
challengeType: 6
forumTopicId: 301414
dashedName: use-a-ternary-expression-for-conditional-rendering
---
# --description--
在繼續使用動態渲染技術之前,還有最後一種方法可以渲染想要的東西,它使用內置的 JavaScript 條件:<dfn>三元運算符</dfn>。 三元運算符經常被用作 JavaScript 中 `if/else` 語句的縮寫。 它們不像傳統的 `if/else` 語句那樣強大,但是在 React 開發人員中非常流行, 原因之一就是 JSX 的編譯原理,`if/else` 語句不能直接插入到 JSX 代碼中。 可能你在前幾個挑戰就注意到了這一點——當需要 `if/else` 語句時,它總是在 `return` 語句的*外面*。 如果想在 JSX 中實現條件邏輯,三元表達式是一個很好的選擇。 回想一下,三元運算符有三個部分,但是可以將多個三元表達式組合在一起。 以下是基本語法:
```jsx
condition ? expressionIfTrue : expressionIfFalse;
```
# --instructions--
代碼編輯器在 `CheckUserAge` 組件的 `render()` 方法中定義了三個常量, 它們分別是 `buttonOne``buttonTwo``buttonThree`。 每個都分配了一個表示按鈕元素的簡單 JSX 表達式。 首先,使用 `input``userAge` 初始化 `CheckUserAge` 的 state並將其值設置爲空字符串。
一旦組件將信息渲染給頁面,用戶應該有一種方法與之交互。 在組件的 `return` 語句中,設置一個實現以下邏輯的三元表達式:當頁面首次加載時,將提交按鈕 `buttonOne` 渲染到頁面。 然後,當用戶輸入年齡並點擊該按鈕時,根據年齡渲染不同的按鈕。 如果用戶輸入的數字小於`18`,則渲染`buttonThree`。 如果用戶輸入的數字大於或等於`18`,則渲染`buttonTwo`
# --hints--
`CheckUserAge` 組件應該渲染出單個 `input` 元素和單個 `button` 元素。
```js
assert(
Enzyme.mount(React.createElement(CheckUserAge)).find('div').find('input')
.length === 1 &&
Enzyme.mount(React.createElement(CheckUserAge)).find('div').find('button')
.length === 1
);
```
`CheckUserAge` 組件的 state 應該用 `userAge` 屬性和 `input` 屬性初始化,並且這兩個屬性的值都被設置爲空字符串。
```js
assert(
Enzyme.mount(React.createElement(CheckUserAge)).state().input === '' &&
Enzyme.mount(React.createElement(CheckUserAge)).state().userAge === ''
);
```
`CheckUserAge` 組件首次渲染到 DOM 時,`button` 內部的文本應爲 Submit。
```js
assert(
Enzyme.mount(React.createElement(CheckUserAge)).find('button').text() ===
'Submit'
);
```
當小於 18 的數字輸入到 `input` 元素中,並點擊該`button` 時,該 `button` 的內部文本應該是 `You Shall Not Pass`
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge));
const initialButton = mockedComponent.find('button').text();
const enter3AndClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '3' } });
mockedComponent.find('button').simulate('click');
mockedComponent.update();
return mockedComponent.find('button').text();
};
const enter17AndClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '17' } });
mockedComponent.find('button').simulate('click');
mockedComponent.update();
return mockedComponent.find('button').text();
};
const userAge3 = enter3AndClickButton();
const userAge17 = enter17AndClickButton();
assert(
initialButton === 'Submit' &&
userAge3 === 'You Shall Not Pass' &&
userAge17 === 'You Shall Not Pass'
);
})();
```
當大於或等於 18 的數字輸入到 `input` 元素中,並點擊該 `button` 時,該 `button` 的內部文本應該是 `You May Enter`
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge));
const initialButton = mockedComponent.find('button').text();
const enter18AndClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '18' } });
mockedComponent.find('button').simulate('click');
mockedComponent.update();
return mockedComponent.find('button').text();
};
const enter35AndClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '35' } });
mockedComponent.find('button').simulate('click');
mockedComponent.update();
return mockedComponent.find('button').text();
};
const userAge18 = enter18AndClickButton();
const userAge35 = enter35AndClickButton();
assert(
initialButton === 'Submit' &&
userAge18 === 'You May Enter' &&
userAge35 === 'You May Enter'
);
})();
```
提交了一個數字之後,並再次更改了 `input` 的值,該 `button` 內部文本應該變回 `Submit`
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge));
const enter18AndClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '18' } });
mockedComponent.find('button').simulate('click');
mockedComponent.update();
return mockedComponent.find('button').text();
};
const changeInputDontClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '5' } });
mockedComponent.update();
return mockedComponent.find('button').text();
};
const enter10AndClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '10' } });
mockedComponent.find('button').simulate('click');
mockedComponent.update();
return mockedComponent.find('button').text();
};
const userAge18 = enter18AndClickButton();
const changeInput1 = changeInputDontClickButton();
const userAge10 = enter10AndClickButton();
const changeInput2 = changeInputDontClickButton();
assert(
userAge18 === 'You May Enter' &&
changeInput1 === 'Submit' &&
userAge10 === 'You Shall Not Pass' &&
changeInput2 === 'Submit'
);
})();
```
你的代碼不應該包含任何 `if/else` 語句。
```js
assert(
new RegExp(/(\s|;)if(\s|\()/).test(
Enzyme.mount(React.createElement(CheckUserAge)).instance().render.toString()
) === false
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<CheckUserAge />, document.getElementById('root'));
```
## --seed-contents--
```jsx
const inputStyle = {
width: 235,
margin: 5
};
class CheckUserAge extends React.Component {
constructor(props) {
super(props);
// Change code below this line
// Change code above this line
this.submit = this.submit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({
input: e.target.value,
userAge: ''
});
}
submit() {
this.setState(state => ({
userAge: state.input
}));
}
render() {
const buttonOne = <button onClick={this.submit}>Submit</button>;
const buttonTwo = <button>You May Enter</button>;
const buttonThree = <button>You Shall Not Pass</button>;
return (
<div>
<h3>Enter Your Age to Continue</h3>
<input
style={inputStyle}
type='number'
value={this.state.input}
onChange={this.handleChange}
/>
<br />
{/* Change code below this line */}
{/* Change code above this line */}
</div>
);
}
}
```
# --solutions--
```jsx
const inputStyle = {
width: 235,
margin: 5
};
class CheckUserAge extends React.Component {
constructor(props) {
super(props);
this.state = {
userAge: '',
input: ''
};
this.submit = this.submit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({
input: e.target.value,
userAge: ''
});
}
submit() {
this.setState(state => ({
userAge: state.input
}));
}
render() {
const buttonOne = <button onClick={this.submit}>Submit</button>;
const buttonTwo = <button>You May Enter</button>;
const buttonThree = <button>You Shall Not Pass</button>;
return (
<div>
<h3>Enter Your Age to Continue</h3>
<input
style={inputStyle}
type='number'
value={this.state.input}
onChange={this.handleChange}
/>
<br />
{this.state.userAge === ''
? buttonOne
: this.state.userAge >= 18
? buttonTwo
: buttonThree}
</div>
);
}
}
```

Some files were not shown because too many files have changed in this diff Show More