mirror of
https://github.com/microsoft/frontend-bootcamp.git
synced 2026-01-26 14:56:42 +08:00
changed up steps 1,2,3 to have exercises. removed js implementation of todo app
This commit is contained in:
@@ -1,27 +0,0 @@
|
||||
## How the Web Works
|
||||
|
||||
A simple web page is rendered on the screen via the following steps.
|
||||
|
||||
*There are many sub-steps in this process, but these are the highlights.*
|
||||
|
||||
1. You instruct the browser which web page you'd like to see
|
||||
2. The browser looks up the site in a "DNS Server"
|
||||
- This is like a big phone book for website server addresses
|
||||
3. The browser asks the server to send over a specific page of the website, such as `developer.mozilla.org/filename.html` or `developer.mozilla.org`
|
||||
- If asked for a "root"-level address, most servers will return `<root>/index.html`
|
||||
4. The server sends the HTML file back to the browser
|
||||
5. The browser starts to read the HTML file from the top to the bottom, stopping any time that additional resources are required:
|
||||
- CSS stylesheets
|
||||
- JavaScript files
|
||||
- Fonts
|
||||
- Images
|
||||
6. Browser makes requests for additional resources
|
||||
- Those resources might request even more files
|
||||
7. Once the browser gets to the bottom of the page it can start working on rendering, and then display the page
|
||||
|
||||

