49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/01 - JavaScript Drum Kit/readme.md:
--------------------------------------------------------------------------------
1 | # JavaScript Drum Kit
2 | The idea behind this first project is to imitate the functionality of a keyboard. Through *audio* tags we will allow the user to reproduce up to 9 different sounds.
3 |
4 | ## Notes
5 |
6 | We make use of two new concepts of HTML5:
7 |
8 | * The HTML **[data-\*][1]** attribute allows us to store custom data on any HTML element. Each `div.key` and `audio` element have a `data-key` attribute that binds them together.
9 | * The **[audio](https://developer.mozilla.org/en/docs/Web/HTML/Element/audio)** tag offers an API that makes simpler to reproduce audio files in the browser.
10 |
11 |
12 | The logic behind it, is very simple, we listen for two events:
13 |
14 | * User presses a key: get audio element, add class to the key and play it:
15 | ```javascript
16 | function keyPressed(e) {
17 | const audio = document.querySelector(`audio[data-key="${e.charCode}"]`),
18 | key = document.querySelector(`div[data-key="${e.charCode}"]`);
19 | if (!audio) return; // Not a valid key
20 | key.classList.add("playing");
21 | audio.currentTime = 0;
22 | audio.play();
23 | };
24 | ```
25 | * css-transition-end: We remove the style previously added:
26 |
27 | ```javascript
28 | function removeStyle(e) {
29 | if (e.type !== "transitionend") return;
30 | e.target.classList.remove("playing");
31 | }
32 | ```
33 |
34 |
35 | ## Events
36 | * **keypress:** user presses a key
37 | * **transitioned:** a css transition has completed.
38 |
39 | [1]:https://developer.mozilla.org/en/docs/Web/Guide/HTML/Using_data_attributes
40 |
--------------------------------------------------------------------------------
/01 - JavaScript Drum Kit/sounds/boom.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhabib/JavaScript30/abe5ed234d0a843f2bf86413f2c71118d6d16cbd/01 - JavaScript Drum Kit/sounds/boom.wav
--------------------------------------------------------------------------------
/01 - JavaScript Drum Kit/sounds/clap.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhabib/JavaScript30/abe5ed234d0a843f2bf86413f2c71118d6d16cbd/01 - JavaScript Drum Kit/sounds/clap.wav
--------------------------------------------------------------------------------
/01 - JavaScript Drum Kit/sounds/hihat.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhabib/JavaScript30/abe5ed234d0a843f2bf86413f2c71118d6d16cbd/01 - JavaScript Drum Kit/sounds/hihat.wav
--------------------------------------------------------------------------------
/01 - JavaScript Drum Kit/sounds/kick.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhabib/JavaScript30/abe5ed234d0a843f2bf86413f2c71118d6d16cbd/01 - JavaScript Drum Kit/sounds/kick.wav
--------------------------------------------------------------------------------
/01 - JavaScript Drum Kit/sounds/openhat.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhabib/JavaScript30/abe5ed234d0a843f2bf86413f2c71118d6d16cbd/01 - JavaScript Drum Kit/sounds/openhat.wav
--------------------------------------------------------------------------------
/01 - JavaScript Drum Kit/sounds/ride.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhabib/JavaScript30/abe5ed234d0a843f2bf86413f2c71118d6d16cbd/01 - JavaScript Drum Kit/sounds/ride.wav
--------------------------------------------------------------------------------
/01 - JavaScript Drum Kit/sounds/snare.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhabib/JavaScript30/abe5ed234d0a843f2bf86413f2c71118d6d16cbd/01 - JavaScript Drum Kit/sounds/snare.wav
--------------------------------------------------------------------------------
/01 - JavaScript Drum Kit/sounds/tink.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhabib/JavaScript30/abe5ed234d0a843f2bf86413f2c71118d6d16cbd/01 - JavaScript Drum Kit/sounds/tink.wav
--------------------------------------------------------------------------------
/01 - JavaScript Drum Kit/sounds/tom.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhabib/JavaScript30/abe5ed234d0a843f2bf86413f2c71118d6d16cbd/01 - JavaScript Drum Kit/sounds/tom.wav
--------------------------------------------------------------------------------
/01 - JavaScript Drum Kit/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | font-size: 10px;
3 | background: url('background.jpg') bottom center;
4 | background-size: cover;
5 | }
6 |
7 | body, html {
8 | margin: 0;
9 | padding: 0;
10 | font-family: sans-serif;
11 | }
12 |
13 | .keys {
14 | display: flex;
15 | flex: 1;
16 | min-height: 100vh;
17 | align-items: center;
18 | justify-content: center;
19 | }
20 |
21 | .key {
22 | border: 4px solid black;
23 | border-radius: 5px;
24 | margin: 1rem;
25 | font-size: 1.5rem;
26 | padding: 1rem .5rem;
27 | transition: all .07s;
28 | width: 100px;
29 | text-align: center;
30 | color: white;
31 | background: rgba(0, 0, 0, 0.4);
32 | text-shadow: 0 0 5px black;
33 | }
34 |
35 | .playing {
36 | transform: scale(1.1);
37 | border-color: #ffc600;
38 | box-shadow: 0 0 10px #ffc600;
39 | }
40 |
41 | kbd {
42 | display: block;
43 | font-size: 40px;
44 | }
45 |
46 | .sound {
47 | font-size: 1.2rem;
48 | text-transform: uppercase;
49 | letter-spacing: 1px;
50 | color: #ffc600;
51 | }
--------------------------------------------------------------------------------
/02 - JS + CSS Clock/DeathtoStock_QuietFrontier-11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhabib/JavaScript30/abe5ed234d0a843f2bf86413f2c71118d6d16cbd/02 - JS + CSS Clock/DeathtoStock_QuietFrontier-11.jpg
--------------------------------------------------------------------------------
/02 - JS + CSS Clock/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | JS + CSS Clock
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
81 |
82 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/02 - JS + CSS Clock/readme.md:
--------------------------------------------------------------------------------
1 | # JS + CSS Clock
2 |
3 | In this second project we are building an *analog* watch using JS and CSS.
4 |
5 |
6 | # Notes
7 |
8 | The most important thing about the JS code is how we do the mapping from time units to degrees:
9 |
10 | ```javascript
11 | function getTime() {
12 | const date = new Date(),
13 | lag = 90,
14 | secs = 6 * date.getSeconds() + lag,
15 | mins = 6 * date.getMinutes() + lag,
16 | hours = 30 * date.getHours() + lag;
17 | secondHand.style.transform = 'rotate(' + secs + 'deg)';
18 | minuteHand.style.transform = 'rotate(' + mins + 'deg)';
19 | hourHand.style.transform = `translateX(5rem) rotate(${hours}deg)`;
20 | }
21 | ```
22 | The `lag` variable is related to the css code, where we have to define an intial rotation of 90° due the
23 | default horizontal position of elements in html:
24 | ```css
25 | .hand {
26 | width:15rem;
27 | height:6px;
28 | background:black;
29 | position: absolute;
30 | top:50%;
31 | transform-origin: 100%;
32 | transform: rotate(90deg);
33 | }
34 | ```
35 |
36 | **NOTE:** ES6 browser compatibility by default for major browsers' last version:
37 | * Safari: 100%
38 | * Chrome, Opera: 97%
39 | * Edge: 93%
40 | * Firefox: 92%
41 |
--------------------------------------------------------------------------------
/03 - CSS Variables/Death_to_stock_BMX1_10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhabib/JavaScript30/abe5ed234d0a843f2bf86413f2c71118d6d16cbd/03 - CSS Variables/Death_to_stock_BMX1_10.jpg
--------------------------------------------------------------------------------
/03 - CSS Variables/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Scoped CSS Variables and JS
7 |
8 |
9 |
10 |
Update CSS Variables with JS
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
66 |
67 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/03 - CSS Variables/readme.md:
--------------------------------------------------------------------------------
1 | # CSS Variables
2 |
3 | The purpose of this challenge is to learn about the **css variables**. You may think that you have already work with them in **LESS** or **SASS** but what we are going to see here are variables
4 | that will live after preprocessing our code.
5 |
6 | ## Notes
7 |
8 | In LESS or SASS you can find something like this:
9 |
10 | ```less
11 | // Variables
12 | @link-color: #428bca; // sea blue
13 | @link-color-hover: darken(@link-color, 10%);
14 | ```
15 | But make no mistake once they go through the preprocessor they will have a fixxed value. Instead of that, with **css variables** you can change its value during the lifecycle of your program.
16 |
17 |
18 | **So how do we work with them?**
19 |
20 | 1. We have to assign them to a component:
21 | ```css
22 | :root {
23 | --spacing: 10px;
24 | --blur: 10px;
25 | --base: white;
26 | }
27 | ```
28 |
29 | 2. Then we use them through our styles:
30 | ```css
31 | img {
32 | width: 95%;
33 | max-width: 900px;
34 | height: auto;
35 | background-color: var(--base);
36 | padding: var(--spacing);
37 | filter: blur(var(--blur));
38 | }
39 | ```
40 |
41 | 3. And we also access them in our JS:
42 | ```javascript
43 | const inputs = document.querySelectorAll('.controls input');
44 | function handleUpdate() {
45 | const data = this.value,
46 | suffix = this.dataset.sizing || '',
47 | type = this.name;
48 | document.documentElement.style.setProperty(`--${type}`, data + suffix);
49 | }
50 |
51 | inputs.forEach(input => input.addEventListener('change', handleUpdate));
52 | ```
53 |
54 | **NOTE**: Calling `.querySelector()` or `.querySelectorAll()` we recieve a data structure that looks like an `Array` but it is't.
55 | The `listNode` object that we obtain has a reduced API, so in case that you prefer to work with arrays there are two ways for the conversion:
56 |
57 | ```javascript
58 | const inputs = document.querySelectorAll('.controls input');
59 | const inputsArr1 = Array.from(inputs);
60 | const inputsArr2 = [...inputs]; // ES6 spread operator
61 | ```
62 |
63 | ## Events
64 |
65 | * **change:** input element's value chages
66 |
--------------------------------------------------------------------------------
/04 - Array Cardio Day 1/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Array Cardio 💪
6 |
7 |
8 |
Psst: have a look at the JavaScript Console 💁
9 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/04 - Array Cardio Day 1/readme.md:
--------------------------------------------------------------------------------
1 | # Array Cardio Day 1
2 |
3 | In this first cardio day we make use of some of the Array build-in methods:
4 |
5 | * [map](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
6 | * [sort](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
7 | * [reduce](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)
8 |
9 | ## Notes
10 |
11 | When working with **sort**, two directions:
12 | * Ascendent: 1 indicates that goes down in the array:
13 |
14 | ```javascript
15 | arr.sort((a, b) => a > b ? 1 : -1);
16 | ```
17 | * Descendent: -1 indicates that goes up in the array:
18 |
19 | ```javascript
20 | arr.sort((a, b) => a > b ? -1 : 1);
21 | ```
22 |
23 | When working with **reduce**, the key to be able to proccess different data types
24 | is the *initialize* value:
25 |
26 | ```javasript
27 | const data = ['car', 'car', 'truck', 'truck', 'bike', 'walk', 'car', 'van', 'bike', 'walk', 'car', 'van', 'car', 'truck' ],
28 | sol = data.reduce((obj, item) => {
29 | if(!obj[item])
30 | obj[item] = 0;
31 | obj[item]++;
32 | return obj;
33 | }, {});
34 | ```
--------------------------------------------------------------------------------
/05 - Flex Panel Gallery/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Flex Panels 💪
6 |
7 |
8 |
9 |
95 |
96 |
97 |
98 |
99 |
Hey
100 |
Let's
101 |
Dance
102 |
103 |
104 |
Give
105 |
Take
106 |
Receive
107 |
108 |
109 |
Experience
110 |
It
111 |
Today
112 |
113 |
114 |
Give
115 |
All
116 |
You can
117 |
118 |
119 |
Life
120 |
In
121 |
Motion
122 |
123 |
124 |
125 |
141 |
142 |
143 |
144 |
145 |
146 |
--------------------------------------------------------------------------------
/05 - Flex Panel Gallery/readme.md:
--------------------------------------------------------------------------------
1 | # Flex Panel Gallery
2 |
3 | In this exercise we are building a nice effect panel effect that will work great
4 | on a gallery. We make use of **flexboxes**.
5 |
6 | ## Notes
7 |
8 | The logic behind this exercise is very simple. We listen for two events:
9 |
10 | * When there is a *click* in a panel, we toggle the `.open` class from the flex panel.
11 | * And when the panel's transition ends(*transitioned*) we toggle the `.open-active` class from
12 | all the childs of the panel. It's worth mentioning how we filter the event, to get only the one
13 | that interests us:
14 |
15 | ```javascript
16 | function toggleActive(e) {
17 | if(e.propertyName.includes('flex'))
18 | this.classList.toggle('open-active');
19 | }
20 | ```
21 |
22 | Now the magic happens in the HTML and CSS code. So the templates structure is:
23 |
24 | ```html
25 |
26 |
27 |
Hey
28 |
Let's
29 |
Dance
30 |
31 |
32 | ...
33 |
34 | ```
35 |
36 | And in the CSS:
37 |
38 | * `panels` is used to define the flex-container. *It is nice how he uses **vh***.
39 | * `panel` here are defined the common properties as a flex box for all the flex-pannels as
40 | well as the background properties. It also contains the transition for which we listen in
41 | the JS:
42 |
43 | ```css
44 | transition:
45 | font-size 0.7s cubic-bezier(0.61,-0.19, 0.7,-0.11),
46 | flex 0.7s cubic-bezier(0.61,-0.19, 0.7,-0.11),
47 | background 0.2s;
48 | ```
49 |
50 | Also we define that all children of the panel are going to have a transition effect,
51 | but later only the first and last will have this `.open-active` class:
52 |
53 | ```css
54 | .panel > * {
55 | transition:transform 0.5s;
56 | }
57 | .panel > *:first-child { transform: translateY(-100%); }
58 | .panel.open-active > *:first-child { transform: translateY(0); }
59 | .panel > *:last-child { transform: translateY(100%); }
60 | .panel.open-active > *:last-child { transform: translateY(0); }
61 | ```
62 | And last but not least the effect to make bigger a panel comes from the flex properties:
63 |
64 | ```css
65 | .panel.open {
66 | flex: 5;
67 | font-size:40px;
68 | }
69 | ```
70 | * `panelsX` where x is between [1, 5] is how we define a particular background
71 | that each panel will have.
72 |
73 |
74 | ## Events
75 | * **click**
76 | * **transitioned:** when a CSS transition ends
--------------------------------------------------------------------------------
/06 - Type Ahead/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Type Ahead 👀
7 |
8 |
9 |
10 |
11 |
12 |
19 |
64 |
65 |
--------------------------------------------------------------------------------
/06 - Type Ahead/readme.md:
--------------------------------------------------------------------------------
1 | # Type Ahead
2 |
3 | In this new exercise making use of a new method added to the browsers, **[fetch][]**,
4 | we build a autocompletition dropdown.
5 |
6 | ## Notes
7 |
8 | First of all, at initialization we call `fetch` to grab all the data that we are going
9 | to use in the dropdown.
10 | We have to perform a mapping from the data raw data that we receive from the BackEnd, because
11 | it is sent to us as Blob:
12 |
13 | ```javascript
14 | fetch(endpoint)
15 | .then(response => response.json())
16 | .then(myObject => cities.push(...myObject))
17 | .catch(err => console.error(err));
18 | ```
19 |
20 | Then we define an auxiliar function that will help us to find a match inside our array of data
21 | `cities`. Making use or a *regular expression* we filter by cities or states that match our condition:
22 |
23 | ```javascript
24 | function findMatches(wordToMatch, cities) {
25 | return cities.filter(place => {
26 | const regex = new RegExp(wordToMatch, 'gi');
27 | return place.city.match(regex) || place.state.match(regex);
28 | });
29 | }
30 | ```
31 | Finally we attach a handler for our *keyup* event. When creating the HTML code
32 | to be inserted into the `suggestion`element, we make use again of *regular expressions*
33 | to replace the values of ???????
34 |
35 | ```javascript
36 | function displayMatches() {
37 | const matches = findMatches(this.value, cities),
38 | html = matches.map(place => {
39 | const regex = new RegExp(this.value, 'gi'),
40 | cityName = place.city.replace(regex, `${this.value}`),
41 | stateName = place.state.replace(regex, `${this.value}`);
42 | return `
43 |
9 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/07 - Array Cardio Day 2/readme.md:
--------------------------------------------------------------------------------
1 | # Array Cardio Day 2
2 |
3 | The continuation of the array workout takes us to use some new build-in methods:
4 |
5 | * [some](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/some)
6 | * [every](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
7 | * [find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find)
8 | * [findIndex](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex)
9 |
10 | ## Notes
11 |
12 | There is a nice trick implemented in this exercise. Following a *functional-programming*
13 | style and making use fo the ES6 *spread* operator we delete an element from an array
14 | without leaving empty places:
15 |
16 | ```javascript
17 | const index = comments.findIndex(comment => comment.id === 823423);
18 | const newComments = [
19 | ...comments.slice(0, index),
20 | ...comments.slice(index + 1, comments.length)
21 | ];
22 | ```
--------------------------------------------------------------------------------
/08 - Fun with HTML5 Canvas/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | HTML5 Canvas
6 |
7 |
8 |
9 |
56 |
57 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/08 - Fun with HTML5 Canvas/readme.md:
--------------------------------------------------------------------------------
1 | # Fun with HTML5 Canvas
2 |
3 | Making use of **HTML5 Canvas** we convert the browser in a grafitti board 😎
4 |
5 | ## Notes
6 |
7 | We start defining two objects that will represent the [canvas][1] and the [context][2]:
8 |
9 | ```javascript
10 | const canvas = document.querySelector('canvas'),
11 | ctx = canvas.getContext('2d');
12 | ```
13 |
14 | Then we define some properties of our canvas and context:
15 |
16 | ```javascript
17 | canvas.width = window.innerWidth;
18 | canvas.height = window.innerHeight;
19 |
20 | ctx.strokeStyle = "#BADA55"; //
21 | ctx.lineJoin = "round"; //
22 | ctx.lineCap = "round"; //
23 | ctx.lineWidth = 10; //
24 | ctx.globalCompositeOperation = 'multiply'; //
25 | ```
26 |
27 | When the `mousedown` event is fired, the handler is called. It takes care of
28 | saving the position where the user clicked making use of the **[ES6][3]** destructuring
29 | arrays:
30 |
31 | ```javascript
32 | function mouseDown(e) {
33 | isDrawing = true;
34 | // cretes two variables
35 | [lastX, lastY] = [e.offsetX, e.offsetY];
36 | }
37 | ```
38 |
39 | When the `mousemove` event is fired, the handler is called. It takes care of drawing the line,
40 | updates the position of the mouse and giving a nice effect through the *hue*
41 | and the *lineWidth*:
42 |
43 | ```javascript
44 | function draw(e) {
45 | if(!isDrawing) return;
46 | ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`;
47 | // where the drawing takes place
48 | ctx.beginPath();
49 | ctx.moveTo(lastX, lastY);
50 | ctx.lineTo(e.offsetX, e.offsetY);
51 | ctx.stroke();
52 |
53 | [lastX, lastY] = [e.offsetX, e.offsetY];
54 |
55 | // effects
56 | hue++;
57 | if(ctx.lineWidth === 80 || ctx.lineWidth === 1)
58 | direction = !direction;
59 | direction ? ctx.lineWidth++ : ctx.lineWidth--;
60 | }
61 | ```
62 |
63 | ## Events
64 |
65 | * **mousemove**
66 | * **mousedown**
67 | * **mouseup**
68 | * **mouseout**
69 |
70 | [1]:https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API
71 | [2]:https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D
72 | [3]:https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
--------------------------------------------------------------------------------
/09 - Dev Tools Domination/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Console Tricks!
6 |
7 |
8 |
9 |
×BREAK×DOWN×
10 |
11 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/09 - Dev Tools Domination/readme.md:
--------------------------------------------------------------------------------
1 | # Dev Tools Domination
2 |
3 | Some nice `console.` tips to improve the way we debug our JS code.
4 |
5 | ## Notes
6 | * **console.log()**
7 |
8 | ```javascript
9 | // Interpolated
10 | console.log("Hi there, a %s", 'shit');
11 |
12 | // Styled
13 | console.log('%c I am some edited text', 'font-size: 20px; background-color: red; text-shadow: 10px 10px 0 blue');
14 | ```
15 | * To filter the *logs* in the browser's developer tools:
16 | * **console.info()**
17 | * **console.warn()**
18 | * **console.error()**
19 | * **console.assert()** for testing.
20 | * **console.clear()**
21 | * **console.dir()** shows the content of a node from the HTML tag.
22 | * **console.table()** for JSON objects.
23 | * **console.count()** to count the number of times of a value.
24 | * Timers:
25 |
26 | ```javascript
27 | console.time('fetching data');
28 | fetch('https://api.github.com/users/yhabib')
29 | .then(data => data.json())
30 | .then(data => {
31 | console.log(data);
32 | console.timeEnd('fetching data'); // fetching data: 461.976ms
33 | });
34 | ```
--------------------------------------------------------------------------------
/10 - Hold Shift and Check Checkboxes/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Document
7 |
8 |
9 |
10 |
63 |
69 |
70 |
71 |
72 |
This is an inbox layout.
73 |
74 |
75 |
76 |
Check one item
77 |
78 |
79 |
80 |
Hold down your Shift key
81 |
82 |
83 |
84 |
Check a lower item
85 |
86 |
87 |
88 |
Everything inbetween should also be set to checked
89 |
90 |
91 |
92 |
Try do it with out any libraries
93 |
94 |
95 |
96 |
Just regular JavaScript
97 |
98 |
99 |
100 |
Good Luck!
101 |
102 |
103 |
104 |
Don't forget to tweet your result!
105 |
106 |
107 |
108 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/10 - Hold Shift and Check Checkboxes/readme.md:
--------------------------------------------------------------------------------
1 | # Hold Shift and Check Checkboxes
2 |
3 | In this exercise we try to imitate a common layout that can be found in many email clients.
4 | When a user clicks a checkbox, holds Shift, and then clicks another checkbox a few rows down,
5 | all the checkboxes inbetween those two checkboxes will be checked.
6 |
7 | ## Notes
8 |
9 | We start selecting all the inputs that are checkboxes, and binding them to the
10 | click event.
11 |
12 | ```javascript
13 | function handleCheck(e) {
14 | if (e.shiftKey && this.checked) {
15 | let flag = false;
16 | checkboxes.forEach(box => {
17 | if (box === lastCheck || box === this) flag = !flag;
18 | if (flag) box.checked = true;
19 | })
20 | }
21 | lastCheck = this;
22 | }
23 | ```
24 |
25 |
26 | ## Events
27 |
28 | * **click**
--------------------------------------------------------------------------------
/11 - Custom Video Player/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | HTML Video Player
6 |
7 |
8 |
9 |
10 |
Hint: The sequenze is: up, up, down, down, left, right, left, rigt, a, b 😉
14 |
15 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/12 - Key Sequence Detection/readme.md:
--------------------------------------------------------------------------------
1 | # Key Sequence
2 | ## Konami Code
3 | The Konami Code( コナミコマンド) is a cheat code that appears in many Konami video games,[1] although the code also appears in some non-Konami games.
4 | The following sequence of buttons on the game controller to enable a kindo of cheat:
5 | [↑↑↓↓←→←→ba](http://konamicodesites.com/)
6 |
7 | ## Notes
8 | * Key Sequence Detection
9 | * Trimming and array to contain the last x elemnts:
10 | ```javascript
11 | let keys = [],
12 | code = "abcd";
13 | keys.splice(-keys.length - 1, code.length - keys.length);
14 | ```
--------------------------------------------------------------------------------
/13 - Slide in on Scroll/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Document
7 |
8 |
9 |
10 |
11 |
12 |
13 |
Slide in on Scroll
14 |
15 |
Consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui libero ipsum delectus quidem
16 | dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas laborum nam! Fuga ad tempora
17 | in aspernatur pariaturlores sunt esse magni, ut, dignissimos.
18 |
Lorem ipsum cupiditate, corporis a qui libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut
19 | asperiores omnis blanditiis quod quas laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt
20 | esse magni, ut, dignissimos.
21 |
Adipisicing elit. Tempore tempora rerum..
22 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui
23 | libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas
24 | laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.
25 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui
26 | libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas
27 | laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.
28 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui
29 | libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas
30 | laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.
31 |
32 |
33 |
34 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptates, deserunt facilis et iste corrupti omnis tenetur est.
35 | Iste ut est dicta dolor itaque adipisci, dolorum minima, veritatis earum provident error molestias. Ratione magni illo
36 | sint vel velit ut excepturi consectetur suscipit, earum modi accusamus voluptatem nostrum, praesentium numquam, reiciendis
37 | voluptas sit id quisquam. Consequatur in quis reprehenderit modi perspiciatis necessitatibus saepe, quidem, suscipit iure
38 | natus dignissimos ipsam, eligendi deleniti accusantium, rerum quibusdam fugit perferendis et optio recusandae sed ratione.
39 | Culpa, dolorum reprehenderit harum ab voluptas fuga, nisi eligendi natus maiores illum quas quos et aperiam aut doloremque
40 | optio maxime fugiat doloribus. Eum dolorum expedita quam, nesciunt
41 |
42 |
43 |
44 |
at provident praesentium atque quas rerum optio dignissimos repudiandae ullam illum quibusdam. Vel ad error quibusdam, illo
45 | ex totam placeat. Quos excepturi fuga, molestiae ea quisquam minus, ratione dicta consectetur officia omnis, doloribus
46 | voluptatibus? Veniam ipsum veritatis architecto, provident quas consequatur doloremque quam quidem earum expedita, ad delectus
47 | voluptatum, omnis praesentium nostrum qui aspernatur ea eaque adipisci et cumque ab? Ea voluptatum dolore itaque odio.
48 | Eius minima distinctio harum, officia ab nihil exercitationem. Tempora rem nemo nam temporibus molestias facilis minus
49 | ipsam quam doloribus consequatur debitis nesciunt tempore officiis aperiam quisquam, molestiae voluptates cum, fuga culpa.
50 | Distinctio accusamus quibusdam, tempore perspiciatis dolorum optio facere consequatur quidem ullam beatae architecto, ipsam
51 | sequi officiis dignissimos amet impedit natus necessitatibus tenetur repellendus dolor rem! Dicta dolorem, iure, facilis
52 | illo ex nihil ipsa amet officia, optio temporibus eum autem odit repellendus nisi. Possimus modi, corrupti error debitis
53 | doloribus dicta libero earum, sequi porro ut excepturi nostrum ea voluptatem nihil culpa? Ullam expedita eligendi obcaecati
54 | reiciendis velit provident omnis quas qui in corrupti est dolore facere ad hic, animi soluta assumenda consequuntur reprehenderit!
55 | Voluptate dolor nihil veniam laborum voluptas nisi pariatur sed optio accusantium quam consectetur, corrupti, sequi et
56 | consequuntur, excepturi doloremque. Tempore quis velit corporis neque fugit non sequi eaque rem hic. Facere, inventore,
57 | aspernatur. Accusantium modi atque, asperiores qui nobis soluta cumque suscipit excepturi possimus doloremque odit saepe
58 | perferendis temporibus molestiae nostrum voluptatum quis id sint quidem nesciunt culpa. Rerum labore dolor beatae blanditiis
59 | praesentium explicabo velit optio esse aperiam similique, voluptatem cum, maiores ipsa tempore. Reiciendis sed culpa atque
60 | inventore, nam ullam enim expedita consectetur id velit iusto alias vitae explicabo nemo neque odio reprehenderit soluta
61 | sint eaque. Aperiam, qui ut tenetur, voluptate doloremque officiis dicta quaerat voluptatem rerum natus magni. Eum amet
62 | autem dolor ullam.
63 |
64 |
65 |
66 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio maiores adipisci quibusdam repudiandae dolor vero placeat
67 | esse sit! Quibusdam saepe aperiam explicabo placeat optio, consequuntur nihil voluptatibus expedita quia vero perferendis,
68 | deserunt et incidunt eveniet
69 | temporibus doloremque possimus facilis. Possimus labore, officia dolore! Eaque ratione saepe, alias harum laboriosam deserunt
70 | laudantium blanditiis eum explicabo placeat reiciendis labore iste sint. Consectetur expedita dignissimos, non quos distinctio,
71 | eos rerum facilis eligendi. Asperiores laudantium, rerum ratione consequatur, culpa consectetur possimus atque ab tempore
72 | illum non dolor nesciunt. Neque, rerum. A vel non incidunt, quod doloremque dignissimos necessitatibus aliquid laboriosam
73 | architecto at cupiditate commodi expedita in, quae blanditiis. Deserunt labore sequi, repellat laboriosam est, doloremque
74 | culpa reiciendis tempore excepturi. Enim nostrum fugit itaque vel corporis ullam sed tenetur ipsa qui rem quam error sint,
75 | libero. Laboriosam rem, ratione. Autem blanditiis
76 |
77 |
78 |
laborum neque repudiandae quam, cumque, voluptate veritatis itaque, placeat veniam ad nisi. Expedita, laborum reprehenderit
79 | ratione soluta velit natus, odit mollitia. Corporis rerum minima fugiat in nostrum. Assumenda natus cupiditate hic quidem
80 | ex, quas, amet ipsum esse dolore facilis beatae maxime qui inventore, iste? Maiores dignissimos dolore culpa debitis voluptatem
81 | harum, excepturi enim reiciendis, tempora ab ipsam illum aspernatur quasi qui porro saepe iure sunt eligendi tenetur quaerat
82 | ducimus quas sequi omnis aperiam suscipit! Molestiae obcaecati officiis quo, ratione eveniet, provident pariatur. Veniam
83 | quasi expedita distinctio, itaque molestiae sequi, dolorum nisi repellendus quia facilis iusto dignissimos nam? Tenetur
84 | fugit quos autem nihil, perspiciatis expedita enim tempore, alias ab maiores quis necessitatibus distinctio molestias eum,
85 | quidem. Delectus impedit quidem laborum, fugit vel neque quo, ipsam, quasi aspernatur quas odio nihil? Veniam amet reiciendis
86 | blanditiis quis reprehenderit repudiandae neque, ab ducimus, odit excepturi voluptate saepe ipsam. Voluptatem eum error
87 | voluptas porro officiis, amet! Molestias, fugit, ut! Tempore non magnam, amet, facere ducimus accusantium eos veritatis
88 | neque.
89 |
90 |
91 |
92 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio maiores adipisci quibusdam repudiandae dolor vero placeat
93 | esse sit! Quibusdam saepe aperiam explicabo placeat optio, consequuntur nihil voluptatibus expedita quia vero perferendis,
94 | deserunt et incidunt eveniet temporibus doloremque possimus facilis. Possimus labore, officia dolore! Eaque ratione saepe,
95 | alias harum laboriosam deserunt laudantium blanditiis eum explicabo placeat reiciendis labore iste sint. Consectetur expedita
96 | dignissimos, non quos distinctio, eos rerum facilis eligendi. Asperiores laudantium, rerum ratione consequatur, culpa consectetur
97 | possimus atque ab tempore illum non dolor nesciunt. Neque, rerum. A vel non incidunt, quod doloremque dignissimos necessitatibus
98 | aliquid laboriosam architecto at cupiditate commodi expedita in, quae blanditiis. Deserunt labore sequi, repellat laboriosam
99 | est, doloremque culpa reiciendis tempore excepturi. Enim nostrum fugit itaque vel corporis ullam sed tenetur ipsa qui rem
100 | quam error sint, libero. Laboriosam rem, ratione. Autem blanditiis laborum neque repudiandae quam, cumque, voluptate veritatis
101 | itaque, placeat veniam ad nisi. Expedita, laborum reprehenderit ratione soluta velit natus, odit mollitia. Corporis rerum
102 | minima fugiat in nostrum. Assumenda natus cupiditate hic quidem ex, quas, amet ipsum esse dolore facilis beatae maxime
103 | qui inventore, iste? Maiores dignissimos dolore culpa debitis voluptatem harum, excepturi enim reiciendis, tempora ab ipsam
104 | illum aspernatur quasi qui porro saepe iure sunt eligendi tenetur quaerat ducimus quas sequi omnis aperiam suscipit! Molestiae
105 | obcaecati officiis quo, ratione eveniet, provident pariatur. Veniam quasi expedita distinctio, itaque molestiae sequi,
106 | dolorum nisi repellendus quia facilis iusto dignissimos nam? Tenetur fugit quos autem nihil, perspiciatis expedita enim
107 | tempore, alias ab maiores quis necessitatibus distinctio molestias eum, quidem. Delectus impedit quidem laborum, fugit
108 | vel neque quo, ipsam, quasi aspernatur quas odio nihil? Veniam amet reiciendis blanditiis quis reprehenderit repudiandae
109 | neque, ab ducimus, odit excepturi voluptate saepe ipsam. Voluptatem eum error voluptas porro officiis, amet! Molestias,
110 | fugit, ut! Tempore non magnam, amet, facere ducimus accusantium eos veritatis neque.
111 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio maiores adipisci quibusdam repudiandae dolor vero placeat
112 | esse sit! Quibusdam saepe aperiam explicabo placeat optio, consequuntur nihil voluptatibus expedita quia vero perferendis,
113 | deserunt et incidunt eveniet temporibus doloremque possimus facilis. Possimus labore, officia dolore! Eaque ratione saepe,
114 | alias harum laboriosam deserunt laudantium blanditiis eum explicabo placeat reiciendis labore iste sint. Consectetur expedita
115 | dignissimos, non quos distinctio, eos rerum facilis eligendi. Asperiores laudantium, rerum ratione consequatur, culpa consectetur
116 | possimus atque ab tempore illum non dolor nesciunt. Neque, rerum. A vel non incidunt, quod doloremque dignissimos necessitatibus
117 | aliquid laboriosam architecto at cupiditate commodi expedita in, quae blanditiis. Deserunt labore sequi, repellat laboriosam
118 | est, doloremque culpa reiciendis tempore excepturi. Enim nostrum fugit itaque vel corporis ullam sed tenetur ipsa qui rem
119 | quam error sint, libero. Laboriosam rem, ratione. Autem blanditiis laborum neque repudiandae quam, cumque, voluptate veritatis
120 | itaque, placeat veniam ad nisi. Expedita, laborum reprehenderit ratione soluta velit natus, odit mollitia. Corporis rerum
121 | minima fugiat in nostrum. Assumenda natus cupiditate hic quidem ex, quas, amet ipsum esse dolore facilis beatae maxime
122 | qui inventore, iste? Maiores dignissimos dolore culpa debitis voluptatem harum, excepturi enim reiciendis, tempora ab ipsam
123 | illum aspernatur quasi qui porro saepe iure sunt eligendi tenetur quaerat ducimus quas sequi omnis aperiam suscipit! Molestiae
124 | obcaecati officiis quo, ratione eveniet, provident pariatur. Veniam quasi expedita distinctio, itaque molestiae sequi,
125 | dolorum nisi repellendus quia facilis iusto dignissimos nam? Tenetur fugit quos autem nihil, perspiciatis expedita enim
126 | tempore, alias ab maiores quis necessitatibus distinctio molestias eum, quidem. Delectus impedit quidem laborum, fugit
127 | vel neque quo, ipsam, quasi aspernatur quas odio nihil? Veniam amet reiciendis blanditiis quis reprehenderit repudiandae
128 | neque, ab ducimus, odit excepturi voluptate saepe ipsam. Voluptatem eum error voluptas porro officiis, amet! Molestias,
129 | fugit, ut! Tempore non magnam, amet, facere ducimus accusantium eos veritatis neque.
130 |
131 |
132 |
133 |
134 |
135 |
136 |
174 |
175 |
230 |
231 |
232 |
233 |
--------------------------------------------------------------------------------
/13 - Slide in on Scroll/readme.md:
--------------------------------------------------------------------------------
1 | # Slide in on Scroll
2 | When scrolling through the page, the images will slide in as soon as the user reaches half the height of the imgage. These will slide out when the user keeps scrolling.
3 |
4 | ## Notes
5 | * There are some calculations to know where exactly the user is reference to an image.
6 |
7 | ```javascript
8 | // half way through the image
9 | const slideInAt = (window.scrollY + window.innerHeight) - sliderImage.height / 2;
10 | // bottom of the image
11 | const imageBottom = sliderImage.offsetTop + sliderImage.height;
12 | // is the user offset position along the image's heigh?
13 | const isHalfShown = slideInAt > sliderImage.offsetTop;
14 | // did not he already pass the image's heigh??
15 | const isNotScrolledPast = window.scrollY < imageBottom;
16 | ```
17 | * Debounce function limits the rate at which a function can fire. Ensures that a given task doesn't fire so often that it bricks browser performance. [More info](https://davidwalsh.name/javascript-debounce-function)
18 |
19 | ```javascript
20 | function debounce(func, wait = 20, immediate = true) {
21 | let timeout;
22 | return function () {
23 | let context = this,
24 | args = arguments,
25 | later = () => {
26 | timeout = null;
27 | if (!immediate) func.apply(context, args);
28 | },
29 | callNow = immediate && !timeout;
30 |
31 | clearTimeout(timeout);
32 | timeout = setTimeout(later, wait);
33 |
34 | if (callNow) func.apply(context, args);
35 | };
36 | }
37 | ```
38 |
39 | ## Events
40 | * **scroll**
41 |
--------------------------------------------------------------------------------
/14 - JavaScript References VS Copying/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | JS Reference VS Copy
6 |
7 |
8 |
9 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/14 - JavaScript References VS Copying/readme.md:
--------------------------------------------------------------------------------
1 | # Objects and Arrays - JavaScript References VS Copying
2 | Comparation about how JS treats referenced and copied variables.
3 | ## Notes
4 | In JS there are two flavor of types:
5 |
6 | ### **Primitive Types**
7 | The following types are considered primitive in JavaScript:
8 | * String
9 | * Number
10 | * Boolean
11 | * Null
12 | * Undefined
13 |
14 | These types are always manipulated by **value**. This means that if we define a variable as of these types, and then we pass it to a function or create a new variable from it, this *copy* variable will will have a copy of the value of the orignal variable. Any change to one of these will have no repercusion for the other one.
15 | For example:
16 |
17 | ```javascript
18 | let age = 100,
19 | age2 = age;
20 | console.log(age === age2, age, age2); // true, 100, 100
21 | age = 200;
22 | console.log(age === age2, age, age2); // false, 200, 100
23 | ```
24 |
25 | ### **Object Type**
26 | Leaving aside that in JS almost everything is an object, we can define that what it isn't a **primitive type** it's an **object type**. Like for example:
27 | * Object
28 | * Array
29 | * Function
30 | * Set
31 | * ...
32 |
33 | These *objects* are passed/treated by reference. This means that now what it's passed to a *copy variable* is the address of this variable instead of its value. So modifying the copy will modify the original variable.
34 | Lets see how this works with arrays:
35 | ```javascript
36 | const arr = [1, 2, 3],
37 | arrC = arr;
38 |
39 | console.log(arr === arrC, arr, arrC); //true, [1,2,3], [1,2,3]
40 | arrC[2] = 101;
41 | console.log(arr, arrC); // [1, 2, 101], [1, 2, 101]
42 |
43 | ```
44 |
45 | The same for objects:
46 | ```javascript
47 | const person = {
48 | name: 'Bruce Wayne',
49 | city: 'Gotham City'
50 | },
51 | hero = person;
52 |
53 | console.log(person === hero); // true
54 |
55 | hero.name = 'Batman';
56 | console.log(person, hero) // {name: 'Batman', city: 'Gotham City'}, {name: 'Batman', city: 'Gotham City'}
57 | ```
58 |
59 | The reason of this is that hero receives the address where person is targeting so both variable share the same object. So how can we overcome this?? How could we create a copy of *object* variables?
60 | ```javascript
61 | // arrays
62 | const arr = [1, 2, 3],
63 | arrC1 = arr.splice(),
64 | arrC2 = [].concat(arr),
65 | arrC3 = [...arr], // ES6
66 | arrC4 = Array.from(arr);
67 |
68 | // objects
69 | const obj = { val:1 , type:'Number' },
70 | objC1 = Object.assign({}, obj);
71 | // It also allows to change some values
72 | const objC2 = Object.assign({}, obj, {val: 2});
73 | ```
74 |
75 | Now these copies recieve a the copied value of the original object. So changing them will not affect the original. But it is important to mark that it is a *shallow* copy, so **this is only 1 level deep**.
76 | ```javascript
77 | const arr = [[1], 2, 3],
78 | arrC1 = arr.splice();
79 |
80 | console.log(arr, arrC1); // false
81 | arrC1[1] = 20;
82 | console.log(arr, arrC1); // [[1], 2, 3], [[1], 20, 3]
83 |
84 | arrC1[0] = 0;
85 | console.log(arr, arrC1); // [[0], 2, 3], [[0], 20, 3]
86 |
87 | // same for objects
88 | const obj = { val:1 , metadata:{ type: 'Number' } },
89 | objC1 = Object.assign({}, obj);
90 |
91 | console.log(obj, objC1); // false
92 | objC1.val = 2;
93 | console.log(obj, objC1); // { val:1 , metadata:{ type: 'Number' } }, { val:2 , metadata:{ type: 'Number' } }
94 |
95 | objC1.metadata.new = 'new';
96 | console.log(obj, objC1); // { val:1 , metadata:{ type: 'Number', new: 'new' } }, { val:2 , metadata:{ type: 'Number', new: 'new' } }
97 | ```
98 |
99 | To solve that we could draw on the *lodash* cloneDeep method, that works with objects and arrays.
100 | As a **cheap** solution for objects we could also do the next(*
101 | JSON serialization and parsing is painfully slo, so native methods will be faster*):
102 | ```javascript
103 | const obj = { val:1 , metadata:{ type: 'Number' } },
104 | objC1 = JSON.parse(JSON.stringify(obj));
105 | ```
106 |
--------------------------------------------------------------------------------
/15 - LocalStorage/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | LocalStorage
7 |
8 |
9 |
10 |
11 |
15 |
16 |
32 |
33 |
34 |
LOCAL TAPAS
35 |
36 |
37 |
Loading Tapas...
38 |
39 |
43 |
44 |
45 |
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/15 - LocalStorage/readme.md:
--------------------------------------------------------------------------------
1 | # Local Storage and Event Delegation
2 |
3 | Allows the user to insert new dishes to be prepared. This dishes are going to be persistent. For that we are using the LocalStorage.
4 | We also work here with **event delegation**, that happends when we want to attach a listener to an element that is going to be created in the future, in this case the checkboxes related to each dish.
5 |
6 | ## Notes
7 | **In this exercise I did't wrap all the js code inside a IIFE becauese doing that doesn't allow to inspect the variables inside the web browser's console!!**
8 |
9 | * Working with **localStorage** to make data persistent when refreshing the browser. API:
10 | - localStorage.get(key);
11 | - localStorage.set(key, value); // It expects plain strings so if the value is an Object -> JSON.stringify(value)
12 | - localStorage.delete(key);
13 | * When working with **submit** events one has to consider that the browser is going to make a redirection to the same url plus the value of the form(GET). So in order to inspect the inhalt:
14 | - In the developer tools -> Preserve log
15 | - e.preventDefault(); // Inside the event handler. No redirection at all
16 | * Event Delegation: When listening for an event in something higher and then inside we check if it is the actually thing that we want:
17 | * A parent element is *responsible* but its children *aren't*.
18 | * He takes the responsability and then inside the event handler checks if it's correct
19 | ```javascript
20 | itemsList.addEventListener('click', toggleDone); // itemsList is an - first
21 | function toggleDone(e) {
22 | if (!e.target.matches('input')) return; // checks if its the desired child
23 | ... // delegates the handler
24 | }
25 | ```
26 | * Using **data-index** to bind the position of the item in the array with an html component!
27 | * How to create custom checkboxes using CSS:
28 | ```css
29 | // hide original boxes
30 | .plates input {
31 | display: none;
32 | }
33 |
34 | .plates input + label:before {
35 | content: '⬜️';
36 | margin-right: 10px;
37 | }
38 |
39 | .plates input:checked + label:before {
40 | content: '🌮';
41 | }
42 | ```
43 |
44 | **Note:** *If one wants to do it smarter so calling populateList will only update a new elements instead of recreating again the whole innerHtml -> AngularJs, React, ... two way bindings ...*
45 |
46 | ## Events
47 | * **submit:** in the callback the **this** object is the form
48 | * **click**
49 |
50 | ## To Do's
51 | 1. [ ] Clear button: Clears all the checkboxes
52 |
--------------------------------------------------------------------------------
/15 - LocalStorage/style.css:
--------------------------------------------------------------------------------
1 |
2 | html {
3 | box-sizing: border-box;
4 | background:url('./../resources/img/pic3.jpg') center no-repeat;
5 | background-size:cover;
6 | min-height:100vh;
7 | display:flex;
8 | justify-content: center;
9 | align-items: center;
10 | text-align: center;
11 | font-family: Futura,"Trebuchet MS",Arial,sans-serif
12 | }
13 | *, *:before, *:after {box-sizing: inherit; }
14 |
15 | svg {
16 | fill:white;
17 | background: rgba(0,0,0,0.1);
18 | padding: 20px;
19 | border-radius: 50%;
20 | width:200px;
21 | margin-bottom: 50px;
22 | }
23 |
24 | .wrapper {
25 | padding: 20px;
26 | max-width: 350px;
27 | background: rgba(255,255,255,0.95);
28 | box-shadow: 0 0 0 10px rgba(0,0,0,0.1);
29 | }
30 |
31 | h2 {
32 | text-align: center;
33 | margin: 0;
34 | font-weight: 200;
35 | }
36 |
37 | .plates {
38 | margin: 0;
39 | padding: 0;
40 | text-align: left;
41 | list-style: none;
42 | }
43 |
44 | .plates li {
45 | border-bottom: 1px solid rgba(0,0,0,0.2);
46 | padding: 10px 0;
47 | font-weight: 100;
48 | display: flex;
49 | }
50 |
51 | .plates label {
52 | flex:1;
53 | cursor: pointer;
54 |
55 | }
56 |
57 | .plates input {
58 | display: none;
59 | }
60 |
61 | .plates input + label:before {
62 | content: '⬜️';
63 | margin-right: 10px;
64 | }
65 |
66 | .plates input:checked + label:before {
67 | content: '🌮';
68 | }
69 |
70 | .add-items {
71 | margin-top: 20px;
72 | }
73 |
74 | .add-items input {
75 | padding:10px;
76 | outline:0;
77 | border:1px solid rgba(0,0,0,0.1);
78 | }
79 |
--------------------------------------------------------------------------------
/16 - Mouse Move Shadow/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Mouse Shadow
6 |
7 |
8 |
9 |
10 |
🔥WOAH!
11 |
12 |
13 |
32 |
33 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/16 - Mouse Move Shadow/readme.md:
--------------------------------------------------------------------------------
1 | # Text Shadow Mouse Move Effect
2 | When the user scrolls a text shadow effect is attached to the *headear* inside a *div* component
3 |
4 | ## Notes
5 | * HTML gloabl attribute **contenteditable** defines that the content of an element is editable.
6 |
7 | ```html
8 |
🔥WOAH!
9 | ```
10 | * Working with **events**:
11 | - **this** represents the the thing that you are listen on for the event (always the same)
12 | - **e.target** represents the thing that actually triggered on the event (the same element than above or also could be its children)
13 | ```javascript
14 | // When they are not the same we have to calculate
15 | if( this !== e.target) {
16 | x = x + e.target.offsetLeft;
17 | y = y + e.target.offsetTop;
18 | }
19 | ```
20 | - Some properties for the measures:
21 | - **this.offsetHeight**, **this.offsetWidth**
22 | - **e.offsetX**, **e.offsetY**
23 | - **e.target.offsetLeft**, **e.target.offsetTop**
24 | ```javascript
25 | // Width & height properties of the 'hero' div in relation to the window object
26 | const { offsetWidth: width, offsetHeight: height } = this;
27 | // Distance of the mouse from the event's target on both axis
28 | let {offsetX: x, offsetY: y } = e;
29 | // If the element that is the current target of the event differs from the event's originating target,
30 | // increment the values of the two previously declared variables by the distance between the originating target and the current target on both axis
31 | if( this !== e.target) {
32 | x = x + e.target.offsetLeft;
33 | y = y + e.target.offsetTop;
34 | }
35 | ```
36 | - **walk** is used to calculate the stretch distance for the element's shadow on both axis.
37 | ```javascript
38 | const walk = 100; // 100px
39 | const xWalk = (x / width * walk) - (walk / 2); // Normaliza and then rest half the walk
40 | const yWalk = (y / height * walk) - (walk / 2);
41 | ```
42 | * ES6 destruturing:
43 |
44 | ```javascript
45 | const width = this.offsetWidth,
46 | height = this.offsetHeight;
47 | const { offsetWidth: width, offsetHeight: height } = hero;
48 | ```
49 |
50 | ## Events
51 | * **mousemove**
52 |
53 |
--------------------------------------------------------------------------------
/17 - Sort Without Articles/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Sort Without Articles
6 |
7 |
8 |
9 |
45 |
46 |
47 |
48 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/17 - Sort Without Articles/readme.md:
--------------------------------------------------------------------------------
1 | # Sort Without Articles
2 |
3 | This one was a short one, the idea was to sort an array of Bands without taking care of the article.
4 |
5 | ## Notes
6 | * How to strip a word with **regular expressions**:
7 |
8 | ```javascript
9 | function strip(bandName) {
10 | return bandName.replace(/^(a |the |an )/i, '').trim();
11 | }
12 | ```
13 | * Sorting and coupling to the html element:
14 |
15 | ```javascript
16 | const sortedBands = bands.sort((a, b) => strip(a) > strip(b) ? 1 : -1);
17 | document.querySelector('#bands').innerHTML = sortedBands.map(band => `
183 |
184 |
210 |
211 |
212 |
--------------------------------------------------------------------------------
/18 - Adding Up Times with Reduce/readme.md:
--------------------------------------------------------------------------------
1 | # Adding Up Times With Reduce
2 | An *ul* representing a playlist where each *li* represents a video and it has a *data-time* attribute with its duration(string). Calculate the total time in *hours:minutes:seconds*
3 |
4 | ## Notes:
5 | * Two implementations, one is self-describing the other is serious stuff 😝
6 | ```javascript
7 | const timeAttributes = Array.from(document.querySelectorAll('[data-time]'));
8 |
9 | const totalSeconds = timeAttributes
10 | .map(node => node.dataset.time)
11 | .map(timeCode => {
12 | const [mins, secs] = timeCode.split(':').map(parseFloat);
13 | return mins * 60 + secs;
14 | })
15 | .reduce((a, b) => a + b);
16 | ```
17 | ```javascript
18 | const timeAttributes = Array.from(document.querySelectorAll('[data-time]'));
19 |
20 | const totalSeconds = timeAttributes
21 | .reduce((a, b) => {
22 | b = b.dataset.time.split(':').map(parseFloat);
23 | return a + b[0]*60 + b[1];
24 | }, 0);
25 | ```
26 | * There is also a function that converts from seconds to a hours:minutes:seconds format.
27 |
--------------------------------------------------------------------------------
/19 - Webcam Fun/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Get User Media Code Along!
6 |
7 |
8 |
9 |
10 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/19 - Webcam Fun/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gum",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "scripts.js",
6 | "scripts": {
7 | "start" : "browser-sync start --server --files '*.css, *.html, *.js'"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "browser-sync": "^2.12.5"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/19 - Webcam Fun/readme.md:
--------------------------------------------------------------------------------
1 | # Unreal Webcam Fun
2 | In this exercise we will first use the **[Navigator][2]** and then the **[mediaDevices][2]** to access the Webcam.
3 | The first one is a representation of the application that is running our code, in our case, the browser. And the second
4 | provides an interface to access the unser's connected media devices(webcam, microphone, ...).
5 | Then we will allow the user to make a picture of the input from the webcam and download it. As a bonus
6 | some effects will be also implemented.
7 |
8 | ## Notes
9 | **This exercise needs to be run in a server in order to be able to have access to the media devices!!**
10 |
11 | The first step is to get the stream from the webcam as an input to our *video* element. Worth to note:
12 | in parameter. This new object represents a **File** or **Blob** object.
13 |
14 | * The **[createObjectURL][2]** method sreates a DOMString containing an URL that represent the object given
15 | * The HTML5 *video* tag expects in the src attribute a Bob file 😉
16 | * The *catch* triggers if the user doesn't allow to access the webcam.
17 |
18 | ```javascript
19 | function getVideo() {
20 | // way to access the media devices
21 | navigator.mediaDevices.getUserMedia( {video: true, audio: false } )
22 | .then(stream => {
23 | video.src = window.URL.createObjectURL(stream);
24 | video.play();
25 | })
26 | .catch(e => console.log('Ohhh No!!!', e));
27 | }
28 | ```
29 |
30 | Then we want to paint in the *canvas* element the output of the webcam. In order to achieve it
31 | we have to:
32 |
33 | 1. Listen for the **canplay** event that will trigger when the video element starts to play.
34 | 2. Set the size of our *canvas* element to be equal to the *video* element.
35 | 3. Apply the desired effects.
36 | 4. Draw the image into the canvas.
37 |
38 | ```javascript
39 | function paintToCanvas() {
40 | const height = video.videoHeight;
41 | const width = video.videoWidth;
42 |
43 | canvas.height = height;
44 | canvas.width = width;
45 |
46 | return setInterval(() => {
47 | ctx.drawImage(video, 0, 0, width, height); // from top left corner to bottom right
48 |
49 | // take pixels out in a special array for big data
50 | let pixels = ctx.getImageData(0, 0, width, height);
51 |
52 | // mess with them
53 | // pixels = redEffect(pixels);
54 | // pixels = rgbSplit(pixels);
55 | pixels = greenScreen(pixels);
56 |
57 | // put pixels back
58 | ctx.putImageData(pixels, 0, 0);
59 | }, 16);
60 | }
61 | ```
62 |
63 | Finally when the user takes a photo:
64 |
65 | 1. Audio element plays a sound
66 | 2. Gets a data URI containing a representation of the image in the specified format through **[toDataUrl][3]**.
67 | 3. We create a new *a* element we insert the picture as an image.
68 |
69 | ```javascript
70 | function takePhoto() {
71 | snap.currentTime = 0;
72 | snap.play();
73 |
74 | const data = canvas.toDataURL('imgage/jpeg');
75 |
76 | const link = document.createElement('a');
77 | link.href = data;
78 | link.setAttribute('download', 'handsome');
79 | link.innerHTML =``;
80 | strip.insertBefore(link, strip.firstChild);
81 | }
82 | ```
83 |
84 | **Why do I need a server for this exercise?** Because of security restrictions of getting the user's webcam, it
85 | must be tied to a security origin like https or localhost.
86 |
87 | ## Events
88 | * **canplay:** the browser can start playing the specified audio/video (when it has buffered enough to begin).
89 |
90 | ## ToDo's
91 | * [ ] Use of *debugger*
92 | * [ ] Interface for the effects.
93 | * [ ] Use return value from *setInterval* to be able to stop it at some point.
94 | * [ ] Document properly the effects.
95 |
96 | [1]:https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices
97 | [2]:https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
98 | [3]:https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
--------------------------------------------------------------------------------
/19 - Webcam Fun/scripts.js:
--------------------------------------------------------------------------------
1 | const video = document.querySelector('.player');
2 | const canvas = document.querySelector('.photo');
3 | const ctx = canvas.getContext('2d');
4 | const strip = document.querySelector('.strip');
5 | const snap = document.querySelector('.snap');
6 |
7 | // secure web: hhtps or localhost
8 |
9 | video.addEventListener('canplay', paintToCanvas);
10 |
11 | getVideo();
12 |
13 | // We need to run it in a server in order to access the getUserMediaProperties
14 | function getVideo() {
15 | // way to access the media devices
16 | navigator.mediaDevices.getUserMedia( {video: true, audio: false } )
17 | .then(stream => {
18 | // video.src spects this type of obj rather than a normal obj
19 | video.src = window.URL.createObjectURL(stream);
20 | video.play();
21 | // video.src is a BLOB!!
22 | })
23 | .catch(e => {
24 | // In case that the user doesn't allow to use the media devices
25 | alert('Allow me to use the WEBCAAAAAAM!!!');
26 | console.log('Ohhh No!!!', e);
27 | });
28 | }
29 |
30 | function paintToCanvas() {
31 | const height = video.videoHeight;
32 | const width = video.videoWidth;
33 |
34 | // canvas should have the same size as its input(webcam)
35 | canvas.height = height;
36 | canvas.width = width;
37 |
38 | // case I want to stop it sometime
39 | return setInterval(() => {
40 | // from top left corner to bottom right corner
41 | ctx.drawImage(video, 0, 0, width, height);
42 |
43 | // take pixels out in a special array for big data
44 | let pixels = ctx.getImageData(0, 0, width, height);
45 |
46 | // mess with them
47 | // pixels = redEffect(pixels);
48 | // pixels = rgbSplit(pixels);
49 | pixels = greenScreen(pixels);
50 | // debugger;
51 |
52 | // put pixels back
53 | ctx.putImageData(pixels, 0, 0);
54 | }, 16);
55 | }
56 |
57 | function takePhoto() {
58 | // reproduce sound
59 | snap.currentTime = 0;
60 | snap.play();
61 |
62 | // take data out of canvas
63 | const data = canvas.toDataURL('imgage/jpeg');
64 | // console.log(data);
65 |
66 | const link = document.createElement('a');
67 | link.href = data;
68 | link.setAttribute('download', 'handsome');
69 | link.innerHTML =``;
70 | // like jquery prepend
71 | strip.insertBefore(link, strip.firstChild);
72 | }
73 |
74 | // -- EFFECTS
75 |
76 | function redEffect(pixels) {
77 | for(let i=0; i < pixels.data.length; i+=4) {
78 | pixels.data[i + 0] = pixels.data[i + 0] + 150; // r
79 | pixels.data[i + 1] = pixels.data[i + 1] - 50; // g
80 | pixels.data[i + 2] = pixels.data[i + 2] * 0.5; // b
81 | }
82 | return pixels;
83 | }
84 |
85 | function rgbSplit(pixels) {
86 | for(let i=0; i < pixels.data.length; i+=4) {
87 | pixels.data[i - 150] = pixels.data[i + 0];
88 | pixels.data[i + 100] = pixels.data[i + 1];
89 | pixels.data[i - 150] = pixels.data[i + 2];
90 | }
91 | return pixels;
92 | }
93 |
94 | // document it properly
95 | function greenScreen(pixels) {
96 | const levels = {};
97 |
98 | document.querySelectorAll('.rgb input').forEach((input) => {
99 | levels[input.name] = input.value;
100 | });
101 |
102 | for (i = 0; i < pixels.data.length; i = i + 4) {
103 | red = pixels.data[i + 0];
104 | green = pixels.data[i + 1];
105 | blue = pixels.data[i + 2];
106 | alpha = pixels.data[i + 3];
107 |
108 | if (red >= levels.rmin
109 | && green >= levels.gmin
110 | && blue >= levels.bmin
111 | && red <= levels.rmax
112 | && green <= levels.gmax
113 | && blue <= levels.bmax) {
114 | // take it out!
115 | pixels.data[i + 3] = 0;
116 | }
117 | }
118 |
119 | return pixels;
120 | }
121 |
--------------------------------------------------------------------------------
/19 - Webcam Fun/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | }
4 |
5 | *, *:before, *:after {
6 | box-sizing: inherit;
7 | }
8 |
9 | html {
10 | font-size: 10px;
11 | background:#ffc600;
12 | }
13 |
14 | .photobooth {
15 | background:white;
16 | max-width:150rem;
17 | margin: 2rem auto;
18 | border-radius:2px;
19 | }
20 |
21 | /*clearfix*/
22 | .photobooth:after {
23 | content: '';
24 | display: block;
25 | clear: both;
26 | }
27 |
28 | .photo {
29 | width:100%;
30 | float:left;
31 | }
32 |
33 | .player {
34 | position: absolute;
35 | top:20px;
36 | right: 20px;
37 | width:200px;
38 | }
39 |
40 | /*
41 | Strip!
42 | */
43 |
44 | .strip {
45 | padding:2rem;
46 | }
47 | .strip img {
48 | width:100px;
49 | overflow-x: scroll;
50 | padding:0.8rem 0.8rem 2.5rem 0.8rem;
51 | box-shadow:0 0 3px rgba(0,0,0,0.2);
52 | background:white;
53 | }
54 |
55 | .strip a:nth-child(5n+1) img { rotate: 10deg; }
56 | .strip a:nth-child(5n+2) img { rotate: -2deg; }
57 | .strip a:nth-child(5n+3) img { rotate: 8deg; }
58 | .strip a:nth-child(5n+4) img { rotate: -11deg; }
59 | .strip a:nth-child(5n+5) img { rotate: 12deg; }
60 |
--------------------------------------------------------------------------------
/20 - Speech Detection/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Speech Detection
6 |
7 |
8 |
9 |
10 |
11 |
12 |
47 |
48 |
49 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/20 - Speech Detection/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gum",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "scripts.js",
6 | "scripts": {
7 | "start" : "browser-sync start --directory --server --files '*.css, *.html, *.js'"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "browser-sync": "^2.12.5"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/20 - Speech Detection/readme.md:
--------------------------------------------------------------------------------
1 | # Native Speech Recognition
2 |
3 | The idea is to use the native speech recognition from the browser to build like a SpeechNotebook where each sentence will be in a different paragraph, that means that once
4 | the user stops talking a new we will append a new **p** element.
5 |
6 |
7 | ## Notes
8 | **This exercise needs to be run in a server in order to be able to have access to the media devices!!**
9 |
10 | The **[Web Speech API][1]** has two components: the **[SpeechRecognition][2]** and the **[SpeechSythesis][3]**. In this first project we are focusing in the first component.
11 |
12 | We initialize it like this:
13 | ```javascript
14 | // Chrome special prefix
15 | window.SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
16 |
17 | const recognition = new SpeechRecognition();
18 | recognition.interimResults = true; //As you are speaking igves values rather than waiting till you end speaking
19 | ```
20 |
21 | Handling the *result* event is the most important part of this exercise. It is in **e.results** were we can find most of the relevant information. This is an array
22 | where we can find the **transcript**, a value between [0, 1] that shows the **confidence** of the transcription and a **isFinal** boolean which tells you if the user stopped
23 | talking to start in a new paragraph.
24 |
25 | ```javascript
26 | recognition.addEventListener('result', e => {
27 | const transcript = Array.from(e.results)
28 | .map(result => result[0])
29 | .map(result => result.transcript)
30 | .join('');
31 |
32 | p.textContent = transcript;
33 | if(e.results[0].isFinal) {
34 | p = document.createElement('p');
35 | words.appendChild(p);
36 | }
37 | });
38 | ```
39 |
40 | Finally we append a new element to the **div** and rebind the listener to the **result** event.
41 |
42 |
43 |
44 | ## Events
45 | * result -> Once we get it, the listener unbinds itself
46 | * end -> We use it to rebind it 😀
47 |
48 | ## To Do's
49 | * [x] Implemente some basic functionality like Siri:
50 | - Siri weather
51 | - Siri NBA
52 |
53 | [1]:https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API
54 | [2]:https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition
55 | [3]:https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis
56 |
--------------------------------------------------------------------------------
/21 - Geolocation/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Document
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 0
14 | KM/H
15 |
16 |
17 |
59 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/21 - Geolocation/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gum",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "scripts.js",
6 | "scripts": {
7 | "start" : "browser-sync start --directory --server --files '*.css, *.html, *.js' --https"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "browser-sync": "^2.12.5"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/21 - Geolocation/readme.md:
--------------------------------------------------------------------------------
1 | ## Geolocation
2 |
3 | The targe of this exercise are the iOS ecosystem so it is not being implemented by me for the moment.
--------------------------------------------------------------------------------
/22 - Follow Along Link Highlighter/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 👀👀👀Follow Along Nav
7 |
8 |
9 |
10 |
11 |
12 |
21 |
22 |
23 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Est explicabo unde natus necessitatibus
24 | esse obcaecati distinctio, aut itaque, qui vitae!
25 |
Aspernatur sapiente quae sint soluta modi, atque praesentium laborum pariatur earum quaerat cupiditate consequuntur facilis ullam dignissimos, aperiam quam veniam.
26 |
Cum ipsam quod, incidunt sit ex tempore placeat maxime corrupti possimus veritatis ipsum fugit recusandae est doloremque? Hic, quibusdam, nulla.
27 |
Esse quibusdam, ad, ducimus cupiditate nulla, quae magni odit totam ut consequatur eveniet
28 | sunt quam provident sapiente dicta neque quod.
29 |
Aliquam dicta sequi culpa fugiat consequuntur pariatur optio ad minima, maxime odio,
30 | distinctio magni impedit tempore enim repellendus repudiandae quas!
31 |
32 |
33 |
64 |
65 |
--------------------------------------------------------------------------------
/22 - Follow Along Link Highlighter/readme.md:
--------------------------------------------------------------------------------
1 | # Follow Along Link Highlighter
2 |
3 | This is the first part of a project where we want to build a replica of the navigation bar from **[Stripe](https://stripe.com)**.
4 | So in this first exercise, we build a **link higlighter** which will transtition from one link
5 | to another with a smoothie effect adding a class to the link.
6 |
7 | ## Notes
8 |
9 | 1. We start creating an `span` element, adding it the `higlight` class and [appending][1] it to the `body`.
10 | 2. Then when the event is triggered we have to calculate the position of the *trigger* and add these
11 | values to the `highlight` class.
12 |
13 | ```javascript
14 | function highlightLink() {
15 | const linkCoords = this.getBoundingClientRect();
16 | const coords = {
17 | width: linkCoords.width,
18 | height: linkCoords.height,
19 | top: linkCoords.top + window.scrollY,
20 | left: linkCoords.left + window.scrollX
21 | };
22 |
23 | highlight.style.width = `${coords.width}px`;
24 | highlight.style.height = `${coords.height}px`;
25 | highlight.style.transform = `translate(${coords.left}px, ${coords.top}px)`;
26 | }
27 | ```
28 | What is important about the `function` above is:
29 | * How we calculate the position of the trigger(`this`) represented by a `a` with *[getBoundingClientRect][2]*.
30 | * How we add the amount of scrolled screen with `.scrollX`and `scrollY`, to position correctly the 'span' element.
31 | * How we use `translate` instead of defining directly the `top` and `left` properties. Doing it this way
32 | we achieve a smoothier effect when we move from a link to another one.
33 |
34 | ## Events
35 | * **mouseenter:** user enters with the mouse inside the trigger element
36 |
37 | [1]:https://developer.mozilla.org/en/docs/Web/API/Node/appendChild
38 | [2]:https://developer.mozilla.org/en/docs/Web/API/Element/getBoundingClientRect
--------------------------------------------------------------------------------
/22 - Follow Along Link Highlighter/style.css:
--------------------------------------------------------------------------------
1 | ‹html {
2 | box-sizing: border-box;
3 | }
4 |
5 | *, *:before, *:after {
6 | box-sizing: inherit;
7 | }
8 |
9 | body {
10 | min-height: 100vh;
11 | margin: 0;
12 | /* Important! */
13 | font-family: sans-serif;
14 | background:
15 | linear-gradient(45deg, hsla(340, 100%, 55%, 1) 0%, hsla(340, 100%, 55%, 0) 70%),
16 | linear-gradient(135deg, hsla(225, 95%, 50%, 1) 10%, hsla(225, 95%, 50%, 0) 80%),
17 | linear-gradient(225deg, hsla(140, 90%, 50%, 1) 10%, hsla(140, 90%, 50%, 0) 80%),
18 | linear-gradient(315deg, hsla(35, 95%, 55%, 1) 100%, hsla(35, 95%, 55%, 0) 70%);
19 | }
20 |
21 | .wrapper {
22 | margin: 0 auto;
23 | max-width: 500px;
24 | font-size: 20px;
25 | line-height: 2;
26 | position: relative;
27 | }
28 |
29 | a {
30 | text-decoration: none;
31 | color: black;
32 | background: rgba(0, 0, 0, 0.05);
33 | border-radius: 20px
34 | }
35 |
36 | .highlight {
37 | transition: all 0.2s;
38 | border-bottom: 2px solid white;
39 | position: absolute;
40 | top: 0;
41 | background: white;
42 | left: 0;
43 | z-index: -1;
44 | border-radius: 20px;
45 | display: block;
46 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.2)
47 | }
48 |
49 | .menu {
50 | padding: 0;
51 | display: flex;
52 | list-style: none;
53 | justify-content: center;
54 | margin: 100px 0;
55 | }
56 |
57 | .menu a {
58 | display: inline-block;
59 | padding: 5px;
60 | margin: 0 20px;
61 | color: black;
62 | }
--------------------------------------------------------------------------------
/23 - Speech Synthesis/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Speech Synthesis
7 |
8 |
9 |
10 |
11 |
12 |
13 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui libero
29 | ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas laborum
30 | nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.
31 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui libero
32 | ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas laborum
33 | nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.
34 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui libero
35 | ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas laborum
36 | nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.
37 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui libero
38 | ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas laborum
39 | nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.
40 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui libero
41 | ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas laborum
42 | nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.
43 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui libero
44 | ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas laborum
45 | nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.
46 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui libero
47 | ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas laborum
48 | nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.
49 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui libero
50 | ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas laborum
51 | nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.
52 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui libero
53 | ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas laborum
54 | nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.
55 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui libero
56 | ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas laborum
57 | nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.
58 |
59 |
60 |
61 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptates, deserunt facilis et iste corrupti omnis tenetur est.
62 | Iste ut est dicta dolor itaque adipisci, dolorum minima, veritatis earum provident error molestias. Ratione magni illo
63 | sint vel velit ut excepturi consectetur suscipit, earum modi accusamus voluptatem nostrum, praesentium numquam, reiciendis
64 | voluptas sit id quisquam. Consequatur in quis reprehenderit modi perspiciatis necessitatibus saepe, quidem, suscipit iure
65 | natus dignissimos ipsam, eligendi deleniti accusantium, rerum quibusdam fugit perferendis et optio recusandae sed ratione.
66 | Culpa, dolorum reprehenderit harum ab voluptas fuga, nisi eligendi natus maiores illum quas quos et aperiam aut doloremque
67 | optio maxime fugiat doloribus. Eum dolorum expedita quam, nesciunt
68 |
69 |
70 |
71 |
at provident praesentium atque quas rerum optio dignissimos repudiandae ullam illum quibusdam. Vel ad error quibusdam,
72 | illo ex totam placeat. Quos excepturi fuga, molestiae ea quisquam minus, ratione dicta consectetur officia omnis, doloribus
73 | voluptatibus? Veniam ipsum veritatis architecto, provident quas consequatur doloremque quam quidem earum expedita, ad
74 | delectus voluptatum, omnis praesentium nostrum qui aspernatur ea eaque adipisci et cumque ab? Ea voluptatum dolore itaque
75 | odio. Eius minima distinctio harum, officia ab nihil exercitationem. Tempora rem nemo nam temporibus molestias facilis
76 | minus ipsam quam doloribus consequatur debitis nesciunt tempore officiis aperiam quisquam, molestiae voluptates cum, fuga
77 | culpa. Distinctio accusamus quibusdam, tempore perspiciatis dolorum optio facere consequatur quidem ullam beatae architecto,
78 | ipsam sequi officiis dignissimos amet impedit natus necessitatibus tenetur repellendus dolor rem! Dicta dolorem, iure,
79 | facilis illo ex nihil ipsa amet officia, optio temporibus eum autem odit repellendus nisi. Possimus modi, corrupti error
80 | debitis doloribus dicta libero earum, sequi porro ut excepturi nostrum ea voluptatem nihil culpa? Ullam expedita eligendi
81 | obcaecati reiciendis velit provident omnis quas qui in corrupti est dolore facere ad hic, animi soluta assumenda consequuntur
82 | reprehenderit! Voluptate dolor nihil veniam laborum voluptas nisi pariatur sed optio accusantium quam consectetur, corrupti,
83 | sequi et consequuntur, excepturi doloremque. Tempore quis velit corporis neque fugit non sequi eaque rem hic. Facere,
84 | inventore, aspernatur. Accusantium modi atque, asperiores qui nobis soluta cumque suscipit excepturi possimus doloremque
85 | odit saepe perferendis temporibus molestiae nostrum voluptatum quis id sint quidem nesciunt culpa. Rerum labore dolor
86 | beatae blanditiis praesentium explicabo velit optio esse aperiam similique, voluptatem cum, maiores ipsa tempore. Reiciendis
87 | sed culpa atque inventore, nam ullam enim expedita consectetur id velit iusto alias vitae explicabo nemo neque odio reprehenderit
88 | soluta sint eaque. Aperiam, qui ut tenetur, voluptate doloremque officiis dicta quaerat voluptatem rerum natus magni.
89 | Eum amet autem dolor ullam.
90 |
91 |
92 |
93 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio maiores adipisci quibusdam repudiandae dolor vero
94 | placeat esse sit! Quibusdam saepe aperiam explicabo placeat optio, consequuntur nihil voluptatibus expedita quia vero
95 | perferendis, deserunt et incidunt eveniet temporibus doloremque possimus facilis. Possimus labore, officia dolore! Eaque
96 | ratione saepe, alias harum laboriosam deserunt laudantium blanditiis eum explicabo placeat reiciendis labore iste sint.
97 | Consectetur expedita dignissimos, non quos distinctio, eos rerum facilis eligendi. Asperiores laudantium, rerum ratione
98 | consequatur, culpa consectetur possimus atque ab tempore illum non dolor nesciunt. Neque, rerum. A vel non incidunt, quod
99 | doloremque dignissimos necessitatibus aliquid laboriosam architecto at cupiditate commodi expedita in, quae blanditiis.
100 | Deserunt labore sequi, repellat laboriosam est, doloremque culpa reiciendis tempore excepturi. Enim nostrum fugit itaque
101 | vel corporis ullam sed tenetur ipsa qui rem quam error sint, libero. Laboriosam rem, ratione. Autem blanditiis laborum
102 | neque repudiandae quam, cumque, voluptate veritatis itaque, placeat veniam ad nisi. Expedita, laborum reprehenderit ratione
103 | soluta velit natus, odit mollitia. Corporis rerum minima fugiat in nostrum. Assumenda natus cupiditate hic quidem ex,
104 | quas, amet ipsum esse dolore facilis beatae maxime qui inventore, iste? Maiores dignissimos dolore culpa debitis voluptatem
105 | harum, excepturi enim reiciendis, tempora ab ipsam illum aspernatur quasi qui porro saepe iure sunt eligendi tenetur quaerat
106 | ducimus quas sequi omnis aperiam suscipit! Molestiae obcaecati officiis quo, ratione eveniet, provident pariatur. Veniam
107 | quasi expedita distinctio, itaque molestiae sequi, dolorum nisi repellendus quia facilis iusto dignissimos nam? Tenetur
108 | fugit quos autem nihil, perspiciatis expedita enim tempore, alias ab maiores quis necessitatibus distinctio molestias
109 | eum, quidem. Delectus impedit quidem laborum, fugit vel neque quo, ipsam, quasi aspernatur quas odio nihil? Veniam amet
110 | reiciendis blanditiis quis reprehenderit repudiandae neque, ab ducimus, odit excepturi voluptate saepe ipsam. Voluptatem
111 | eum error voluptas porro officiis, amet! Molestias, fugit, ut! Tempore non magnam, amet, facere ducimus accusantium eos
112 | veritatis neque.
113 |
114 |
115 |
116 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio maiores adipisci quibusdam repudiandae dolor vero
117 | placeat esse sit! Quibusdam saepe aperiam explicabo placeat optio, consequuntur nihil voluptatibus expedita quia vero
118 | perferendis, deserunt et incidunt eveniet temporibus doloremque possimus facilis. Possimus labore, officia dolore! Eaque
119 | ratione saepe, alias harum laboriosam deserunt laudantium blanditiis eum explicabo placeat reiciendis labore iste sint.
120 | Consectetur expedita dignissimos, non quos distinctio, eos rerum facilis eligendi. Asperiores laudantium, rerum ratione
121 | consequatur, culpa consectetur possimus atque ab tempore illum non dolor nesciunt. Neque, rerum. A vel non incidunt, quod
122 | doloremque dignissimos necessitatibus aliquid laboriosam architecto at cupiditate commodi expedita in, quae blanditiis.
123 | Deserunt labore sequi, repellat laboriosam est, doloremque culpa reiciendis tempore excepturi. Enim nostrum fugit itaque
124 | vel corporis ullam sed tenetur ipsa qui rem quam error sint, libero. Laboriosam rem, ratione. Autem blanditiis laborum
125 | neque repudiandae quam, cumque, voluptate veritatis itaque, placeat veniam ad nisi. Expedita, laborum reprehenderit ratione
126 | soluta velit natus, odit mollitia. Corporis rerum minima fugiat in nostrum. Assumenda natus cupiditate hic quidem ex,
127 | quas, amet ipsum esse dolore facilis beatae maxime qui inventore, iste? Maiores dignissimos dolore culpa debitis voluptatem
128 | harum, excepturi enim reiciendis, tempora ab ipsam illum aspernatur quasi qui porro saepe iure sunt eligendi tenetur quaerat
129 | ducimus quas sequi omnis aperiam suscipit! Molestiae obcaecati officiis quo, ratione eveniet, provident pariatur. Veniam
130 | quasi expedita distinctio, itaque molestiae sequi, dolorum nisi repellendus quia facilis iusto dignissimos nam? Tenetur
131 | fugit quos autem nihil, perspiciatis expedita enim tempore, alias ab maiores quis necessitatibus distinctio molestias
132 | eum, quidem. Delectus impedit quidem laborum, fugit vel neque quo, ipsam, quasi aspernatur quas odio nihil? Veniam amet
133 | reiciendis blanditiis quis reprehenderit repudiandae neque, ab ducimus, odit excepturi voluptate saepe ipsam. Voluptatem
134 | eum error voluptas porro officiis, amet! Molestias, fugit, ut! Tempore non magnam, amet, facere ducimus accusantium eos
135 | veritatis neque.
136 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio maiores adipisci quibusdam repudiandae dolor vero
137 | placeat esse sit! Quibusdam saepe aperiam explicabo placeat optio, consequuntur nihil voluptatibus expedita quia vero
138 | perferendis, deserunt et incidunt eveniet temporibus doloremque possimus facilis. Possimus labore, officia dolore! Eaque
139 | ratione saepe, alias harum laboriosam deserunt laudantium blanditiis eum explicabo placeat reiciendis labore iste sint.
140 | Consectetur expedita dignissimos, non quos distinctio, eos rerum facilis eligendi. Asperiores laudantium, rerum ratione
141 | consequatur, culpa consectetur possimus atque ab tempore illum non dolor nesciunt. Neque, rerum. A vel non incidunt, quod
142 | doloremque dignissimos necessitatibus aliquid laboriosam architecto at cupiditate commodi expedita in, quae blanditiis.
143 | Deserunt labore sequi, repellat laboriosam est, doloremque culpa reiciendis tempore excepturi. Enim nostrum fugit itaque
144 | vel corporis ullam sed tenetur ipsa qui rem quam error sint, libero. Laboriosam rem, ratione. Autem blanditiis laborum
145 | neque repudiandae quam, cumque, voluptate veritatis itaque, placeat veniam ad nisi. Expedita, laborum reprehenderit ratione
146 | soluta velit natus, odit mollitia. Corporis rerum minima fugiat in nostrum. Assumenda natus cupiditate hic quidem ex,
147 | quas, amet ipsum esse dolore facilis beatae maxime qui inventore, iste? Maiores dignissimos dolore culpa debitis voluptatem
148 | harum, excepturi enim reiciendis, tempora ab ipsam illum aspernatur quasi qui porro saepe iure sunt eligendi tenetur quaerat
149 | ducimus quas sequi omnis aperiam suscipit! Molestiae obcaecati officiis quo, ratione eveniet, provident pariatur. Veniam
150 | quasi expedita distinctio, itaque molestiae sequi, dolorum nisi repellendus quia facilis iusto dignissimos nam? Tenetur
151 | fugit quos autem nihil, perspiciatis expedita enim tempore, alias ab maiores quis necessitatibus distinctio molestias
152 | eum, quidem. Delectus impedit quidem laborum, fugit vel neque quo, ipsam, quasi aspernatur quas odio nihil? Veniam amet
153 | reiciendis blanditiis quis reprehenderit repudiandae neque, ab ducimus, odit excepturi voluptate saepe ipsam. Voluptatem
154 | eum error voluptas porro officiis, amet! Molestias, fugit, ut! Tempore non magnam, amet, facere ducimus accusantium eos
155 | veritatis neque.
156 |
157 |
158 |
178 |
179 |
180 |
181 |
--------------------------------------------------------------------------------
/24 - Sticky Nav/readme.md:
--------------------------------------------------------------------------------
1 | # Sticky Nav
2 |
3 | This exercise is about building a fixed `nav`, that adds some effects when the user scrolls and hits the top of the
4 | browser with it.
5 |
6 | ## Notes
7 |
8 | It is important to know that when an element's position changes to *fixed* this element no more takes
9 | space in the document. Also is worth mentioning that we define it programatically because is the only way to calculate the height of
10 | the *header*. If we instead would have it defined in the CSS file we should have hard coded its value.
11 | ```javascript
12 | const nav = document.querySelector('#main');
13 | const topOfNav = nav.offsetTop; // relative distance to the top of the node parent
14 |
15 | function fixNav() {
16 | if (window.scrollY > topOfNav) {
17 | document.body.classList.add('fixed-nav');
18 | document.body.style.paddingTop = nav.offsetHeight + 'px';
19 | } else {
20 | document.body.classList.remove('fixed-nav');
21 | document.body.style.paddingTop = 0;
22 | }
23 | }
24 | ```
25 |
26 | This time the CSS file is where almost all of the magic happens. We add three effects:
27 |
28 | * Position fixed and a subtle shadow:
29 | ```css
30 | .fixed-nav nav {
31 | position: fixed;
32 | box-shadow: 0 5px rgba(0, 0, 0, 0);
33 | }
34 | ```
35 | * When the `nav`is fixed, it shows a logo.
36 | ```css
37 | li.logo {
38 | max-width: 0;
39 | overflow: hidden;
40 | background: white;
41 | transition: all 1s;
42 | font-weight: 600;
43 | font-size: 30px;
44 | }
45 |
46 | .fixed-nav li.logo {
47 | max-width: 500px; // Width doesn't allow the transition effect
48 | }
49 | ```
50 | * A nice scalation of the content:
51 | ```css
52 | site-wrap {
53 | max-width: 700px;
54 | margin: 70px auto;
55 | background: white;
56 | padding: 40px;
57 | text-align: justify;
58 | box-shadow: 0 0 10px 5px rgba(0, 0, 0, 0.05);
59 | transform: scale(0.98);
60 | transition: transform 0.5s;
61 | }
62 |
63 | .fixed-nav .site-wrap {
64 | transform: scale(1);
65 | }
66 | ```
67 |
68 | ## Events
69 | * **scroll**
70 |
71 |
--------------------------------------------------------------------------------
/24 - Sticky Nav/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | background: #eeeeee;
4 | font-family: 'helvetica neue';
5 | font-size: 20px;
6 | font-weight: 200;
7 | }
8 |
9 | body {
10 | margin: 0;
11 | }
12 |
13 | *, *:before, *:after {
14 | box-sizing: inherit;
15 | }
16 |
17 | .site-wrap {
18 | max-width: 700px;
19 | margin: 70px auto;
20 | background: white;
21 | padding: 40px;
22 | text-align: justify;
23 | box-shadow: 0 0 10px 5px rgba(0, 0, 0, 0.05);
24 | transform: scale(0.98);
25 | transition: transform 0.5s;
26 | }
27 |
28 | .fixed-nav .site-wrap {
29 | transform: scale(1);
30 | }
31 |
32 | header {
33 | text-align: center;
34 | height: 50vh;
35 | background: url(./../resources/img/pic5.jpg) bottom center no-repeat;
36 | background-size: cover;
37 | display: flex;
38 | align-items: center;
39 | justify-content: center;
40 | }
41 |
42 | h1 {
43 | color: white;
44 | font-size: 7vw;
45 | text-shadow: 3px 4px 0 rgba(0, 0, 0, 0.2)
46 | }
47 |
48 | nav {
49 | background: black;
50 | top: 0;
51 | width: 100%;
52 | transition: all 0.5s;
53 | position: relative;
54 | z-index: 1;
55 | }
56 |
57 | .fixed-nav nav {
58 | position: fixed;
59 | box-shadow: 0 5px rgba(0, 0, 0, 0);
60 | }
61 |
62 | nav ul {
63 | margin: 0;
64 | padding: 0;
65 | list-style: none;
66 | display: flex;
67 | }
68 |
69 | nav li {
70 | flex: 1;
71 | text-align: center;
72 | display: flex;
73 | justify-content: center;
74 | align-items: center;
75 | }
76 |
77 | li.logo {
78 | max-width: 0;
79 | overflow: hidden;
80 | background: white;
81 | transition: all 1s;
82 | font-weight: 600;
83 | font-size: 30px;
84 | }
85 |
86 | .fixed-nav li.logo {
87 | /* Using width does not allow */
88 | max-width: 500px;
89 | }
90 |
91 | li.logo a {
92 | color: black;
93 | }
94 |
95 | nav a {
96 | text-decoration: none;
97 | padding: 20px;
98 | display: inline-block;
99 | color: white;
100 | transition: all 0.2s;
101 | text-transform: uppercase;
102 | }
--------------------------------------------------------------------------------
/25 - Event Capture, Propagation, Bubbling and Once/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Understanding JavaScript's Capture
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
41 |
42 |
43 |
61 |
62 |
--------------------------------------------------------------------------------
/25 - Event Capture, Propagation, Bubbling and Once/readme.md:
--------------------------------------------------------------------------------
1 | # Event Capture, Propagation, Bubbling and Once
2 |
3 | ## Notes
4 |
5 | This exercises uses a set of nested **divs** with a **click** event attached
6 | to each of them:
7 |
8 | ```html
9 |
10 |
11 |
12 |
13 |
14 |
15 | ```
16 | ```javascript
17 | divs.forEach(div => div.addEventListener('click', logText));
18 | ```
19 |
20 | ### **So, how does it work when an event is fired???**
21 |
22 | 1. The user clicks the `
`.
23 | 2. Then the browser ripples down, so goes from the most external element to the deepest one and captures
24 | all off the events binded to them. This process is called **Event Capture**. This process has the aim to
25 | figure it out what the user has clicked on:
26 |
27 | ```javascript
28 | // The browser stores the events in this order
29 | // Event attached to
30 | // Event attached to
31 | // Event attached to
32 | ```
33 |
34 | 3. At this moment the events are not fired yet. So starting from the bottom, the browser does something called **bubble up**
35 | and fires each of these events:
36 |
37 | ```javascript
38 | // The browser fires the events in this order
39 | // Event attached to
40 | // Event attached to
41 | // Event attached to
42 | ```
43 | But we can change the way this works using the `capture` property:
44 |
45 | ```javascript
46 | divs.forEach(div => div.addEventListener('click', logText, {
47 | capture: true // by default is false
48 | }));
49 | ```
50 | So now when the browser captures each of the events, it will inmediately fire them. That means that
51 | the handler for the event is not going to get run on the *buble up* but rather on the *capture down*:
52 |
53 | ```javascript
54 | // The browser fires the events in this order
55 | // Event attached to
56 | // Event attached to
57 | // Event attached to
58 | ```
59 |
60 | 4. We can also call **stop propagation** in the event handler, this way it will stop a **buble up** process,
61 | firing only the deepest event, or viceversa:
62 |
63 | ```javascript
64 | function logText(e) {
65 | console.log(this.classList.value);
66 | e.stopPropagation();
67 | }
68 | // now the the browser only fires the one event because capture=true
69 | // Event attached to
70 | ```
71 |
72 | * Last but not least, **Once** is a very new feature in the browser, that allows to listen for an event and then unbinds
73 | itself, so the event will never be triggered again:
74 |
75 | ```javascript
76 | divs.forEach(div => div.addEventListener('click', logText, {
77 | capture: false,
78 | once: true
79 | }));
80 |
81 | // this is like doing
82 | divs.forEach(div => div.removeEventListener('click', logText));
83 | ```
84 |
--------------------------------------------------------------------------------
/26 - Stripe Follow Along Nav/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Follow Along Nav
7 |
8 |
9 |
10 |
70 |
71 |
213 |
214 |
251 |
252 |
253 |
254 |
--------------------------------------------------------------------------------
/26 - Stripe Follow Along Nav/readme.md:
--------------------------------------------------------------------------------
1 | # Stripe Follow Along Nav
2 |
3 | This is the second and final part of the project where we wanted to build a replica of the navigation bar from **[Stripe](https://stripe.com)**.
4 | The way it works this effect is through sharing a *background* for all the *dropdonws*. For this it has to be calculate
5 | the size of the *background* according to the content of the *dropdown* as well as its position.
6 |
7 | ## Notes
8 |
9 | The process is very similar to the one showed in the **[Follow Along Lingk Highlighter][1]** exercise
10 | with the particularity that now our *background* element belongs to the *navigation* element
11 | rather to be a direct child of the *body*. This affects the way we calculate the coordinates of the *background*.
12 |
13 | When the mouse hovers one of the hoverable elements, we add the styles and calculate the size and new coordinates
14 | for the background:
15 | ```javascript
16 | function handleEnter() {
17 | this.classList.add('trigger-enter');
18 | setTimeout(() => this.classList.contains('trigger-enter') && this.classList.add('trigger-enter-active') , 150);
19 |
20 | background.classList.add('open');
21 |
22 | const dropdown = this.querySelector('.dropdown');
23 | const dropdownCoords = dropdown.getBoundingClientRect();
24 | const navCoords = nav.getBoundingClientRect();
25 |
26 | const coords = {
27 | height: dropdownCoords.height,
28 | width: dropdownCoords.width,
29 | top: dropdownCoords.top - navCoords.top,
30 | left: dropdownCoords.left - navCoords.left
31 | }
32 |
33 | background.style.setProperty('width', `${coords.width}px`);
34 | background.style.setProperty('height', `${coords.height}px`);
35 | background.style.setProperty('transform', `translate(${coords.left}px, ${coords.top}px)`);
36 | }
37 | ```
38 | And once the mouse leaves the element we remove the classes:
39 |
40 | ```javascript
41 | function handleLeave() {
42 | this.classList.remove('trigger-enter', 'trigger-enter-active');
43 | background.classList.remove('open');
44 | }
45 | ```
46 |
47 | ## Event
48 | * **mouseenter**
49 | * **mouseleave**
50 |
51 | [1]:https://github.com/yhabib/JavaScript30/tree/master/22%20-%20Follow%20Along%20Link%20Highlighter
--------------------------------------------------------------------------------
/27 - Click and Drag/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Click and Drag
7 |
8 |
9 |
10 |
11 |
12 |
01
13 |
02
14 |
03
15 |
04
16 |
05
17 |
06
18 |
07
19 |
08
20 |
09
21 |
10
22 |
11
23 |
12
24 |
13
25 |
14
26 |
15
27 |
16
28 |
17
29 |
18
30 |
19
31 |
20
32 |
21
33 |
22
34 |
23
35 |
24
36 |
25
37 |
38 |
39 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/27 - Click and Drag/readme.md:
--------------------------------------------------------------------------------
1 | # Click and Drag to Scroll
2 | In this exercise we build a replica of a **grab and scroll** effect found in **[Hover States](http://hoverstat.es/)**.
3 |
4 | ## Notes
5 |
6 | The logic to get this effect is based on getting the position of the mouse relative to the left edge(`startX`) as well the value of the amount of scroll of
7 | the scrollable container(`scrollLeft`) when a *clickdown* event is fired. We also set a flag letting know that the
8 | mouse is down.
9 |
10 | The more challenging handler is the one that takes care of the *mousemove* effect. Here we have to calculate
11 | the *walk* or amount of scroll, when the users moves the mouse:
12 |
13 | ```javascript
14 | slider.addEventListener('mousemove', e => {
15 | if(!isDown) return;
16 |
17 | e.preventDefault();
18 | const x = e.pageX - slider.offsetLeft;
19 | const walk = (x - startX) * 3;
20 |
21 | slider.scrollLeft = scrollLeft - walk;
22 | });
23 | ```
24 |
25 | **Worth to look:** In the CSS file there are some nice effects, like perspectives and zooms.
26 |
27 | **Note:** In the CSS file it is used the **[will-change][1]** property whose mission is to
28 | let know the browser that a transformation will be performed so this way the browser can
29 | set up appropriate optimizations ahead of time.
30 |
31 | ## Events
32 |
33 | * **mouseup**
34 | * **mouseleave**
35 | * **mousemove**
36 | * **mousedown**
37 |
38 | [1]:https://developer.mozilla.org/en/docs/Web/CSS/will-change
--------------------------------------------------------------------------------
/27 - Click and Drag/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | background: url('./../resources/img/pic1.jpg') fixed;
4 | background-size: cover;
5 | }
6 |
7 | *, *:before, *:after {
8 | box-sizing: inherit;
9 | }
10 |
11 | body {
12 | min-height: 100vh;
13 | display: flex;
14 | justify-content: center;
15 | align-items: center;
16 | font-family: sans-serif;
17 | font-size: 20px;
18 | margin: 0;
19 | }
20 |
21 | .items {
22 | height: 800px;
23 | padding: 100px;
24 | width: 100%;
25 | border: 1px solid white;
26 | box-shadow: 0 0 10px 7px rgba(0, 0, 0, 0.09);
27 | overflow-x: scroll;
28 | overflow-y: hidden;
29 | white-space: nowrap;
30 | user-select: none;
31 | cursor: pointer;
32 | transition: all 0.2s;
33 | transform: scale(0.98);
34 | will-change: transform; /* prepares the browser for a change 😮 */
35 | position: relative;
36 | background: rgba(255, 255, 255, 0.1);
37 | font-size: 0;
38 | perspective: 500px;
39 | }
40 |
41 | .items.active {
42 | background: rgba(255, 255, 255, 0.3);
43 | cursor: grabbing;
44 | cursor: -webkit-grabbing;
45 | transform: scale(1);
46 | }
47 |
48 | .item {
49 | width: 200px;
50 | height: calc(100% - 40px);
51 | display: inline-flex;
52 | align-items: center;
53 | justify-content: center;
54 | font-size: 80px;
55 | font-weight: 100;
56 | color: rgba(0, 0, 0, 0.15);
57 | box-shadow: inset 0 0 0 10px rgba(0, 0, 0, 0.15);
58 | }
59 |
60 | .item:nth-child(9n+1) {
61 | background: dodgerblue;
62 | }
63 |
64 | .item:nth-child(9n+2) {
65 | background: goldenrod;
66 | }
67 |
68 | .item:nth-child(9n+3) {
69 | background: paleturquoise;
70 | }
71 |
72 | .item:nth-child(9n+4) {
73 | background: gold;
74 | }
75 |
76 | .item:nth-child(9n+5) {
77 | background: cadetblue;
78 | }
79 |
80 | .item:nth-child(9n+6) {
81 | background: tomato;
82 | }
83 |
84 | .item:nth-child(9n+7) {
85 | background: lightcoral;
86 | }
87 |
88 | .item:nth-child(9n+8) {
89 | background: darkslateblue;
90 | }
91 |
92 | .item:nth-child(9n+9) {
93 | background: rebeccapurple;
94 | }
95 |
96 | .item:nth-child(even) {
97 | transform: scaleX(1.31) rotateY(40deg);
98 | }
99 |
100 | .item:nth-child(odd) {
101 | transform: scaleX(1.31) rotateY(-40deg);
102 | }
103 |
104 | .wrap {
105 | width: auto;
106 | border: 2px solid green;
107 | height: 100%;
108 | }
--------------------------------------------------------------------------------
/28 - Video Speed Controller/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Video Speed Scrubber
7 |
8 |
9 |
10 |
11 |
12 |
13 |
15 |
16 |
1×
17 |
18 |
19 |
20 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/28 - Video Speed Controller/readme.md:
--------------------------------------------------------------------------------
1 | # Video Speed Controll
2 | In this one, we build a hoverable video speed controller, with a range between 0.4 and 4.
3 |
4 | ## Notes
5 | There is not much to commet, except for the calculation of the position of the mouse inside
6 | the *speed-bar* ( the `y` variable).
7 | * The `e.pageY` returns the position of the mouse relative to the top edge of the document.
8 | * The `this.offsetTop` returns the offset of the `.speed-bar` to the top of the document.
9 |
10 | ```javascript
11 | function handleMove(e) {
12 | // this === .speed-bar
13 | const y = e.pageY - this.offsetTop,
14 | percent = y / this.offsetHeight,
15 | min = 0.4,
16 | max = 4,
17 | height = Math.round(percent * 100) + '%',
18 | playbackRate = percent * (max - min) + min;
19 |
20 | bar.style.height = height;
21 | bar.textContent = `${playbackRate.toFixed(2)} X`;
22 | video.playbackRate = playbackRate;
23 | }
24 | ```
25 |
26 | ## Events
27 | * mousemove
--------------------------------------------------------------------------------
/28 - Video Speed Controller/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | min-height: 100vh;
6 | background: #4C4C4C url('./../resources/img/pic2.jpg');
7 | background-size: cover;
8 | font-family: sans-serif;
9 | }
10 |
11 | .wrapper {
12 | width: 850px;
13 | display: flex;
14 | }
15 |
16 | video {
17 | box-shadow: 0 0 1px 3px rgba(0, 0, 0, 0.1);
18 | }
19 |
20 | .speed {
21 | background: #efefef;
22 | flex: 1;
23 | display: flex;
24 | align-items: flex-start;
25 | margin: 10px;
26 | border-radius: 50px;
27 | box-shadow: 0 0 1px 3px rgba(0, 0, 0, 0.1);
28 | overflow: hidden;
29 | }
30 |
31 | .speed-bar {
32 | width: 100%;
33 | background: linear-gradient(-170deg, #2376ae 0%, #c16ecf 100%);
34 | text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.2);
35 | display: flex;
36 | align-items: center;
37 | justify-content: center;
38 | padding: 2px;
39 | color: white;
40 | height: 16.3%;
41 | }
--------------------------------------------------------------------------------
/29 - Countdown Timer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Countdown Timer
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/29 - Countdown Timer/readme.md:
--------------------------------------------------------------------------------
1 | # Countdown Clock
2 |
3 | We build in this example a [Pomodoro Clock][1]
4 |
5 | ## Notes
6 |
7 | The main function takes care of most of the logic of this exercise. Some comments about it:
8 |
9 | * We don't implement it through decrementing a variable inside a `setInterval`, because
10 | we cannot be sure if it's going to work as expected, because sometimes the browser
11 | will shut it down due performance.
12 |
13 | ```javascript
14 | let leftSeconds = 15;
15 | setInterval(() => leftSeconds--, 1000);
16 | ```
17 |
18 | * At the beginning we clean the last `setInterval` defined in order to have always
19 | one referece.
20 | * `setInterval` returns always a value that identifies the timer and allows to clear it
21 | at some point in the future.
22 | * The `Date.now()` returns a value in miliseconds
23 |
24 | ```javascript
25 | function timer(seconds) {
26 | clearInterval(countDown);
27 |
28 | const now = Date.now();
29 | const then = now + seconds * 1000;
30 | displayTimeLeft(seconds);
31 | displayEndTime(then);
32 |
33 | countDown = setInterval(() => {
34 | const secondsLeft = Math.round((then - Date.now()) / 1000);
35 | if(secondsLeft < 0) {
36 | clearInterval(countDown);
37 | return;
38 | }
39 | displayTimeLeft(secondsLeft);
40 | }, 1000);
41 | }
42 | ```
43 |
44 | The other two functions implemente have the mission to generate the HTML code
45 | to be inserted into the corresponging HTML elements:
46 |
47 | ```javascript
48 | function displayTimeLeft(seconds) {
49 | const minutes = Math.floor(seconds / 60);
50 | const remainerSeconds = seconds % 60;
51 | const display = `${minutes}:${remainerSeconds < 10 ? '0' : ''}${remainerSeconds}`;
52 |
53 | document.title = display;
54 | timerDisplay.textContent = display;
55 | }
56 |
57 | function displayEndTime(timestampt) {
58 | const end = new Date(timestampt);
59 | const hours = end.getHours();
60 | const minutes = end.getMinutes();
61 |
62 | endTimeDisplay.textContent = `Be back at ${hours}:${minutes < 10 ? '0' : ''}${minutes}`;
63 | }
64 | ```
65 |
66 | **Note:** When getting values from HTML elements either through `data` attributes or
67 | `input` values, the return value will be always a string so in this case if we are working with numbers
68 | we have to cast it to Number:
69 |
70 | ```javascript
71 | const dataAttribute = parseInte(this.dataset.time);
72 | // or
73 | const dataAttribute = +this.dataset.time;
74 | ```
75 |
76 | **Note:** All HTML elements that have a name attribute can be accessed directly from
77 | the `document` object, without using the `querySelector` method:
78 |
79 | ```html
80 |
83 | ```
84 |
85 | ```javascript
86 | const minuteValue = document.customForm.minutes.value;
87 | ```
88 |
89 | ## Events
90 | * **click**
91 | * **submit**
92 |
93 | [1]:https://en.wikipedia.org/wiki/Pomodoro_Technique
--------------------------------------------------------------------------------
/29 - Countdown Timer/script.js:
--------------------------------------------------------------------------------
1 | let countDown ;
2 |
3 | const timerDisplay = document.querySelector('.display__time-left');
4 | const endTimeDisplay = document.querySelector('.display__end-time');
5 | const buttons = document.querySelectorAll('[data-time]');
6 |
7 |
8 |
9 | buttons.forEach(button => button.addEventListener('click', setTimer));
10 | // works with the 'name' attribute
11 | document.customForm.addEventListener('submit', setTimerFromForm);
12 |
13 |
14 | function setTimer() {
15 | timer(+this.dataset.time);
16 | }
17 |
18 | function setTimerFromForm(e) {
19 | e.preventDefault();
20 | timer(+this.minutes.value * 60);
21 | this.reset();
22 | }
23 |
24 | function timer(seconds) {
25 | // clears any existing times
26 | clearInterval(countDown);
27 |
28 | // setInterval(fn, 1000); Where fn makes seconds-- Could not work as expected, the browser can shut it down for performance
29 | const now = Date.now(); // new to the browser => ms
30 | const then = now + seconds * 1000; // convert seconds to ms
31 | displayTimeLeft(seconds);
32 | displayEndTime(then);
33 |
34 | countDown = setInterval(() => {
35 | const secondsLeft = Math.round((then - Date.now()) / 1000);
36 | if(secondsLeft < 0) {
37 | clearInterval(countDown);
38 | return;
39 | }
40 | displayTimeLeft(secondsLeft);
41 | }, 1000);
42 | }
43 |
44 |
45 | function displayTimeLeft(seconds) {
46 | const minutes = Math.floor(seconds / 60);
47 | const remainerSeconds = seconds % 60;
48 | const display = `${minutes}:${remainerSeconds < 10 ? '0' : ''}${remainerSeconds}`;
49 |
50 | document.title = display;
51 | timerDisplay.textContent = display;
52 | }
53 |
54 | function displayEndTime(timestampt) {
55 | const end = new Date(timestampt);
56 | const hours = end.getHours();
57 | const minutes = end.getMinutes();
58 |
59 | endTimeDisplay.textContent = `Be back at ${hours}:${minutes < 10 ? '0' : ''}${minutes}`;
60 | }
61 |
--------------------------------------------------------------------------------
/29 - Countdown Timer/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | font-size: 10px;
4 | background: #8E24AA;
5 | background: linear-gradient(45deg, #42a5f5 0%, #478ed1 50%, #0d47a1 100%);
6 | }
7 |
8 | *, *:before, *:after {
9 | box-sizing: inherit;
10 | }
11 |
12 | body {
13 | margin: 0;
14 | text-align: center;
15 | font-family: 'Inconsolata', monospace;
16 | }
17 |
18 | .display__time-left {
19 | font-weight: 100;
20 | font-size: 20rem;
21 | margin: 0;
22 | color: white;
23 | text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.05);
24 | }
25 |
26 | .timer {
27 | display: flex;
28 | min-height: 100vh;
29 | flex-direction: column;
30 | }
31 |
32 | .timer__controls {
33 | display: flex;
34 | }
35 |
36 | .timer__controls > * {
37 | flex: 1;
38 | }
39 |
40 | .timer__controls form {
41 | flex: 1;
42 | display: flex;
43 | }
44 |
45 | .timer__controls input {
46 | flex: 1;
47 | border: 0;
48 | padding: 2rem;
49 | }
50 |
51 | .timer__button {
52 | background: none;
53 | border: 0;
54 | cursor: pointer;
55 | color: white;
56 | font-size: 2rem;
57 | text-transform: uppercase;
58 | background: rgba(0, 0, 0, 0.1);
59 | border-bottom: 3px solid rgba(0, 0, 0, 0.2);
60 | border-right: 1px solid rgba(0, 0, 0, 0.2);
61 | padding: 1rem;
62 | font-family: 'Inconsolata', monospace;
63 | }
64 |
65 | .timer__button:hover, .timer__button:focus {
66 | background: rgba(0, 0, 0, 0.2);
67 | outline: 0;
68 | }
69 |
70 | .display {
71 | flex: 1;
72 | display: flex;
73 | flex-direction: column;
74 | align-items: center;
75 | justify-content: center;
76 | }
77 |
78 | .display__end-time {
79 | font-size: 4rem;
80 | color: white;
81 | }
--------------------------------------------------------------------------------
/30 - Whack A Mole/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Whack A Mole!
7 |
8 |
9 |
10 |
11 |
12 |
13 |
Whack-a-mole! 0
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/30 - Whack A Mole/mole.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/30 - Whack A Mole/readme.md:
--------------------------------------------------------------------------------
1 | # Whack a Mole!
2 |
3 | Let's finish this amazing serie of tutorials with a game 😀.
4 |
5 | ## Notes
6 |
7 | After a random time a mole will appear in a random hole. For that we defined
8 | two functions. In the first one we generate a random number in between a range defined
9 | as a parameter and in the second one a number between zero and the number of holes. We also
10 | save the last hole to avoid returning the same hole twice:
11 |
12 | ```javascript
13 | function randTime(min, max) {
14 | return Math.round(Math.random() * (max - min) + min);
15 | }
16 |
17 | function randomHole(holes) {
18 | const idx = Math.floor(Math.random() * holes.length);
19 | const hole = holes[idx];
20 | if (hole === lastHole)
21 | return randomHole(holes);
22 | lastHole = hole;
23 | }
24 | ```
25 | We need to add a CSS class when we want to show a mole. For this we generate a random
26 | amount of time and a random hole, and we add the class to this particular HTML node, that represents
27 | a mole. Making use of `setTimeout` we hide the mole after a random time and recursively we call again
28 | the function if the user has still time.
29 |
30 | ```javascript
31 | function peep() {
32 | const time = randTime(200, 1000);
33 | const hole = randomHole(holes);
34 |
35 | hole.classList.add('up');
36 | setTimeout(() => {
37 | hole.classList.remove('up');
38 | if (!timeUp) peep();
39 | }, time);
40 | }
41 | ```
42 |
43 | We listen for clicks on the moles:
44 |
45 | ```javascript
46 | function bonk(e) {
47 | if(!e.isTrusted) return;
48 |
49 | score++;
50 | this.classList.remove('up');
51 | scoreBoard.textContent = score;
52 | }
53 | ```
54 |
55 | And finally it is attached a method to the *start* button. This will be responsible
56 | of initializing all variables for a new game and setting a timeout that will end the game
57 | after 10 seconds:
58 |
59 | ```javascript
60 | function startGame() {
61 | if (startedGame) return;
62 | scoreBoard.textContent = 0;
63 | score = 0;
64 | timeUp = false;
65 | startedGame = true;
66 | peep();
67 | setTimeout(() => timeUp = true, 10000);
68 | }
69 | ```
70 |
71 | ## Events
72 | * **click**
--------------------------------------------------------------------------------
/30 - Whack A Mole/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | font-size: 10px;
4 | background: #ffc600;
5 | }
6 |
7 | *, *:before, *:after {
8 | box-sizing: inherit;
9 | }
10 |
11 | body {
12 | padding: 0;
13 | margin: 0;
14 | font-family: 'Amatic SC', cursive;
15 | }
16 |
17 | h1 {
18 | text-align: center;
19 | font-size: 10rem;
20 | line-height: 1;
21 | margin-bottom: 0;
22 | }
23 |
24 | .score {
25 | background: rgba(255, 255, 255, 0.2);
26 | padding: 0 3rem;
27 | line-height: 1;
28 | border-radius: 1rem;
29 | }
30 |
31 | .game {
32 | width: 600px;
33 | height: 400px;
34 | display: flex;
35 | flex-wrap: wrap;
36 | margin: 0 auto;
37 | }
38 |
39 | .hole {
40 | flex: 1 0 33.33%;
41 | overflow: hidden;
42 | position: relative;
43 | }
44 |
45 | .hole:after {
46 | display: block;
47 | background: url(dirt.svg) bottom center no-repeat;
48 | background-size: contain;
49 | content: '';
50 | width: 100%;
51 | height: 70px;
52 | position: absolute;
53 | z-index: 2;
54 | bottom: -30px;
55 | }
56 |
57 | .mole {
58 | background: url('mole.svg') bottom center no-repeat;
59 | background-size: 60%;
60 | position: absolute;
61 | top: 100%;
62 | width: 100%;
63 | height: 100%;
64 | transition: all 0.4s;
65 | }
66 |
67 | .hole.up .mole {
68 | top: 0;
69 | }
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | My JavaScript30
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
24 |
25 |
26 |
27 |
JavaScript 30
28 |
29 | An amazing serie of mini projects with VanillaJS by Wes Bos.
30 | This amazing course can be found here, wesbos repo with original files and solutions are here,
31 | and last but not least, the images used in my solutions are from here.
32 | I added some notes to each of this mini-projects that can be found on my repo.
33 |