├── .eslintrc-FINISHED ├── .gitignore ├── .npmrc ├── base.css ├── exercises ├── 16 - Debugging │ ├── debugging-FINISHED.js │ ├── debugging-START.js │ └── debugging.html ├── 20 - The DOM │ ├── DOM-Cardio-FINISHED.html │ ├── DOM-Cardio-FINISHED.js │ ├── DOM-Cardio.html │ ├── DOM-Cardio.js │ ├── creating-FINISHED.js │ ├── creating-with-strings-FINISHED.js │ ├── creating-with-strings.js │ ├── creating.js │ ├── index-FINISHED.html │ ├── index.html │ ├── the-dom-FINISHED.js │ ├── the-dom.js │ ├── traversing-FINISHED.js │ └── traversing.js ├── 29 - Events │ ├── events-FINISHED.html │ ├── events-FINISHED.js │ ├── events.html │ ├── events.js │ ├── forms-FINISHED.html │ ├── forms-FINISHED.js │ ├── forms.html │ └── forms.js ├── 33 - Etch-a-Sketch │ ├── etch-a-sketch-FINISHED.js │ ├── etch-a-sketch.js │ └── index.html ├── 34 - Click Outside │ ├── click-outside-FINISHED.html │ ├── click-outside-FINISHED.js │ ├── click-outside.html │ └── click-outside.js ├── 35 - Scroll To Accept │ ├── scroll-to-accept-FINISHED.html │ ├── scroll-to-accept-FINISHED.js │ ├── scroll-to-accept.html │ └── scroll-to-accept.js ├── 36 - Tabs │ ├── index.html │ ├── tabs-finished.js │ ├── tabs-style.css │ └── tabs.js ├── 46 - Arrays │ ├── array-loopings-methods-FINISHED.html │ ├── array-loopings-methods-START.html │ ├── array-methods-FINISHED.html │ ├── array-methods-START.html │ ├── for-loops-FINISHED.html │ ├── for-loops-START.html │ ├── reduce-challenge-FINISHED.html │ └── reduce-challenge-START.html ├── 55 - Face Detection Censorship │ ├── .npmrc │ ├── face-FINISHED.html │ ├── face.html │ ├── package-lock.json │ ├── package.json │ ├── pixelated-face-DEMO.js │ ├── pixelated-face-FINISHED.js │ └── pixelated-face.js ├── 56 - Sarcastic Text │ ├── index.html │ ├── text-DEMO.js │ ├── text-FINISHED.js │ └── text.js ├── 57 - Shopping List │ ├── index.html │ ├── shopping-DEMO.js │ ├── shopping-FINISHED.js │ ├── shopping.css │ └── shopping.js ├── 58 - Gallery │ ├── gallery-DEMO.js │ ├── gallery-FINISHED.js │ ├── gallery-prototype-FINISHED.js │ ├── gallery.css │ ├── gallery.js │ ├── images │ │ ├── 270-camo-sunset.jpg │ │ ├── TNF-fanorak.png │ │ ├── canada-goose.jpg │ │ ├── coral-yeti.jpg │ │ ├── hondo.jpg │ │ ├── kith-hoodie.jpg │ │ ├── naked-and-famous-denim.jpg │ │ ├── nudie-belt.jpg │ │ ├── patagonia black hole.jpg │ │ ├── rimowa.png │ │ ├── ultra-boost.jpg │ │ └── vapormax.jpg │ └── index.html ├── 59 - Slider │ ├── .npmrc │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── index-FINISHED.js │ │ ├── index-prototype-FINISHED.js │ │ └── index.js │ └── style.scss ├── 72 - Async Prompts │ ├── index.html │ ├── scripts-FINISHED.js │ ├── scripts.js │ └── style.css ├── 73 - Async Typer │ ├── index.html │ ├── scripts-FINISHED.js │ └── scripts.js ├── 75 - CORS and Recipes │ ├── .npmrc │ ├── index-FINISHED.html │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── scripts-FINISHED.js │ └── scripts.js ├── 76 - Dad Jokes │ ├── index-FINISHED.html │ ├── index.html │ ├── jokes-FINISHED.js │ └── jokes.js ├── 77 - Currency Converter │ ├── index.html │ ├── money-FINISHED.js │ ├── money.css │ └── money.js ├── 79 - Currency Converter Modules Refactor - FINISHED │ ├── currencies.js │ ├── elements.js │ ├── handlers.js │ ├── index.html │ ├── init.js │ ├── lib.js │ ├── money.css │ ├── money.js │ └── utils.js ├── 80 - Dad Jokes Modules - FINISHED │ ├── data │ │ └── buttonText.js │ ├── index.html │ ├── jokes.js │ └── lib │ │ ├── elements.js │ │ ├── handlers.js │ │ ├── index.js │ │ └── utils.js ├── 81 - Dad Jokes Bundling - FINISHED │ ├── .npmrc │ ├── data │ │ └── buttonText.js │ ├── index.html │ ├── jokes.js │ ├── lib │ │ ├── elements.js │ │ ├── handlers.js │ │ ├── index.js │ │ └── utils.js │ ├── package-lock.json │ └── package.json ├── 82 - npm modules - FINISHED │ ├── .npmrc │ ├── index.html │ ├── index.js │ ├── package-lock.json │ └── package.json ├── 83 - Security │ ├── .npmrc │ ├── index.html │ ├── nasty-FINISHED.js │ ├── nasty.js │ ├── package-lock.json │ └── package.json ├── 84 - Web Speech Colour Game │ ├── .npmrc │ ├── colors-FINISHED.js │ ├── colors.js │ ├── handlers-FINISHED.js │ ├── handlers.js │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── speech-FINISHED.js │ ├── speech.js │ └── style.css └── 85 - Audio Visualizer Oscilloscope │ ├── .npmrc │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── sound-FINISHED.js │ ├── sound.js │ └── utils.js ├── function-definition.jpg ├── package.json ├── playground ├── apis-FINISHED.html ├── apis.html ├── arrays-FINISHED.html ├── arrays.html ├── async-await-FINISHED.html ├── async-await-error-handling-FINISHED.html ├── async-await-error-handling.html ├── async-await.html ├── bedmas-FINISHED.html ├── bedmas.html ├── bind-call-apply-FINISHED.html ├── bind-call-apply.html ├── classes-FINISHED.html ├── classes.html ├── closures-FINISHED.html ├── closures.html ├── custom-functions │ ├── cf-FINISHED.js │ ├── cf.js │ ├── index-FINISHED.html │ ├── index.html │ ├── ways-to-make-a-function-FINISHED.js │ └── ways-to-make-a-function.js ├── event-loop-FINISHED.html ├── event-loop.html ├── functions-FINISHED.html ├── functions.html ├── hoisting-FINISHED.html ├── hoisting-FINISHED.js ├── hoisting.html ├── hoisting.js ├── if-statements-FINISHED.html ├── if-statements.html ├── intervals-FINISHED.html ├── intervals.html ├── maps-FINISHED.html ├── maps.html ├── modules │ ├── base.css │ ├── currencies.js │ ├── handlers-FINISHED.js │ ├── handlers.js │ ├── index-FINISHED.html │ ├── index.html │ ├── scripts-FINISHED.js │ ├── scripts.js │ ├── utils-FINISHED.js │ ├── utils.js │ ├── wes-FINISHED.js │ └── wes.js ├── new-this-FINISHED.html ├── new-this.html ├── node-example.js ├── objects-FINISHED.html ├── objects.html ├── promise-chain-FINISHED.html ├── promises-FINISHED.html ├── promises.html ├── running-js-FINISHED.html ├── running-js.html ├── scope-FINISHED.html ├── scope-FINISHED.js ├── scope.html ├── scope.js ├── some-FINISHED.js ├── some.js ├── switch-statements-FINISHED.html ├── switch-statements.html ├── turtle.png ├── types-FINISHED.html ├── types-FINISHED.js ├── types.html ├── types.js ├── variables-FINISHED.html └── variables.html ├── readme.md └── snippets ├── README.md ├── htmlbase.html └── htmlbase.json /.eslintrc-FINISHED: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "wesbos" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store 3 | haters/ 4 | .cache/ 5 | dist/ 6 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | fund=false 2 | audit=false 3 | legacy-peer-deps=true 4 | -------------------------------------------------------------------------------- /exercises/16 - Debugging/debugging-FINISHED.js: -------------------------------------------------------------------------------- 1 | const people = [ 2 | { name: 'Wes', cool: true, country: 'Canada' }, 3 | { name: 'Scott', cool: true, country: 'Merica' }, 4 | { name: 'Snickers', cool: false, country: 'Dog Country' }, 5 | ]; 6 | 7 | people.forEach((person, index) => { 8 | console.groupCollapsed(`${person.name}`); 9 | console.log(person.country); 10 | console.log(person.cool); 11 | console.log('DONE'); 12 | console.groupEnd(`${person.name}`); 13 | }); 14 | 15 | console.table(people); 16 | 17 | // Console Methods 18 | 19 | // Callstack, Stack Trace 20 | 21 | // Grabbing Elements 22 | 23 | // Breakpoints 24 | 25 | // Scope 26 | 27 | // Network Requests 28 | 29 | // Break On Attribute 30 | 31 | // Some Setup Code 32 | 33 | function doALotOfStuff() { 34 | console.group('Doing some stuff'); 35 | console.log('Hey Im one'); 36 | console.warn('watch out!'); 37 | console.error('hey'); 38 | console.groupEnd('Doing some stuff'); 39 | } 40 | 41 | function doctorize(name) { 42 | // console.count(`running Doctorize for ${name}`); 43 | return `Dr. ${name}`; 44 | } 45 | 46 | function greet(name) { 47 | doesntExist(); // Cause an error 48 | return `Hello ${name}`; 49 | } 50 | 51 | function go() { 52 | const name = doctorize(greet('Wes')); 53 | console.log(name); 54 | } 55 | 56 | function bootstrap() { 57 | console.log('Starting the app!'); 58 | go(); 59 | } 60 | 61 | // bootstrap(); 62 | 63 | const button = document.querySelector('.bigger'); 64 | button.addEventListener('click', function(e) { 65 | const newFontSize = 66 | parseFloat(getComputedStyle(e.currentTarget).fontSize) + 1; 67 | e.currentTarget.style.fontSize = `${newFontSize}px`; 68 | }); 69 | 70 | // A Dad joke fetch 71 | async function fetchDadJoke() { 72 | const res = await fetch('https://icanhazdadjoke.com/', { 73 | headers: { 74 | Accept: 'text/plain', 75 | }, 76 | }); 77 | const joke = await res.text(); 78 | console.log(joke); 79 | return joke; 80 | } 81 | -------------------------------------------------------------------------------- /exercises/16 - Debugging/debugging-START.js: -------------------------------------------------------------------------------- 1 | const people = [ 2 | { name: 'Wes', cool: true, country: 'Canada' }, 3 | { name: 'Scott', cool: true, country: 'Merica' }, 4 | { name: 'Snickers', cool: false, country: 'Dog Country' }, 5 | ]; 6 | 7 | people.forEach((person, index) => { 8 | console.log(person.name); 9 | }); 10 | 11 | // Console Methods 12 | 13 | // Callstack 14 | 15 | // Grabbing Elements 16 | 17 | // Breakpoints 18 | 19 | // Scope 20 | 21 | // Network Requests 22 | 23 | // Break On Attribute 24 | 25 | // Some Setup Code 26 | 27 | function doctorize(name) { 28 | return `Dr. ${name}`; 29 | } 30 | 31 | function greet(name) { 32 | doesntExist(); 33 | return `Hello ${name}`; 34 | } 35 | 36 | function go() { 37 | const name = doctorize(greet('Wes')); 38 | console.log(name); 39 | } 40 | 41 | const button = document.querySelector('.bigger'); 42 | button.addEventListener('click', function(e) { 43 | const newFontSize = 44 | parseFloat(getComputedStyle(e.currentTarget).fontSize) + 1; 45 | e.currentTarget.style.fontSize = `${newFontSize}px`; 46 | }); 47 | 48 | // A Dad joke fetch 49 | async function fetchDadJoke() { 50 | const res = await fetch('https://icanhazdadjoke.com/', { 51 | headers: { 52 | Accept: 'text/plain', 53 | }, 54 | }); 55 | const joke = await res.text(); 56 | console.log(joke); 57 | return joke; 58 | } 59 | -------------------------------------------------------------------------------- /exercises/16 - Debugging/debugging.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /exercises/20 - The DOM/DOM-Cardio-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Dom Cardio 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 32 | 33 | -------------------------------------------------------------------------------- /exercises/20 - The DOM/DOM-Cardio-FINISHED.js: -------------------------------------------------------------------------------- 1 | // Make a div 2 | const div = document.createElement('div'); 3 | // add a class of wrapper to it 4 | div.classList.add('wrapper'); 5 | // put it into the body 6 | document.body.appendChild(div); 7 | 8 | // make an unordered list 9 | const ul = ``; 14 | // add three list items with the words "one, two three" in them 15 | // put that list into the above wrapper 16 | div.innerHTML = ul; 17 | 18 | // create an image 19 | const img = document.createElement('img'); 20 | // set the source to an image 21 | img.src = 'https://picsum.photos/500'; 22 | // set the width to 250 23 | img.width = 250; 24 | img.height = 250; 25 | // add a class of cute 26 | img.classList.add('cute'); 27 | // add an alt of Cute Puppy 28 | img.alt = 'Cute Puppy!'; 29 | // Append that image to the wrapper 30 | div.appendChild(img); 31 | 32 | // with HTML string, make a div, with two paragraphs inside of it 33 | const myHTML = ` 34 |
35 |

