@ -0,0 +1,47 @@ |
|||||||
|
const body = document.querySelector('body') |
||||||
|
|
||||||
|
const create = (tag) => { |
||||||
|
const element = document.createElement(tag) |
||||||
|
return element |
||||||
|
} |
||||||
|
|
||||||
|
export const build = (amount = 54) => { |
||||||
|
let count = 1 |
||||||
|
const intervalID = setInterval(() => { |
||||||
|
const brick = create('div') |
||||||
|
brick.title = 'brick' |
||||||
|
brick.id = `brick-${count}` |
||||||
|
if (count % 3 === 2) { |
||||||
|
brick.dataset.foundation = true |
||||||
|
} |
||||||
|
brick.append(count) |
||||||
|
body.append(brick) |
||||||
|
|
||||||
|
if (count === amount) { |
||||||
|
window.clearInterval(intervalID) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
count++ |
||||||
|
}, 100) |
||||||
|
} |
||||||
|
|
||||||
|
export const repair = (...ids) => { |
||||||
|
ids.forEach((id) => { |
||||||
|
const toRepair = document.getElementById(id) |
||||||
|
if (toRepair) { |
||||||
|
toRepair.dataset.repaired = toRepair.hasAttribute('data-foundation') |
||||||
|
? 'in progress' |
||||||
|
: true |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
export const destroy = () => { |
||||||
|
const bricks = [...document.querySelectorAll('[title="brick"]')] |
||||||
|
const toRemove = bricks[bricks.length - 1] |
||||||
|
|
||||||
|
if (toRemove) { |
||||||
|
toRemove.remove() |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
import { colors } from './data.js' |
||||||
|
|
||||||
|
export const generateClasses = () => { |
||||||
|
document.head.append( |
||||||
|
Object.assign(document.createElement('style'), { |
||||||
|
type: 'text/css', |
||||||
|
id: 'colors', |
||||||
|
innerHTML: colors |
||||||
|
.map((color) => `.${color} { background: ${color}; }`) |
||||||
|
.join('\n'), |
||||||
|
}), |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
const body = document.querySelector('body') |
||||||
|
|
||||||
|
const cold = ['aqua', 'blue', 'turquoise', 'green', 'purple', 'cyan', 'navy'] |
||||||
|
|
||||||
|
export const generateColdShades = () => { |
||||||
|
const shades = colors.filter((color) => { |
||||||
|
for (const c of cold) { |
||||||
|
if (color.includes(c)) { |
||||||
|
return true |
||||||
|
} |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
shades.forEach((c) => { |
||||||
|
const shade = document.createElement('div') |
||||||
|
shade.className = c |
||||||
|
shade.textContent = c |
||||||
|
body.append(shade) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
export const choseShade = (shade) => { |
||||||
|
const all = [...document.querySelectorAll('div')] |
||||||
|
all.forEach((a) => { |
||||||
|
if (!a.classList.contains(shade)) { |
||||||
|
a.classList.replace(a.className, shade) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
generateClasses() |
||||||
|
generateColdShades() |
@ -0,0 +1,25 @@ |
|||||||
|
export const getArchitects = () => { |
||||||
|
const architects = [...document.getElementsByTagName('a')] |
||||||
|
const others = [...document.getElementsByTagName('span')] |
||||||
|
return [architects, others] |
||||||
|
} |
||||||
|
|
||||||
|
export const getClassical = () => { |
||||||
|
const classicals = [...document.getElementsByClassName('classical')] |
||||||
|
const others = [...document.querySelectorAll('a:not(.classical)')] |
||||||
|
return [classicals, others] |
||||||
|
} |
||||||
|
|
||||||
|
export const getActive = () => { |
||||||
|
const active = [...document.querySelectorAll('.classical.active')] |
||||||
|
const others = [...document.querySelectorAll('.classical:not(.active)')] |
||||||
|
return [active, others] |
||||||
|
} |
||||||
|
|
||||||
|
export const getBonannoPisano = () => { |
||||||
|
const bonanno = document.getElementById('BonannoPisano') |
||||||
|
const others = [ |
||||||
|
...document.querySelectorAll('a.classical.active:not(#BonannoPisano)'), |
||||||
|
] |
||||||
|
return [bonanno, others] |
||||||
|
} |
@ -0,0 +1,169 @@ |
|||||||
|
import { deepStrictEqual } from 'assert' |
||||||
|
import puppeteer from 'puppeteer-core' |
||||||
|
import people from '../assets/data/get-them-all.js' |
||||||
|
|
||||||
|
const config = { |
||||||
|
headless: false, |
||||||
|
executablePath: |
||||||
|
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', |
||||||
|
} |
||||||
|
|
||||||
|
const browser = await puppeteer.launch(config) |
||||||
|
const [page] = await browser.pages() |
||||||
|
await page.goto('http://localhost:8000/dom-js/get-them-all/') |
||||||
|
|
||||||
|
const architects = people |
||||||
|
.filter((p) => p.tag === 'a') |
||||||
|
.map((e) => e.id) |
||||||
|
.sort((a, b) => a.localeCompare(b)) |
||||||
|
|
||||||
|
const notArchitects = people |
||||||
|
.filter((p) => p.tag !== 'a') |
||||||
|
.map((e) => e.id) |
||||||
|
.sort((a, b) => a.localeCompare(b)) |
||||||
|
|
||||||
|
const checkArchitects = async () => { |
||||||
|
const btnArchitect = await page.$(`#btnArchitect`) |
||||||
|
btnArchitect.click() |
||||||
|
await page.waitFor(500) |
||||||
|
|
||||||
|
const selected = await page.$$eval('a', (nodes) => |
||||||
|
nodes |
||||||
|
.filter((node) => node.textContent === 'Architect') |
||||||
|
.map((node) => node.id) |
||||||
|
.sort((a, b) => a.localeCompare(b)), |
||||||
|
) |
||||||
|
|
||||||
|
const eliminated = await page.$$eval('span', (nodes) => |
||||||
|
nodes |
||||||
|
.filter((node) => node.style.opacity === '0.2') |
||||||
|
.map((node) => node.id) |
||||||
|
.sort((a, b) => a.localeCompare(b)), |
||||||
|
) |
||||||
|
|
||||||
|
deepStrictEqual(`architects: ${selected}`, `architects: ${architects}`) |
||||||
|
deepStrictEqual( |
||||||
|
`not architects: ${eliminated}`, |
||||||
|
`not architects: ${notArchitects}`, |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
checkArchitects() |
||||||
|
await page.waitFor(1000) |
||||||
|
|
||||||
|
// get classical
|
||||||
|
const classical = people |
||||||
|
.filter((p) => p.classe === 'classical') |
||||||
|
.map((e) => e.id) |
||||||
|
.sort((a, b) => a.localeCompare(b)) |
||||||
|
|
||||||
|
const notClassical = people |
||||||
|
.filter((p) => p.tag === 'a' && p.classe !== 'classical') |
||||||
|
.map((e) => e.id) |
||||||
|
.sort((a, b) => a.localeCompare(b)) |
||||||
|
|
||||||
|
const checkClassical = async () => { |
||||||
|
const btnClassical = await page.$(`#btnClassical`) |
||||||
|
btnClassical.click() |
||||||
|
await page.waitFor(500) |
||||||
|
|
||||||
|
const selected = await page.$$eval('.classical', (nodes) => |
||||||
|
nodes |
||||||
|
.filter((node) => node.textContent === 'Classical') |
||||||
|
.map((node) => node.id) |
||||||
|
.sort((a, b) => a.localeCompare(b)), |
||||||
|
) |
||||||
|
|
||||||
|
const eliminated = await page.$$eval('a:not(.classical)', (nodes) => |
||||||
|
nodes |
||||||
|
.filter((node) => node.style.opacity === '0.2') |
||||||
|
.map((node) => node.id) |
||||||
|
.sort((a, b) => a.localeCompare(b)), |
||||||
|
) |
||||||
|
|
||||||
|
deepStrictEqual(`classical: ${selected}`, `classical: ${classical}`) |
||||||
|
deepStrictEqual( |
||||||
|
`not classical: ${eliminated}`, |
||||||
|
`not classical: ${notClassical}`, |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
checkClassical() |
||||||
|
await page.waitFor(1000) |
||||||
|
|
||||||
|
// get active
|
||||||
|
const active = people |
||||||
|
.filter((p) => p.classe === 'classical' && p.active) |
||||||
|
.map((e) => e.id) |
||||||
|
.sort((a, b) => a.localeCompare(b)) |
||||||
|
|
||||||
|
const notActive = people |
||||||
|
.filter( |
||||||
|
(p) => p.tag === 'a' && p.classe === 'classical' && p.active === false, |
||||||
|
) |
||||||
|
.map((e) => e.id) |
||||||
|
.sort((a, b) => a.localeCompare(b)) |
||||||
|
|
||||||
|
const checkActive = async () => { |
||||||
|
const btnActive = await page.$(`#btnActive`) |
||||||
|
btnActive.click() |
||||||
|
await page.waitFor(500) |
||||||
|
|
||||||
|
const selected = await page.$$eval('.classical.active', (nodes) => |
||||||
|
nodes |
||||||
|
.filter((node) => node.textContent === 'Active') |
||||||
|
.map((node) => node.id) |
||||||
|
.sort((a, b) => a.localeCompare(b)), |
||||||
|
) |
||||||
|
|
||||||
|
const eliminated = await page.$$eval('.classical:not(.active)', (nodes) => |
||||||
|
nodes |
||||||
|
.filter((node) => node.style.opacity === '0.2') |
||||||
|
.map((node) => node.id) |
||||||
|
.sort((a, b) => a.localeCompare(b)), |
||||||
|
) |
||||||
|
|
||||||
|
deepStrictEqual(`active: ${selected}`, `active: ${active}`) |
||||||
|
deepStrictEqual(`not active: ${eliminated}`, `not active: ${notActive}`) |
||||||
|
} |
||||||
|
|
||||||
|
checkActive() |
||||||
|
await page.waitFor(1000) |
||||||
|
|
||||||
|
// get bonanno
|
||||||
|
const bonanno = people.find((p) => p.id === 'BonannoPisano').id |
||||||
|
|
||||||
|
const notBonanno = people |
||||||
|
.filter( |
||||||
|
(p) => |
||||||
|
p.tag === 'a' && |
||||||
|
p.classe === 'classical' && |
||||||
|
p.active && |
||||||
|
p.id !== 'BonannoPisano', |
||||||
|
) |
||||||
|
.map((e) => e.id) |
||||||
|
.sort((a, b) => a.localeCompare(b)) |
||||||
|
|
||||||
|
const checkBonanno = async () => { |
||||||
|
const btnBonanno = await page.$(`#btnBonanno`) |
||||||
|
btnBonanno.click() |
||||||
|
await page.waitFor(500) |
||||||
|
|
||||||
|
const selected = await page.$eval('#BonannoPisano', (node) => { |
||||||
|
if (node.textContent === 'Bonanno Pisano') return node.id |
||||||
|
}) |
||||||
|
|
||||||
|
const eliminated = await page.$$eval( |
||||||
|
'a.classical.active:not(#BonannoPisano)', |
||||||
|
(nodes) => |
||||||
|
nodes |
||||||
|
.filter((node) => node.style.opacity === '0.2') |
||||||
|
.map((node) => node.id) |
||||||
|
.sort((a, b) => a.localeCompare(b)), |
||||||
|
) |
||||||
|
|
||||||
|
deepStrictEqual(`bonanno: ${selected}`, `bonanno: ${bonanno}`) |
||||||
|
deepStrictEqual(`not bonanno: ${eliminated}`, `not bonanno: ${notBonanno}`) |
||||||
|
} |
||||||
|
|
||||||
|
checkBonanno() |
@ -0,0 +1,112 @@ |
|||||||
|
import { gossips as archived } from './data.js' |
||||||
|
|
||||||
|
const body = document.querySelector('body') |
||||||
|
|
||||||
|
const ranges = document.createElement('div') |
||||||
|
ranges.className = 'ranges' |
||||||
|
body.append(ranges) |
||||||
|
|
||||||
|
const inputs = [ |
||||||
|
{ props: ['width'], min: 200, max: 800, value: 250 }, |
||||||
|
{ props: ['fontSize', 'lineHeight'], min: 20, max: 40, value: 25 }, |
||||||
|
{ props: ['background'], min: 20, max: 75, value: 60 }, |
||||||
|
] |
||||||
|
|
||||||
|
let gossips = new Proxy(archived, { |
||||||
|
set: (target, prop, value) => { |
||||||
|
target[prop] = value |
||||||
|
createGossip(value, true) |
||||||
|
return true |
||||||
|
}, |
||||||
|
}) |
||||||
|
|
||||||
|
export const grid = () => { |
||||||
|
inputs.forEach((input) => createInput(input)) |
||||||
|
createAddGossip() |
||||||
|
gossips.forEach((g) => createGossip(g)) |
||||||
|
} |
||||||
|
|
||||||
|
const createGossip = (g, isNew = false) => { |
||||||
|
const gossip = document.createElement('div') |
||||||
|
const addGossip = document.getElementById('add-gossip') |
||||||
|
const { fontSize, lineHeight, width, background } = addGossip.style |
||||||
|
gossip.className = 'gossip' |
||||||
|
gossip.textContent = g |
||||||
|
if (isNew) { |
||||||
|
gossip.style.fontSize = fontSize |
||||||
|
gossip.style.lineHeight = lineHeight |
||||||
|
gossip.style.width = width |
||||||
|
gossip.style.background = background |
||||||
|
gossip.classList.add('fade-in') |
||||||
|
body.insertBefore(gossip, addGossip.nextElementSibling) |
||||||
|
} else { |
||||||
|
body.append(gossip) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const createAddGossip = () => { |
||||||
|
const addGossip = document.createElement('div') |
||||||
|
addGossip.className = 'gossip' |
||||||
|
addGossip.id = 'add-gossip' |
||||||
|
|
||||||
|
const newInput = document.createElement('textarea') |
||||||
|
newInput.autofocus = true |
||||||
|
newInput.placeholder = 'Got a gossip to share ?' |
||||||
|
newInput.addEventListener('keyup', (e) => addNewGossip(newInput, e)) |
||||||
|
|
||||||
|
const button = document.createElement('div') |
||||||
|
button.className = 'button' |
||||||
|
button.textContent = 'Share gossip!' |
||||||
|
button.addEventListener('click', (e) => addNewGossip(newInput)) |
||||||
|
|
||||||
|
addGossip.append(newInput, button) |
||||||
|
body.append(addGossip) |
||||||
|
} |
||||||
|
|
||||||
|
const addNewGossip = (input, event) => { |
||||||
|
const noValue = !input.value |
||||||
|
const notEnterKey = event && event.keyCode !== 13 |
||||||
|
if (notEnterKey || noValue) { |
||||||
|
input.focus() |
||||||
|
return |
||||||
|
} |
||||||
|
gossips[gossips.length] = input.value |
||||||
|
input.value = '' |
||||||
|
input.focus() |
||||||
|
} |
||||||
|
|
||||||
|
const createInput = ({ props, min, max, value }) => { |
||||||
|
const range = document.createElement('div') |
||||||
|
range.className = 'range' |
||||||
|
|
||||||
|
const input = document.createElement('input') |
||||||
|
input.type = 'range' |
||||||
|
input.min = min |
||||||
|
input.max = max |
||||||
|
input.value = value |
||||||
|
input.addEventListener('input', (e) => customize(e, ...props)) |
||||||
|
|
||||||
|
const propLabel = document.createElement('label') |
||||||
|
propLabel.textContent = props[0] |
||||||
|
|
||||||
|
const valueLabel = document.createElement('span') |
||||||
|
valueLabel.textContent = value |
||||||
|
|
||||||
|
range.append(propLabel, input, valueLabel) |
||||||
|
ranges.append(range) |
||||||
|
} |
||||||
|
|
||||||
|
const customize = ({ target }, ...props) => { |
||||||
|
const gossips = [...document.querySelectorAll('.gossip')] |
||||||
|
gossips.forEach((gossip) => { |
||||||
|
props.forEach((prop) => { |
||||||
|
const updatedValue = |
||||||
|
(prop === 'lineHeight' && `${Number(target.value) * 1.5}px`) || |
||||||
|
(prop === 'background' && `hsl(280, 50%, ${target.value}%)`) || |
||||||
|
`${target.value}px` |
||||||
|
gossip.style[prop] = updatedValue |
||||||
|
}) |
||||||
|
}) |
||||||
|
const valueLabel = target.nextElementSibling |
||||||
|
valueLabel.textContent = target.value |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
const body = document.querySelector('body') |
||||||
|
|
||||||
|
const shapes = [...Array(100).keys()] |
||||||
|
|
||||||
|
const random = (min, max) => { |
||||||
|
min = Math.ceil(min) |
||||||
|
max = Math.floor(max) |
||||||
|
return Math.floor(Math.random() * (max - min + 1)) + min |
||||||
|
} |
||||||
|
|
||||||
|
const alphabet = 'abcdefghijklmnopqrstuvwxyz' |
||||||
|
|
||||||
|
export const generateLetters = () => { |
||||||
|
shapes.forEach((c) => { |
||||||
|
const shape = document.createElement('div') |
||||||
|
const third = shapes.length / 3 |
||||||
|
const firstThird = c < third |
||||||
|
const secondThird = c > third && c < third * 2 |
||||||
|
|
||||||
|
shape.textContent = alphabet[random(0, alphabet.length - 1)] |
||||||
|
shape.style.fontSize = `${c + 10 * 2}px` |
||||||
|
shape.style.fontWeight = (firstThird && 300) || (secondThird && 400) || 600 |
||||||
|
body.append(shape) |
||||||
|
}) |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
const body = document.querySelector('body') |
||||||
|
|
||||||
|
export const compose = () => { |
||||||
|
document.addEventListener('keydown', (e) => handleKey(e)) |
||||||
|
setTimeout( |
||||||
|
() => document.removeEventListener('keydown', (e) => handleKey(e)), |
||||||
|
500, |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
const handleKey = (e) => { |
||||||
|
const notes = [...document.querySelectorAll('.note')] |
||||||
|
|
||||||
|
if (e.key === 'Backspace') { |
||||||
|
const last = notes[notes.length - 1] |
||||||
|
last && last.remove() |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if (e.key === 'Escape') { |
||||||
|
if (notes.length) { |
||||||
|
notes.forEach((note) => note.remove()) |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
createNote(e) |
||||||
|
} |
||||||
|
|
||||||
|
const createNote = ({ key }) => { |
||||||
|
const number = key.charCodeAt(0) * 2 - 150 |
||||||
|
const note = document.createElement('div') |
||||||
|
note.className = 'note' |
||||||
|
note.textContent = key |
||||||
|
note.style.background = `hsl(270, ${number}%, ${number}%)` |
||||||
|
body.append(note) |
||||||
|
} |
@ -0,0 +1,66 @@ |
|||||||
|
const body = document.querySelector('body') |
||||||
|
|
||||||
|
const box = document.createElement('div') |
||||||
|
box.className = 'box' |
||||||
|
body.append(box) |
||||||
|
|
||||||
|
const { top, bottom, left, right } = box.getBoundingClientRect() |
||||||
|
|
||||||
|
const diameter = 50 |
||||||
|
const radius = diameter / 2 |
||||||
|
|
||||||
|
const insideX = (clientX) => clientX > left + radius && clientX < right - radius |
||||||
|
const insideY = (clientY) => clientY > top + radius && clientY < bottom - radius |
||||||
|
|
||||||
|
let isInside = false |
||||||
|
|
||||||
|
export const createCircle = () => { |
||||||
|
document.addEventListener('click', (e) => create(e)) |
||||||
|
setTimeout(() => document.removeEventListener('click', create), 500) |
||||||
|
} |
||||||
|
|
||||||
|
const create = ({ clientX, clientY }) => { |
||||||
|
const elem = document.createElement('div') |
||||||
|
elem.className = 'elem' |
||||||
|
body.append(elem) |
||||||
|
elem.style.top = `${clientY - radius}px` |
||||||
|
elem.style.left = `${clientX - radius}px` |
||||||
|
const hasEntered = insideX(clientX) && insideY(clientY) |
||||||
|
if (hasEntered) { |
||||||
|
elem.style.background = 'var(--purple)' |
||||||
|
} |
||||||
|
isInside = false |
||||||
|
} |
||||||
|
|
||||||
|
export const moveCircle = () => { |
||||||
|
document.addEventListener('mousemove', (e) => move(e)) |
||||||
|
setTimeout(() => document.removeEventListener('mousemove', move), 500) |
||||||
|
} |
||||||
|
|
||||||
|
const move = (e) => { |
||||||
|
const elems = [...document.getElementsByClassName('elem')] |
||||||
|
const elem = elems[elems.length - 1] |
||||||
|
if (!elem) return |
||||||
|
position(e, elem) |
||||||
|
} |
||||||
|
|
||||||
|
const position = ({ clientX, clientY }, elem) => { |
||||||
|
const hasEntered = insideX(clientX) && insideY(clientY) |
||||||
|
|
||||||
|
if (hasEntered) { |
||||||
|
isInside = true |
||||||
|
elem.style.background = 'var(--purple)' |
||||||
|
} |
||||||
|
|
||||||
|
if (isInside) { |
||||||
|
if (insideY(clientY)) { |
||||||
|
elem.style.top = `${clientY - radius}px` |
||||||
|
} |
||||||
|
if (insideX(clientX)) { |
||||||
|
elem.style.left = `${clientX - radius}px` |
||||||
|
} |
||||||
|
} else { |
||||||
|
elem.style.top = `${clientY - radius}px` |
||||||
|
elem.style.left = `${clientX - radius}px` |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
{ |
||||||
|
"type": "module", |
||||||
|
"scripts": { |
||||||
|
"test": "node --harmony-top-level-await pimp-my-style/test.js" |
||||||
|
}, |
||||||
|
"dependencies": { |
||||||
|
"puppeteer-core": "^3.3.0" |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,105 @@ |
|||||||
|
const body = document.querySelector('body') |
||||||
|
|
||||||
|
const count = document.createElement('div') |
||||||
|
count.className = 'count' |
||||||
|
count.textContent = 'hsl(0, 50%, 0%)' |
||||||
|
|
||||||
|
const hueText = document.createElement('div') |
||||||
|
hueText.className = 'text hue' |
||||||
|
hueText.textContent = 'hue' |
||||||
|
|
||||||
|
const luminosityText = document.createElement('div') |
||||||
|
luminosityText.className = 'text luminosity' |
||||||
|
luminosityText.textContent = 'luminosity' |
||||||
|
|
||||||
|
const origin = document.createElement('div') |
||||||
|
origin.className = 'text origin' |
||||||
|
|
||||||
|
const picked = document.createElement('div') |
||||||
|
picked.className = 'text picked' |
||||||
|
picked.textContent = 'Color successfully picked!' |
||||||
|
|
||||||
|
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') |
||||||
|
svg.setAttributeNS( |
||||||
|
'http://www.w3.org/2000/xmlns/', |
||||||
|
'xmlns:xlink', |
||||||
|
'http://www.w3.org/1999/xlink', |
||||||
|
) |
||||||
|
svg.setAttribute('width', window.innerWidth) |
||||||
|
svg.setAttribute('height', window.innerHeight) |
||||||
|
svg.setAttribute('viewBox', `0 0 ${window.innerWidth} ${window.innerHeight}`) |
||||||
|
|
||||||
|
const axisX = document.createElementNS('http://www.w3.org/2000/svg', 'line') |
||||||
|
axisX.setAttribute('y1', window.innerHeight) |
||||||
|
axisX.setAttribute('y2', 0) |
||||||
|
svg.append(axisX) |
||||||
|
|
||||||
|
const axisY = document.createElementNS('http://www.w3.org/2000/svg', 'line') |
||||||
|
axisY.setAttribute('x1', window.innerWidth) |
||||||
|
axisY.setAttribute('x2', 0) |
||||||
|
svg.append(axisY) |
||||||
|
|
||||||
|
body.append(count, hueText, luminosityText, origin, picked, svg) |
||||||
|
|
||||||
|
export const pick = () => { |
||||||
|
document.addEventListener('mousemove', (e) => set(e)) |
||||||
|
setTimeout( |
||||||
|
() => document.removeEventListener('mousemove', (e) => set(e)), |
||||||
|
500, |
||||||
|
) |
||||||
|
|
||||||
|
body.addEventListener('click', click) |
||||||
|
setTimeout(() => document.removeEventListener('click', click), 500) |
||||||
|
|
||||||
|
body.addEventListener('copy', copy) |
||||||
|
setTimeout(() => document.removeEventListener('copy', copy), 500) |
||||||
|
} |
||||||
|
|
||||||
|
const click = (e) => { |
||||||
|
document.execCommand('copy') |
||||||
|
const wave = document.createElement('div') |
||||||
|
wave.className = 'wave' |
||||||
|
wave.style.top = `${e.clientY - 10}px` |
||||||
|
wave.style.left = `${e.clientX - 10}px` |
||||||
|
body.append(wave) |
||||||
|
setTimeout(() => wave.remove(), 150) |
||||||
|
} |
||||||
|
|
||||||
|
const copy = (event) => { |
||||||
|
event.preventDefault() |
||||||
|
if (event.clipboardData) { |
||||||
|
event.clipboardData.setData('text/plain', count.textContent) |
||||||
|
picked.classList.add('fade-in') |
||||||
|
setTimeout(() => picked.classList.remove('fade-in'), 1000) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const calc = (number, max) => |
||||||
|
Math.round(Math.min(max, Math.max(0, max * number))) |
||||||
|
|
||||||
|
const set = ({ clientX, clientY }) => { |
||||||
|
const { innerWidth, innerHeight } = window |
||||||
|
const padding = 100 |
||||||
|
const mouseX = clientX - padding |
||||||
|
const mouseY = clientY - padding |
||||||
|
|
||||||
|
const hue = calc(mouseX / (innerWidth - padding * 2), 360) |
||||||
|
const luminosity = calc(mouseY / (innerHeight - padding * 2), 100) |
||||||
|
const color = `hsl(${hue}, 50%, ${luminosity}%)` |
||||||
|
|
||||||
|
axisX.setAttribute('x1', clientX) |
||||||
|
axisX.setAttribute('x2', clientX) |
||||||
|
axisY.setAttribute('y1', clientY) |
||||||
|
axisY.setAttribute('y2', clientY) |
||||||
|
|
||||||
|
axisX.setAttribute('stroke', color) |
||||||
|
axisY.setAttribute('stroke', color) |
||||||
|
|
||||||
|
body.style.color = color |
||||||
|
body.style.background = color |
||||||
|
origin.style.background = color |
||||||
|
count.textContent = color |
||||||
|
|
||||||
|
hueText.textContent = `hue\n${hue}` |
||||||
|
luminosityText.textContent = `${luminosity}\nluminosity` |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
import { styles } from './data.js' |
||||||
|
|
||||||
|
let count = 0 |
||||||
|
let increment = 1 |
||||||
|
|
||||||
|
export const pimp = () => { |
||||||
|
const button = document.querySelector('.button') |
||||||
|
|
||||||
|
const ceiling = count === styles.length - 1 |
||||||
|
const floor = !count |
||||||
|
|
||||||
|
const increasing = increment > 0 |
||||||
|
|
||||||
|
if (increasing || (floor && !increment)) { |
||||||
|
button.classList.add(styles[count]) |
||||||
|
} else { |
||||||
|
button.classList.remove(styles[count]) |
||||||
|
} |
||||||
|
|
||||||
|
if (ceiling) { |
||||||
|
increment = increment ? 0 : -1 |
||||||
|
} |
||||||
|
if (floor) { |
||||||
|
increment = increment < 0 ? 0 : 1 |
||||||
|
} |
||||||
|
|
||||||
|
button.classList.toggle('unpimp', increment < 0 || (!increment && ceiling)) |
||||||
|
|
||||||
|
count += increment |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
import styles from '../assets/data/pimp-my-style.js' |
||||||
|
|
||||||
|
export const tests = [] |
||||||
|
|
||||||
|
const formatClass = (limit, unpimp) => |
||||||
|
['button', ...styles.slice(0, limit), unpimp && 'unpimp'].filter(Boolean) |
||||||
|
|
||||||
|
const max = styles.length - 1 |
||||||
|
|
||||||
|
export const setup = async ({ page }) => { |
||||||
|
const btn = await page.$('.button') |
||||||
|
|
||||||
|
return { |
||||||
|
btn, |
||||||
|
getClass: async () => |
||||||
|
(await (await btn.getProperty('className')).jsonValue()).split(' '), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
tests.push(async ({ page, eq, btn, getClass }) => { |
||||||
|
// pimp
|
||||||
|
for (const i of styles.keys()) { |
||||||
|
console.log('pimp click', i + 1) |
||||||
|
await btn.click() |
||||||
|
eq(formatClass(i + 1, i === max), await getClass()) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
tests.push(async ({ page, eq, btn, getClass }) => { |
||||||
|
// unpimp !
|
||||||
|
for (const i of styles.keys()) { |
||||||
|
console.log('unpimp click', i + 1) |
||||||
|
await btn.click() |
||||||
|
eq(formatClass(max - i, i !== max), await getClass()) |
||||||
|
} |
||||||
|
}) |
@ -0,0 +1,60 @@ |
|||||||
|
import http from 'http' |
||||||
|
import fs from 'fs' |
||||||
|
import path from 'path' |
||||||
|
import { deepStrictEqual } from 'assert' |
||||||
|
import puppeteer from 'puppeteer-core' |
||||||
|
|
||||||
|
const exercise = 'pimp-my-style' |
||||||
|
const PORT = 9898 |
||||||
|
const config = { |
||||||
|
headless: false, |
||||||
|
executablePath: |
||||||
|
'/usr/bin/google-chrome', |
||||||
|
// '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
||||||
|
} |
||||||
|
|
||||||
|
const mediaTypes = { |
||||||
|
jpg: 'image/jpeg', |
||||||
|
png: 'image/png', |
||||||
|
html: 'text/html', |
||||||
|
css: 'text/css', |
||||||
|
js: 'application/javascript', |
||||||
|
json: 'application/json', |
||||||
|
} |
||||||
|
|
||||||
|
const server = http.createServer(({ url, method }, response) => { |
||||||
|
console.log(method + ' ' + url) |
||||||
|
const filepath = path.join('.', url) |
||||||
|
const ext = path.extname(filepath) |
||||||
|
response.setHeader('Content-Type', mediaTypes[ext.slice(1)] || 'text/plain') |
||||||
|
|
||||||
|
const stream = fs.createReadStream(filepath) |
||||||
|
.pipe(response) |
||||||
|
.once('error', err => { |
||||||
|
console.log(err) |
||||||
|
response.statusCode = 500 // handle 404 ?
|
||||||
|
response.end('oopsie') |
||||||
|
}) |
||||||
|
}).listen(PORT, async (err) => { |
||||||
|
err && (console.error(err.stack) || process.exit(1)) |
||||||
|
const { setup, tests } = await import(`./${exercise}/test.js`) |
||||||
|
const browser = await puppeteer.launch(config) |
||||||
|
const [page] = await browser.pages() |
||||||
|
await page.goto(`http://localhost:${PORT}/${exercise}/index.html`) |
||||||
|
const context = await setup({ page }) |
||||||
|
let code = 0 |
||||||
|
for (const [n, test] of tests.entries()) { |
||||||
|
try { |
||||||
|
await test({ page, eq: deepStrictEqual, ...context }) |
||||||
|
} catch (err) { |
||||||
|
code = 1 |
||||||
|
console.log(`test #${n} failed:`) |
||||||
|
console.log(test.toString()) |
||||||
|
console.log(err.stack) |
||||||
|
} |
||||||
|
} |
||||||
|
server.close() |
||||||
|
await browser.close() |
||||||
|
process.exit(code) |
||||||
|
}) |
||||||
|
|
@ -0,0 +1,72 @@ |
|||||||
|
import { places } from './data.js' |
||||||
|
|
||||||
|
const body = document.querySelector('body') |
||||||
|
|
||||||
|
export const scroll = () => { |
||||||
|
createSections() |
||||||
|
|
||||||
|
const location = document.createElement('div') |
||||||
|
location.className = 'location' |
||||||
|
setLocation(location) |
||||||
|
|
||||||
|
const direction = document.createElement('div') |
||||||
|
direction.className = 'direction' |
||||||
|
|
||||||
|
body.append(location, direction) |
||||||
|
|
||||||
|
document.addEventListener('wheel', (event) => |
||||||
|
setLocation(location, direction, event), |
||||||
|
) |
||||||
|
setTimeout(() => |
||||||
|
document.removeEventListener('wheel', (event) => |
||||||
|
setLocation(location, direction, event), |
||||||
|
), |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
const createSections = () => { |
||||||
|
const sorted = places.sort( |
||||||
|
(a, b) => getDegree(b.coordinates) - getDegree(a.coordinates), |
||||||
|
) |
||||||
|
|
||||||
|
sorted.map(({ name, color }) => { |
||||||
|
const nameDashCase = name |
||||||
|
.toLowerCase() |
||||||
|
.split(',')[0] |
||||||
|
.split(' ') |
||||||
|
.join('-') |
||||||
|
|
||||||
|
const url = `https://raw.githubusercontent.com/MarieMalarme/dom-js/master/assets/images/${nameDashCase}.jpg` |
||||||
|
|
||||||
|
const section = document.createElement('section') |
||||||
|
section.style.background = `center / cover url(${url})` |
||||||
|
body.append(section) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
const getDegree = (coordinates) => { |
||||||
|
const north = coordinates.includes('N') |
||||||
|
const degree = coordinates.split("'")[0].replace('°', '.') |
||||||
|
return north ? degree : -degree |
||||||
|
} |
||||||
|
|
||||||
|
const setLocation = (location, direction, event) => { |
||||||
|
const { name, coordinates, color } = getLocation() |
||||||
|
location.textContent = `${name}\n${coordinates}` |
||||||
|
location.style.color = color |
||||||
|
location.onclick = () => { |
||||||
|
window.open(`https://www.google.com/maps/place/${coordinates}`, '_blank') |
||||||
|
} |
||||||
|
|
||||||
|
if (!event) return |
||||||
|
const scrollUp = event.deltaY < 0 |
||||||
|
direction.innerHTML = `<div style="transform: rotate(${ |
||||||
|
scrollUp ? -90 : 90 |
||||||
|
}deg)">➢</div><div>${scrollUp ? 'N' : 'S'}</div>` |
||||||
|
} |
||||||
|
|
||||||
|
const getLocation = () => { |
||||||
|
const { innerHeight, scrollY } = window |
||||||
|
const index = Math.ceil((scrollY - innerHeight / 2) / innerHeight) |
||||||
|
return places[index] |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
## Build brick and break |
||||||
|
|
||||||
|
### Instructions |
||||||
|
|
||||||
|
Today, your mission is to build a 3-column brick tower, maintain it and finally break it! |
||||||
|
|
||||||
|
Create a function `build` which will create and display the given amount of bricks passed as argument: |
||||||
|
|
||||||
|
- each brick has to be created and added to the page at a regular interval of time (at least 50ms), |
||||||
|
- each brick will receive a unique `id` property, like following: |
||||||
|
|
||||||
|
```html |
||||||
|
<div id="brick-1"></div> |
||||||
|
``` |
||||||
|
|
||||||
|
- each brick in the middle column has to be set with the custom attribute `foundation` receiving the value `true` |
||||||
|
|
||||||
|
Each one of the two emojis in the top-right corner fires a function on click: |
||||||
|
|
||||||
|
- 🔨 triggers the function `repair` |
||||||
|
- 🧨 triggers the function `destroy` |
||||||
|
|
||||||
|
Write the body of the `repair` function, which receives any number of `ids`, and for each `id`, retrieves the HTML element and set a custom attribute `repaired` set to `in progress` if it is a brick situated in the middle column, and `true` if not. |
||||||
|
|
||||||
|
Write the body of the `destroy` function, which removes the current last brick in the tower. |
||||||
|
|
||||||
|
### Notions |
||||||
|
|
||||||
|
- [`createElement()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement) |
||||||
|
- [`append()`](https://developer.mozilla.org/fr/docs/Web/API/ParentNode/append) |
||||||
|
- [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) |
||||||
|
- [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval) / [`clearInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/clearInterval) |
||||||
|
- [`hasAttribute()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/hasAttribute) |
||||||
|
- [dataset](https://developer.mozilla.org/en-US/docs/Web/API/HTMLOrForeignElement/dataset) |
||||||
|
- [`remove()`](https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove) |
||||||
|
|
||||||
|
### Provided files |
||||||
|
|
||||||
|
- Use this CSS file: [style.css](./style.css) |
||||||
|
|
||||||
|
### Expected result |
||||||
|
|
||||||
|
You can see an example of the expected result [here](https://youtu.be/OjSP_7u9CZ4) |
@ -0,0 +1,104 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title>Build brick and break</title> |
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
||||||
|
<link id="fav" rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;,"> |
||||||
|
<link rel="stylesheet" type="text/css" href="./style.css"> |
||||||
|
<style type="text/css"> |
||||||
|
:root { |
||||||
|
--background: hsl(0, 0%, 12%); |
||||||
|
--text: hsl(0, 0%, 80%); |
||||||
|
--clear: hsl(0, 0%, 65%); |
||||||
|
--disabled: hsl(0, 0%, 35%); |
||||||
|
--purple: #bb73e6; |
||||||
|
} |
||||||
|
|
||||||
|
* { |
||||||
|
margin: 0; |
||||||
|
padding: 0; |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
font-family: sans-serif; |
||||||
|
letter-spacing: 1.5px; |
||||||
|
background: var(--background); |
||||||
|
display: flex; |
||||||
|
flex-wrap: wrap; |
||||||
|
align-content: flex-end; |
||||||
|
height: 100vh; |
||||||
|
color: var(--text); |
||||||
|
padding: 10vh 36.5vw; |
||||||
|
} |
||||||
|
|
||||||
|
[title='brick'] { |
||||||
|
text-align: center; |
||||||
|
font-size: 10px; |
||||||
|
width: 9vw; |
||||||
|
display: inline-flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
height: 4.44vh; |
||||||
|
background: linear-gradient(-25deg, var(--clear) 30%, var(--disabled) 90%); |
||||||
|
} |
||||||
|
|
||||||
|
#tools { |
||||||
|
position: fixed; |
||||||
|
right: 100px; |
||||||
|
font-size: 80px; |
||||||
|
cursor: pointer; |
||||||
|
user-select: none; |
||||||
|
} |
||||||
|
|
||||||
|
[data-repaired='true'] { |
||||||
|
color: hsl(275, 100%, 50%); |
||||||
|
} |
||||||
|
|
||||||
|
[data-repaired='true']:after { |
||||||
|
content: '-repaired'; |
||||||
|
} |
||||||
|
|
||||||
|
[data-repaired='in progress'] { |
||||||
|
color: black; |
||||||
|
} |
||||||
|
|
||||||
|
[data-repaired='in progress']:after { |
||||||
|
content: '-in progress'; |
||||||
|
} |
||||||
|
|
||||||
|
</style> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<script type="module"> |
||||||
|
import { build, repair, destroy } from './build-bricks-and-break.js' |
||||||
|
|
||||||
|
build() |
||||||
|
|
||||||
|
const body = document.querySelector('body') |
||||||
|
|
||||||
|
const tools = document.createElement('div') |
||||||
|
tools.id = 'tools' |
||||||
|
body.append(tools) |
||||||
|
|
||||||
|
const dynamite = document.createElement('div') |
||||||
|
dynamite.textContent = '🧨' |
||||||
|
dynamite.addEventListener('click', destroy) |
||||||
|
|
||||||
|
const hammer = document.createElement('div') |
||||||
|
hammer.textContent = '🔨' |
||||||
|
hammer.addEventListener('click', () => repair('brick-26', ...reparations)) |
||||||
|
|
||||||
|
tools.append(dynamite, hammer) |
||||||
|
|
||||||
|
const random = (min, max) => { |
||||||
|
min = Math.ceil(min) |
||||||
|
max = Math.floor(max) |
||||||
|
return Math.floor(Math.random() * (max - min + 1)) + min |
||||||
|
} |
||||||
|
|
||||||
|
const reparations = [...Array(15).keys()].map((e) => `brick-${random(0, 54)}`) |
||||||
|
|
||||||
|
</script> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,39 @@ |
|||||||
|
## Fifty shades of cold |
||||||
|
|
||||||
|
### Instructions |
||||||
|
|
||||||
|
You've been asked to freshen a webpage atmosphere by displaying shades of cold colors. |
||||||
|
|
||||||
|
Check the `colors` array provided in the data file below. |
||||||
|
|
||||||
|
- Create a `<style>` tag in the `<head>` tag and generate, for each color of `colors`, a class setting the `background` attribute and taking the color as value, like following: |
||||||
|
|
||||||
|
```css |
||||||
|
.indianred { |
||||||
|
background: indianred; |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
- Create a `<div>` for each color of the `colors` array whose name contains `aqua`, `blue`, `turquoise`, `green`, `cyan`, `navy` or `purple`.\ |
||||||
|
Each `<div>` must have the corresponding generated class and display the name of the color, like following: |
||||||
|
|
||||||
|
```html |
||||||
|
<div class="indianred">indianred</div> |
||||||
|
``` |
||||||
|
|
||||||
|
- The function `choseShade` is triggered when clicking on a `div`.\ |
||||||
|
Write the body of this function, which receives the shade of the clicked element as argument, and replaces all the other elements class by the chosen shade. |
||||||
|
|
||||||
|
### Notions |
||||||
|
|
||||||
|
- [`head`](https://developer.mozilla.org/en-US/docs/Web/API/Document/head) / [style tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style) |
||||||
|
- [`className`](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) |
||||||
|
- [`classList`](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList): `contains()`, `replace()` |
||||||
|
|
||||||
|
### Provided files |
||||||
|
|
||||||
|
- Import the `colors` from the data file: [data.js](./data.js) |
||||||
|
|
||||||
|
### Expected result |
||||||
|
|
||||||
|
You can see an example of the expected result [here](https://youtu.be/a-3JDEvW-Qg) |
@ -0,0 +1,143 @@ |
|||||||
|
export const colors = [ |
||||||
|
'indianred', |
||||||
|
'lightcoral', |
||||||
|
'salmon', |
||||||
|
'darksalmon', |
||||||
|
'lightsalmon', |
||||||
|
'crimson', |
||||||
|
'red', |
||||||
|
'firebrick', |
||||||
|
'darkred', |
||||||
|
'pink', |
||||||
|
'lightpink', |
||||||
|
'hotpink', |
||||||
|
'deeppink', |
||||||
|
'mediumvioletred', |
||||||
|
'palevioletred', |
||||||
|
'orange', |
||||||
|
'coral', |
||||||
|
'tomato', |
||||||
|
'orangered', |
||||||
|
'darkorange', |
||||||
|
'yellow', |
||||||
|
'gold', |
||||||
|
'lightyellow', |
||||||
|
'lemonchiffon', |
||||||
|
'lightgoldenrodyellow', |
||||||
|
'papayawhip', |
||||||
|
'moccasin', |
||||||
|
'peachpuff', |
||||||
|
'palegoldenrod', |
||||||
|
'khaki', |
||||||
|
'darkkhaki', |
||||||
|
'lavender', |
||||||
|
'thistle', |
||||||
|
'plum', |
||||||
|
'violet', |
||||||
|
'orchid', |
||||||
|
'fuchsia', |
||||||
|
'magenta', |
||||||
|
'mediumorchid', |
||||||
|
'mediumpurple', |
||||||
|
'rebeccapurple', |
||||||
|
'blueviolet', |
||||||
|
'darkviolet', |
||||||
|
'darkorchid', |
||||||
|
'darkmagenta', |
||||||
|
'purple', |
||||||
|
'indigo', |
||||||
|
'slateblue', |
||||||
|
'darkslateblue', |
||||||
|
'green', |
||||||
|
'greenyellow', |
||||||
|
'chartreuse', |
||||||
|
'lawngreen', |
||||||
|
'lime', |
||||||
|
'limegreen', |
||||||
|
'palegreen', |
||||||
|
'lightgreen', |
||||||
|
'mediumspringgreen', |
||||||
|
'springgreen', |
||||||
|
'mediumseagreen', |
||||||
|
'seagreen', |
||||||
|
'forestgreen', |
||||||
|
'darkgreen', |
||||||
|
'yellowgreen', |
||||||
|
'olivedrab', |
||||||
|
'olive', |
||||||
|
'darkolivegreen', |
||||||
|
'mediumaquamarine', |
||||||
|
'darkseagreen', |
||||||
|
'lightseagreen', |
||||||
|
'darkcyan', |
||||||
|
'teal', |
||||||
|
'aqua', |
||||||
|
'cyan', |
||||||
|
'lightcyan', |
||||||
|
'paleturquoise', |
||||||
|
'aquamarine', |
||||||
|
'turquoise', |
||||||
|
'mediumturquoise', |
||||||
|
'darkturquoise', |
||||||
|
'cadetblue', |
||||||
|
'steelblue', |
||||||
|
'lightsteelblue', |
||||||
|
'powderblue', |
||||||
|
'lightblue', |
||||||
|
'skyblue', |
||||||
|
'lightskyblue', |
||||||
|
'deepskyblue', |
||||||
|
'dodgerblue', |
||||||
|
'cornflowerblue', |
||||||
|
'mediumslateblue', |
||||||
|
'royalblue', |
||||||
|
'blue', |
||||||
|
'mediumblue', |
||||||
|
'darkblue', |
||||||
|
'navy', |
||||||
|
'midnightblue', |
||||||
|
'brown', |
||||||
|
'cornsilk', |
||||||
|
'blanchedalmond', |
||||||
|
'bisque', |
||||||
|
'navajowhite', |
||||||
|
'wheat', |
||||||
|
'burlywood', |
||||||
|
'tan', |
||||||
|
'rosybrown', |
||||||
|
'sandybrown', |
||||||
|
'goldenrod', |
||||||
|
'darkgoldenrod', |
||||||
|
'peru', |
||||||
|
'chocolate', |
||||||
|
'saddlebrown', |
||||||
|
'sienna', |
||||||
|
'maroon', |
||||||
|
'white', |
||||||
|
'snow', |
||||||
|
'honeydew', |
||||||
|
'mintcream', |
||||||
|
'azure', |
||||||
|
'aliceblue', |
||||||
|
'ghostwhite', |
||||||
|
'whitesmoke', |
||||||
|
'seashell', |
||||||
|
'beige', |
||||||
|
'oldlace', |
||||||
|
'floralwhite', |
||||||
|
'ivory', |
||||||
|
'antiquewhite', |
||||||
|
'linen', |
||||||
|
'lavenderblush', |
||||||
|
'mistyrose', |
||||||
|
'gainsboro', |
||||||
|
'lightgray', |
||||||
|
'silver', |
||||||
|
'darkgray', |
||||||
|
'gray', |
||||||
|
'dimgray', |
||||||
|
'lightslategray', |
||||||
|
'slategray', |
||||||
|
'darkslategray', |
||||||
|
'black', |
||||||
|
] |
@ -0,0 +1,58 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title>Fifty shades of cold</title> |
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
||||||
|
<link id="fav" rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;,"> |
||||||
|
<style> |
||||||
|
|
||||||
|
:root { |
||||||
|
--background: hsl(0, 0%, 12%); |
||||||
|
--text: hsl(0, 0%, 80%); |
||||||
|
--clear: hsl(0, 0%, 65%); |
||||||
|
--disabled: hsl(0, 0%, 35%); |
||||||
|
--purple: #bb73e6; |
||||||
|
} |
||||||
|
|
||||||
|
* { |
||||||
|
margin: 0; |
||||||
|
padding: 0; |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
font-family: sans-serif; |
||||||
|
letter-spacing: 1.5px; |
||||||
|
background: var(--background); |
||||||
|
display: flex; |
||||||
|
flex-wrap: wrap; |
||||||
|
justify-content: center; |
||||||
|
align-content: center; |
||||||
|
width: 100vw; |
||||||
|
padding: 5rem; |
||||||
|
font-size: 12px; |
||||||
|
} |
||||||
|
|
||||||
|
div { |
||||||
|
cursor: pointer; |
||||||
|
width: 150px; |
||||||
|
height: 150px; |
||||||
|
margin: 20px; |
||||||
|
padding: 5px 8px; |
||||||
|
} |
||||||
|
|
||||||
|
</style> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<script type="module"> |
||||||
|
|
||||||
|
import { choseShade } from './fifty-shades-of-cold.js' |
||||||
|
|
||||||
|
const divs = [...document.querySelectorAll('div')] |
||||||
|
divs.map((d) => { |
||||||
|
d.addEventListener('click', () => choseShade(d.textContent)) |
||||||
|
}) |
||||||
|
|
||||||
|
</script> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,30 @@ |
|||||||
|
## Get them all |
||||||
|
|
||||||
|
### Instructions |
||||||
|
|
||||||
|
You've been attributed the task to find the main architect of the Tower of Pisa before he achieves his plans, avoiding us nowadays all those lame pictures of people pretending to stop it from falling. |
||||||
|
|
||||||
|
You arrived at the architects' chamber to find him, but all you have in front of you is a bunch of unknown people.\ |
||||||
|
Step by step, with the little information you have, gather information and figure out by elimination who he is. |
||||||
|
|
||||||
|
On top of the webpage, each of the four buttons fires a function which has to return an array containing 2 entries: the targetted people, and the others eliminated at that step (the ones previously eliminated mustn't be included). |
||||||
|
|
||||||
|
- Write the body of the `getArchitects` function, which targets the architects, all corresponding to a `<a>` tag. |
||||||
|
|
||||||
|
- Write the body of the `getClassical` function, which targets the architects belonging to the `classical` class. |
||||||
|
|
||||||
|
- Write the body of the `getActive` function, which targets the classical architects who are `active` in their class. |
||||||
|
|
||||||
|
- Write the body of the `getBonannoPisano` function, which targets the architect you're looking for, whose `id` is `BonannoPisano`. |
||||||
|
|
||||||
|
### Notions |
||||||
|
|
||||||
|
- [`getElementsByTagName()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByTagName) |
||||||
|
- [`getElementsByClassName()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName) |
||||||
|
- [`getElementById()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById) |
||||||
|
- [`querySelectorAll()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) / [`querySelector()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector) |
||||||
|
|
||||||
|
### Provided files |
||||||
|
|
||||||
|
- Use this CSS file: [style.css](./style.css) |
||||||
|
- You can take a look at the data: [data.js](./data.js) |
@ -0,0 +1,221 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title>Get them all</title> |
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
||||||
|
<link id="fav" rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;,"> |
||||||
|
<style> |
||||||
|
|
||||||
|
:root { |
||||||
|
--background: hsl(0, 0%, 12%); |
||||||
|
--text: hsl(0, 0%, 80%); |
||||||
|
--clear: hsl(0, 0%, 65%); |
||||||
|
--disabled: hsl(0, 0%, 35%); |
||||||
|
--purple: #bb73e6; |
||||||
|
} |
||||||
|
|
||||||
|
* { |
||||||
|
margin: 0; |
||||||
|
padding: 0; |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
font-family: sans-serif; |
||||||
|
letter-spacing: 1.5px; |
||||||
|
background: var(--background); |
||||||
|
display: flex; |
||||||
|
flex-wrap: wrap; |
||||||
|
padding: 100px; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
font-size: 15px; |
||||||
|
margin-top: 150px; |
||||||
|
} |
||||||
|
|
||||||
|
#buttons { |
||||||
|
position: fixed; |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
width: 100%; |
||||||
|
top: 0; |
||||||
|
height: 150px; |
||||||
|
background: var(--background); |
||||||
|
box-shadow: 0 0 50px black; |
||||||
|
} |
||||||
|
|
||||||
|
#buttons * { |
||||||
|
margin: 0 20px; |
||||||
|
padding: 10px 20px; |
||||||
|
background: var(--clear); |
||||||
|
border-radius: 20px; |
||||||
|
cursor: pointer; |
||||||
|
user-select: none; |
||||||
|
width: 200px; |
||||||
|
text-align: center; |
||||||
|
} |
||||||
|
|
||||||
|
.disabled { |
||||||
|
pointer-events: none; |
||||||
|
opacity: 0.3; |
||||||
|
} |
||||||
|
|
||||||
|
a, |
||||||
|
span { |
||||||
|
min-width: 110px; |
||||||
|
min-height: 110px; |
||||||
|
width: 5vw; |
||||||
|
height: 5vw; |
||||||
|
border-radius: 50%; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
flex-direction: column; |
||||||
|
text-align: center; |
||||||
|
border: solid 1px var(--clear); |
||||||
|
line-height: 22px; |
||||||
|
padding: 10px; |
||||||
|
color: var(--clear); |
||||||
|
margin: 30px; |
||||||
|
} |
||||||
|
|
||||||
|
.found { |
||||||
|
box-shadow: 8px 8px 15px rgba(0, 0, 0, 0.6), |
||||||
|
-10px -10px 15px rgba(255, 255, 255, 0.074); |
||||||
|
border: none; |
||||||
|
background: var(--purple); |
||||||
|
color: var(--background); |
||||||
|
} |
||||||
|
|
||||||
|
</style> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<script type="module"> |
||||||
|
|
||||||
|
import { |
||||||
|
getBonannoPisano, |
||||||
|
getActive, |
||||||
|
getArchitects, |
||||||
|
getClassical, |
||||||
|
} from './get-them-all.js' |
||||||
|
|
||||||
|
const people = [ |
||||||
|
{ id: 'LolaDunam', tag: 'span', classe: 'modern', active: false }, |
||||||
|
{ id: 'LeeMarley', tag: 'span', classe: 'baroque', active: false }, |
||||||
|
{ id: 'JeanDujardin', tag: 'a', classe: 'classical', active: true }, |
||||||
|
{ id: 'MarloStanfield', tag: 'span', classe: 'modern', active: false }, |
||||||
|
{ id: 'GeorgesDrumond', tag: 'span', classe: 'baroque', active: true }, |
||||||
|
{ id: 'JuliaWhite', tag: 'span', classe: 'modern', active: true }, |
||||||
|
{ id: 'BarneyLeberre', tag: 'span', classe: 'modern', active: true }, |
||||||
|
{ id: 'DavidCarretta', tag: 'a', classe: 'classical', active: false }, |
||||||
|
{ id: 'AugustoCesar', tag: 'span', classe: 'modern', active: true }, |
||||||
|
{ id: 'DavidGuetta', tag: 'a', classe: 'modern', active: false }, |
||||||
|
{ id: 'MarlonBrando', tag: 'a', classe: 'classical', active: false }, |
||||||
|
{ id: 'BonannoPisano', tag: 'a', classe: 'classical', active: true }, |
||||||
|
{ id: 'AvonBarksdale', tag: 'span', classe: 'baroque', active: true }, |
||||||
|
{ id: 'BarackObama', tag: 'span', classe: 'baroque', active: false }, |
||||||
|
{ id: 'MarcDupont', tag: 'span', classe: 'modern', active: false }, |
||||||
|
{ id: 'BillieElliott', tag: 'a', classe: 'baroque', active: true }, |
||||||
|
{ id: 'MariaCallas', tag: 'a', classe: 'baroque', active: false }, |
||||||
|
{ id: 'SteveJobbs', tag: 'a', classe: 'classical', active: false }, |
||||||
|
{ id: 'JoeLee', tag: 'span', classe: 'baroque', active: false }, |
||||||
|
{ id: 'AnthonyGrant', tag: 'span', classe: 'baroque', active: false }, |
||||||
|
{ id: 'ShakimaGreggs', tag: 'a', classe: 'modern', active: true }, |
||||||
|
{ id: 'RoyDeere', tag: 'span', classe: 'baroque', active: true }, |
||||||
|
{ id: 'BobTurner', tag: 'a', classe: 'classical', active: true }, |
||||||
|
{ id: 'AngeloCapri', tag: 'span', classe: 'modern', active: false }, |
||||||
|
{ id: 'SamMcDonald', tag: 'span', classe: 'baroque', active: true }, |
||||||
|
{ id: 'FannyLelouche', tag: 'span', classe: 'baroque', active: true }, |
||||||
|
{ id: 'ClarkLoister', tag: 'a', classe: 'classical', active: false }, |
||||||
|
{ id: 'FinanObrien', tag: 'span', classe: 'modern', active: false }, |
||||||
|
{ id: 'ClariceSterling', tag: 'a', classe: 'modern', active: true }, |
||||||
|
{ id: 'JayHernan', tag: 'span', classe: 'baroque', active: true }, |
||||||
|
{ id: 'HelenMirren', tag: 'a', classe: 'classical', active: false }, |
||||||
|
{ id: 'SarahForestier', tag: 'a', classe: 'modern', active: false }, |
||||||
|
{ id: 'JacquesChirac', tag: 'a', classe: 'classical', active: true }, |
||||||
|
{ id: 'MartinWealer', tag: 'a', classe: 'baroque', active: true }, |
||||||
|
{ id: 'JodieFoster', tag: 'span', classe: 'baroque', active: true }, |
||||||
|
{ id: 'JeanJacques', tag: 'span', classe: 'modern', active: false }, |
||||||
|
{ id: 'MollyHeart', tag: 'a', classe: 'baroque', active: false }, |
||||||
|
{ id: 'FabioSalso', tag: 'a', classe: 'classical', active: true }, |
||||||
|
{ id: 'CarlosSanchez', tag: 'span', classe: 'baroque', active: true }, |
||||||
|
{ id: 'RussellBell', tag: 'a', classe: 'classical', active: false }, |
||||||
|
{ id: 'JackDoe', tag: 'span', classe: 'baroque', active: true }, |
||||||
|
{ id: 'EricCarver', tag: 'a', classe: 'classical', active: false }, |
||||||
|
{ id: 'LouisDeschamps', tag: 'span', classe: 'baroque', active: true }, |
||||||
|
{ id: 'HoracioCane', tag: 'a', classe: 'baroque', active: true }, |
||||||
|
{ id: 'HenryBright', tag: 'a', classe: 'baroque', active: true }, |
||||||
|
] |
||||||
|
|
||||||
|
const body = document.querySelector('body') |
||||||
|
|
||||||
|
const shuffle = array => { |
||||||
|
const test = array.length - 1 |
||||||
|
for (let i = test; i > 0; i--) { |
||||||
|
const j = Math.floor(Math.random() * i) |
||||||
|
const temp = array[i] |
||||||
|
array[i] = array[j] |
||||||
|
array[j] = temp |
||||||
|
} |
||||||
|
return array |
||||||
|
} |
||||||
|
|
||||||
|
shuffle(people).map(({ id, classe, address, plans, tag, active }) => { |
||||||
|
const people = document.createElement(tag) |
||||||
|
people.id = id |
||||||
|
people.textContent = 'Someone' |
||||||
|
people.className = `${classe} ${active ? 'active' : ''}` |
||||||
|
body.append(people) |
||||||
|
}) |
||||||
|
|
||||||
|
const buttonsContainer = document.createElement('div') |
||||||
|
buttonsContainer.id = 'buttons' |
||||||
|
body.append(buttonsContainer) |
||||||
|
|
||||||
|
const buttons = [ |
||||||
|
{ name: 'Architect', action: getArchitects }, |
||||||
|
{ name: 'Classical', action: getClassical }, |
||||||
|
{ name: 'Active', action: getActive }, |
||||||
|
{ name: 'Bonanno', action: getBonannoPisano }, |
||||||
|
] |
||||||
|
|
||||||
|
buttons.forEach(({ name, action }, i) => { |
||||||
|
const btn = document.createElement('div') |
||||||
|
btn.id = `btn${name}` |
||||||
|
btn.textContent = `Get ${name}${i === 0 ? 's' : ''}` |
||||||
|
|
||||||
|
if (i > 0) { |
||||||
|
btn.className = 'disabled' |
||||||
|
} |
||||||
|
|
||||||
|
btn.addEventListener('click', () => { |
||||||
|
const [targetted, others] = action() |
||||||
|
|
||||||
|
if (name === 'Bonanno') { |
||||||
|
targetted.textContent = targetted.id.replace('P', ' P') |
||||||
|
targetted.classList.add('found') |
||||||
|
} else { |
||||||
|
targetted.forEach(t => { |
||||||
|
t.textContent = name |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
others.forEach(o => { |
||||||
|
o.style.opacity = 0.2 |
||||||
|
}) |
||||||
|
|
||||||
|
btn.className = 'disabled' |
||||||
|
|
||||||
|
const last = i + 1 === buttons.length |
||||||
|
if (last) return |
||||||
|
const next = document.getElementById(`btn${buttons[i + 1].name}`) |
||||||
|
next.classList.remove('disabled') |
||||||
|
}) |
||||||
|
|
||||||
|
buttonsContainer.append(btn) |
||||||
|
}) |
||||||
|
|
||||||
|
</script> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,22 @@ |
|||||||
|
## Gossip grid |
||||||
|
|
||||||
|
### Instructions |
||||||
|
|
||||||
|
Good information is the pillar of society, that's why you've decided to dedicate your time to reveal the powerful truth to the world and deliver essential and strong news: you're launching a gossip grid. |
||||||
|
|
||||||
|
Create the function `grid` which displays all the `gossips`, provided in the data file below, as cards on a grid, and allows the user to: |
||||||
|
|
||||||
|
- add a new gossip to the list |
||||||
|
- customize the width, font size and background of each card with `range` inputs. |
||||||
|
|
||||||
|
### Notions |
||||||
|
|
||||||
|
- [Inputs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input): [`text`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/text), [`range`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range) |
||||||
|
|
||||||
|
### Provided files |
||||||
|
|
||||||
|
- Import the `gossips` from the data file: [data.js](./data.js) |
||||||
|
|
||||||
|
### Expected result |
||||||
|
|
||||||
|
You can see an example of the expected result [here](https://youtu.be/nbR2eHBqTxU) |
@ -0,0 +1,19 @@ |
|||||||
|
export const gossips = [ |
||||||
|
`Oasis star Noel Gallagher used to gorge on Greggs pastries and donuts every day`, |
||||||
|
`Lea Michele's lookalike Monica Moskatow says Glee star called her ugly`, |
||||||
|
`WE PAY FOR JUICY INFO!`, |
||||||
|
`Trainer to Hollywood's biggest stars reveals how to get an A-list body`, |
||||||
|
`Ed Sheeran comes out of music retirement to write brand new song`, |
||||||
|
`Kylie Jenner & Travis Scott’s breakup timeline`, |
||||||
|
`Quiet on the set: temper tantrums stars hope you forget`, |
||||||
|
`The style & grace of Chloë Grace Moretz: her top 20 red carpet looks`, |
||||||
|
`Paulina Porizkova feels betrayed after being cut out of husband Ric Ocasek's will`, |
||||||
|
`From too hot to not: Paris Hilton and Chris Zylka's relationship history`, |
||||||
|
`No bite in the big apple? Celine Dion looks scary skinny in New York`, |
||||||
|
`Jennifer Aniston and Brad Pitt relationship timeline`, |
||||||
|
`They shouldn’t have said that: 10 celebrity rants heard around the world`, |
||||||
|
`The most intense celebrity fights on set`, |
||||||
|
`The 18 most bitter real housewives feuds`, |
||||||
|
`Tristan Thompson's remarkable transformation from skinny teen to hulking NBA ace`, |
||||||
|
`Kim Kardashian 'considers leaving home' with Kanye West to 'save marriage'`, |
||||||
|
] |
@ -0,0 +1,135 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title>Gossip grid</title> |
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
||||||
|
<link id="fav" rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;,"> |
||||||
|
<style> |
||||||
|
|
||||||
|
:root { |
||||||
|
--background: hsl(0, 0%, 12%); |
||||||
|
--text: hsl(0, 0%, 80%); |
||||||
|
--clear: hsl(0, 0%, 65%); |
||||||
|
--disabled: hsl(0, 0%, 35%); |
||||||
|
--purple: #bb73e6; |
||||||
|
} |
||||||
|
|
||||||
|
* { |
||||||
|
margin: 0; |
||||||
|
padding: 0; |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
font-family: sans-serif; |
||||||
|
letter-spacing: 1.5px; |
||||||
|
background: var(--background); |
||||||
|
display: flex; |
||||||
|
flex-wrap: wrap; |
||||||
|
justify-content: center; |
||||||
|
width: 100vw; |
||||||
|
height: 100vh; |
||||||
|
padding: 10rem; |
||||||
|
align-items: flex-start; |
||||||
|
} |
||||||
|
|
||||||
|
.gossip { |
||||||
|
background: hsl(280, 50%, 50%); |
||||||
|
margin: 20px; |
||||||
|
width: 250px; |
||||||
|
padding: 20px 25px; |
||||||
|
line-height: 30px; |
||||||
|
font-size: 20px; |
||||||
|
word-break: break-word; |
||||||
|
color: white; |
||||||
|
display: flex; |
||||||
|
justify-content: space-between; |
||||||
|
flex-direction: column; |
||||||
|
} |
||||||
|
|
||||||
|
.gossip:first-letter { |
||||||
|
text-transform: uppercase; |
||||||
|
} |
||||||
|
|
||||||
|
.ranges { |
||||||
|
position: fixed; |
||||||
|
top: 5rem; |
||||||
|
display: flex; |
||||||
|
} |
||||||
|
|
||||||
|
.range { |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
color: var(--clear); |
||||||
|
font-family: monospace; |
||||||
|
} |
||||||
|
|
||||||
|
.range label, |
||||||
|
.range span { |
||||||
|
width: 100px; |
||||||
|
} |
||||||
|
|
||||||
|
.range label { |
||||||
|
text-align: right; |
||||||
|
} |
||||||
|
|
||||||
|
input { |
||||||
|
margin: 20px 25px; |
||||||
|
} |
||||||
|
|
||||||
|
textarea { |
||||||
|
height: 100%; |
||||||
|
width: 100%; |
||||||
|
max-width: 100%; |
||||||
|
outline: none; |
||||||
|
border: none; |
||||||
|
font-family: inherit; |
||||||
|
font-size: inherit; |
||||||
|
line-height: inherit; |
||||||
|
letter-spacing: inherit; |
||||||
|
color: inherit; |
||||||
|
background-color: transparent; |
||||||
|
padding: 0; |
||||||
|
margin: 0; |
||||||
|
resize: none; |
||||||
|
} |
||||||
|
|
||||||
|
textarea::placeholder { |
||||||
|
color: rgba(255, 255, 255, 0.5); |
||||||
|
} |
||||||
|
|
||||||
|
.button { |
||||||
|
margin-top: 20px; |
||||||
|
text-align: right; |
||||||
|
border: solid 1px white; |
||||||
|
align-self: flex-end; |
||||||
|
padding: 5px 15px; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.fade-in { |
||||||
|
animation: fade-in 0.75s; |
||||||
|
} |
||||||
|
|
||||||
|
@keyframes fade-in { |
||||||
|
from { |
||||||
|
opacity: 0%; |
||||||
|
} |
||||||
|
to { |
||||||
|
opacity: 100%; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
</style> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<script type="module"> |
||||||
|
|
||||||
|
import { grid } from './gossip-grid.js' |
||||||
|
|
||||||
|
grid() |
||||||
|
|
||||||
|
</script> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,17 @@ |
|||||||
|
## Harder, bigger, bolder, stronger |
||||||
|
|
||||||
|
### Instructions |
||||||
|
|
||||||
|
Being stuck at home, bored, desperate and coming up with a lot of weird ideas, a friend asks you to develop a tool to measure his ocular skills: one of those [Monoyer charts](https://en.wikipedia.org/wiki/Monoyer_chart) that ophthalmologists use. |
||||||
|
|
||||||
|
Generate a board where each new letter is harder, bigger, bolder and stronger! |
||||||
|
|
||||||
|
Create the function `generateLetters` which creates 100 `div`, each containing a letter randomly picked through the alphabet, and whose style properties have to be increased: |
||||||
|
|
||||||
|
- `font-size` has to grow from `20` to at least `100` pixels |
||||||
|
- `font-weigth` has to be `300` for the first third of the letters, `400` for the second third, and `600` for the last third |
||||||
|
|
||||||
|
### Notions |
||||||
|
|
||||||
|
- [`style`](https://developer.mozilla.org/en-US/docs/Web/API/ElementCSSInlineStyle/style) |
||||||
|
- [`textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent) |
@ -0,0 +1,57 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title>Harder, bigger, bolder, stronger</title> |
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
||||||
|
<link id="fav" rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;,"> |
||||||
|
<style> |
||||||
|
|
||||||
|
:root { |
||||||
|
--background: hsl(0, 0%, 12%); |
||||||
|
--text: hsl(0, 0%, 80%); |
||||||
|
--clear: hsl(0, 0%, 65%); |
||||||
|
--disabled: hsl(0, 0%, 35%); |
||||||
|
--purple: #bb73e6; |
||||||
|
} |
||||||
|
|
||||||
|
* { |
||||||
|
margin: 0; |
||||||
|
padding: 0; |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
font-family: sans-serif; |
||||||
|
letter-spacing: 1.5px; |
||||||
|
background: var(--background); |
||||||
|
display: flex; |
||||||
|
flex-wrap: wrap; |
||||||
|
justify-content: center; |
||||||
|
align-content: center; |
||||||
|
width: 100vw; |
||||||
|
padding: 5rem; |
||||||
|
font-size: 12px; |
||||||
|
} |
||||||
|
|
||||||
|
div { |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
margin: 5px; |
||||||
|
width: 200px; |
||||||
|
height: 200px; |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
</style> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<script type="module"> |
||||||
|
|
||||||
|
import { generateLetters } from './harder-bigger-bolder-stronger.js' |
||||||
|
|
||||||
|
generateLetters() |
||||||
|
|
||||||
|
</script> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,20 @@ |
|||||||
|
## Keycodes symphony |
||||||
|
|
||||||
|
### Instructions |
||||||
|
|
||||||
|
Like an inspired Beethoven who's going to write his Moonlight Sonata, you're about to compose a colourful symphony of letters with your keyboard. |
||||||
|
|
||||||
|
Export the function `compose`: |
||||||
|
|
||||||
|
- Make it fire every time a key is pressed |
||||||
|
- Create a new `note`, which has a background color generated using its `keyCode`, and displays the corresponding letter pressed |
||||||
|
- If the pressed key is the `Delete` one, delete the last note |
||||||
|
- If the pressed key is the `Escape` one, clear all the notes |
||||||
|
|
||||||
|
### Notions |
||||||
|
|
||||||
|
- [Keyboard event](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent): [`keydown`](https://developer.mozilla.org/en-US/docs/Web/API/Document/keydown_event), [`key`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) |
||||||
|
|
||||||
|
### Expected result |
||||||
|
|
||||||
|
You can see an example of the expected result [here](https://youtu.be/5DdijwBnpAk) |
@ -0,0 +1,57 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title>Keycodes symphony</title> |
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
||||||
|
<link id="fav" rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;,"> |
||||||
|
<style> |
||||||
|
|
||||||
|
:root { |
||||||
|
--background: hsl(0, 0%, 12%); |
||||||
|
--text: hsl(0, 0%, 80%); |
||||||
|
--clear: hsl(0, 0%, 65%); |
||||||
|
--disabled: hsl(0, 0%, 35%); |
||||||
|
--purple: #bb73e6; |
||||||
|
} |
||||||
|
|
||||||
|
* { |
||||||
|
margin: 0; |
||||||
|
padding: 0; |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
font-family: sans-serif; |
||||||
|
letter-spacing: 1.5px; |
||||||
|
background: var(--background); |
||||||
|
display: flex; |
||||||
|
flex-wrap: wrap; |
||||||
|
width: 100vw; |
||||||
|
height: 100vh; |
||||||
|
} |
||||||
|
|
||||||
|
.note { |
||||||
|
width: 100%; |
||||||
|
flex: 1 1 80px; |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
text-align: center; |
||||||
|
padding: 15px; |
||||||
|
font-size: 40px; |
||||||
|
color: white; |
||||||
|
transition: all 0.2s ease-in-out; |
||||||
|
} |
||||||
|
|
||||||
|
</style> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<script type="module"> |
||||||
|
|
||||||
|
import { compose } from './keycodes-symphony.js' |
||||||
|
|
||||||
|
compose() |
||||||
|
|
||||||
|
</script> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,22 @@ |
|||||||
|
## Mouse trap |
||||||
|
|
||||||
|
### Instructions |
||||||
|
|
||||||
|
Develop a trap to capture the elements when the mouse is getting too close to the center of the page! |
||||||
|
|
||||||
|
- Create a function `createCircle`: make it fire on every click on the page, and create a white circle at the position of the mouse on the screen |
||||||
|
|
||||||
|
- Create a function `moveCircle`: make it fire when the mouse moves, and get the last circle created and makes it move along with the mouse |
||||||
|
|
||||||
|
- Set a box in the center of the page ; when a circle is inside that box, it has to be purple ; once a circle enters the box, it is trapped inside and cannot go out of it anymore. |
||||||
|
|
||||||
|
### Notions |
||||||
|
|
||||||
|
- [`addEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener): `click`, `mousemove` |
||||||
|
- [`removeEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener) |
||||||
|
- [Mouse event](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent): [`click`](https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event), [`mousemove`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event) / [`clientX`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX), [`clientY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientY) |
||||||
|
- [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect) |
||||||
|
|
||||||
|
### Expected result |
||||||
|
|
||||||
|
You can see an example of the expected result [here](https://youtu.be/qF843P-V2Yw) |
@ -0,0 +1,63 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title>Mouse trap</title> |
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
||||||
|
<link id="fav" rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;,"> |
||||||
|
<style> |
||||||
|
:root { |
||||||
|
--background: hsl(0, 0%, 12%); |
||||||
|
--text: hsl(0, 0%, 80%); |
||||||
|
--clear: hsl(0, 0%, 65%); |
||||||
|
--disabled: hsl(0, 0%, 35%); |
||||||
|
--purple: #bb73e6; |
||||||
|
} |
||||||
|
|
||||||
|
* { |
||||||
|
margin: 0; |
||||||
|
padding: 0; |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
font-family: sans-serif; |
||||||
|
letter-spacing: 1.5px; |
||||||
|
background: var(--background); |
||||||
|
display: flex; |
||||||
|
flex-wrap: wrap; |
||||||
|
justify-content: center; |
||||||
|
align-content: center; |
||||||
|
width: 100vw; |
||||||
|
height: 100vh; |
||||||
|
padding: 5rem; |
||||||
|
font-size: 12px; |
||||||
|
} |
||||||
|
|
||||||
|
.elem { |
||||||
|
width: 50px; |
||||||
|
height: 50px; |
||||||
|
border-radius: 50%; |
||||||
|
background: var(--clear); |
||||||
|
position: absolute; |
||||||
|
opacity: 0.75; |
||||||
|
} |
||||||
|
|
||||||
|
.box { |
||||||
|
width: 25vw; |
||||||
|
height: 25vh; |
||||||
|
border: solid 1px var(--clear); |
||||||
|
} |
||||||
|
|
||||||
|
</style> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<script type="module"> |
||||||
|
|
||||||
|
import { createCircle, moveCircle } from './mouse-trap.js' |
||||||
|
|
||||||
|
createCircle() |
||||||
|
moveCircle() |
||||||
|
|
||||||
|
</script> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,24 @@ |
|||||||
|
## Pick & click |
||||||
|
|
||||||
|
### Instructions |
||||||
|
|
||||||
|
Today, you're gonna create your own color picker. |
||||||
|
|
||||||
|
Write the function `pick` which creates a `hsl` color picker varying the `hue` and `luminosity` according to the position of the mouse, which: |
||||||
|
|
||||||
|
- displays the `hue` value in text |
||||||
|
- displays the `luminosity` value in text |
||||||
|
- displays the full `hsl` value in text |
||||||
|
- copies that value in the clipboard on click |
||||||
|
- displays two lines, for X and Y axis, following the cursor |
||||||
|
|
||||||
|
### Notions |
||||||
|
|
||||||
|
- [Copy event](https://developer.mozilla.org/en-US/docs/Web/API/Element/copy_event) |
||||||
|
- [Mouse move event](https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event) |
||||||
|
- [SVG](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg): [`createElementNS`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS), [`setAttribute`](https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute) |
||||||
|
|
||||||
|
### Expected result |
||||||
|
|
||||||
|
You can see an example of the expected result: |
||||||
|
[![video](https://img.youtube.com/vi/eE4eE9_eKZI/0.jpg)](https://www.youtube.com/watch?v=eE4eE9_eKZI) |
@ -0,0 +1,134 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title>Pick & click</title> |
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
||||||
|
<link id="fav" rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;,"> |
||||||
|
<style> |
||||||
|
|
||||||
|
:root { |
||||||
|
--background: hsl(0, 0%, 12%); |
||||||
|
--text: hsl(0, 0%, 80%); |
||||||
|
--clear: hsl(0, 0%, 65%); |
||||||
|
--disabled: hsl(0, 0%, 35%); |
||||||
|
--purple: #bb73e6; |
||||||
|
} |
||||||
|
|
||||||
|
* { |
||||||
|
margin: 0; |
||||||
|
padding: 0; |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
font-family: sans-serif; |
||||||
|
letter-spacing: 1.5px; |
||||||
|
background: var(--background); |
||||||
|
display: flex; |
||||||
|
flex-wrap: wrap; |
||||||
|
justify-content: center; |
||||||
|
align-content: center; |
||||||
|
width: 100vw; |
||||||
|
height: 100vh; |
||||||
|
padding: 5rem; |
||||||
|
cursor: crosshair; |
||||||
|
} |
||||||
|
|
||||||
|
svg { |
||||||
|
filter: invert(100%); |
||||||
|
position: fixed; |
||||||
|
width: 100vw; |
||||||
|
height: 100vh; |
||||||
|
top: 0; |
||||||
|
left: 0; |
||||||
|
} |
||||||
|
|
||||||
|
svg line { |
||||||
|
stroke-width: 0.6px; |
||||||
|
} |
||||||
|
|
||||||
|
.count { |
||||||
|
filter: invert(100%); |
||||||
|
font-size: 17px; |
||||||
|
} |
||||||
|
|
||||||
|
.text { |
||||||
|
position: fixed; |
||||||
|
filter: invert(100%); |
||||||
|
font-size: 50px; |
||||||
|
cursor: pointer; |
||||||
|
white-space: pre-wrap; |
||||||
|
} |
||||||
|
|
||||||
|
.hue { |
||||||
|
top: 100px; |
||||||
|
right: 100px; |
||||||
|
text-align: right; |
||||||
|
} |
||||||
|
|
||||||
|
.luminosity { |
||||||
|
bottom: 100px; |
||||||
|
left: 100px; |
||||||
|
} |
||||||
|
|
||||||
|
.origin { |
||||||
|
width: 40px; |
||||||
|
height: 40px; |
||||||
|
top: 80px; |
||||||
|
left: 80px; |
||||||
|
border-radius: 50%; |
||||||
|
background: black; |
||||||
|
} |
||||||
|
|
||||||
|
.picked { |
||||||
|
opacity: 0; |
||||||
|
bottom: 100px; |
||||||
|
right: 100px; |
||||||
|
} |
||||||
|
|
||||||
|
.wave { |
||||||
|
background: white; |
||||||
|
width: 20px; |
||||||
|
height: 20px; |
||||||
|
border-radius: 50%; |
||||||
|
position: absolute; |
||||||
|
animation: wave 0.15s ease-out; |
||||||
|
opacity: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.fade-in { |
||||||
|
animation: fade-in 1s; |
||||||
|
} |
||||||
|
|
||||||
|
@keyframes fade-in { |
||||||
|
from { |
||||||
|
opacity: 0; |
||||||
|
} |
||||||
|
to { |
||||||
|
opacity: 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@keyframes wave { |
||||||
|
from { |
||||||
|
opacity: 0; |
||||||
|
transform: scale(1); |
||||||
|
} |
||||||
|
to { |
||||||
|
opacity: 0.4; |
||||||
|
transform: scale(2); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
</style> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<script type="module"> |
||||||
|
|
||||||
|
import { pick } from './pick-and-click.js' |
||||||
|
|
||||||
|
pick() |
||||||
|
|
||||||
|
</script> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,49 @@ |
|||||||
|
## Pimp my style |
||||||
|
|
||||||
|
### Instructions |
||||||
|
|
||||||
|
Check out that button on the HTML page: |
||||||
|
|
||||||
|
```html |
||||||
|
<div class="button">pimp my style</div> |
||||||
|
``` |
||||||
|
|
||||||
|
For now, it's only a lonely, basic and sad element ; let's pimp it up! |
||||||
|
|
||||||
|
On each click on the page, a function `pimp` is triggered. |
||||||
|
Write the body of that function so that the button's class is altered: |
||||||
|
|
||||||
|
- Add in order the next class of the `styles` array provided in the data file below |
||||||
|
- When the end of the array is reached, remove backwards each class |
||||||
|
- Toggle the class 'unpimp' when removing classes |
||||||
|
|
||||||
|
``` |
||||||
|
Example for a `styles` array with only 3 classes: |
||||||
|
|
||||||
|
Page load --> <div class="button"></div> |
||||||
|
|
||||||
|
...adding |
||||||
|
Click 1 --> <div class="button one"></div> |
||||||
|
Click 2 --> <div class="button one two"></div> |
||||||
|
|
||||||
|
...toggling `unpimp` |
||||||
|
Click 3 --> <div class="button one two three unpimp"></div> |
||||||
|
|
||||||
|
...and removing backwards |
||||||
|
Click 4 --> <div class="button one two unpimp"></div> |
||||||
|
Click 5 --> <div class="button one unpimp"></div> |
||||||
|
Click 6 --> <div class="button"></div> |
||||||
|
``` |
||||||
|
|
||||||
|
### Notions |
||||||
|
|
||||||
|
- [`classList`](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList): `add()`, `remove()`, `toggle()` |
||||||
|
|
||||||
|
### Provided files |
||||||
|
|
||||||
|
- Use this CSS file: [https://mariemalarme.github.io/dom-js/assets/style/pimp-my-style.css](https://mariemalarme.github.io/dom-js/assets/style/pimp-my-style.css) |
||||||
|
- Import the `styles` from the data file: [https://mariemalarme.github.io/dom-js/assets/data/pimp-my-style.js](https://mariemalarme.github.io/dom-js/assets/data/pimp-my-style.js) |
||||||
|
|
||||||
|
### Expected result |
||||||
|
|
||||||
|
You can see an example of the expected result [here](https://youtu.be/VIRf3TBDTN4) |
@ -0,0 +1,17 @@ |
|||||||
|
export const styles = [ |
||||||
|
'one', |
||||||
|
'two', |
||||||
|
'three', |
||||||
|
'four', |
||||||
|
'five', |
||||||
|
'six', |
||||||
|
'seven', |
||||||
|
'eight', |
||||||
|
'nine', |
||||||
|
'ten', |
||||||
|
'eleven', |
||||||
|
'twelve', |
||||||
|
'thirteen', |
||||||
|
'fourteen', |
||||||
|
'fifteen', |
||||||
|
] |
@ -0,0 +1,179 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title>Pimp my style</title> |
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
||||||
|
<link id="fav" rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;,"> |
||||||
|
<style> |
||||||
|
|
||||||
|
:root { |
||||||
|
--background: hsl(0, 0%, 12%); |
||||||
|
--text: hsl(0, 0%, 80%); |
||||||
|
--clear: hsl(0, 0%, 65%); |
||||||
|
--disabled: hsl(0, 0%, 35%); |
||||||
|
--purple: #bb73e6; |
||||||
|
} |
||||||
|
|
||||||
|
* { |
||||||
|
margin: 0; |
||||||
|
padding: 0; |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
font-family: sans-serif; |
||||||
|
letter-spacing: 1.5px; |
||||||
|
background: var(--background); |
||||||
|
display: flex; |
||||||
|
flex-wrap: wrap; |
||||||
|
justify-content: center; |
||||||
|
align-content: center; |
||||||
|
width: 100vw; |
||||||
|
height: 100vh; |
||||||
|
color: var(--text); |
||||||
|
padding: 2.5rem 0; |
||||||
|
} |
||||||
|
|
||||||
|
div { |
||||||
|
user-select: none; |
||||||
|
letter-spacing: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.button:first-letter { |
||||||
|
text-transform: uppercase; |
||||||
|
} |
||||||
|
|
||||||
|
.button.unpimp:before { |
||||||
|
content: 'Un'; |
||||||
|
} |
||||||
|
|
||||||
|
.button { |
||||||
|
background: var(--background); |
||||||
|
font-family: serif; |
||||||
|
cursor: pointer; |
||||||
|
width: 70%; |
||||||
|
text-align: center; |
||||||
|
} |
||||||
|
|
||||||
|
.one { |
||||||
|
font-size: 75px; |
||||||
|
} |
||||||
|
|
||||||
|
.two { |
||||||
|
font-family: sans-serif; |
||||||
|
} |
||||||
|
|
||||||
|
.three { |
||||||
|
letter-spacing: 15px; |
||||||
|
} |
||||||
|
|
||||||
|
.four { |
||||||
|
padding: 20px 40px; |
||||||
|
border: solid 1px var(--clear); |
||||||
|
} |
||||||
|
|
||||||
|
.five { |
||||||
|
border-radius: 100px; |
||||||
|
} |
||||||
|
|
||||||
|
.six { |
||||||
|
border: none; |
||||||
|
box-shadow: 8px 8px 15px rgba(255, 255, 255, 0.075), |
||||||
|
-10px -10px 15px rgba(0, 0, 0, 0.3); |
||||||
|
} |
||||||
|
|
||||||
|
.seven { |
||||||
|
color: var(--purple); |
||||||
|
} |
||||||
|
|
||||||
|
.eight { |
||||||
|
border: solid 0.5px var(--purple); |
||||||
|
} |
||||||
|
|
||||||
|
.nine { |
||||||
|
background: var(--purple); |
||||||
|
color: white; |
||||||
|
position: relative; |
||||||
|
box-shadow: 0px 0px 35px rgba(0, 0, 0, 0.8); |
||||||
|
} |
||||||
|
|
||||||
|
.ten:after { |
||||||
|
position: absolute; |
||||||
|
content: ''; |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
padding: 15px; |
||||||
|
top: -16px; |
||||||
|
left: -16px; |
||||||
|
border-radius: 100px; |
||||||
|
border: solid 1px var(--clear); |
||||||
|
} |
||||||
|
|
||||||
|
.eleven:after { |
||||||
|
position: absolute; |
||||||
|
content: ''; |
||||||
|
padding: 30px; |
||||||
|
top: -31px; |
||||||
|
left: -31px; |
||||||
|
background: white; |
||||||
|
z-index: -1; |
||||||
|
} |
||||||
|
|
||||||
|
.twelve { |
||||||
|
color: var(--background); |
||||||
|
} |
||||||
|
|
||||||
|
.thirteen { |
||||||
|
text-decoration: underline; |
||||||
|
} |
||||||
|
|
||||||
|
.fourteen { |
||||||
|
animation: animation 1.5s linear infinite; |
||||||
|
background: linear-gradient( |
||||||
|
to right, |
||||||
|
var(--purple) 0%, |
||||||
|
white 48%, |
||||||
|
white 52%, |
||||||
|
var(--purple) 100% |
||||||
|
); |
||||||
|
background-size: 500px 640px; |
||||||
|
position: relative; |
||||||
|
} |
||||||
|
|
||||||
|
@keyframes animation { |
||||||
|
0% { |
||||||
|
background-position: 0 0; |
||||||
|
} |
||||||
|
100% { |
||||||
|
background-position: 500px 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.fifteen { |
||||||
|
height: 100vh; |
||||||
|
width: 100vw; |
||||||
|
border-radius: 0; |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
} |
||||||
|
|
||||||
|
</style> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<script type="module"> |
||||||
|
|
||||||
|
import { pimp } from './pimp-my-style.js' |
||||||
|
|
||||||
|
const body = document.querySelector('body') |
||||||
|
|
||||||
|
const button = document.createElement('div') |
||||||
|
button.className = 'button' |
||||||
|
button.textContent = 'pimp my style' |
||||||
|
body.append(button) |
||||||
|
|
||||||
|
button.addEventListener('click', pimp) |
||||||
|
|
||||||
|
</script> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,26 @@ |
|||||||
|
## Where do we go? |
||||||
|
|
||||||
|
### Instructions |
||||||
|
|
||||||
|
Tired of staying home for too long, you decide to develop a page to index ideas for your next travel destinations, so that next time you'll ask yourself 'Where do we go?', you won't need to get lost for 3 hours! |
||||||
|
|
||||||
|
Create a page which displays the list of `places` provided in the data file below: |
||||||
|
|
||||||
|
- sort the `places` from the Northest to the Southest |
||||||
|
- display a fullscreen-size image for each place ; use the images hosted here: `https://github.com/MarieMalarme/dom-js/tree/master/assets/images`, also available as Github Pages here `https://mariemalarme.github.io/dom-js/assets/images/locationName.jpg` |
||||||
|
- display a location indicator, displaying the `name` and the `coordinates` of the current place featured in the image, using the corresponding `color` as text color, which updates on scroll when another image is reached |
||||||
|
- display a compass indicating the latitude direction ; North if the user is scrolling up, South if he's scrolling down |
||||||
|
- when clicking on the page, open a link redirecting to the Google Maps' coordinates of the place currently displayed. |
||||||
|
|
||||||
|
### Notions |
||||||
|
|
||||||
|
- [Wheel event](https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event): [`deltaY`](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaY) |
||||||
|
- [`window`](https://developer.mozilla.org/en-US/docs/Web/API/Window): [`innerHeight`](https://developer.mozilla.org/en-US/docs/Web/API/Window/innerHeight), [`scrollY`](https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY), [`open()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) |
||||||
|
|
||||||
|
### Provided files |
||||||
|
|
||||||
|
- Import the `places` from the data file: [https://mariemalarme.github.io/dom-js/assets/data/where-do-we-go.js](https://mariemalarme.github.io/dom-js/assets/data/where-do-we-go.js) |
||||||
|
|
||||||
|
### Expected result |
||||||
|
|
||||||
|
You can see an example of the expected result [here](https://youtu.be/BLxNi1WH6_0) |
@ -0,0 +1,107 @@ |
|||||||
|
export const places = [ |
||||||
|
{ |
||||||
|
name: 'Cordoba, Spain', |
||||||
|
color: 'deeppink', |
||||||
|
coordinates: `37°53'17.43"N 4°46'45.78"W`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Yuanyang County, China', |
||||||
|
color: 'cyan', |
||||||
|
coordinates: `23°09'32.30"N 102°44'41.46"E`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Namib Desert, Namibia', |
||||||
|
color: 'lime', |
||||||
|
coordinates: `24°45'4.19"S 15°16'21.00"E`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Newark, New Jersey, USA', |
||||||
|
color: 'yellow', |
||||||
|
coordinates: `40°44'8.37"N 74°10'20.52"W`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Nishinoshima Island, Japan', |
||||||
|
color: 'lightcoral', |
||||||
|
coordinates: `27°14'50.84"N 140°52'46.04"E`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Lisse, The Netherlands', |
||||||
|
color: 'cornflowerblue', |
||||||
|
coordinates: `52°15'28.55"N 4°33'26.94"E`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Shadegan Lagoon, Iran', |
||||||
|
color: 'firebrick', |
||||||
|
coordinates: `30°39'16.55"N 48°39'14.14"E`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Qinhuangdao, China', |
||||||
|
color: 'seashell', |
||||||
|
coordinates: `39°56'7.3"N 119°36'1.88"E`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Marrakesh, Morocco', |
||||||
|
color: 'orange', |
||||||
|
coordinates: `31°37'46.1"N 7°58'51.9"W`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Los Caracoles Pass, Chile', |
||||||
|
color: 'violet', |
||||||
|
coordinates: `32°49'51.6"S 70°05'22.9"W`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Tucson, Arizona, USA', |
||||||
|
color: 'springgreen', |
||||||
|
coordinates: `32°13'21.38"N 110°58'28.96"W`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Arlit, Niger', |
||||||
|
color: 'blue', |
||||||
|
coordinates: `18°44'20.41"N 7°23'22.12"E`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Black Rock Desert, Nevada, USA', |
||||||
|
color: 'crimson', |
||||||
|
coordinates: `40°54'35.0"N 119°03'26.5"W`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Mount Fuji, Japan', |
||||||
|
color: 'darkviolet', |
||||||
|
coordinates: `35°21'37.0"N 138°43'38.1"E`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Moab, Utah, USA', |
||||||
|
color: 'gold', |
||||||
|
coordinates: `38°34'23.94"N 109°32'59.42"W`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Rio de Janeiro, Brasil', |
||||||
|
color: 'hotpink', |
||||||
|
coordinates: `22°59'13.4"S 43°12'15.9"W`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Killeen, Texas, USA', |
||||||
|
color: 'greenyellow', |
||||||
|
coordinates: `31°07'1.63"N 97°43'40.07"W`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Skafta River, Iceland', |
||||||
|
color: 'mistyrose', |
||||||
|
coordinates: `63°39'47.7"N 17°47'57.9"W`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Almeria, Spain', |
||||||
|
color: 'mediumturquoise', |
||||||
|
coordinates: `36°50'08.7"N 2°27'44.8"W`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Atlanta, Georgia, USA', |
||||||
|
color: 'white', |
||||||
|
coordinates: `33°45'39.0"N 84°23'50.1"W`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Georgetown, California, USA', |
||||||
|
color: 'sandybrown', |
||||||
|
coordinates: `38°54'22.4"N 120°50'23.9"W`, |
||||||
|
}, |
||||||
|
] |
After Width: | Height: | Size: 206 KiB |
After Width: | Height: | Size: 155 KiB |
After Width: | Height: | Size: 218 KiB |
After Width: | Height: | Size: 244 KiB |
After Width: | Height: | Size: 610 KiB |
After Width: | Height: | Size: 248 KiB |
After Width: | Height: | Size: 296 KiB |
After Width: | Height: | Size: 166 KiB |
After Width: | Height: | Size: 209 KiB |
After Width: | Height: | Size: 444 KiB |
After Width: | Height: | Size: 223 KiB |
After Width: | Height: | Size: 133 KiB |
After Width: | Height: | Size: 164 KiB |
After Width: | Height: | Size: 227 KiB |
After Width: | Height: | Size: 222 KiB |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 282 KiB |
After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 163 KiB |
After Width: | Height: | Size: 168 KiB |
After Width: | Height: | Size: 197 KiB |
@ -0,0 +1,68 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title>Where do we go?</title> |
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
||||||
|
<link id="fav" rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;,"> |
||||||
|
<style> |
||||||
|
:root { |
||||||
|
--background: hsl(0, 0%, 12%); |
||||||
|
--text: hsl(0, 0%, 80%); |
||||||
|
--clear: hsl(0, 0%, 65%); |
||||||
|
--disabled: hsl(0, 0%, 35%); |
||||||
|
--purple: #bb73e6; |
||||||
|
} |
||||||
|
|
||||||
|
* { |
||||||
|
margin: 0; |
||||||
|
padding: 0; |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
font-family: sans-serif; |
||||||
|
letter-spacing: 1.5px; |
||||||
|
background: var(--background); |
||||||
|
} |
||||||
|
|
||||||
|
section { |
||||||
|
height: 100vh; |
||||||
|
width: 100vw; |
||||||
|
} |
||||||
|
|
||||||
|
.location { |
||||||
|
position: fixed; |
||||||
|
top: 0; |
||||||
|
color: white; |
||||||
|
width: 100vw; |
||||||
|
height: 100vh; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
font-size: 100px; |
||||||
|
line-height: 130px; |
||||||
|
white-space: pre-wrap; |
||||||
|
text-align: center; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.direction { |
||||||
|
position: fixed; |
||||||
|
right: 100px; |
||||||
|
top: 100px; |
||||||
|
text-align: center; |
||||||
|
font-size: 40px; |
||||||
|
line-height: 60px; |
||||||
|
color: white; |
||||||
|
white-space: pre-wrap; |
||||||
|
} |
||||||
|
</style> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<script type="module"> |
||||||
|
import { scroll } from './where-do-we-go.js' |
||||||
|
|
||||||
|
scroll() |
||||||
|
</script> |
||||||
|
</body> |
||||||
|
</html> |