UP & DOWN 게임
html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/reset-css@5.0.1/reset.min.css"
/>
<link rel="stylesheet" href="css/main.css" />
<script src="js/app.js" defer></script>
</head>
<body>
<div class="wrapper">
<section class="main">
<h1 class="main-title">신나는 UP&DOWN 게임</h1>
<div class="number-wrapper">
<h2>
<em id="begin">1</em>부터 <em id="end">100</em>사이의 숫자를
클릭하세요.
</h2>
<div id="numbers">
</div>
</div>
</section>
<aside class="result">
<div id="up">UP!!</div>
<div id="down">DOWN!!</div>
</aside>
<div id="finish">Congratulation!!!</div>
</div>
</body>
</html>
- defer가 있으면 script가 head에 존재해도 나중에 실행되기 때문에 적용됨
javascript
const gameData = {
secret: Math.floor(Math.random() * 100) + 1,
answer: null,
min: 1,
max: 100
};
function makeNewIcon($container, i) {
const $newDiv = document.createElement('div');
$newDiv.classList.add('icon');
$newDiv.textContent = i;
$container.appendChild($newDiv);
}
function createNumberIcons() {
const $numbersContainer = document.getElementById('numbers');
$numbersContainer.innerHTML = '';
const {min, max} = gameData;
for (let i = min; i <= max; i++) {
makeNewIcon($numbersContainer, i);
}
}
function updateRangeAndRefreshIcons(isUp) {
const $begin = document.getElementById('begin');
const $end = document.getElementById('end');
if (isUp) {
gameData.min = gameData.answer + 1;
$begin.textContent = gameData.min;
} else {
gameData.max = gameData.answer - 1;
$end.textContent = gameData.max;
}
createNumberIcons();
}
function handleCorrectAnswer($target) {
document.getElementById('finish').classList.add('show');
$target.setAttribute('id', 'move');
document.getElementById('numbers').removeEventListener('click', iconClickHandler);
}
function verifyAnswer($target) {
const { secret, answer } = gameData;
if (secret === answer) {
handleCorrectAnswer($target);
} else {
const isUp = secret > answer;
document.getElementById(isUp ? 'up' : 'down').classList.add('selected');
document.getElementById(isUp ? 'down' : 'up').classList.remove('selected');
updateRangeAndRefreshIcons(isUp);
}
}
function iconClickHandler(e) {
if (!e.target.matches("#numbers .icon")) return;
gameData.answer = +e.target.textContent;
verifyAnswer(e.target);
}
createNumberIcons();
document.getElementById('numbers').addEventListener('click', iconClickHandler);
TODOLIST
html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>일정관리 앱</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reset-css@5.0.1/reset.min.css">
<link rel="stylesheet" href="https://cdn.linearicons.com/free/1.0.0/icon-font.min.css">
<link rel="stylesheet" href="css/main.css">
<script src="js/app.js" defer></script>
</head>
<body>
<div class="wrapper">
<section class="todo-template">
<h1 class="app-title">일정 관리</h1>
<div class="content">
<form class="todo-insert">
<input type="text" id="todo-text" placeholder="할 일을 입력하세요" autocomplete="off">
<button id="add"><span class="lnr lnr-plus-circle"></span></button>
</form>
<ul class="todo-list">
<li data-id="1" class="todo-list-item">
<label class="checkbox">
<input type="checkbox">
<span class="text">할 일 1</span>
</label>
<div class="modify">
<span class="lnr lnr-undo"></span>
</div>
<div class="remove">
<span class="lnr lnr-cross-circle"></span>
</div>
</li>
<li data-id="2" class="todo-list-item">
<label class="checkbox">
<input type="checkbox">
<span class="text">할 일 2</span>
</label>
<div class="modify"><span class="lnr lnr-undo"></span></div>
<div class="remove"><span class="lnr lnr-cross-circle"></span></div>
</li>
<li data-id="3" class="todo-list-item">
<label class="checkbox">
<input type="checkbox">
<span class="text">할 일 3</span>
</label>
<div class="modify"><span class="lnr lnr-undo"></span></div>
<div class="remove"><span class="lnr lnr-cross-circle"></span></div>
</li>
</ul>
</div>
</section>
</div>
</body>
</html>
javascript
let isEnterEditMode = false;
const todos = [
{
id: 1,
text: "할 일1",
done: false,
},
{
id: 2,
text: "할 일2",
done: false,
},
{
id: 3,
text: "할 일3",
done: false,
},
];
function renderNewTodoElement({id, text}) {
const $newTodoLi = document.createElement("li");
$newTodoLi.dataset.id = id;
$newTodoLi.classList.add("todo-list-item");
$newTodoLi.innerHTML = `
<label class="checkbox">
<input type="checkbox">
<span class="text">${text}</span>
</label>
<div class="modify">
<span class="lnr lnr-undo"></span>
</div>
<div class="remove">
<span class="lnr lnr-cross-circle"></span>
</div>
`;
const $todoListUl = document.querySelector(".todo-list");
$todoListUl.appendChild($newTodoLi);
}
function isValidate($textInput) {
const text = $textInput.value;
if (text.trim() === '' || text.length > 10) {
$textInput.style.background = 'orangered';
$textInput.setAttribute('placeholder', '필수 입력사항입니다 (10자 이내)');
$textInput.value = '';
return false;
}
$textInput.style.background = '';
$textInput.setAttribute('placeholder', '할 일을 입력하세요.');
return true;
}
function insertTodoData() {
const $todoText = document.getElementById("todo-text");
const inputText = $todoText.value;
if (!isValidate($todoText)) return;
const makeNewId = () => {
return todos.length === 0 ? 1 : todos[todos.length - 1].id + 1;
};
const newTodo = {
id: makeNewId(),
text: inputText,
done: false,
};
todos.push(newTodo);
renderNewTodoElement(newTodo);
}
function removeTodoDate(dataId) {
const index = todos.findIndex(todo => todo.id === dataId);
todos.splice(index, 1);
}
function checked(target) {
target.classList.add('checked');
}
function unChecked(target) {
target.classList.remove('checked');
}
function toggleDone(dataId) {
const todo = todos.find(todo => todo.id === dataId);
todo.done = !todo.done;
console.log(todo);
}
function toggleModifyMode($modifyBtn) {
const isModifying = $modifyBtn.classList.contains('lnr-undo');
if (isEnterEditMode && isModifying) return;
isEnterEditMode = isModifying;
$modifyBtn.classList.toggle('lnr-undo');
$modifyBtn.classList.toggle('lnr-checkmark-circle');
const $label = $modifyBtn.parentElement.previousElementSibling;
if (isModifying) {
const $textSpan = $label.lastElementChild;
const $modifyInput = document.createElement('input');
$modifyInput.classList.add('modify-input');
$modifyInput.setAttribute('type', 'text');
$modifyInput.value = $textSpan.textContent;
$label.replaceChild($modifyInput, $textSpan);
} else {
const $modifyInput = $label.querySelector('.modify-input');
const $newTextSpan = document.createElement('span');
$newTextSpan.classList.add('text');
$newTextSpan.textContent = $modifyInput.value;
$label.replaceChild($newTextSpan, $modifyInput);
const dataId = +$label.parentElement.dataset.id;
const foundTodo = todos.find(todo => todo.id === dataId);
foundTodo.text = $newTextSpan.textContent;
console.log(todos);
}
}
document.getElementById("add").addEventListener("click", (e) => {
e.preventDefault();
insertTodoData();
});
document.querySelector('.todo-list').addEventListener('click', e => {
const $targetLi = e.target.closest('.todo-list-item');
const dataId = +$targetLi.dataset.id;
if (e.target.matches('.remove span')) {
document.querySelector('.todo-list').removeChild($targetLi);
removeTodoDate(dataId);
} else if (e.target.matches('.checkbox input[type=checkbox]')) {
e.target.closest('.checkbox').classList.toggle('checked');
toggleDone(dataId);
} else if (e.target.matches('.modify span')) {
toggleModifyMode(e.target);
}
});