Paragraph One

36 |

Paragraph Two

37 |
38 | `; 39 | const ulElement = div.querySelector('ul'); 40 | console.log(ulElement); 41 | // put this div before the unordered list from above 42 | ulElement.insertAdjacentHTML('beforebegin', myHTML); 43 | // add a class to the second paragraph called warning 44 | const myDiv = div.querySelector('.myDiv'); 45 | myDiv.children[1].classList.add('warning'); 46 | // remove the first paragraph 47 | myDiv.firstElementChild.remove(); 48 | 49 | // create a function called generatePlayerCard that takes in three arguments: name, age, and height 50 | function generatePlayerCard(name, age, height) { 51 | const html = ` 52 |
53 |

${name} — ${age}

54 |

Their Height is ${height} and ${age} years old. In Dog years this person would be ${age * 55 | 7}. That would be a tall dog! 56 | 57 |

58 | 59 |
60 | `; 61 | return html; 62 | } 63 | 64 | // have that function return html that looks like this: 65 | //
66 | //

NAME — AGE

67 | //

They are HEIGHT and AGE years old. In Dog years this person would be AGEINDOGYEARS. That would be a tall dog!

68 | //
69 | 70 | // make a new div with a class of cards 71 | const cards = document.createElement('div'); 72 | cards.classList.add('cards'); 73 | // Have that function make 4 cards 74 | let cardsHTML = generatePlayerCard('wes', 12, 150); 75 | cardsHTML += generatePlayerCard('scott', 12, 150); 76 | cardsHTML += generatePlayerCard('kait', 12, 150); 77 | cardsHTML += generatePlayerCard('snickers', 12, 150); 78 | 79 | cards.innerHTML = cardsHTML; 80 | div.insertAdjacentElement('beforebegin', cards); 81 | // append those cards to the div 82 | // put the div into the DOM just before the wrapper element 83 | // Bonus, put a delete Button on each card so when you click it, the whole card is removed 84 | 85 | // select all the buttons! 86 | const buttons = document.querySelectorAll('.delete'); 87 | // make out delete function 88 | function deleteCard(event) { 89 | const buttonThatGotClicked = event.currentTarget; 90 | // buttonThatGotClicked.parentElement.remove(); 91 | buttonThatGotClicked.closest('.playerCard').remove(); 92 | } 93 | // loop over them and attach a listener 94 | buttons.forEach(button => button.addEventListener('click', deleteCard)); 95 | -------------------------------------------------------------------------------- /exercises/20 - The DOM/DOM-Cardio.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Dom Cardio 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /exercises/20 - The DOM/DOM-Cardio.js: -------------------------------------------------------------------------------- 1 | // Make a div 2 | 3 | // add a class of wrapper to it 4 | 5 | // put it into the body 6 | 7 | // make an unordered list 8 | 9 | // add three list items with the words "one, two, three" in them 10 | // put that list into the above wrapper 11 | 12 | // create an image 13 | 14 | // set the source to an image 15 | // set the width to 250 16 | // add a class of cute 17 | // add an alt of Cute Puppy 18 | // Append that image to the wrapper 19 | 20 | // with HTML string, make a div, with two paragraphs inside of it 21 | // put this div before the unordered list from above 22 | 23 | // add a class to the second paragraph called warning 24 | // remove the first paragraph 25 | 26 | // create a function called generatePlayerCard that takes in three arguments: name, age, and height 27 | 28 | // have that function return html that looks like this: 29 | //
30 | //

NAME — AGE

31 | //

They are HEIGHT and AGE years old. In Dog years this person would be AGEINDOGYEARS. That would be a tall dog!

32 | //
33 | 34 | // make a new div with a class of cards 35 | 36 | // make 4 player cards using generatePlayerCard 37 | 38 | // append those cards to the div 39 | // put the div into the DOM just before the wrapper element 40 | // Bonus, put a delete Button on each card so when you click it, the whole card is removed 41 | 42 | // select all the buttons! 43 | // make out delete function 44 | // loop over them and attach a listener 45 | -------------------------------------------------------------------------------- /exercises/20 - The DOM/creating-FINISHED.js: -------------------------------------------------------------------------------- 1 | console.log('it works!'); 2 | 3 | const myParagraph = document.createElement('p'); 4 | myParagraph.textContent = 'I am a P'; 5 | myParagraph.classList.add('special'); 6 | console.log(myParagraph); 7 | 8 | const myImage = document.createElement('img'); 9 | myImage.src = 'https://picsum.photos/500'; 10 | myImage.alt = 'Nice photo'; 11 | 12 | const myDiv = document.createElement('div'); 13 | myDiv.classList.add('wrapper'); 14 | console.log(myDiv); 15 | 16 | myDiv.appendChild(myParagraph); 17 | myDiv.appendChild(myImage); 18 | 19 | document.body.appendChild(myDiv); 20 | 21 | // oh shoot! we need to add somethint to the top. like a heading! 22 | const heading = document.createElement('h2'); 23 | heading.textContent = 'Cool Things'; 24 | 25 | myDiv.insertAdjacentElement('beforebegin', heading); 26 | 27 | // 34 | 35 | const list = document.createElement('ul'); 36 | const li = document.createElement('li'); 37 | li.textContent = 'three'; 38 | list.appendChild(li); 39 | 40 | document.body.insertAdjacentElement('afterbegin', list); 41 | 42 | const li5 = document.createElement('li'); 43 | li5.textContent = 'Five'; 44 | list.append(li5); 45 | 46 | const li1 = li5.cloneNode(true); 47 | li1.textContent = 'one'; 48 | list.insertAdjacentElement('afterbegin', li1); 49 | 50 | const li4 = document.createElement('li'); 51 | li4.textContent = 'four'; 52 | li5.insertAdjacentElement('beforebegin', li4); 53 | 54 | const li2 = document.createElement('li'); 55 | li2.textContent = 'two'; 56 | li1.insertAdjacentElement('afterend', li2); 57 | -------------------------------------------------------------------------------- /exercises/20 - The DOM/creating-with-strings-FINISHED.js: -------------------------------------------------------------------------------- 1 | const item = document.querySelector('.item'); 2 | 3 | const width = 500; 4 | const src = `https://picsum.photos/${width}`; 5 | const desc = `Cute Pup `; 6 | const myHTML = ` 7 |
8 |