|
||||
|
||||
|
||||
## Next Step
|
||||
|
||||
[HTML Demo](./html-demo)
|
||||
@@ -1,92 +0,0 @@
|
||||
/* Targeting the entire page */
|
||||
body {
|
||||
font: 1.2em sans-serif;
|
||||
}
|
||||
|
||||
/* Targeting an HTML tag */
|
||||
h1 {
|
||||
/* Color name */
|
||||
color: black;
|
||||
|
||||
/* 6-digit hex */
|
||||
background: #ababab;
|
||||
|
||||
/* Margin: specified separately for each side */
|
||||
margin-bottom: 15px;
|
||||
margin-top: 15px;
|
||||
|
||||
/* Shorthand: Padding applies to all sides */
|
||||
padding: 10px;
|
||||
|
||||
/* Border shorthand and 3-digit hex */
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
/* Overriding inherited styles */
|
||||
span {
|
||||
color: #004578;
|
||||
}
|
||||
|
||||
/* Sibling selector */
|
||||
a ~ a {
|
||||
/* Changing elements from inline to block */
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Targeting a class name */
|
||||
.tiles {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Descendant selector */
|
||||
.tiles img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Direct descendant selector */
|
||||
.tiles > div {
|
||||
/* rgb color */
|
||||
background: rgb(10, 10, 10);
|
||||
color: white;
|
||||
flex-basis: 100%;
|
||||
/* Padding/margin shorthand. Goes clockwise from top.
|
||||
10px - all
|
||||
10px 20px - top/bottom left/right
|
||||
10px 20px 15px - top left/right bottom
|
||||
*/
|
||||
padding: 10px 20px 15px;
|
||||
margin: 10px 20px 10px 0;
|
||||
border: 1px solid white;
|
||||
}
|
||||
|
||||
/* Qualified selector */
|
||||
div.important-links {
|
||||
background: #004578;
|
||||
}
|
||||
|
||||
/* Style inheritance only works for unstyled elements */
|
||||
a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Hover pseudo-selector */
|
||||
a:hover {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
/* Positional pseudo-selector */
|
||||
.tiles > div:last-child {
|
||||
/* overrides margin-right but leaves other margins alone */
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
/* ID selector */
|
||||
#contact-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Attribute selector */
|
||||
input[type='submit'] {
|
||||
margin-top: 10px;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="./css-demo-final.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h1>This is my <span>Title</span></h1>
|
||||
<div class="tiles">
|
||||
<div class="important-links">
|
||||
<h2>Important Links</h2>
|
||||
<a href="#">We're Awesome</a>
|
||||
<a href="#">Learn More</a>
|
||||
<a href="#">Hire Us</a>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Our Logo</h2>
|
||||
<img src="../../assets/fabric.jpg" width="100" alt="fabric logo" />
|
||||
</div>
|
||||
<div>
|
||||
<h2>Contact Us</h2>
|
||||
<div id="contact-form">
|
||||
<label>Email</label><input type="email" />
|
||||
<input value="Submit" type="submit" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,62 +0,0 @@
|
||||
/* body {
|
||||
font: 1.2em sans-serif;
|
||||
} */
|
||||
|
||||
/* h1 {
|
||||
color: black;
|
||||
background: #ababab;
|
||||
margin-bottom: 15px;
|
||||
margin-top: 15px;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
} */
|
||||
|
||||
/* span {
|
||||
color: #004578;
|
||||
} */
|
||||
|
||||
/* a ~ a {
|
||||
display: block;
|
||||
} */
|
||||
|
||||
/* .tiles {
|
||||
display: flex;
|
||||
} */
|
||||
|
||||
/* .tiles img {
|
||||
width: 100%;
|
||||
} */
|
||||
|
||||
/* .tiles > div {
|
||||
background: rgb(10, 10, 10);
|
||||
color: white;
|
||||
flex-basis: 100%;
|
||||
padding: 10px 20px 15px;
|
||||
margin: 10px 20px 10px 0;
|
||||
border: 1px solid white;
|
||||
} */
|
||||
|
||||
/* div.important-links {
|
||||
background: #004578;
|
||||
} */
|
||||
|
||||
/* a {
|
||||
color: white;
|
||||
} */
|
||||
|
||||
/* a:hover {
|
||||
color: #ccc;
|
||||
} */
|
||||
|
||||
/* .tiles > div:last-child {
|
||||
margin-right: 0;
|
||||
} */
|
||||
|
||||
/* #contact-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
} */
|
||||
|
||||
/* input[type='submit'] {
|
||||
margin-top: 10px;
|
||||
} */
|
||||
77
step1-01/demo/README.md
Normal file
77
step1-01/demo/README.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# How the Web Works
|
||||
|
||||
A simple web page is rendered on the screen via the following steps.
|
||||
|
||||
_There are many sub-steps in this process, but these are the highlights._
|
||||
|
||||
1. You instruct the browser which web page you'd like to see
|
||||
2. The browser looks up the site in a "DNS Server"
|
||||
- This is like a big phone book for website server addresses
|
||||
3. The browser asks the server to send over a specific page of the website, such as `developer.mozilla.org/filename.html` or `developer.mozilla.org`
|
||||
- If asked for a "root"-level address, most servers will return `<root>/index.html`
|
||||
4. The server sends the HTML file back to the browser
|
||||
5. The browser starts to read the HTML file from the top to the bottom, stopping any time that additional resources are required:
|
||||
- CSS stylesheets
|
||||
- JavaScript files
|
||||
- Fonts
|
||||
- Images
|
||||
6. Browser makes requests for additional resources
|
||||
- Those resources might request even more files
|
||||
7. Once the browser gets to the bottom of the page it can start working on rendering, and then display the page
|
||||
|
||||

|
||||
|
||||
# HTML Demo
|
||||
|
||||
HTML tags are the basis of all web applications. They give the page structure, and define the content within.
|
||||
|
||||
An HTML tag takes the following form:
|
||||
|
||||
```html
|
||||
<tag class="foo" onclick="myFunction()" otherAttributes="values"> </tag>
|
||||
```
|
||||
|
||||
HTML tags can also be nested to create a tree that we call the [Document Object Model](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction)
|
||||
|
||||
The [HTML demo page](https://microsoft.github.io/frontend-bootcamp/step1-01/html-demo/html-demo.html) is a large collection of HTML elements that you will come across during development. The full list of elements can be found on [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element).
|
||||
|
||||
## Sample Website
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<title>Frontend Workshop: By Micah Godbolt and Ken Chau</title>
|
||||
<link rel="stylesheet" href="./style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Frontend Workshop</h1>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="./about.html">About This Workshop</a></li>
|
||||
<li><a href="./participate.html">Take This Workshop</a></li>
|
||||
<li><a href="./contribute.html">Contribute to This Workshop</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<h2>About This Workshop</h2>
|
||||
<p>
|
||||
The first day provides an introduction to the fundamentals of the web: HTML, CSS and JavaScript.
|
||||
</p>
|
||||
<img src="../../assets/todo_screenshot.jpg" alt="Picture of the Todo App we will build" />
|
||||
<p>
|
||||
On the second day we'll dive into more advanced topics like TypeScript, testing, and state management.
|
||||
</p>
|
||||
</main>
|
||||
<footer>
|
||||
<h2>Get More Information</h2>
|
||||
<ul>
|
||||
<li><a href="https://github.com/Microsoft/frontend-bootcamp"> Frontend Bootcamp </a></li>
|
||||
<li><a href="https://twitter.com/micahgodbolt"> @micahgodbolt </a></li>
|
||||
<li><a href="https://twitter.com/kenneth_chau"> @kenneth_chau</a></li>
|
||||
</ul>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
@@ -32,39 +32,27 @@
|
||||
|
||||
<section>
|
||||
<h2><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element#Content_sectioning">Content Sections</a></h2>
|
||||
<pre>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Heading 1</h1>
|
||||
<h2>Heading 2</h2>
|
||||
<h3>Heading 3</h3>
|
||||
<h4>Heading 4</h4>
|
||||
<h5>Heading 5</h5>
|
||||
<h6>Heading 6</h6>
|
||||
|
||||
<div>
|
||||
<header>
|
||||
<h1>My Website H1 inside Header</h1>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="page1">About Me</a></li>
|
||||
<li><a href="page2">Contact Me</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
<nav></nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<h2>My Blog H2 inside Main</h2>
|
||||
<article>
|
||||
<header><h3>Blog Title 1 (H3 in Article Header)</h3></header>
|
||||
<aside>
|
||||
<p>Aside: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent dictum ultricies elit eget luctus.</p>
|
||||
</aside>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Tenetur architecto mollitia ducimus. Tempora dignissimos incidunt
|
||||
consequuntur amet recusandae, eligendi eaque maxime in veritatis delectus non, molestiae vel ipsa! Natus, fuga!
|
||||
</p>
|
||||
</article>
|
||||
<article>
|
||||
<header><h3>Blog Title 2</h3></header>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magnam, rem.
|
||||
</p>
|
||||
</article>
|
||||
</main>
|
||||
<footer>Copyright 2019 in the Footer</footer>
|
||||
</div>
|
||||
<main>
|
||||
<article></article>
|
||||
<aside></aside>
|
||||
</main>
|
||||
|
||||
<footer></footer>
|
||||
</body>
|
||||
</pre>
|
||||
</section>
|
||||
<hr />
|
||||
<section>
|
||||
@@ -135,21 +123,18 @@
|
||||
<a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element#Inline_text_semantics">Inline text elements</a>
|
||||
</h2>
|
||||
|
||||
<h3>Anchor tag, br and span</h3>
|
||||
<h3>Anchor tag and span</h3>
|
||||
|
||||
<a target="_blank" href="https://example.com"> Website <span style="color: red">address in span tag</span> </a> (<br> tag here)
|
||||
<br />
|
||||
<a target="_blank" href="mailto:m.bluth@example.com">Email</a> (no <br>) <a target="_blank" href="tel:+123456789">Phone</a
|
||||
><br />
|
||||
<a target="_blank" href="https://example.com"> Anchor Tag with <span style="color: red">span tag wrapped around part of it</span> </a>
|
||||
|
||||
<h3>Inline style tags</h3>
|
||||
<p><b>b tag</b> <em>em tag</em> <i>i tag</i> <sub>sub tag</sub> <sup>sup tab</sup> <code>code tag</code></p>
|
||||
|
||||
<h3>Image tag</h3>
|
||||
<div>
|
||||
<img src="../../assets/fabric.jpg" width="100" />
|
||||
<img src="../../assets/fabric.jpg" width="50" />
|
||||
<img src="../../assets/fabric.jpg" width="150" />
|
||||
<img src="../../assets/fabric.jpg" alt="Fabric Logo" width="100" />
|
||||
<img src="../../assets/fabric.jpg" alt="Fabric Logo" width="50" />
|
||||
<img src="../../assets/fabric.jpg" alt="Fabric Logo" width="150" />
|
||||
</div>
|
||||
</section>
|
||||
<hr />
|
||||
@@ -248,6 +233,5 @@
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
<a href="../css-demo/css-demo.html">CSS Demo</a>
|
||||
</body>
|
||||
</html>
|
||||
49
step1-01/exercise/README.md
Normal file
49
step1-01/exercise/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Step 1 Exercise
|
||||
|
||||
The power of HTML is its ability to represent complex information in a way that conveys meaning. In this exercise you are going to be creating an HTML page for my favorite recipe.
|
||||
|
||||
## The Exercise
|
||||
|
||||
1. Create a recipe page to host our recipe (page title, headings, sections, paragraphs, lists etc)
|
||||
2. Use title, header, main, footer, headings (h1/h2 etc), paragraphs, lists
|
||||
3. Use ordered and unordered lists appropriately
|
||||
4. Add the `baked_beans.jpg` iagine included in this folder
|
||||
5. Add an anchor tag around 'Wisconsin Beer Brats'
|
||||
|
||||
## 4th of July Baked Beans
|
||||
|
||||
It's great how a single meal can take you back dozens of years. This is one of those recipes that never seems to fail to impress.
|
||||
|
||||
I learned this recipe for the cousin of one of my college friends back in Nashville Tenessee. We had an amazing 4th of July feast which included this recipe and some bratworst like these Wisconsin Beer Brats https://www.culinaryhill.com/wisconsin-beer-brats/
|
||||
|
||||
Prep Time: 10 minutes
|
||||
Cook time: 3+ hours
|
||||
Servings: 12
|
||||
|
||||
**Ingredients**
|
||||
1LB Bacon chopped
|
||||
3 Cans Bush's Origin Baked Beans
|
||||
1 Walla Wall Onion chopped
|
||||
2 ground garlic cloves
|
||||
3 Tablespoons of mustard
|
||||
2 Tablespoons of molasses
|
||||
3 Tablespoons of brown sugar
|
||||
|
||||
**Directions**
|
||||
Cook bacon until it is mostly cooked, then drain most of the grease and put aside
|
||||
Cook onion in remaining bacon grease
|
||||
Combine onions and bacon, then add garlic, cook for a few more minutes
|
||||
Add beans and get up to simmer temperate
|
||||
Add mustard until your beans are nice and yellow
|
||||
Add molassas until color darkens again
|
||||
Add brown sugar until properly sweet
|
||||
Simmer for a long time, occassionally sturing
|
||||
|
||||
**Expert Tipes**
|
||||
Burning off most of the liquid gives you nice, hearty, sticky beans.
|
||||
If the beans get too try, you can always add beer!
|
||||
|
||||
**Nutritional Information**
|
||||
Calories: lots
|
||||
Fat: lots
|
||||
Fun: lots
|
||||
BIN
step1-01/exercise/baked_beans.jpg
Normal file
BIN
step1-01/exercise/baked_beans.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
4
step1-01/exercise/index.html
Normal file
4
step1-01/exercise/index.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body></body>
|
||||
</html>
|
||||
@@ -1,99 +0,0 @@
|
||||
# HTML Demo
|
||||
|
||||
HTML tags are the basis of all web applications. They give the page structure, and define the content within.
|
||||
|
||||
An HTML tag takes the following form:
|
||||
|
||||
```html
|
||||
<tag class="foo" onclick="myFunction()" otherAttributes="values"> </tag>
|
||||
```
|
||||
|
||||
HTML tags can also be nested to create a tree that we call the [Document Object Model](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction)
|
||||
|
||||
```html
|
||||
<div class="my-page">
|
||||
<h1>My Page</h1>
|
||||
<ul class="link">
|
||||
<li><a href="https://github.com/Microsoft/frontend-bootcamp"> Frontend Bootcamp </a></li>
|
||||
<li><a href="https://twitter.com/micahgodbolt"> @micahgodbolt </a></li>
|
||||
<li><a href="https://twitter.com/kenneth_chau"> @kenneth_chau</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
```
|
||||
|
||||
The [HTML demo page](https://microsoft.github.io/frontend-bootcamp/step1-01/html-demo/html-demo.html) is a large collection of HTML elements that you will come across during development. The full list of elements can be found on [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element).
|
||||
|
||||
To learn more about each element, click on the element names below.
|
||||
|
||||
## [Document Metadata](https://developer.mozilla.org/en-US/docs/Web/HTML/Element#Document_metadata)
|
||||
|
||||
- [`html`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html) - Root-level element
|
||||
- [`head`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head) - Provides general information (metadata) about the page
|
||||
- [`title`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title) - Defines document title shown in browser tab/title bar
|
||||
- [`link`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) - Links to external resources (usually stylesheets)
|
||||
- [`style`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style) - Inline style tag
|
||||
|
||||
## [Content Sectioning](https://developer.mozilla.org/en-US/docs/Web/HTML/Element#Content_sectioning)
|
||||
|
||||
- [`section`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section) - Generic section of content
|
||||
- [`header`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/header) - Introductory content or navigational aid
|
||||
- [`footer`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/footer) - Footer for nearest sectioning element
|
||||
- [`main`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main) - Dominant content
|
||||
- [`nav`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/nav) - Navigational aid
|
||||
- [`article`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/article) - Syndicated content
|
||||
- [`aside`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside) - Related information
|
||||
- [`h1`,`h2`,`h3`,`h4`,`h5`,`h6`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements) - Section headings
|
||||
|
||||
## [Block Text Content](https://developer.mozilla.org/en-US/docs/Web/HTML/Element#Text_content)
|
||||
|
||||
- [`div`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div) - Generic block level container
|
||||
- [`p`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p) - Paragraph
|
||||
- [`ol`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol) - Ordered list (1,2,3)
|
||||
- [`ul`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul) - Unordered list (bullets)
|
||||
- [`li`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li) - List item
|
||||
- [`pre`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre) - Preformatted text
|
||||
|
||||
## [Inline Text Elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element#Inline_text_semantics)
|
||||
|
||||
- [`a`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) - Anchor element for creating links to other pages, files, programs
|
||||
- [`span`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span) - Generic inline container
|
||||
- [`b`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/b) - Bring attention to content (usually bold)
|
||||
- [`em`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/em) - Stress emphasis (usually italic)
|
||||
- [`i`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/i) - Range of text set off from normal text (usually italic)
|
||||
- [`sub`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sub) - Subscript text
|
||||
- [`sup`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup) - Superscript text
|
||||
- [`code`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code) - Fragment of computer code (monospace)
|
||||
|
||||
## [Image and multimedia](https://developer.mozilla.org/en-US/docs/Web/HTML/Element#Inline_text_semantics)
|
||||
|
||||
- [`img`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img) - Embeds image into document
|
||||
|
||||
## [Table Content](https://developer.mozilla.org/en-US/docs/Web/HTML/Element#Table_content)
|
||||
|
||||
- [`table`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table) - Root table container
|
||||
- [`thead`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead) - Table head container
|
||||
- [`tr`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tr) - Table row
|
||||
- [`th`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/th) - Table head cell
|
||||
- [`tbody`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody) - Table body container
|
||||
- [`td`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td) - Normal table cell
|
||||
|
||||
> We used to use tables to lay out applications. Each cell would be filled with slices of images from Photoshop or Fireworks. Rounded corners were created by elaborate table tricks
|
||||
|
||||
## [Forms](https://developer.mozilla.org/en-US/docs/Web/HTML/Element#Forms)
|
||||
|
||||
- [`form`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) - Form container
|
||||
- [`label`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label) - Label text for form elements
|
||||
- [`select`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select) - A dropdown container
|
||||
- [`option`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option) - Dropdown elements
|
||||
- [`input`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) - A form field to collect various types of input.
|
||||
Possible `type` values include:
|
||||
- `text`
|
||||
- `checkbox`
|
||||
- `color`
|
||||
- `date`
|
||||
- `radio`
|
||||
- `submit`
|
||||
|
||||
# Next Step
|
||||
|
||||
[CSS Demo](../css-demo)
|
||||
@@ -1,19 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../assets/shared.css" />
|
||||
<link rel="stylesheet" href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/9.6.1/css/fabric.min.css" />
|
||||
</head>
|
||||
|
||||
<body class="ms-Fabric">
|
||||
<div class="Container">
|
||||
<ul class="Tiles">
|
||||
<li class="Tile"><a href="./html-demo/html-demo.html" class="Tile-link">HTML Demo</a></li>
|
||||
<li class="Tile"><a href="./css-demo/css-demo.html" class="Tile-link">CSS Demo</a></li>
|
||||
<li class="Tile"><a href="./css-demo/css-demo-final.html" class="Tile-link">CSS Demo Final</a></li>
|
||||
<li class="Tile"><a href="./js-demo/js-demo.html" class="Tile-link">JS Demo</a></li>
|
||||
<li class="Tile"><a href="./js-demo/js-demo-final.html" class="Tile-link">JS Demo Final</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<script src="../assets/scripts.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,214 +0,0 @@
|
||||
# JavaScript Demo
|
||||
|
||||
It's entirely possible to create a website with nothing but HTML and CSS, but as soon as you want user interaction other than links and forms, you'll need to reach for JavaScript, the scripting language of the web. Fortunately, JavaScript has grown up quite a bit since it was introduced in the 90s, and now runs just about everything: web applications, mobile applications, native applications, servers, robots and rocket ships.
|
||||
|
||||
In this demo we are going to cover a few of the core basics of the language that will help us when we start writing our TODO app. At the end of this demo we will be able to display the number of the letter "a"s in our email input. Here's the markup we're working with:
|
||||
|
||||
```html
|
||||
<div id="contact-form">
|
||||
<label>Email</label><input id="email" type="email" />
|
||||
<input class="submit" value="Submit" type="submit" />
|
||||
</div>
|
||||
```
|
||||
|
||||
By the end of the demo we'll have covered the following:
|
||||
|
||||
- Variables
|
||||
- Eventing
|
||||
- Functions
|
||||
- Conditionals
|
||||
- Loops
|
||||
- Interacting with the DOM (Document Object Model)
|
||||
|
||||
## Introduction To Variables
|
||||
|
||||
We can create a new variable with the keywords `var`, `let`, `const` and use them within our application. These variables can contain one of the following types of values:
|
||||
|
||||
> Use `const` for variables you never expect to change, and `let` for anything else. `var` is mostly out of fasion.
|
||||
|
||||
- **boolean**: `true`, `false`
|
||||
- **number**: `1`, `3.14`
|
||||
- **string**: `'single quotes'`, `"double quotes"`, or `` `backticks` ``
|
||||
- **array**: `[ 1, 2, 3, 'hello', 'world']`
|
||||
- **object**: `{ foo: 3, bar: 'hello' }`
|
||||
- **function**: `function(foo) { return foo + 1 }`
|
||||
- **null**
|
||||
- **undefined**
|
||||
|
||||
### Variable Examples
|
||||
|
||||
```js
|
||||
let myBoolean = true;
|
||||
let myNumber = 5;
|
||||
let myString = `Using backticks I can reuse other variables ${myNumber}`;
|
||||
let myArray = [1, 'cat', false, myString];
|
||||
let myObject = { key1: 'value1', anotherKey: myArray };
|
||||
let myFunction = function(myNumberParam) {
|
||||
console.log(myNumber + myNumberParam);
|
||||
};
|
||||
```
|
||||
|
||||
> JavaScript is a loosely typed (dynamic) language, so if you initially store a number in a variable (`let myVar = 0`), you can change it to contain a string by simply writing `myVar = 'hello'` without any trouble.
|
||||
|
||||
### Adding Variables
|
||||
|
||||
Let's start off our demo by adding some variables to our [script tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script). The other examples on this page will reference these variables.
|
||||
|
||||
```js
|
||||
const match = 'a';
|
||||
let matches = 0;
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
Functions are reusable pieces of functionality. Functions can take inputs (parameters) and return values (or neither). Functions can be called from within your program, from within other functions, or invoked from within the DOM itself.
|
||||
|
||||
In our example we'll create a function called `displayMatches` (camelCase is typical for functions) and we'll invoke this function every time that our submit button is clicked. For now we'll simply have our function call `alert("I'm Clicked")`, which is a function that creates an alert in your browser.
|
||||
|
||||
```js
|
||||
function displayMatches() {
|
||||
alert("I'm Clicked");
|
||||
}
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
Functions on their own don't have any affect on the page. When I declare `function displayMatches()` I have only defined the function, I haven't actually triggered it.
|
||||
|
||||
To execute a function we need to attach it to an event. There are a number of possible events: keyboard strokes, mouse clicks, document loading etc...
|
||||
|
||||
### Add Event Listeners
|
||||
|
||||
To attach a function to an event, we use an [`addEventListener`](https://developer.mozilla.org/en-US/docs/Web/API/EventListener) like this:
|
||||
|
||||
```js
|
||||
window.addEventListener('load', function() {
|
||||
console.log('loaded');
|
||||
});
|
||||
|
||||
window.addEventListener('click', function() {
|
||||
console.log('click');
|
||||
});
|
||||
```
|
||||
|
||||
> The `window` is a reference to the entire HTML document
|
||||
|
||||
### Global Event Handlers
|
||||
|
||||
If this feels a little verbose, you're not alone. Many of the [most common event types](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers) are added as element properties. This way we can set properties like `onload` or `onclick` like this:
|
||||
|
||||
```js
|
||||
window.onload = function() {
|
||||
console.log('loaded!');
|
||||
};
|
||||
window.onclick = function() {
|
||||
console.log('clicked!');
|
||||
};
|
||||
```
|
||||
|
||||
> Note that only a single function can be assigned to `onload`, but many eventListeners can be added to `load`
|
||||
|
||||
In our example we want to trigger a function based on the click of a button. To do this, we first need to get a reference to the button. We can use `querySelector` to get that reference. And then we can set its `onclick` value just like above.
|
||||
|
||||
```js
|
||||
const button = docment.querySelector('.submit');
|
||||
button.onclick = displayMatches();
|
||||
```
|
||||
|
||||
You can also combine these together like this:
|
||||
|
||||
```js
|
||||
docment.querySelector('.submit').onclick = displayMatches();
|
||||
```
|
||||
|
||||
Wire this up and see you function in action!
|
||||
|
||||
## Iteration
|
||||
|
||||
Next we'll update our function to iterate through a string of letters. We loop over each letter using the [`for of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) syntax. We'll use real input later, but for now this verifies that our function is working.
|
||||
|
||||
```js
|
||||
function displayMatches() {
|
||||
const text = 'abcda';
|
||||
for (let letter of text) {
|
||||
console.log(letter);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Conditionals
|
||||
|
||||
Next we want to compare each `letter` with our `match` value, and if they are the same, we will increment our `matches` variable. Remember that `letter = match` would set the `letter` variable to the value in `match`, so to do comparisons, we use the equality operator `==` or the strict equality operator `===`.
|
||||
|
||||
```js
|
||||
function displayMatches() {
|
||||
const text = 'abcda';
|
||||
for (let letter of text) {
|
||||
if (letter === match) {
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
console.log(matches);
|
||||
}
|
||||
```
|
||||
|
||||
> In JavaScript, it's safest to use strict `===` for comparisons, because `==` will try to convert the operands to the same type. For example, `"1" == 1` is true whereas `"1" === 1` is false, but the behavior in certain other cases is [not what you'd expect](https://www.youtube.com/watch?v=et8xNAc2ic8). (See [this video](https://www.destroyallsoftware.com/talks/wat) for more strange JavaScript behavior.)
|
||||
|
||||
## Interacting with the DOM
|
||||
|
||||
Now that we have a function performing all of our logic, it's time to connect this to our DOM by using some of the browser's built-in functions.
|
||||
|
||||
First we need to get a reference to the email field in our app's DOM. To do this, I've added an `id` to the input, and we will call one of JavaScript's oldest methods (IE 5.5), `getElementById`, which we find on the browser-provided `document` global variable. This function will return a reference to that input, and we can store it in the `email` variable.
|
||||
|
||||
```js
|
||||
function displayMatches() {
|
||||
const email = document.getElementById('email');
|
||||
console.log(email);
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Since what we're actually after is the value of the input field, we can set our `text` variable to the string assigned to the email input's `value` key. To see this in action, in Chrome you can right click on the console message created by the code above, choose "save as variable" and then type `variableName.value`.
|
||||
|
||||
```js
|
||||
function displayMatches() {
|
||||
const email = document.getElementById('email');
|
||||
const text = email.value;
|
||||
console.log(text);
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Returning the values to the DOM
|
||||
|
||||
Now that we've read values from the DOM and fed that into our matching logic, we are ready to return the number of matches to our app. To do this we first need to grab a reference to our submit button, and since this button has no `id` we are going to use the more modern (IE8+) `querySelector` to get it. This function takes any valid CSS selector and returns the first match found.
|
||||
|
||||
```js
|
||||
function displayMatches() {
|
||||
...
|
||||
const submit = document.querySelector('.submit');
|
||||
}
|
||||
```
|
||||
|
||||
Now that we have a reference to the submit input, we can set its value contain to the number of matches.
|
||||
|
||||
```js
|
||||
function displayMatches() {
|
||||
...
|
||||
const submit = document.querySelector('.submit');
|
||||
submit.value = matches + ' matches';
|
||||
}
|
||||
```
|
||||
|
||||
We could also have done this in a single line as follows:
|
||||
|
||||
```js
|
||||
function displayMatches() {
|
||||
...
|
||||
document.querySelector('.submit').value = matches + ' matches';
|
||||
}
|
||||
```
|
||||
|
||||
## Next Step
|
||||
|
||||
[Start our Todo App](../../step1-02/demo/)
|
||||
@@ -1,45 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../css-demo/css-demo-final.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h1>This is my <span>Title</span></h1>
|
||||
<div class="tiles">
|
||||
<div class="links">
|
||||
<h2>Important Links</h2>
|
||||
<a href="#">We're Awesome</a>
|
||||
<a href="#">Learn More</a>
|
||||
<a href="#">Hire Us</a>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Our Logo</h2>
|
||||
<img src="../../assets/fabric.jpg" width="100" alt="fabric logo" />
|
||||
</div>
|
||||
<div>
|
||||
<h2>Contact Us</h2>
|
||||
<div id="contact-form">
|
||||
<label>Email</label><input id="email" type="email" />
|
||||
<input onclick="displayMatches()" class="submit" value="Submit" type="submit" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
const match = 'a';
|
||||
let matches = 0;
|
||||
|
||||
function displayMatches() {
|
||||
const email = document.getElementById('email');
|
||||
const text = email.value;
|
||||
for (let letter of text) {
|
||||
if (letter === match) {
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
const submit = document.querySelector('.submit');
|
||||
submit.value = matches + ' matches';
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
@@ -1,19 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../css-demo/css-demo-final.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div class="tiles">
|
||||
<div>
|
||||
<h2>Contact Us</h2>
|
||||
<div id="contact-form">
|
||||
<label>Email</label><input id="email" type="email" />
|
||||
<input class="submit" value="Submit" type="submit" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script></script>
|
||||
</html>
|
||||
@@ -1,86 +0,0 @@
|
||||
## What We'll Be Building
|
||||
|
||||

|
||||
|
||||
## HTML and CSS
|
||||
|
||||
Every website, application, form or component starts with markup. The HTML will change over time as you develop, but a first pass helps you understand the UI you are trying to build.
|
||||
|
||||
## Demo
|
||||
|
||||
In this exercise we will scaffold out some HTML for our todo app, then add some basic styling to it.
|
||||
|
||||
### Page scaffold
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head></head>
|
||||
<body></body>
|
||||
</html>
|
||||
```
|
||||
|
||||
1. The [`DOCTYPE`](https://developer.mozilla.org/en-US/docs/Glossary/Doctype) tells the browser that this file is written in modern HTML.
|
||||
2. The `html` tag wraps the entire page and is the page root. Nothing is placed outside of this tag.
|
||||
3. `head` will contain all of the page's metadata, in this case a link to our CSS file.
|
||||
4. `body` is where all of the visible content should be placed.
|
||||
|
||||
### Content Sectioning
|
||||
|
||||
As we saw in the previous demo, HTML elements can be used to describe different content sections of the applications. Let's add `header`, `main` and `footer`, as well as populate the header with an `h1`, addTodo `div`, and `nav` for our filters.
|
||||
|
||||
```html
|
||||
<body>
|
||||
<header>
|
||||
<h1></h1>
|
||||
<div class="addTodo"></div>
|
||||
<nav class="filter"></nav>
|
||||
</header>
|
||||
<main></main>
|
||||
<footer></footer>
|
||||
</body>
|
||||
```
|
||||
|
||||
> Note that a `form` element would have been more semantically correct than a `div`, but we aren't using this form to POST to a server, so for this example a div is easier to use.
|
||||
|
||||
### Updating the header
|
||||
|
||||
The header of our page is where most of the action will happen. First, let's give our app a name, adding "TODO" to our `h1`. Then we can add an input and button to our `addTodo` div.
|
||||
|
||||
```html
|
||||
<input class="textfield" placeholder="add todo" /> <button class="submit">Add</button>
|
||||
```
|
||||
|
||||
#### Navigation
|
||||
|
||||
The navigation for this application is quite simple. We want users to be able to switch between three filtered states. Since we need to track which state is currently selected, we'll add a `selected` class to the first item.
|
||||
|
||||
```html
|
||||
<nav class="filter">
|
||||
<button class="selected">all</button>
|
||||
<button>active</button>
|
||||
<button>completed</button>
|
||||
</nav>
|
||||
```
|
||||
|
||||
### Adding styles
|
||||
|
||||
Now that we've got the top of our application scaffolded, we can add some styles in the `head`.
|
||||
|
||||
```html
|
||||
<head>
|
||||
<link rel="stylesheet" href="./style.css" />
|
||||
</head>
|
||||
```
|
||||
|
||||
### Updating styles
|
||||
|
||||
It looks like the selected button isn't getting any special styles. Let's dig in and see why that is.
|
||||
|
||||
Open up the browser inspector and target our "all" button. You'll notice that the blue style is present on the list, but it is being overridden by the `border: none` above it. This is a situation where specificity is winning out over the cascade.
|
||||
|
||||
> **Cascade** states that if two selectors are equal, the lowest one on the page wins
|
||||
|
||||
> **Specificity** states that regardless of cascade, the selector with the highest specificity wins
|
||||
|
||||
To fix this problem we need to either reduce the specificity of our button styles, or increase the specificity of the selected style. In this situation we will add `.filter` in front of the `.selected` selector, because the selected style only applies to the filter anyway.
|
||||
@@ -1,6 +1,6 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="./css-demo.css" />
|
||||
<link rel="stylesheet" href="./style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
@@ -1 +0,0 @@
|
||||
this page is intentionally blank
|
||||
@@ -1,41 +1,62 @@
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
width: 400px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
/* body {
|
||||
font: 1.2em sans-serif;
|
||||
} */
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
/* h1 {
|
||||
color: black;
|
||||
background: #ababab;
|
||||
margin-bottom: 15px;
|
||||
margin-top: 15px;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
} */
|
||||
|
||||
.addTodo {
|
||||
/* span {
|
||||
color: #004578;
|
||||
} */
|
||||
|
||||
/* a ~ a {
|
||||
display: block;
|
||||
} */
|
||||
|
||||
/* .tiles {
|
||||
display: flex;
|
||||
}
|
||||
} */
|
||||
|
||||
.textfield {
|
||||
flex-grow: 1;
|
||||
margin-right: 10px;
|
||||
}
|
||||
/* .tiles img {
|
||||
width: 100%;
|
||||
} */
|
||||
|
||||
.submit {
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
/* .tiles > div {
|
||||
background: rgb(10, 10, 10);
|
||||
color: white;
|
||||
flex-basis: 100%;
|
||||
padding: 10px 20px 15px;
|
||||
margin: 10px 20px 10px 0;
|
||||
border: 1px solid white;
|
||||
} */
|
||||
|
||||
.filter {
|
||||
margin: 10px 0 0;
|
||||
}
|
||||
/* div.important-links {
|
||||
background: #004578;
|
||||
} */
|
||||
|
||||
.filter button {
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
/* a {
|
||||
color: white;
|
||||
} */
|
||||
|
||||
.selected {
|
||||
border-bottom: 2px solid blue;
|
||||
}
|
||||
/* a:hover {
|
||||
color: #ccc;
|
||||
} */
|
||||
|
||||
.todos {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
/* .tiles > div:last-child {
|
||||
margin-right: 0;
|
||||
} */
|
||||
|
||||
/* #contact-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
} */
|
||||
|
||||
/* input[type='submit'] {
|
||||
margin-top: 10px;
|
||||
} */
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
## Exercise
|
||||
|
||||
If you don't already have the app running, start it with `npm run static` from the root of the `frontend-bootcamp` folded and go to [Todo HTML/CSS Exercise Page](http://localhost:8080/step1-02/exercise/)
|
||||
|
||||
Open the `index.html` file in this exercise folder.
|
||||
|
||||
1. Add an unordered list with class `todos` to the main section
|
||||
2. Add 4 `list items` (`li`) with class `todo` inside of that list with the following content
|
||||
3. Add this content to each of the 4 list items `<label><input type="checkbox" /> <span class="title"> Todo </span> </label>`
|
||||
4. Add a span and a button to your `footer`
|
||||
5. Span content should be `<i class="remaining">4</i> items left` and button should say `Clear Completed` and have a class of `submit`
|
||||
6. Go into the CSS file and add `display: flex` to the footer. Also add `flex-grow:1` to the span inside of the footer
|
||||
|
||||
> Hint: Look back at the CSS demo to see the various ways you can use selectors to target existing HTML
|
||||
|
||||
> There are many strategies for creating and organizing class names in a large application, like [bem](http://getbem.com/), [OOCSS](http://oocss.org/) and [SMACSS](https://smacss.com/). This lesson is focused on using CSS selectors, not the optimized way to scale your CSS.
|
||||
@@ -1,22 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="./style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>todos - step1-02 exercise</h1>
|
||||
<div class="addTodo">
|
||||
<input class="textfield" placeholder="add todo" />
|
||||
<button class="submit">Add</button>
|
||||
</div>
|
||||
<nav class="filter">
|
||||
<button class="selected">all</button>
|
||||
<button>active</button>
|
||||
<button>completed</button>
|
||||
</nav>
|
||||
</header>
|
||||
<main></main>
|
||||
<footer></footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,41 +0,0 @@
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
width: 400px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.addTodo {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.textfield {
|
||||
flex-grow: 1;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.submit {
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.filter {
|
||||
margin: 10px 0 0;
|
||||
}
|
||||
|
||||
.filter button {
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.filter .selected {
|
||||
border-bottom: 2px solid blue;
|
||||
}
|
||||
|
||||
.todos {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="./style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>todos - step1-02 final</h1>
|
||||
<div class="addTodo">
|
||||
<input class="textfield" placeholder="add todo" />
|
||||
<button class="submit">Add</button>
|
||||
</div>
|
||||
<nav class="filter">
|
||||
<button class="selected">all</button>
|
||||
<button>active</button>
|
||||
<button>completed</button>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<ul class="todos">
|
||||
<li class="todo">
|
||||
<label><input type="checkbox" /> <span class="title"> Todo 1 </span> </label>
|
||||
</li>
|
||||
<li class="todo">
|
||||
<label><input type="checkbox" /> <span class="title"> Todo 2 </span> </label>
|
||||
</li>
|
||||
<li class="todo">
|
||||
<label><input type="checkbox" /> <span class="title"> Todo 3 </span> </label>
|
||||
</li>
|
||||
<li class="todo">
|
||||
<label><input type="checkbox" /> <span class="title"> Todo 4 </span> </label>
|
||||
</li>
|
||||
</ul>
|
||||
</main>
|
||||
<footer>
|
||||
<span><i class="remaining">4</i> items left</span> <button class="submit">Clear Completed</button>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,49 +0,0 @@
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
width: 400px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.addTodo {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.textfield {
|
||||
flex-grow: 1;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.submit {
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.filter {
|
||||
margin: 10px 0 0;
|
||||
}
|
||||
|
||||
.filter button {
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.filter .selected {
|
||||
border-bottom: 2px solid blue;
|
||||
}
|
||||
|
||||
.todos {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
footer span {
|
||||
flex-grow: 1;
|
||||
}
|
||||
@@ -1,104 +1,214 @@
|
||||
# JavaScript Demo
|
||||
|
||||
Now that we have a UI that looks like a todo app, we need to make it **function** like a todo app. In this example we are going to use raw JavaScript to explicitly modify our application as we interact with it. This will be in stark contrast to the implicit approach we will take when we do this with React in the next exercise.
|
||||
It's entirely possible to create a website with nothing but HTML and CSS, but as soon as you want user interaction other than links and forms, you'll need to reach for JavaScript, the scripting language of the web. Fortunately, JavaScript has grown up quite a bit since it was introduced in the 90s, and now runs just about everything: web applications, mobile applications, native applications, servers, robots and rocket ships.
|
||||
|
||||
> Keep an eye on how often user actions directly modify the HTML on the page. You'll see this number drop to zero when we start using React.
|
||||
|
||||
## What we're starting with
|
||||
|
||||
This demo starts off with a few functions already in place. Let's walk through what's already here.
|
||||
|
||||
- `clearInput()` - This is a generic, reusable function that takes in a `selector` parameter, finds the first matching element, and sets the element's value to an empty string. This direct modification is called a **side effect**.
|
||||
- `getTodoText()` - This is a helper function that returns the value inside of our text field. Notice how some functions return values and how you can save that return value in a variable.
|
||||
- `filter()` - This function takes in a `filterName` string, and a `button` which is a reference to the clicked button.
|
||||
1. Remove the `selected` class from the previously selected element.
|
||||
2. Add `selected` to the clicked button.
|
||||
3. Set `filterName` to the clicked button's `innerText` value.
|
||||
4. Get all of the todos with [`querySelectAll`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll), and then loop through them.
|
||||
5. Set the `hidden` property of each todo based on the filter/state combination.
|
||||
|
||||
## Writing `addTodo` Function
|
||||
|
||||
We start writing all functions with the `function` keyword and the name of our function. Functions can take parameters, but in this case we don't need to pass any through, so we follow the function name with an empty `()`. Everything we want this function to do will then be placed in a set of brackets `{}`.
|
||||
|
||||
```js
|
||||
function addTodo() {}
|
||||
```
|
||||
|
||||
### Creating a Todo Clone
|
||||
|
||||
The first thing we need to do in this function is create a `newTodo` which is a clone of an existing Todo.
|
||||
|
||||
```js
|
||||
function addTodo() {
|
||||
const todo = document.querySelector('.todo');
|
||||
const newTodo = todo.cloneNode(true);
|
||||
}
|
||||
```
|
||||
|
||||
Passing true to our `cloneNode` means it is a deep clone, so we get a copy of the todo's children as well.
|
||||
|
||||
> Note that this approach is very fragile, as it requires a todo node to always be present on the page.
|
||||
|
||||
### Updating the newTodos's text
|
||||
|
||||
With this clone created, we need to update the `innerText` of the node with our todo text, which is returned from `getTodoText()`.
|
||||
|
||||
```js
|
||||
function addTodo() {
|
||||
const todo = document.querySelector('.todo');
|
||||
const newTodo = todo.cloneNode(true);
|
||||
|
||||
newTodo.querySelector('.title').innerText = getTodoText();
|
||||
}
|
||||
```
|
||||
|
||||
We can target a child node by calling `querySelector` again and asking for the child with the `.child` class.
|
||||
|
||||
> Note that if we left off the `()` we'd actually be assigning innerText to the 'function' instead of the function return.
|
||||
|
||||
### Placing the newTodo into the list of todos
|
||||
|
||||
Making a clone only stores the clone inside of our variable. If we want to place it back into the DOM, we'll need to insert it manually. For that we can use [insertBefore](https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore).
|
||||
|
||||
This function actually needs to target the parent element, which we can get by calling `todo.parentElement` and passing parameters of `(elementToInsert, elementToInsertBefore)`.
|
||||
|
||||
```js
|
||||
function addTodo() {
|
||||
const todo = document.querySelector('.todo');
|
||||
const newTodo = todo.cloneNode(true);
|
||||
newTodo.querySelector('.title').innerText = getTodoText();
|
||||
todo.parentElement.insertBefore(newTodo, todo);
|
||||
}
|
||||
```
|
||||
|
||||
### Cleanup
|
||||
|
||||
Now that our todo has been inserted into the DOM, we can clear the text input. In the exercise, we'll also update the count of the remaining todos in the footer from here.
|
||||
|
||||
```js
|
||||
function addTodo() {
|
||||
...
|
||||
clearInput('.textfield');
|
||||
}
|
||||
```
|
||||
|
||||
> Note how often we have to reach into the DOM to find nodes, manipulate content, insert back into the DOM and manually change the values in inputs. This is the error prone manipulation that React helps us avoid.
|
||||
|
||||
## Triggering functions from click events
|
||||
|
||||
Now that we have a working `addTodo` function, we need a way to trigger it when the user is ready. This can be done in two ways.
|
||||
|
||||
1. We can find the element with `querySelector`, then set its `onclick` to our function
|
||||
|
||||
```js
|
||||
document.querySelector('.addTodo .submit').onclick = addTodo;
|
||||
```
|
||||
|
||||
2. We can add the function directly to our button in our HTML
|
||||
In this demo we are going to cover a few of the core basics of the language that will help us when we start writing our TODO app. At the end of this demo we will be able to display the number of the letter "a"s in our email input. Here's the markup we're working with:
|
||||
|
||||
```html
|
||||
<button onclick="addTodo()" class="submit">Add</button>
|
||||
<div id="contact-form">
|
||||
<label>Email</label><input id="email" type="email" />
|
||||
<input class="submit" value="Submit" type="submit" />
|
||||
</div>
|
||||
```
|
||||
|
||||
Today we'll use #2, as this is the way it will work in React as well.
|
||||
By the end of the demo we'll have covered the following:
|
||||
|
||||
- Variables
|
||||
- Eventing
|
||||
- Functions
|
||||
- Conditionals
|
||||
- Loops
|
||||
- Interacting with the DOM (Document Object Model)
|
||||
|
||||
## Introduction To Variables
|
||||
|
||||
We can create a new variable with the keywords `var`, `let`, `const` and use them within our application. These variables can contain one of the following types of values:
|
||||
|
||||
> Use `const` for variables you never expect to change, and `let` for anything else. `var` is mostly out of fasion.
|
||||
|
||||
- **boolean**: `true`, `false`
|
||||
- **number**: `1`, `3.14`
|
||||
- **string**: `'single quotes'`, `"double quotes"`, or `` `backticks` ``
|
||||
- **array**: `[ 1, 2, 3, 'hello', 'world']`
|
||||
- **object**: `{ foo: 3, bar: 'hello' }`
|
||||
- **function**: `function(foo) { return foo + 1 }`
|
||||
- **null**
|
||||
- **undefined**
|
||||
|
||||
### Variable Examples
|
||||
|
||||
```js
|
||||
let myBoolean = true;
|
||||
let myNumber = 5;
|
||||
let myString = `Using backticks I can reuse other variables ${myNumber}`;
|
||||
let myArray = [1, 'cat', false, myString];
|
||||
let myObject = { key1: 'value1', anotherKey: myArray };
|
||||
let myFunction = function(myNumberParam) {
|
||||
console.log(myNumber + myNumberParam);
|
||||
};
|
||||
```
|
||||
|
||||
> JavaScript is a loosely typed (dynamic) language, so if you initially store a number in a variable (`let myVar = 0`), you can change it to contain a string by simply writing `myVar = 'hello'` without any trouble.
|
||||
|
||||
### Adding Variables
|
||||
|
||||
Let's start off our demo by adding some variables to our [script tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script). The other examples on this page will reference these variables.
|
||||
|
||||
```js
|
||||
const match = 'a';
|
||||
let matches = 0;
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
Functions are reusable pieces of functionality. Functions can take inputs (parameters) and return values (or neither). Functions can be called from within your program, from within other functions, or invoked from within the DOM itself.
|
||||
|
||||
In our example we'll create a function called `displayMatches` (camelCase is typical for functions) and we'll invoke this function every time that our submit button is clicked. For now we'll simply have our function call `alert("I'm Clicked")`, which is a function that creates an alert in your browser.
|
||||
|
||||
```js
|
||||
function displayMatches() {
|
||||
alert("I'm Clicked");
|
||||
}
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
Functions on their own don't have any affect on the page. When I declare `function displayMatches()` I have only defined the function, I haven't actually triggered it.
|
||||
|
||||
To execute a function we need to attach it to an event. There are a number of possible events: keyboard strokes, mouse clicks, document loading etc...
|
||||
|
||||
### Add Event Listeners
|
||||
|
||||
To attach a function to an event, we use an [`addEventListener`](https://developer.mozilla.org/en-US/docs/Web/API/EventListener) like this:
|
||||
|
||||
```js
|
||||
window.addEventListener('load', function() {
|
||||
console.log('loaded');
|
||||
});
|
||||
|
||||
window.addEventListener('click', function() {
|
||||
console.log('click');
|
||||
});
|
||||
```
|
||||
|
||||
> The `window` is a reference to the entire HTML document
|
||||
|
||||
### Global Event Handlers
|
||||
|
||||
If this feels a little verbose, you're not alone. Many of the [most common event types](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers) are added as element properties. This way we can set properties like `onload` or `onclick` like this:
|
||||
|
||||
```js
|
||||
window.onload = function() {
|
||||
console.log('loaded!');
|
||||
};
|
||||
window.onclick = function() {
|
||||
console.log('clicked!');
|
||||
};
|
||||
```
|
||||
|
||||
> Note that only a single function can be assigned to `onload`, but many eventListeners can be added to `load`
|
||||
|
||||
In our example we want to trigger a function based on the click of a button. To do this, we first need to get a reference to the button. We can use `querySelector` to get that reference. And then we can set its `onclick` value just like above.
|
||||
|
||||
```js
|
||||
const button = docment.querySelector('.submit');
|
||||
button.onclick = displayMatches();
|
||||
```
|
||||
|
||||
You can also combine these together like this:
|
||||
|
||||
```js
|
||||
docment.querySelector('.submit').onclick = displayMatches();
|
||||
```
|
||||
|
||||
Wire this up and see you function in action!
|
||||
|
||||
## Iteration
|
||||
|
||||
Next we'll update our function to iterate through a string of letters. We loop over each letter using the [`for of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) syntax. We'll use real input later, but for now this verifies that our function is working.
|
||||
|
||||
```js
|
||||
function displayMatches() {
|
||||
const text = 'abcda';
|
||||
for (let letter of text) {
|
||||
console.log(letter);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Conditionals
|
||||
|
||||
Next we want to compare each `letter` with our `match` value, and if they are the same, we will increment our `matches` variable. Remember that `letter = match` would set the `letter` variable to the value in `match`, so to do comparisons, we use the equality operator `==` or the strict equality operator `===`.
|
||||
|
||||
```js
|
||||
function displayMatches() {
|
||||
const text = 'abcda';
|
||||
for (let letter of text) {
|
||||
if (letter === match) {
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
console.log(matches);
|
||||
}
|
||||
```
|
||||
|
||||
> In JavaScript, it's safest to use strict `===` for comparisons, because `==` will try to convert the operands to the same type. For example, `"1" == 1` is true whereas `"1" === 1` is false, but the behavior in certain other cases is [not what you'd expect](https://www.youtube.com/watch?v=et8xNAc2ic8). (See [this video](https://www.destroyallsoftware.com/talks/wat) for more strange JavaScript behavior.)
|
||||
|
||||
## Interacting with the DOM
|
||||
|
||||
Now that we have a function performing all of our logic, it's time to connect this to our DOM by using some of the browser's built-in functions.
|
||||
|
||||
First we need to get a reference to the email field in our app's DOM. To do this, I've added an `id` to the input, and we will call one of JavaScript's oldest methods (IE 5.5), `getElementById`, which we find on the browser-provided `document` global variable. This function will return a reference to that input, and we can store it in the `email` variable.
|
||||
|
||||
```js
|
||||
function displayMatches() {
|
||||
const email = document.getElementById('email');
|
||||
console.log(email);
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Since what we're actually after is the value of the input field, we can set our `text` variable to the string assigned to the email input's `value` key. To see this in action, in Chrome you can right click on the console message created by the code above, choose "save as variable" and then type `variableName.value`.
|
||||
|
||||
```js
|
||||
function displayMatches() {
|
||||
const email = document.getElementById('email');
|
||||
const text = email.value;
|
||||
console.log(text);
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Returning the values to the DOM
|
||||
|
||||
Now that we've read values from the DOM and fed that into our matching logic, we are ready to return the number of matches to our app. To do this we first need to grab a reference to our submit button, and since this button has no `id` we are going to use the more modern (IE8+) `querySelector` to get it. This function takes any valid CSS selector and returns the first match found.
|
||||
|
||||
```js
|
||||
function displayMatches() {
|
||||
...
|
||||
const submit = document.querySelector('.submit');
|
||||
}
|
||||
```
|
||||
|
||||
Now that we have a reference to the submit input, we can set its value contain to the number of matches.
|
||||
|
||||
```js
|
||||
function displayMatches() {
|
||||
...
|
||||
const submit = document.querySelector('.submit');
|
||||
submit.value = matches + ' matches';
|
||||
}
|
||||
```
|
||||
|
||||
We could also have done this in a single line as follows:
|
||||
|
||||
```js
|
||||
function displayMatches() {
|
||||
...
|
||||
document.querySelector('.submit').value = matches + ' matches';
|
||||
}
|
||||
```
|
||||
|
||||
## Next Step
|
||||
|
||||
[Start our Todo App](../../step1-02/demo/)
|
||||
|
||||
@@ -1,66 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="./style.css" />
|
||||
<link rel="stylesheet" href="../css-demo/css-demo-final.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>todos - step1-03 demo</h1>
|
||||
<div class="addTodo">
|
||||
<input class="textfield" placeholder="add todo" />
|
||||
<button class="submit">Add</button>
|
||||
<div>
|
||||
<div class="tiles">
|
||||
<div>
|
||||
<h2>Contact Us</h2>
|
||||
<div id="contact-form">
|
||||
<label>Email</label><input id="email" type="email" />
|
||||
<input class="submit" value="Submit" type="submit" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="filter">
|
||||
<button class="selected">all</button>
|
||||
<button>active</button>
|
||||
<button>completed</button>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<ul class="todos">
|
||||
<li class="todo">
|
||||
<label><input type="checkbox" /> <span class="title"> Todo 1 </span></label>
|
||||
</li>
|
||||
<li class="todo">
|
||||
<label><input type="checkbox" /> <span class="title"> Todo 2 </span></label>
|
||||
</li>
|
||||
<li class="todo">
|
||||
<label><input type="checkbox" /> <span class="title"> Todo 3 </span></label>
|
||||
</li>
|
||||
<li class="todo">
|
||||
<label><input type="checkbox" /> <span class="title"> Todo 4 </span></label>
|
||||
</li>
|
||||
</ul>
|
||||
<footer>
|
||||
<span><i class="remaining">4</i> items left</span>
|
||||
<button class="submit">Clear Completed</button>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
<script type="text/javascript">
|
||||
function clearInput(selector) {
|
||||
document.querySelector(selector).value = '';
|
||||
}
|
||||
|
||||
function getTodoText() {
|
||||
return document.querySelector('.textfield').value;
|
||||
}
|
||||
|
||||
function filter(button) {
|
||||
document.querySelector('.selected').classList.remove('selected');
|
||||
button.classList.add('selected');
|
||||
|
||||
const filterName = button.innerText;
|
||||
for (let todo of document.querySelectorAll('.todo')) {
|
||||
const checked = todo.querySelector('input').checked === true;
|
||||
if (filterName === 'all') {
|
||||
todo.hidden = false;
|
||||
} else if (filterName === 'active') {
|
||||
todo.hidden = checked;
|
||||
} else if (filterName === 'completed') {
|
||||
todo.hidden = !checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script></script>
|
||||
</html>
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
width: 400px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.addTodo {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.textfield {
|
||||
flex-grow: 1;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.submit {
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.filter {
|
||||
margin: 10px 0 0;
|
||||
}
|
||||
|
||||
.filter button {
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.filter .selected {
|
||||
border-bottom: 2px solid blue;
|
||||
}
|
||||
|
||||
.todos {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
footer span {
|
||||
flex-grow: 1;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
## Exercise
|
||||
|
||||
Open up the [Todo JavaScript Exercise Page](http://localhost:8080/step1-03/exercise/)
|
||||
|
||||
Open the `index.html` file in this exercise folder.
|
||||
|
||||
### Update Navigation
|
||||
|
||||
1. Add an `onclick` attribute to all three buttons in the navigation.
|
||||
2. In each `onclick` call the `filter` function. In our function we need a reference to the clicked button, so pass in the keyword `this` as the only parameter.
|
||||
|
||||
### Complete the `updateRemaining` function
|
||||
|
||||
1. Using `querySelector`, get a reference to the span with the `remaining` class, and store it in a variable .
|
||||
2. Use [`querySelectorAll`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) to get all of the todos.
|
||||
3. Set the `innerText` of the remaining span to the length of those todos.
|
||||
4. Add `updateRemaining()` to the end of the `addTodo` function.
|
||||
|
||||
### Write a `clearCompleted` function
|
||||
|
||||
1. Get a reference to all of the todos with [`querySelectorAll`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll).
|
||||
2. Use a `for (let todo of todos)` loop to iterate over each todo.
|
||||
3. Inside the for loop write an `if` statement to test if the `input` inside of the todo has a checked value of true.
|
||||
> Hint: you can use `querySelector` on any HTML element to find matching child elements.
|
||||
4. Call `todo.remove()` for any todo whose input is checked.
|
||||
5. After the loop is done, run `updateRemaining()`.
|
||||
6. Attach `clearCompleted()` function to the `onclick` of the footer button.
|
||||
7. Test it out!
|
||||
|
||||
@@ -1,80 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="./style.css" />
|
||||
</head>
|
||||
<style>
|
||||
label,
|
||||
button {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<header>
|
||||
<h1>todos - step1-03 exercise</h1>
|
||||
<div class="addTodo">
|
||||
<input class="textfield" placeholder="add todo" />
|
||||
<button onclick="addTodo()" class="submit">Add</button>
|
||||
</div>
|
||||
<nav class="filter">
|
||||
<button class="selected">all</button>
|
||||
<button>active</button>
|
||||
<button>completed</button>
|
||||
</nav>
|
||||
</header>
|
||||
<label><input type="checkbox" />Ice cream</label>
|
||||
<label><input type="checkbox" />Pizza</label>
|
||||
<label><input type="checkbox" />Tacos</label>
|
||||
<label><input type="checkbox" />Meatloaf</label>
|
||||
<label><input type="checkbox" />Brocolli</label>
|
||||
|
||||
<ul class="todos">
|
||||
<li class="todo">
|
||||
<label><input type="checkbox" /> <span class="title"> Todo 1 </span></label>
|
||||
</li>
|
||||
<li class="todo">
|
||||
<label><input type="checkbox" /> <span class="title"> Todo 2 </span></label>
|
||||
</li>
|
||||
<li class="todo">
|
||||
<label><input type="checkbox" /> <span class="title"> Todo 3 </span></label>
|
||||
</li>
|
||||
<li class="todo">
|
||||
<label><input type="checkbox" /> <span class="title"> Todo 4 </span></label>
|
||||
</li>
|
||||
</ul>
|
||||
<footer>
|
||||
<span><i class="remaining">4</i> items left</span>
|
||||
<button class="submit">Clear Completed</button>
|
||||
</footer>
|
||||
<button>Display Your Favorites</button>
|
||||
|
||||
<div class="favorites"></div>
|
||||
</body>
|
||||
|
||||
<script type="text/javascript">
|
||||
function clearInput(selector) {
|
||||
document.querySelector(selector).value = '';
|
||||
}
|
||||
|
||||
function getTodoText() {
|
||||
return document.querySelector('.textfield').value;
|
||||
}
|
||||
|
||||
function updateRemaining() {
|
||||
}
|
||||
|
||||
function addTodo() {
|
||||
const todo = document.querySelector('.todo');
|
||||
const newTodo = todo.cloneNode(true);
|
||||
newTodo.querySelector('.title').innerText = getTodoText();
|
||||
todo.parentElement.insertBefore(newTodo, todo);
|
||||
|
||||
clearInput('.textfield');
|
||||
}
|
||||
|
||||
// clearCompleted
|
||||
|
||||
function filter(button) {
|
||||
document.querySelector('.selected').classList.remove('selected');
|
||||
button.classList.add('selected');
|
||||
|
||||
const filterName = button.innerText;
|
||||
for (let todo of document.querySelectorAll('.todo')) {
|
||||
const checked = todo.querySelector('input').checked === true;
|
||||
if (filterName === 'all') {
|
||||
todo.hidden = false;
|
||||
} else if (filterName === 'active') {
|
||||
todo.hidden = checked;
|
||||
} else if (filterName === 'completed') {
|
||||
todo.hidden = !checked;
|
||||
<script>
|
||||
function getFavs() {
|
||||
let favList = [];
|
||||
const inputs = document.querySelectorAll('input');
|
||||
for (const input of inputs) {
|
||||
if (input.checked === true) {
|
||||
favList.push(input.parentNode.textContent);
|
||||
}
|
||||
}
|
||||
document.querySelector('.favorites').textContent = favList.join(' ');
|
||||
}
|
||||
|
||||
let button = document.querySelector('button');
|
||||
|
||||
button.addEventListener('click', getFavs);
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
width: 400px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.addTodo {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.textfield {
|
||||
flex-grow: 1;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.submit {
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.filter {
|
||||
margin: 10px 0 0;
|
||||
}
|
||||
|
||||
.filter button {
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.filter .selected {
|
||||
border-bottom: 2px solid blue;
|
||||
}
|
||||
|
||||
.todos {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
footer span {
|
||||
flex-grow: 1;
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="./style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>todos - step1-03 final</h1>
|
||||
<div class="addTodo">
|
||||
<input class="textfield" placeholder="add todo" />
|
||||
<button onclick="addTodo()" class="submit">Add</button>
|
||||
</div>
|
||||
<nav class="filter">
|
||||
<button onclick="filter(this)" class="selected">all</button>
|
||||
<button onclick="filter(this)">active</button>
|
||||
<button onclick="filter(this)">completed</button>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<ul class="todos">
|
||||
<li class="todo">
|
||||
<label><input type="checkbox" /> <span class="title"> Todo 1 </span></label>
|
||||
</li>
|
||||
<li class="todo">
|
||||
<label><input type="checkbox" /> <span class="title"> Todo 2 </span></label>
|
||||
</li>
|
||||
<li class="todo">
|
||||
<label><input type="checkbox" /> <span class="title"> Todo 3 </span></label>
|
||||
</li>
|
||||
<li class="todo">
|
||||
<label><input type="checkbox" /> <span class="title"> Todo 4 </span></label>
|
||||
</li>
|
||||
</ul>
|
||||
<footer>
|
||||
<span><i class="remaining">4</i> items left</span>
|
||||
<button onclick="clearCompleted()" class="submit">Clear Completed</button>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
<script type="text/javascript">
|
||||
function clearInput(selector) {
|
||||
document.querySelector(selector).value = '';
|
||||
}
|
||||
|
||||
function getTodoText() {
|
||||
return document.querySelector('.textfield').value;
|
||||
}
|
||||
|
||||
function updateRemaining() {
|
||||
const remaining = document.querySelector('.remaining');
|
||||
const todos = document.querySelectorAll('.todo').length;
|
||||
remaining.innerText = todos;
|
||||
}
|
||||
|
||||
function addTodo(ev) {
|
||||
const todo = document.querySelector('.todo');
|
||||
const newTodo = todo.cloneNode(true);
|
||||
newTodo.querySelector('.title').innerText = getTodoText();
|
||||
todo.parentElement.insertBefore(newTodo, todo);
|
||||
|
||||
clearInput('.textfield');
|
||||
updateRemaining();
|
||||
}
|
||||
|
||||
function clearCompleted() {
|
||||
const todos = document.querySelectorAll('.todo');
|
||||
for (let todo of todos) {
|
||||
if (todo.querySelector('input').checked === true) {
|
||||
todo.remove();
|
||||
}
|
||||
}
|
||||
updateRemaining();
|
||||
}
|
||||
|
||||
function filter(button) {
|
||||
document.querySelector('.selected').classList.remove('selected');
|
||||
button.classList.add('selected');
|
||||
|
||||
const filterName = button.innerText;
|
||||
for (let todo of document.querySelectorAll('.todo')) {
|
||||
const checked = todo.querySelector('input').checked === true;
|
||||
if (filterName === 'all') {
|
||||
todo.hidden = false;
|
||||
} else if (filterName === 'active') {
|
||||
todo.hidden = checked;
|
||||
} else if (filterName === 'completed') {
|
||||
todo.hidden = !checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
@@ -1,49 +0,0 @@
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
width: 400px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.addTodo {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.textfield {
|
||||
flex-grow: 1;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.submit {
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.filter {
|
||||
margin: 10px 0 0;
|
||||
}
|
||||
|
||||
.filter button {
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.filter .selected {
|
||||
border-bottom: 2px solid blue;
|
||||
}
|
||||
|
||||
.todos {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
footer span {
|
||||
flex-grow: 1;
|
||||
}
|
||||
Reference in New Issue
Block a user