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