Cute ${desc}

9 | ${desc} 10 |
11 | `; 12 | 13 | // turn a string into a DOM element 14 | const myFragment = document.createRange().createContextualFragment(myHTML); 15 | 16 | document.body.appendChild(myFragment); 17 | -------------------------------------------------------------------------------- /exercises/20 - The DOM/creating-with-strings.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/beginner-javascript/9b7e0da744ed19cd3f81bb453d96964ccd7b5e89/exercises/20 - The DOM/creating-with-strings.js -------------------------------------------------------------------------------- /exercises/20 - The DOM/creating.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/beginner-javascript/9b7e0da744ed19cd3f81bb453d96964ccd7b5e89/exercises/20 - The DOM/creating.js -------------------------------------------------------------------------------- /exercises/20 - The DOM/index-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | The DOM 8 | 9 | 10 | 11 | 12 | 13 |

I am Wes, I love to bbq and Make websites!

14 | 15 | 16 |
17 |
18 | 19 | 20 | 21 | 22 |

Hi I'm a item

23 |
24 |
25 | 26 |

27 | I am a heading 28 | I am hidden! 29 |

30 |

Hi I'm a item

31 |
32 |
33 |

Im an article

34 |

This is how many pizzas I ate! 🍕

35 |
36 |
37 | 38 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /exercises/20 - The DOM/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | The DOM 8 | 9 | 10 | 11 | 12 |
13 |

Hi I'm a item

14 |
15 |

Sub Div

16 |

Hi I'm a item

17 | 18 |
19 | 20 |

Hi I'm a item

21 |

Hi I'm a item

22 |
23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /exercises/20 - The DOM/the-dom-FINISHED.js: -------------------------------------------------------------------------------- 1 | // const p = document.querySelector('p'); 2 | // const imgs = document.querySelectorAll('.item img'); 3 | // const item2 = document.querySelector('.item2'); 4 | // const item2Image = item2.querySelector('img'); 5 | // const heading = document.querySelector('h2'); 6 | 7 | // console.log(heading.textContent); 8 | // console.log(heading.innerText); 9 | // // set the textContent property on that elment 10 | // // heading.textContent = 'Wes is cool'; 11 | // // console.log(heading.textContent); 12 | // // console.log(heading.innerText); 13 | 14 | // console.log(heading.innerHTML); 15 | // console.log(heading.outerHTML); 16 | 17 | // const pizzaList = document.querySelector('.pizza'); 18 | // console.log(pizzaList.textContent); 19 | 20 | // // pizzaList.textContent = `${pizzaList.textContent} 🍕`; 21 | // pizzaList.insertAdjacentText('afterbegin', '🍕'); 22 | // pizzaList.insertAdjacentText('beforeend', '🍕'); 23 | 24 | // Classes! 25 | const pic = document.querySelector('.nice'); 26 | pic.classList.add('open'); 27 | pic.classList.remove('cool'); 28 | 29 | console.log(pic.classList); 30 | 31 | function toggleRound() { 32 | pic.classList.toggle('round'); 33 | } 34 | 35 | pic.addEventListener('click', toggleRound); 36 | 37 | pic.alt = 'Cute Pup'; // setter 38 | console.log(pic.alt); // getter 39 | console.log(pic.naturalWidth); // getter 40 | pic.width = 200; 41 | 42 | // pic.setAttribute('wes-is-cool', 'REALLY CUTE PUP'); 43 | // console.log(pic.getAttribute('alt')); 44 | 45 | const custom = document.querySelector('.custom'); 46 | console.log(custom.dataset); 47 | 48 | custom.addEventListener('click', function() { 49 | alert(`Welcome ${custom.dataset.name} ${custom.dataset.last} `); 50 | }); 51 | -------------------------------------------------------------------------------- /exercises/20 - The DOM/the-dom.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/beginner-javascript/9b7e0da744ed19cd3f81bb453d96964ccd7b5e89/exercises/20 - The DOM/the-dom.js -------------------------------------------------------------------------------- /exercises/20 - The DOM/traversing-FINISHED.js: -------------------------------------------------------------------------------- 1 | const wes = document.querySelector('.wes'); 2 | 3 | // console.log(wes.children); 4 | // console.log(wes.firstElementChild); 5 | // console.log(wes.lastElementChild); 6 | // console.log(wes.previousElementSibling); 7 | // console.log(wes.nextElementSibling); 8 | // console.log(wes.parentElement); 9 | 10 | const p = document.createElement('p'); 11 | p.textContent = 'I will be removed'; 12 | document.body.appendChild(p); 13 | 14 | p.remove(); 15 | 16 | console.log(p); 17 | 18 | document.body.appendChild(p); 19 | -------------------------------------------------------------------------------- /exercises/20 - The DOM/traversing.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/beginner-javascript/9b7e0da744ed19cd3f81bb453d96964ccd7b5e89/exercises/20 - The DOM/traversing.js -------------------------------------------------------------------------------- /exercises/29 - Events/events-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | JavaScript Events 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

Buy Buttons!

16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | Nice 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /exercises/29 - Events/events-FINISHED.js: -------------------------------------------------------------------------------- 1 | const butts = document.querySelector(".butts"); 2 | const coolButton = document.querySelector(".cool"); 3 | 4 | function handleClick() { 5 | console.log("🐛 IT GOT CLICKED!!!"); 6 | } 7 | 8 | const hooray = () => console.log("HOORAY!"); 9 | 10 | butts.addEventListener("click", function () { 11 | console.log("Im an anon!"); 12 | }); 13 | coolButton.addEventListener("click", hooray); 14 | 15 | butts.removeEventListener("click", handleClick); 16 | 17 | // Listen on multiple items 18 | const buyButtons = document.querySelectorAll("button.buy"); 19 | 20 | function handleBuyButtonClick(event) { 21 | console.log("You clicked a button!"); 22 | const button = event.target; 23 | // console.log(button.textContent); 24 | // console.log(parseFloat(event.target.dataset.price)); 25 | console.log(event.target); 26 | console.log(event.currentTarget); 27 | console.log(event.target === event.currentTarget); 28 | // Stop this event from bubbling up 29 | // event.stopPropagation(); 30 | } 31 | 32 | buyButtons.forEach(function (buyButton) { 33 | buyButton.addEventListener("click", handleBuyButtonClick); 34 | }); 35 | 36 | window.addEventListener( 37 | "click", 38 | function (event) { 39 | console.log("YOU CLICKED THE WINDOW"); 40 | console.log(event.target); 41 | console.log(event.type); 42 | // event.stopPropagation(); 43 | console.log(event.bubbles); 44 | }, 45 | { capture: true } 46 | ); 47 | 48 | const photoEl = document.querySelector(".photo"); 49 | 50 | photoEl.addEventListener("mouseenter", (e) => { 51 | console.log(e.currentTarget); 52 | console.log(this); 53 | }); 54 | -------------------------------------------------------------------------------- /exercises/29 - Events/events.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | JavaScript Events 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /exercises/29 - Events/events.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/beginner-javascript/9b7e0da744ed19cd3f81bb453d96964ccd7b5e89/exercises/29 - Events/events.js -------------------------------------------------------------------------------- /exercises/29 - Events/forms-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | HTML Forms 8 | 9 | 10 | 11 | 12 |
13 | Wes Bos 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 |
24 |
25 | 26 |
aSDFASDF
27 | Nice 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /exercises/29 - Events/forms-FINISHED.js: -------------------------------------------------------------------------------- 1 | const wes = document.querySelector('.wes'); 2 | 3 | wes.addEventListener('click', function(event) { 4 | const shouldChangePage = confirm( 5 | 'This website might be malicious!, do you wish to proceed?' 6 | ); 7 | if (!shouldChangePage) { 8 | event.preventDefault(); 9 | } 10 | }); 11 | 12 | const signupForm = document.querySelector('[name="signup"]'); 13 | 14 | signupForm.addEventListener('submit', function(event) { 15 | const name = event.currentTarget.name.value; 16 | if (name.includes('chad')) { 17 | alert('Sorry bro'); 18 | event.preventDefault(); 19 | } 20 | }); 21 | 22 | function logEvent(event) { 23 | console.log(event.type); 24 | console.log(event.currentTarget.value); 25 | } 26 | signupForm.name.addEventListener('keyup', logEvent); 27 | signupForm.name.addEventListener('keydown', logEvent); 28 | signupForm.name.addEventListener('focus', logEvent); 29 | signupForm.name.addEventListener('blur', logEvent); 30 | // 'keyup' 31 | // 'keydown' 32 | // 'focus' 33 | // 'blur' 34 | 35 | const photo = document.querySelector('.photo'); 36 | 37 | function handlePhotoClick(event) { 38 | if (event.type === 'click' || event.key === 'Enter') { 39 | console.log('You clicked the photo'); 40 | } 41 | } 42 | 43 | photo.addEventListener('click', handlePhotoClick); 44 | photo.addEventListener('keyup', handlePhotoClick); 45 | -------------------------------------------------------------------------------- /exercises/29 - Events/forms.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | HTML Forms 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 |
24 |
25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /exercises/29 - Events/forms.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/beginner-javascript/9b7e0da744ed19cd3f81bb453d96964ccd7b5e89/exercises/29 - Events/forms.js -------------------------------------------------------------------------------- /exercises/33 - Etch-a-Sketch/etch-a-sketch-FINISHED.js: -------------------------------------------------------------------------------- 1 | // Select the elements on the page - canvas, shake button 2 | const canvas = document.querySelector('#etch-a-sketch'); 3 | const ctx = canvas.getContext('2d'); 4 | const shakebutton = document.querySelector('.shake'); 5 | const MOVE_AMOUNT = 50; 6 | // Setup our canvas for drawing 7 | // make a variable called height and width from the same properties on our canvas. 8 | const { width, height } = canvas; 9 | 10 | let x = Math.floor(Math.random() * width); 11 | let y = Math.floor(Math.random() * height); 12 | // create random x and y starting points on the canvas 13 | 14 | ctx.lineJoin = 'round'; 15 | ctx.lineCap = 'round'; 16 | ctx.lineWidth = MOVE_AMOUNT; 17 | 18 | let hue = 0; 19 | ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`; 20 | ctx.beginPath(); // start the drawing 21 | ctx.moveTo(x, y); 22 | ctx.lineTo(x, y); 23 | ctx.stroke(); 24 | 25 | // write a draw function 26 | function draw({ key }) { 27 | // increment the hue 28 | hue += 1; 29 | console.log(hue); 30 | ctx.strokeStyle = `hsl(${Math.random() * 360}, 100%, 50%)`; 31 | console.log(key); 32 | // start the path 33 | ctx.beginPath(); 34 | ctx.moveTo(x, y); 35 | // move our x and y values depending on what the user did 36 | switch (key) { 37 | case 'ArrowUp': 38 | y -= MOVE_AMOUNT; 39 | break; 40 | case 'ArrowRight': 41 | x += MOVE_AMOUNT; 42 | break; 43 | case 'ArrowDown': 44 | y += MOVE_AMOUNT; 45 | break; 46 | case 'ArrowLeft': 47 | x -= MOVE_AMOUNT; 48 | break; 49 | default: 50 | break; 51 | } 52 | ctx.lineTo(x, y); 53 | ctx.stroke(); 54 | } 55 | 56 | // write a handler for the keys 57 | function handleKey(e) { 58 | if (e.key.includes('Arrow')) { 59 | e.preventDefault(); 60 | draw({ key: e.key }); 61 | } 62 | } 63 | // clear /shke function 64 | function clearCanvas() { 65 | canvas.classList.add('shake'); 66 | ctx.clearRect(0, 0, width, height); 67 | canvas.addEventListener( 68 | 'animationend', 69 | function() { 70 | console.log('Done the shake!'); 71 | canvas.classList.remove('shake'); 72 | }, 73 | { once: true } 74 | ); 75 | } 76 | 77 | // listen for arrow keys 78 | window.addEventListener('keydown', handleKey); 79 | shakebutton.addEventListener('click', clearCanvas); 80 | -------------------------------------------------------------------------------- /exercises/33 - Etch-a-Sketch/etch-a-sketch.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/beginner-javascript/9b7e0da744ed19cd3f81bb453d96964ccd7b5e89/exercises/33 - Etch-a-Sketch/etch-a-sketch.js -------------------------------------------------------------------------------- /exercises/33 - Etch-a-Sketch/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Etch A Sketch 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 | 16 |
17 | 18 |
19 | 20 | 21 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /exercises/34 - Click Outside/click-outside-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | Wes Bos 15 |

Wes Bos

16 | 17 |
18 |
19 | Wes Bos 20 |

Scott Tolinski

21 | 22 |
23 |
24 | Wes Bos 25 |

Kait Bos

26 | 27 |
28 |
29 | Wes Bos 30 |

Snickers the dog

31 | 32 |
33 | 34 |
35 | 36 | 41 | 42 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /exercises/34 - Click Outside/click-outside-FINISHED.js: -------------------------------------------------------------------------------- 1 | const cardButtons = document.querySelectorAll(".card button"); 2 | const modalOuter = document.querySelector(".modal-outer"); 3 | const modalInner = document.querySelector(".modal-inner"); 4 | 5 | function handleCardButtonClick(event) { 6 | const button = event.currentTarget; 7 | const card = button.closest(".card"); 8 | // Grab the image src 9 | const imgSrc = card.querySelector("img").src; 10 | const desc = card.dataset.description; 11 | const name = card.querySelector("h2").textContent; 12 | // populate the modal with the new info 13 | modalInner.innerHTML = ` 14 | ${name} 18 |

${desc}

19 | `; 20 | // show the modal 21 | modalOuter.classList.add("open"); 22 | } 23 | 24 | cardButtons.forEach((button) => 25 | button.addEventListener("click", handleCardButtonClick) 26 | ); 27 | 28 | function closeModal() { 29 | modalOuter.classList.remove("open"); 30 | } 31 | 32 | modalOuter.addEventListener("click", function (event) { 33 | const isOutside = !event.target.closest(".modal-inner"); 34 | if (isOutside) { 35 | closeModal(); 36 | } 37 | }); 38 | 39 | window.addEventListener("keydown", (event) => { 40 | console.log(event); 41 | if (event.key === "Escape") { 42 | closeModal(); 43 | } 44 | }); 45 | -------------------------------------------------------------------------------- /exercises/34 - Click Outside/click-outside.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | Wes Bos 15 |

Wes Bos

16 | 17 |
18 |
19 | Wes Bos 20 |

Scott Tolinski

21 | 22 |
23 |
24 | Wes Bos 25 |

Kait Bos

26 | 27 |
28 |
29 | Wes Bos 30 |

Snickers the dog

31 | 32 |
33 | 34 |
35 | 36 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /exercises/34 - Click Outside/click-outside.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/beginner-javascript/9b7e0da744ed19cd3f81bb453d96964ccd7b5e89/exercises/34 - Click Outside/click-outside.js -------------------------------------------------------------------------------- /exercises/35 - Scroll To Accept/scroll-to-accept-FINISHED.js: -------------------------------------------------------------------------------- 1 | const terms = document.querySelector('.terms-and-conditions'); 2 | const watch = document.querySelector('.watch'); 3 | const button = document.querySelector('.accept'); 4 | 5 | function obCallback(payload) { 6 | if (payload[0].intersectionRatio === 1) { 7 | button.disabled = false; 8 | // stop observing the button 9 | ob.unobserve(terms.lastElementChild); 10 | } 11 | } 12 | const ob = new IntersectionObserver(obCallback, { 13 | root: terms, 14 | threshold: 1, 15 | }); 16 | 17 | ob.observe(terms.lastElementChild); 18 | -------------------------------------------------------------------------------- /exercises/35 - Scroll To Accept/scroll-to-accept.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/beginner-javascript/9b7e0da744ed19cd3f81bb453d96964ccd7b5e89/exercises/35 - Scroll To Accept/scroll-to-accept.js -------------------------------------------------------------------------------- /exercises/36 - Tabs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Tabs 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 | 19 | 21 | 24 |
25 |
26 |

JavaScript is great!

27 |
28 | 31 | 34 |
35 |
36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /exercises/36 - Tabs/tabs-finished.js: -------------------------------------------------------------------------------- 1 | const tabs = document.querySelector('.tabs'); 2 | const tabButtons = tabs.querySelectorAll('[role="tab"]'); 3 | const tabPanels = Array.from(tabs.querySelectorAll('[role="tabpanel"]')); 4 | 5 | function handleTabClick(event) { 6 | // hide all tab panels 7 | tabPanels.forEach(panel => { 8 | panel.hidden = true; 9 | }); 10 | // mark all tabs as unselected 11 | tabButtons.forEach(tab => { 12 | // tab.ariaSelected = false; 13 | tab.setAttribute('aria-selected', false); 14 | }); 15 | // mark the clicked tab as selected 16 | event.currentTarget.setAttribute('aria-selected', true); 17 | // find the associated tabPanel and show it! 18 | const { id } = event.currentTarget; 19 | 20 | /* 21 | METHOD 1 22 | const tabPanel = tabs.querySelector(`[aria-labelledby="${id}"]`); 23 | console.log(tabPanel); 24 | tabPanel.hidden = false; 25 | */ 26 | 27 | // METHOD 2 - find in the array of tabPanels 28 | console.log(tabPanels); 29 | const tabPanel = tabPanels.find( 30 | panel => panel.getAttribute('aria-labelledby') === id 31 | ); 32 | tabPanel.hidden = false; 33 | } 34 | 35 | tabButtons.forEach(button => button.addEventListener('click', handleTabClick)); 36 | -------------------------------------------------------------------------------- /exercises/36 - Tabs/tabs-style.css: -------------------------------------------------------------------------------- 1 | .tabs { 2 | display: grid; 3 | } 4 | 5 | [role="tablist"] { 6 | display: grid; 7 | grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); 8 | grid-gap: 10px; 9 | } 10 | 11 | [role="tabpanel"] { 12 | background: var(--yellow); 13 | padding: 2rem; 14 | } 15 | 16 | button { 17 | background: var(--grey); 18 | border: 0; 19 | color: black; 20 | border-radius: 5px 5px 0 0; 21 | --bs-color: rgba(0,0,0,0.1); 22 | box-shadow: inset 0 -2px 5px var(--bs-color); 23 | cursor:pointer; 24 | } 25 | 26 | button[aria-selected="true"] { 27 | background: var(--yellow); 28 | box-shadow: none; 29 | color: rgba(0,0,0,0.7); 30 | } 31 | 32 | button:focus { 33 | outline: 0; 34 | --bs-color: rgba(0,0,0,0.6); 35 | } 36 | -------------------------------------------------------------------------------- /exercises/36 - Tabs/tabs.js: -------------------------------------------------------------------------------- 1 | console.log('ya ya wes we get it.. IT WORKS!'); 2 | -------------------------------------------------------------------------------- /exercises/46 - Arrays/array-loopings-methods-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /exercises/46 - Arrays/array-methods-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /exercises/46 - Arrays/for-loops-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | For Loops 9 | 10 | 11 | 12 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /exercises/46 - Arrays/for-loops-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | For Loops 9 | 10 | 11 | 12 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /exercises/46 - Arrays/reduce-challenge-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Reduce! 8 | 9 | 10 | 11 | 12 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /exercises/55 - Face Detection Censorship/.npmrc: -------------------------------------------------------------------------------- 1 | fund=false 2 | audit=false 3 | legacy-peer-deps=true 4 | -------------------------------------------------------------------------------- /exercises/55 - Face Detection Censorship/face-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Censorship 7 | 8 | 35 | 36 | 37 | 38 |
39 | 43 | 47 |
48 |
49 | 50 | 51 | 52 |
53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /exercises/55 - Face Detection Censorship/face.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Censorship 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 |
16 | 17 | 18 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /exercises/55 - Face Detection Censorship/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pixelated-face", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "pixelated-face.js", 6 | "scripts": { 7 | "start": "parcel face.html" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "browserslist": [ 12 | "last 1 Chrome version" 13 | ], 14 | "dependencies": { 15 | "parcel": "2.0.1" 16 | } 17 | } -------------------------------------------------------------------------------- /exercises/55 - Face Detection Censorship/pixelated-face-DEMO.js: -------------------------------------------------------------------------------- 1 | // The face detection does not work on all browsers and operating systems. 2 | // If you are getting a `Face detection service unavailable` error or similar, 3 | // it's possible that it won't work for you at the moment. 4 | 5 | const faceDetector = new window.FaceDetector(); 6 | const video = document.querySelector('video.webcam'); 7 | const canvas = document.querySelector('canvas.video'); 8 | const ctx = canvas.getContext('2d'); 9 | const faceCanvas = document.querySelector('canvas.face'); 10 | const faceCtx = faceCanvas.getContext('2d'); 11 | const SCALE = 1.2; 12 | const SIZE = 10; 13 | 14 | async function populateVideo() { 15 | const stream = await navigator.mediaDevices.getUserMedia({ 16 | video: { width: 1280, height: 720 }, 17 | }); 18 | video.srcObject = stream; 19 | await video.play(); 20 | canvas.width = video.videoWidth; 21 | canvas.height = video.videoHeight; 22 | faceCanvas.width = video.videoWidth; 23 | faceCanvas.height = video.videoHeight; 24 | } 25 | 26 | async function detect() { 27 | const faces = await faceDetector.detect(video); 28 | // ctx.drawImage(video, 0, 0, canvas.width, canvas.height); 29 | ctx.clearRect(0, 0, canvas.width, canvas.height); 30 | // paintFace(faces); 31 | faces.forEach(drawFace); 32 | faces.forEach(censor); 33 | requestAnimationFrame(detect); 34 | } 35 | 36 | function censor({ boundingBox: face }) { 37 | faceCtx.imageSmoothingEnabled = false; 38 | faceCtx.clearRect(0, 0, faceCanvas.width, faceCanvas.height); 39 | // First draw it small 40 | faceCtx.drawImage( 41 | video, // Where should I grab the photo from? 42 | face.x, // from what x and y should I start capturing from? 43 | face.y, 44 | face.width, // how wide and high should I capture from? 45 | face.height, 46 | face.x, // now to draw it, where should I start x and y? 47 | face.y, 48 | SIZE, // how wide and high should it be? 49 | SIZE 50 | ); 51 | 52 | const width = face.width * SCALE; 53 | const height = face.height * SCALE; 54 | 55 | // then draw it back on, but scaled up 56 | faceCtx.drawImage( 57 | faceCanvas, // Where should I grab the photo from? 58 | face.x, // from what x and y should I start capturing from? 59 | face.y, // from what x and y should I start capturing from? 60 | SIZE, 61 | SIZE, 62 | // Drawing 63 | face.x - (width - face.width) / 2, 64 | face.y - (height - face.height) / 2, 65 | width, 66 | height 67 | ); 68 | } 69 | function drawFace(face) { 70 | const { width, height, top, left } = face.boundingBox; 71 | ctx.strokeStyle = '#ffc600'; 72 | ctx.lineWidth = 1; 73 | ctx.strokeRect(left, top, width, height); 74 | ctx.stroke(); 75 | } 76 | 77 | populateVideo().then(detect); 78 | -------------------------------------------------------------------------------- /exercises/55 - Face Detection Censorship/pixelated-face-FINISHED.js: -------------------------------------------------------------------------------- 1 | // The face detection does not work on all browsers and operating systems. 2 | // If you are getting a `Face detection service unavailable` error or similar, 3 | // it's possible that it won't work for you at the moment. 4 | 5 | const video = document.querySelector('.webcam'); 6 | const canvas = document.querySelector('.video'); 7 | const ctx = canvas.getContext('2d'); 8 | const faceCanvas = document.querySelector('.face'); 9 | const faceCtx = faceCanvas.getContext('2d'); 10 | const faceDetector = new window.FaceDetector(); 11 | const optionsInputs = document.querySelectorAll( 12 | '.controls input[type="range"]' 13 | ); 14 | 15 | const options = { 16 | SIZE: 10, 17 | SCALE: 1.35, 18 | }; 19 | 20 | function handleOption(event) { 21 | const { value, name } = event.currentTarget; 22 | options[name] = parseFloat(value); 23 | } 24 | optionsInputs.forEach(input => input.addEventListener('input', handleOption)); 25 | 26 | // Write a fucntion that will populate the users video 27 | async function populateVideo() { 28 | const stream = await navigator.mediaDevices.getUserMedia({ 29 | video: { width: 1280, height: 720 }, 30 | }); 31 | video.srcObject = stream; 32 | await video.play(); 33 | // size the canvases to be the same size as the video 34 | console.log(video.videoWidth, video.videoHeight); 35 | canvas.width = video.videoWidth; 36 | canvas.height = video.videoHeight; 37 | faceCanvas.width = video.videoWidth; 38 | faceCanvas.height = video.videoHeight; 39 | } 40 | 41 | async function detect() { 42 | const faces = await faceDetector.detect(video); 43 | // ask the browser when the next animation frame is, and tell it to run detect for us 44 | faces.forEach(drawFace); 45 | faces.forEach(censor); 46 | requestAnimationFrame(detect); 47 | } 48 | 49 | function drawFace(face) { 50 | const { width, height, top, left } = face.boundingBox; 51 | ctx.clearRect(0, 0, canvas.width, canvas.height); 52 | ctx.strokeStyle = '#ffc600'; 53 | ctx.lineWidth = 2; 54 | ctx.strokeRect(left, top, width, height); 55 | } 56 | 57 | function censor({ boundingBox: face }) { 58 | faceCtx.imageSmoothingEnabled = false; 59 | faceCtx.clearRect(0, 0, faceCanvas.width, faceCanvas.height); 60 | // draw the small face 61 | faceCtx.drawImage( 62 | // 5 source args 63 | video, // where does the source come from? 64 | face.x, // where do we start the source pull from? 65 | face.y, 66 | face.width, 67 | face.height, 68 | // 4 draw args 69 | face.x, // where should we start drawing the x and y? 70 | face.y, 71 | options.SIZE, 72 | options.SIZE 73 | ); 74 | // draw the small face back on, but scale up 75 | 76 | const width = face.width * options.SCALE; 77 | const height = face.height * options.SCALE; 78 | faceCtx.drawImage( 79 | faceCanvas, // source 80 | face.x, // where do we start the source pull from? 81 | face.y, 82 | options.SIZE, 83 | options.SIZE, 84 | // Drawing args 85 | face.x - (width - face.width) / 2, 86 | face.y - (height - face.height) / 2, 87 | width, 88 | height 89 | ); 90 | } 91 | 92 | populateVideo().then(detect); 93 | -------------------------------------------------------------------------------- /exercises/55 - Face Detection Censorship/pixelated-face.js: -------------------------------------------------------------------------------- 1 | // The face detection does not work on all browsers and operating systems. 2 | // If you are getting a `Face detection service unavailable` error or similar, 3 | // it's possible that it won't work for you at the moment. 4 | -------------------------------------------------------------------------------- /exercises/56 - Sarcastic Text/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Text Generator 8 | 9 | 30 | 31 | 32 | 33 | 34 |
35 | 39 | 43 | 47 | 48 |

49 |
50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /exercises/56 - Sarcastic Text/text-DEMO.js: -------------------------------------------------------------------------------- 1 | // functions as arguments 2 | 3 | const textarea = document.querySelector('[name="text"]'); 4 | const result = document.querySelector('.result'); 5 | const filterInputs = document.querySelectorAll('[name="filter"]'); 6 | 7 | /* eslint-disable */ 8 | const funkyLetters = { 9 | '-': '₋', '!': 'ᵎ', '?': 'ˀ', '(': '⁽', ')': '₎', '+': '⁺', '=': '₌', '0': '⁰', '1': '₁', '2': '²', '4': '₄', '5': '₅', '6': '₆', '7': '⁷', '8': '⁸', '9': '⁹', a: 'ᵃ', A: 'ᴬ', B: 'ᴮ', b: 'ᵦ', C: '𝒸', d: 'ᵈ', D: 'ᴰ', e: 'ₑ', E: 'ᴱ', f: '𝒻', F: 'ᶠ', g: 'ᵍ', G: 'ᴳ', h: 'ʰ', H: 'ₕ', I: 'ᵢ', i: 'ᵢ', j: 'ʲ', J: 'ᴶ', K: 'ₖ', k: 'ₖ', l: 'ˡ', L: 'ᴸ', m: 'ᵐ', M: 'ₘ', n: 'ₙ', N: 'ᴺ', o: 'ᵒ', O: 'ᴼ', p: 'ᵖ', P: 'ᴾ', Q: 'ᵠ', q: 'ᑫ', r: 'ʳ', R: 'ᵣ', S: 'ˢ', s: 'ˢ', t: 'ᵗ', T: 'ₜ', u: 'ᵘ', U: 'ᵤ', v: 'ᵛ', V: 'ᵥ', w: '𝓌', W: 'ʷ', x: 'ˣ', X: 'ˣ', y: 'y', Y: 'Y', z: '𝓏', Z: 'ᶻ' }; 10 | /* eslint-enable */ 11 | 12 | const filters = { 13 | sarcastic(letter, index) { 14 | if (index % 2) { 15 | return letter.toUpperCase(); 16 | } 17 | return letter.toLowerCase(); 18 | }, 19 | funky(letter, index) { 20 | // first check if there is a letter in this case 21 | let funkyLetter = funkyLetters[letter]; 22 | console.log(funkyLetter); 23 | if (!funkyLetter) { 24 | // then check for a lowercase version 25 | funkyLetter = funkyLetters[letter.toLowerCase()]; 26 | } 27 | // if we still don't have something, just use the regular letter 28 | if (!funkyLetter) { 29 | funkyLetter = letter; 30 | } 31 | return funkyLetter; 32 | }, 33 | unable(letter) { 34 | const random = Math.floor(Math.random() * 3); 35 | if (letter === ' ' && random === 2) { 36 | return '...'; 37 | } 38 | return letter; 39 | }, 40 | }; 41 | 42 | function handleInput(text) { 43 | const filter = document.querySelector('[name="filter"]:checked').value; 44 | const mod = Array.from(text) 45 | .map(filters[filter]) 46 | .join(''); 47 | result.textContent = mod; 48 | } 49 | 50 | textarea.addEventListener('input', e => handleInput(e.target.value)); 51 | 52 | filterInputs.forEach(input => 53 | input.addEventListener('input', () => handleInput(textarea.value)) 54 | ); 55 | -------------------------------------------------------------------------------- /exercises/56 - Sarcastic Text/text-FINISHED.js: -------------------------------------------------------------------------------- 1 | const textarea = document.querySelector('[name="text"]'); 2 | const result = document.querySelector('.result'); 3 | const filterInputs = Array.from(document.querySelectorAll('[name="filter"]')); 4 | 5 | /* eslint-disable */ 6 | const funkyLetters = { 7 | '-': '₋', '!': 'ᵎ', '?': 'ˀ', '(': '⁽', ')': '₎', '+': '⁺', '=': '₌', '0': '⁰', '1': '₁', '2': '²', '4': '₄', '5': '₅', '6': '₆', '7': '⁷', '8': '⁸', '9': '⁹', a: 'ᵃ', A: 'ᴬ', B: 'ᴮ', b: 'ᵦ', C: '𝒸', d: 'ᵈ', D: 'ᴰ', e: 'ₑ', E: 'ᴱ', f: '𝒻', F: 'ᶠ', g: 'ᵍ', G: 'ᴳ', h: 'ʰ', H: 'ₕ', I: 'ᵢ', i: 'ᵢ', j: 'ʲ', J: 'ᴶ', K: 'ₖ', k: 'ₖ', l: 'ˡ', L: 'ᴸ', m: 'ᵐ', M: 'ₘ', n: 'ₙ', N: 'ᴺ', o: 'ᵒ', O: 'ᴼ', p: 'ᵖ', P: 'ᴾ', Q: 'ᵠ', q: 'ᑫ', r: 'ʳ', R: 'ᵣ', S: 'ˢ', s: 'ˢ', t: 'ᵗ', T: 'ₜ', u: 'ᵘ', U: 'ᵤ', v: 'ᵛ', V: 'ᵥ', w: '𝓌', W: 'ʷ', x: 'ˣ', X: 'ˣ', y: 'y', Y: 'Y', z: '𝓏', Z: 'ᶻ' 8 | }; 9 | /* eslint-enable */ 10 | 11 | const filters = { 12 | sarcastic(letter, index) { 13 | // if it is odd, it will return 1, and that is truthy, so this if statement will trip 14 | if (index % 2) { 15 | return letter.toUpperCase(); 16 | } 17 | // if it is even, it will return zero and we will lowercase it 18 | return letter.toLowerCase(); 19 | }, 20 | funky(letter) { 21 | // first check if there is a funky letter for this case 22 | let funkyLetter = funkyLetters[letter]; 23 | if (funkyLetter) return funkyLetter; 24 | // if there is not, check if there is a lowercase version 25 | funkyLetter = funkyLetters[letter.toLowerCase()]; 26 | if (funkyLetter) return funkyLetter; 27 | // if there is nothing, return the regular letter 28 | return letter; 29 | }, 30 | unable(letter) { 31 | const random = Math.floor(Math.random() * 3); 32 | if (letter === ' ' && random === 2) { 33 | return '...'; 34 | } 35 | return letter; 36 | }, 37 | }; 38 | 39 | function transformText(text) { 40 | // const filter = document.querySelector('[name="filter"]:checked').value; 41 | const filter = filterInputs.find(input => input.checked).value; 42 | // take the text, and loop over each letter. 43 | const mod = Array.from(text).map(filters[filter]); 44 | result.textContent = mod.join(''); 45 | } 46 | 47 | textarea.addEventListener('input', e => transformText(e.target.value)); 48 | 49 | filterInputs.forEach(input => 50 | input.addEventListener('input', () => { 51 | transformText(textarea.value); 52 | }) 53 | ); 54 | -------------------------------------------------------------------------------- /exercises/56 - Sarcastic Text/text.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesbos/beginner-javascript/9b7e0da744ed19cd3f81bb453d96964ccd7b5e89/exercises/56 - Sarcastic Text/text.js -------------------------------------------------------------------------------- /exercises/57 - Shopping List/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Shopping List 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 | 17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /exercises/57 - Shopping List/shopping-DEMO.js: -------------------------------------------------------------------------------- 1 | // Topics: Custom Events, Event Delegation, local storage, DOM Events, Object Reference, 2 | 3 | const shoppingForm = document.querySelector('.shopping'); 4 | const list = document.querySelector('.list'); 5 | 6 | let items = []; 7 | 8 | function mirrorToLocalStorage() { 9 | localStorage.setItem('items', JSON.stringify(items)); 10 | } 11 | 12 | function restoreFromLocalStorage() { 13 | const lsItems = JSON.parse(localStorage.getItem('items')); 14 | if (lsItems.length) { 15 | items = lsItems; 16 | // fire event 17 | list.dispatchEvent(new CustomEvent('itemsUpdated')); 18 | } 19 | } 20 | 21 | function handleSubmit(e) { 22 | e.preventDefault(); 23 | if (!e.target.item.value) return; 24 | const item = { 25 | id: Date.now(), 26 | name: e.target.item.value, 27 | complete: false, 28 | }; 29 | items.push(item); 30 | list.dispatchEvent(new CustomEvent('itemsUpdated')); 31 | e.target.reset(); 32 | } 33 | 34 | function markAsComplete(id) { 35 | const itemRef = items.find(item => item.id === id); 36 | console.log(itemRef); 37 | // this is just a reference to the item 38 | itemRef.complete = !itemRef.complete; 39 | list.dispatchEvent(new CustomEvent('itemsUpdated')); 40 | } 41 | 42 | function deleteItem(id) { 43 | console.log(items, id); 44 | // find the item's index 45 | items = items.filter(item => item.id !== id); 46 | list.dispatchEvent(new CustomEvent('itemsUpdated')); 47 | } 48 | function displayItems() { 49 | const html = items 50 | .map( 51 | item => `
  • 52 | 55 | ${item.name} 56 | 57 |
  • ` 58 | ) 59 | .join(''); 60 | list.innerHTML = html; 61 | } 62 | 63 | shoppingForm.addEventListener('submit', handleSubmit); 64 | 65 | // List is an empty div where we add items with JS 66 | list.addEventListener('click', e => { 67 | if (e.target.matches('button')) { 68 | deleteItem(parseFloat(e.target.value)); 69 | } 70 | }); 71 | 72 | list.addEventListener('input', e => { 73 | if (e.target.matches('input')) { 74 | markAsComplete(parseFloat(e.target.value)); 75 | } 76 | }); 77 | 78 | list.addEventListener('itemsUpdated', displayItems); 79 | list.addEventListener('itemsUpdated', mirrorToLocalStorage); 80 | 81 | restoreFromLocalStorage(); 82 | 83 | // Challenge - put the done items at the bottom 84 | -------------------------------------------------------------------------------- /exercises/57 - Shopping List/shopping-FINISHED.js: -------------------------------------------------------------------------------- 1 | const shoppingForm = document.querySelector('.shopping'); 2 | const list = document.querySelector('.list'); 3 | 4 | // We need an array to hold our state 5 | let items = []; 6 | 7 | function handleSubmit(e) { 8 | e.preventDefault(); 9 | console.log('submitted!!'); 10 | const name = e.currentTarget.item.value; 11 | // if its empty, then dont submit it 12 | if (!name) return; 13 | 14 | const item = { 15 | name, 16 | id: Date.now(), 17 | complete: false, 18 | }; 19 | // Push the items into our state 20 | items.push(item); 21 | console.log(`There are now ${items.length} in your state`); 22 | // Clear the form 23 | e.target.reset(); 24 | // fire off a custom event that will tell anyone else who cares that the items have been updated! 25 | list.dispatchEvent(new CustomEvent('itemsUpdated')); 26 | } 27 | 28 | function displayItems() { 29 | console.log(items); 30 | const html = items 31 | .map( 32 | item => `
  • 33 | 38 | ${item.name} 39 |
  • ` 44 | ) 45 | .join(''); 46 | list.innerHTML = html; 47 | } 48 | 49 | function mirrorToLocalStorage() { 50 | console.info('Saving items to localstorage'); 51 | localStorage.setItem('items', JSON.stringify(items)); 52 | } 53 | 54 | function restoreFromLocalStorage() { 55 | console.info('Restoring from LS'); 56 | // pull the items from LS 57 | const lsItems = JSON.parse(localStorage.getItem('items')); 58 | if (lsItems.length) { 59 | // items = lsItems; 60 | // lsItems.forEach(item => items.push(item)); 61 | // items.push(lsItems[0], lsItems[1]); 62 | items.push(...lsItems); 63 | list.dispatchEvent(new CustomEvent('itemsUpdated')); 64 | } 65 | } 66 | 67 | function deleteItem(id) { 68 | console.log('DELETIENG ITEM', id); 69 | // update our items array without this one 70 | items = items.filter(item => item.id !== id); 71 | console.log(items); 72 | list.dispatchEvent(new CustomEvent('itemsUpdated')); 73 | } 74 | 75 | function markAsComplete(id) { 76 | console.log('Marking as complete', id); 77 | const itemRef = items.find(item => item.id === id); 78 | itemRef.complete = !itemRef.complete; 79 | list.dispatchEvent(new CustomEvent('itemsUpdated')); 80 | } 81 | 82 | shoppingForm.addEventListener('submit', handleSubmit); 83 | list.addEventListener('itemsUpdated', displayItems); 84 | list.addEventListener('itemsUpdated', mirrorToLocalStorage); 85 | // Event Delegation: We listen or the click on the list