* 사용자 입력 이벤트 처리하기
<!DOCTYPE html>
<html lang="en">
<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="style.css">
</head>
<body>
<div class="color-box"></div>
<input id="color-input" type="text" placeholder="give color name ..."/>
<script src='app.js'></script>
</body>
</html>
index.html 파일을 위와 같이 작성하자! 사용자 입력을 처리하기 위한 input 요소와 배경색상을 변경하기 위한 div 요소를 정의한다.
body{
margin: 0;
padding: 0;
background: #0e1111;
}
.color-box{
background-color: #0e1111;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100vh;
}
#color-input{
all: unset;
width: 300px;
height: 50px;
border: 2px solid lightgreen;
padding: 10px;
border-radius: 15px;
color: lightgreen;
font-size: 1.2rem;
font-weight: bold;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
#color-input::placeholder{
color: lightgreen;
}
style.css 파일을 위와 같이 작성하자!
const colorBox = document.querySelector('.color-box')
const colorInput = document.getElementById('color-input')
console.log(colorInput)
function setColor(e){
console.log(e.target.value)
colorBox.style.backgroundColor = e.target.value
}
colorInput.addEventListener('input', setColor)
app.js 파일을 위와 같이 작성하자!
const colorBox = document.querySelector('.color-box')
해당 요소의 배경색을 변경하기 위하여 color-box 요소를 검색한다.
const colorInput = document.getElementById('color-input')
input 요소에 사용자 입력에 대한 이벤트를 등록하기 위하여 color-input 요소를 검색한다.
colorInput.addEventListener('input', setColor)
input 요소에 input 이벤트를 등록한다. input 이벤트는 사용자가 입력창에 뭔가를 입력할때마다 발생한다. 또한, input 이벤트에 대하여 setColor 라는 이벤트핸들러 함수를 등록한다.
function setColor(e){
console.log(e.target.value)
colorBox.style.backgroundColor = e.target.value
}
사용자가 입력창에 뭔가를 입력할때 실행되는 이벤트핸들러 함수이다. e.target.value 는 현재 사용자가 입력창에 타이핑하는 문자열이다. 사용자가 색상 이름을 입력하면 배경색이 변경된다.
자동완성 기능으로 사용자가 선택한 색상으로 배경색을 변경하는 앱을 만들어보자!
<!DOCTYPE html>
<html lang="en">
<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="style.css">
</head>
<body>
<div id='auto-complete'>
<input id="color-input" type="text" placeholder="give color name ..."/>
<div class='color-list'></div>
</div>
<div class="color-box"></div>
<script src='app.js'></script>
</body>
</html>
index.html 파일을 위와 같이 수정하자! auto-complete 요소는 사용자 입력창과 배경색 목록을 보여줄 리스트를 감싸고 있다. color-box 요소는 사용자가 선택한 색상으로 배경색이 변경될 요소이다.
body{
margin: 0;
padding: 0;
background: #0e1111;
}
#auto-complete{
position: absolute;
left: 50%;
top: 100px;
transform: translate(-50%);
}
#color-input{
all: unset;
width: 300px;
height: 50px;
border: 2px solid lightgreen;
padding: 10px;
border-radius: 15px;
color: lightgreen;
font-size: 1.2rem;
font-weight: bold;
box-sizing: border-box;
position: relative;
}
#color-input::placeholder{
color: lightgreen;
}
.color-list{
width: 300px;
margin-top: 10px;
display: none;
position: absolute;
}
.show{
display: block;
}
.color-item{
width: 100%;
height: 50px;
border: 1px solid #a9a9a9;
color: lightgreen;
line-height: 50px;
text-align: center;
font-weight: bold;
cursor: pointer;
}
.color-item:hover{
background-color: lightgreen;
color: white;
}
.color-box{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100vh;
z-index: -1;
}
style.css 파일을 위와 같이 수정하자!
const colorBox = document.querySelector('.color-box')
const colorList = document.querySelector('.color-list')
const colorInput = document.getElementById('color-input')
const colors = ['orange', 'blue', 'brown', 'green', 'red', 'skyblue']
function addColors(colors){
for(let color of colors){
const item = `<div class='color-item'>${color}</div>`
colorList.innerHTML += item
}
}
function typeColor(e){
console.log(e.target.value)
if(e.target.value !== ''){
console.log('you typed something')
colorList.classList.add('show')
}else{
console.log('you didn\'t type anything')
colorList.classList.remove('show')
}
}
function setColor(e){
console.log(e.target)
if(e.target.className === 'color-item'){
const pickedColor = e.target.innerText
console.log('you picked color !', pickedColor)
colorInput.value = pickedColor
colorList.classList.remove('show')
colorBox.style.background = pickedColor
}
}
addColors(colors)
colorInput.addEventListener('input', typeColor)
colorList.addEventListener('click', setColor)
app.js 파일을 위와 같이 수정하자!
const colorList = document.querySelector('.color-list')
색상목록을 보여주고, 사용자가 색상 목록에서 배경색을 선택할 경우의 이벤트를 등록하기 위하여 color-list 요소를 검색한다.
const colors = ['orange', 'blue', 'brown', 'green', 'red', 'skyblue']
웹페이지에 색상 목록을 동적으로 생성하기 위하여 색상 데이터를 담고 있는 배열이다.
function addColors(colors){
for(let color of colors){
const item = `<div class='color-item'>${color}</div>`
colorList.innerHTML += item
}
}
색상 데이터를 이용하여 color-list 요소에 색상 목록을 보여주는 함수이다.
addColors(colors)
실제 화면에 색상 목록을 보여준다.
const colorInput = document.getElementById('color-input')
사용자가 입력창에 뭔가를 입력할때의 이벤트를 등록하기 위하여 input 요소를 검색한다.
colorInput.addEventListener('input', typeColor)
input 요소에 input 이벤트를 등록하고, typeColor 이벤트핸들러 함수를 연결한다.
function typeColor(e){
console.log(e.target.value)
if(e.target.value !== ''){
console.log('you typed something')
colorList.classList.add('show')
}else{
console.log('you didn\'t type anything')
colorList.classList.remove('show')
}
}
사용자가 입력창에 뭔가를 입력할때 실행되는 이벤트핸들러 함수이다. e.target.value 는 사용자가 입력한 문자열이다. 사용자가 뭔가를 입력할때는 색상 목록을 보여주고, 입력창에 아무것도 입력하지 않거나 입력한 내용을 지운 경우에는 색상 목록을 숨긴다.
colorList.addEventListener('click', setColor)
color-list 요소에 click 이벤트를 등록하고, setColor 이벤트핸들러 함수를 연결한다.
function setColor(e){
console.log(e.target)
if(e.target.className === 'color-item'){
const pickedColor = e.target.innerText
console.log('you picked color !', pickedColor)
colorInput.value = pickedColor // 사용자가 선택한 색상으로 입력창 화면 설정하기
colorList.classList.remove('show') // 색상 목록 숨기기
colorBox.style.background = pickedColor // 배경색 변경하기
}
}
사용자가 색상 목록에서 특정 색상을 선택할때 실행되는 이벤트핸들러 함수이다. 사용자가 특정 색상을 선택하면, 선택한 색상의 이름으로 입력창 화면을 설정한다. 그런 다음 색상 목록을 숨긴다. 마지막으로 color-box 요소의 배경색을 사용자가 선택한 색상으로 변경한다.
* 키 이벤트 처리하기
사용자 입력 처리하기 예제코드에서 사용한 자동완성 기능을 좀 더 발전시켜보자!
<!DOCTYPE html>
<html lang="en">
<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="style.css">
</head>
<body>
<div id='auto-complete'>
<input id="color-input" type="text" placeholder="give color name ..."/>
<div class='color-list'></div>
</div>
<div class="color-box"></div>
<script src='app.js'></script>
</body>
</html>
index.html 파일을 위와 같이 작성하자!
body{
margin: 0;
padding: 0;
background: #0e1111;
}
#auto-complete{
position: absolute;
left: 50%;
top: 100px;
transform: translate(-50%);
}
#color-input{
all: unset;
width: 300px;
height: 50px;
border: 2px solid lightgreen;
padding: 10px;
border-radius: 15px;
color: lightgreen;
font-size: 1.2rem;
font-weight: bold;
box-sizing: border-box;
position: relative;
}
#color-input::placeholder{
color: lightgreen;
}
.color-list{
width: 300px;
margin-top: 10px;
display: none;
position: absolute;
}
.show{
display: block;
}
.color-item{
width: 100%;
height: 50px;
border: 1px solid #a9a9a9;
color: lightgreen;
line-height: 50px;
text-align: center;
font-weight: bold;
cursor: pointer;
}
.color-item:hover{
background-color: lightgreen;
color: white;
}
.highlight{
background-color: lightgreen;
color: white;
}
.color-box{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100vh;
z-index: -1;
}
style.css 파일을 위와 같이 수정하자!
.highlight{
background-color: lightgreen;
color: white;
}
변경된 부분은 위와 같다. 사용자가 키 입력을 사용하여 색상을 선택할때 해당 색상이 선택되었음을 보여주기 위하여 위와 같이 하이라이트를 적용한다.
const colorBox = document.querySelector('.color-box')
const colorList = document.querySelector('.color-list')
const colorInput = document.getElementById('color-input')
const colors = ['orange', 'blue', 'brown', 'green', 'red', 'skyblue'] // 색상배열
let index = -1 // 사용자가 화살표키로 선택한 현재 색상에 대한 인덱스 값
let prevIndex = -1 // 사용자가 화살표키로 선택한 직전 색상에 대한 인덱스 값
function addColors(colors){
for(let color of colors){
const item = `<div class='color-item'>${color}</div>` // 색상 엘리먼트
colorList.innerHTML += item // 컨테이너에 색상 엘리먼트 추가하기
}
}
function changeHighLightItem(key){
prevIndex = index // 현재 사용자가 선택한 색상에 대한 인덱스값 저장하기
if(key === 40){ // 아래쪽 화살표키를 누른 경우 색상 인덱스 값 증가
index++
if(index > colors.length -1){ // 인덱스 값이 색상 배열의 마지막 인덱스를 벗어난 경우 다시 첫번째 색상을 선택하도록 0 으로 초기화
index = 0
}
}else if(key === 38){ // 위쪽 화살표키를 누른 경우 색상 인덱스 값 감소
index--
if(index < 0){ // 인덱스값이 0 보다 작은 경우 다시 마지막 색상을 선택하도록 색상 배열의 마지막 인덱스값으로 설정
index = colors.length -1
}
}else if(key === 13){ // 엔터키를 누른 경우 사용자가 화살표키로 선택한 현재 색상을 화면의 배경색으로 설정
const colorItem = document.querySelectorAll('.color-item')[index]
setBackground(colorItem.innerText)
}
console.log('직전에 선택한 색상', prevIndex)
console.log('현재 선택한 색상', index)
}
function unsetHightLightItem(colorIndex){
if(colorIndex >= 0 && colorIndex < colors.length){ // colorIndex 의 유효범위가 색상배열의 인덱스 값인 경우
const colorItem = document.querySelectorAll('.color-item')[colorIndex]
colorItem.classList.remove('highlight') // 해당 색상 엘리먼트에 대한 하이라이트 해제
}
}
function highLightColorItem(colorIndex){
if(colorIndex >= 0 && colorIndex < colors.length){
const colorItem = document.querySelectorAll('.color-item')[colorIndex]
colorItem.classList.add('highlight') // 해당 색상 엘리먼트에 대한 하이라이트 적용
}
}
function selectColor(e){ // 사용자가 화살표키로 색상을 선택할때 하이라이트 적용하기
console.log(e.keyCode) // 키보드 키에 대한 ID 값
if(e.keyCode){
changeHighLightItem(e.keyCode) // 사용자가 선택한 색상에 대한 인덱스 값 변경
unsetHightLightItem(prevIndex) // 사용자가 직전에 선택한 색상에 적용된 하이라이트 제거
highLightColorItem(index) // 사용자가 현재 선택한 색상에 대한 하이라이트 적용
}
}
function typeColor(e){
console.log(e.target.value)
if(e.target.value !== ''){
console.log('you typed something')
colorList.classList.add('show') // 색상목록 보여주기
colorInput.addEventListener('keyup', selectColor) // 화살표키로 색상목록을 선택하기 위한 키 이벤트 등록하기
}else{
console.log('you didn\'t type anything')
colorList.classList.remove('show') // 색상목록 감추기
colorInput.removeEventListener('keyup', selectColor) // 화살표키로 색상목록을 선택하지 못하도록 키 이벤트 해제하기
}
}
function initHighlight(){
unsetHightLightItem(index) // 현재 인덱스 값에 대한 하이라이트 해제
index = -1 // 인덱스 값 초기화
prevIndex = -1
}
function setBackground(color){
console.log('you picked color !', color)
colorInput.value = color // 사용자가 선택한 색상을 입력창에 보여주기
colorList.classList.remove('show') // 색상 목록 감추기
colorBox.style.background = color // 사용자가 선택한 색상으로 화면 배경색 변경하기
initHighlight() // 하이라이트 초기화
colorInput.removeEventListener('keyup', selectColor) // 색상목록이 안보일때는 화살표키로 색상을 선택하지 못하도록 키 이벤트 해제하기
}
function setColor(e){
console.log(e.target)
if(e.target.className === 'color-item'){
const pickedColor = e.target.innerText // 사용자가 마우스로 선택한 색상 문자열
setBackground(pickedColor) // 배경색 변경
}
}
addColors(colors) // 화면에 색상 목록 기보여주기
colorInput.addEventListener('input', typeColor) // 사용자가 색상을 입력할때 적용할 이벤트
colorList.addEventListener('click', setColor) // 사용자가 색상목록에서 특정 색상을 마우스로 선택할때 적용할 이벤트
app.js 파일을 위와 같이 수정하자!
사용자가 입력창에 색상을 입력할때 색상 목록이 나타나므로 이때 키 이벤트를 동적으로 등록하여 화살표키로 원하는 색상을 선택할 수 있도록 한다. 반대로 사용자가 입력창에 아무것도 입력하지 않거나 입력한 색상을 지운 경우에는 키 이벤트를 해제하여 화살표키로 색상을 선택하지 못하게 한다.
function typeColor(e){
console.log(e.target.value)
if(e.target.value !== ''){
console.log('you typed something')
colorList.classList.add('show') // 색상목록 보여주기
colorInput.addEventListener('keyup', selectColor) // 화살표키로 색상목록을 선택하기 위한 키 이벤트 등록하기
}else{
console.log('you didn\'t type anything')
colorList.classList.remove('show') // 색상목록 감추기
colorInput.removeEventListener('keyup', selectColor) // 화살표키로 색상목록을 선택하지 못하도록 키 이벤트 해제하기
}
}
사용자가 화살표키로 색상을 선택할때마다 해당 색상에 하이라이트를 적용한다.
function selectColor(e){ // 사용자가 화살표키로 색상을 선택할때 하이라이트 적용하기
console.log(e.keyCode) // 키보드 키에 대한 코드 값
if(e.keyCode){
changeHighLightItem(e.keyCode) // 사용자가 선택한 색상에 대한 인덱스 값 변경
unsetHightLightItem() // 사용자가 직전에 선택한 색상에 적용된 하이라이트 제거
highLightColorItem() // 사용자가 현재 선택한 색상에 대한 하이라이트 적용
}
}
changeHighLightItem 함수는 사용자가 입력한 키의 코드값을 이용하여 하이라이트를 적용할 색상 인덱스값을 변경한다. unsetHightLightItem 함수는 사용자가 선택한 이전 색상에 대한 하이라이트를 제거한다. highlightColorItem 함수는 사용자가 선택한 현재 색상에 하이라이트를 적용한다.
let index = -1
let prevIndex = -1
사용자가 화살표키로 선택한 현재 색상과 직전 색상을 알아내기 위하여 위와 같이 인덱스 변수를 선언한다. 초기값이 -1 인 이유는 색상 배열의 인덱스 값은 0부터 시작하므로 아무 색상도 선택하지 않았기 때문이다.
function changeHighLightItem(key){
prevIndex = index // 현재 사용자가 선택한 색상에 대한 인덱스값 저장하기
if(key === 40){ // 아래쪽 화살표키를 누른 경우 색상 인덱스 값 증가
index++
if(index > colors.length -1){ // 인덱스 값이 색상 배열의 마지막 인덱스를 벗어난 경우 다시 첫번째 색상을 선택하도록 0 으로 초기화
index = 0
}
}else if(key === 38){ // 위쪽 화살표키를 누른 경우 색상 인덱스 값 감소
index--
if(index < 0){ // 인덱스값이 0 보다 작은 경우 다시 마지막 색상을 선택하도록 색상 배열의 마지막 인덱스값으로 설정
index = colors.length -1
}
}
console.log('직전에 선택한 색상', prevIndex)
console.log('현재 선택한 색상', index)
}
사용자가 입력한 키의 코드값을 이용하여 하이라이트를 적용할 요소의 인덱스 값을 변경하는 함수이다. 다시 말해 사용자가 화살표키(위쪽, 아래쪽 방향)를 누르면 색상 배열에서 특정 색상을 선택하기 위하여 인덱스 값을 변경한다. prevIndex 변수를 이용하여 사용자가 직전에 선택한 색상에 대한 인덱스 값을 저장해둔다.
function unsetHightLightItem(colorIndex){
if(colorIndex >= 0 && colorIndex < colors.length){ // colorIndex 의 유효범위가 색상배열의 인덱스 값인 경우
const colorItem = document.querySelectorAll('.color-item')[colorIndex]
colorItem.classList.remove('highlight') // 해당 색상 엘리먼트에 대한 하이라이트 해제
}
}
사용자가 화살표키로 선택한 색상에 대한 하이라이트를 제거하는 함수이다. 다시 말해 파라미터로 주어진 colorIndex 에 해당하는 색상 엘리먼트를 조회하고, 해당 엘리먼트에 대한 하이라이트를 해제한다. colorIndex 값은 색상 배열의 인덱스 값 유효범위를 벗어날 수 없으므로 조건문을 사용하여 유효성 검증을 해준다.
prevIndex 값을 이용하여 사용자가 이전에 선택한 색상에 대한 하이라이트를 제거한다.
unsetHightLightItem(prevIndex) // 사용자가 직전에 선택한 색상에 적용된 하이라이트 제거
사용자가 직전에 선택한 색상에 대한 하이라이트를 해제하도록 prevIndex 값을 인자로 넘겨준다.
function highLightColorItem(colorIndex){
if(colorIndex >= 0 && colorIndex < colors.length){
const colorItem = document.querySelectorAll('.color-item')[colorIndex]
colorItem.classList.add('highlight') // 해당 색상 엘리먼트에 대한 하이라이트 적용
}
}
사용자가 화살표키로 선택한 색상에 대하여 하이라이트를 적용하는 함수이다. 다시 말해 파라미터로 주어진 colorIndex 에 해당하는 색상 엘리먼트를 조회하고, 해당 엘리먼트에 대한 하이라이트를 적용한다. colorIndex 값은 색상 배열의 인덱스 값 유효범위를 벗어날 수 없으므로 조건문을 사용하여 유효성 검증을 해준다.
highLightColorItem(index) // 사용자가 현재 선택한 색상에 대한 하이라이트 적용
사용자가 현재 선택한 색상에 대하여 하이라이트를 적용하도록 index 값을 인자로 넘겨준다. 이제 화살표키(위쪽, 아래쪽 방향)로 색상을 선택하면 하이라이트가 적용된다.
다음은 화살표키로 색상을 선택한 다음 엔터키를 누르면 마우스로 클릭하여 색상을 선택한것과 동일한 동작을 하도록 구현해보자!
function setColor(e){
console.log(e.target)
if(e.target.className === 'color-item'){
const pickedColor = e.target.innerText // 사용자가 마우스로 선택한 색상 문자열
setBackground(pickedColor) // 배경색 변경
}
}
마우스로 클릭할때와 엔터키를 누를때 동일한 동작을 하므로 코드가 중복된다. 중복되는 코드는 아래와 같이 setBackground 함수로 따로 빼낸다.
function setBackground(color){
console.log('you picked color !', color)
colorInput.value = color // 사용자가 선택한 색상을 입력창에 보여주기
colorList.classList.remove('show') // 색상 목록 감추기
colorBox.style.background = color // 사용자가 선택한 색상으로 화면 배경색 변경하기
}
엔터키의 키 코드는 13번이다. 엔터키를 누르면 현재 사용자가 선택한 색상을 조회하고, 해당 색상으로 화면의 배경색을 지정한다.
function changeHighLightItem(key){
prevIndex = index // 현재 사용자가 선택한 색상에 대한 인덱스값 저장하기
if(key === 40){ // 아래쪽 화살표키를 누른 경우 색상 인덱스 값 증가
index++
if(index > colors.length -1){ // 인덱스 값이 색상 배열의 마지막 인덱스를 벗어난 경우 다시 첫번째 색상을 선택하도록 0 으로 초기화
index = 0
}
}else if(key === 38){ // 위쪽 화살표키를 누른 경우 색상 인덱스 값 감소
index--
if(index < 0){ // 인덱스값이 0 보다 작은 경우 다시 마지막 색상을 선택하도록 색상 배열의 마지막 인덱스값으로 설정
index = colors.length -1
}
}else if(key === 13){ // 엔터키를 누른 경우 사용자가 화살표키로 선택한 현재 색상을 화면의 배경색으로 설정
const colorItem = document.querySelectorAll('.color-item')[index]
setBackground(colorItem.innerText)
}
console.log('직전에 선택한 색상', prevIndex)
console.log('현재 선택한 색상', index)
}
changeHighLightItem 함수를 위와 같이 수정하자. 아래 구문을 추가하면 된다.
else if(key === 13){ // 엔터키를 누른 경우 사용자가 화살표키로 선택한 현재 색상을 화면의 배경색으로 설정
const colorItem = document.querySelectorAll('.color-item')[index]
setBackground(colorItem.innerText)
}
하지만 사용자가 화살표키로 색상을 선택하고, 다시 입력창에 새로운 색상을 입력하면 이전에 화살표키로 선택한 색상에 하이라이트가 적용되어 있다. 다음번에는 마우스로 색상을 선택할 수도 있으므로 이전에 화살표키로 적용된 하이라이트는 색상을 선택할때마다 초기화해주는 것이 좋다.
function setBackground(color){
console.log('you picked color !', color)
colorInput.value = color // 사용자가 선택한 색상을 입력창에 보여주기
colorList.classList.remove('show') // 색상 목록 감추기
colorBox.style.background = color // 사용자가 선택한 색상으로 화면 배경색 변경하기
initHighlight() // 하이라이트 초기화
}
initHighlight 함수를 추가해준다.
function initHighlight(){
unsetHightLightItem(index) // 현재 인덱스 값에 대한 하이라이트 해제
index = -1 // 인덱스 값 초기화
prevIndex = -1
}
해당 함수에서는 사용자가 선택한 현재 색상에 대한 하이라이트를 해제해주고(스타일변경), 인덱스 값들도 초기화해준다. 이렇게 하면 마우스로 색상을 선택할때나 엔터키로 색상을 선택할때마다 하이라이트가 초기화된다. 즉, 다음번에 새로운 색상을 입력할때는 이전에 적용된 하이라이트가 보이지 않고, 화살표키로 색상을 고르더라도 첫번째 색상부터 선택된다.
하지만 또다른 문제가 존재한다. 색상을 선택한 직후에 화살표키를 누르면 색상목록이 화면에 보이지 않는데도 불구하고 색상에 대한 인덱스 값이 계속 변경된다. 콘솔에 인덱스 값이 계속 출력됨을 확인할 수 있다. 그리고 다시 입력창에 새로운 색상을 입력하면 (색상목록이 화면에 보이지 않을때 화살표키로 선택한) 하이라이트가 적용되어 있다. 즉, 색상목록이 화면에 보이지 않는데도 화살표키로 색상을 선택하게 된다.
function typeColor(e){
console.log(e.target.value)
if(e.target.value !== ''){
console.log('you typed something')
colorList.classList.add('show') // 색상목록 보여주기
colorInput.addEventListener('keyup', selectColor) // 화살표키로 색상목록을 선택하기 위한 키 이벤트 등록하기
}else{
console.log('you didn\'t type anything')
colorList.classList.remove('show') // 색상목록 감추기
colorInput.removeEventListener('keyup', selectColor) // 화살표키로 색상목록을 선택하지 못하도록 키 이벤트 해제하기
}
}
이러한 문제가 발생하는 이유는 위 코드처럼 입력창이 비어있지 않은 경우 키 이벤트가 계속 적용되기 때문이다.
function setBackground(color){
console.log('you picked color !', color)
colorInput.value = color // 사용자가 선택한 색상을 입력창에 보여주기
colorList.classList.remove('show') // 색상 목록 감추기
colorBox.style.background = color // 사용자가 선택한 색상으로 화면 배경색 변경하기
initHighlight() // 하이라이트 초기화
colorInput.removeEventListener('keyup', selectColor) // 색상목록이 안보일때는 화살표키로 색상을 선택하지 못하도록 키 이벤트 해제하기
}
문제를 해결하기 위해서는 위와 같이 마우스나 엔터키로 색상을 선택할때 키 이벤트를 해제해줘야 한다. 이렇게 하면 색상목록이 화면에 보이지 않을때는 키 이벤트가 동작하지 않기 때문에 색상을 선택할 수 없다. 즉, 색상 인덱스값이 변경되지 않는다.
* 파일 입력 처리하기
<!DOCTYPE html>
<html lang="en">
<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="style.css">
</head>
<body>
<input id="file-input" type="file">
<div id='img-box'></div>
<script src='app.js'></script>
</body>
</html>
index.html 파일을 위와 같이 작성하자! 파일을 업로드하기 위한 input 요소와 사용자가 업로드한 파일을 보여주기 위한 div 요소를 정의한다.
body{
margin: 0;
padding: 0;
}
#file-input{
position: absolute;
left: 50%;
top: 100px;
transform: translate(-50%);
}
#img-box{
width: 200px;
height: 250px;
overflow: hidden;
position: absolute;
left: 50%;
top: 200px;
transform: translate(-50%);
}
#img-box img{
width: 100%;
height: 100%;
}
style.css 파일을 위와 같이 작성하자!
const fileInput = document.getElementById('file-input')
const imgBox = document.getElementById('img-box')
function isValid(type){
return type.split('/')[0] === "image" // "image/jpg" => ["image", "jpg"]
}
function displayImg(e){
console.log(e.target.files) // 사용자가 업로드한 파일 배열
const file = e.target.files[0]
if(!isValid(file.type)){
imgBox.innerHTML = 'File type not valid !'
return;
} // 파일 타입 검증
const img = document.createElement('img') // 이미지 태그 동적으로 생성
img.src = URL.createObjectURL(file) // 업로드한 파일의 임시경로 (이미지 경로)
imgBox.innerHTML = '' // 다음 이미지를 보여주기 위해서 imgBox 초기화
imgBox.appendChild(img) // 이미지 보여주기
}
fileInput.addEventListener('change', displayImg)
app.js 파일을 위와 같이 작성하자!
const fileInput = document.getElementById('file-input')
사용자가 파일을 업로드하는 경우의 이벤트를 등록하기 위하여 input 요소를 검색한다.
const imgBox = document.getElementById('img-box')
사용자가 업로드한 파일을 보여주기 위하여 img-box 요소를 검색한다.
fileInput.addEventListener('change', displayImg)
input 요소에 change 이벤트를 등록하고, displayImg 라는 이벤트핸들러 함수를 연결한다. change 이벤트는 input 요소의 type 속성이 file 인 경우에 사용자가 파일을 업로드할때 발생한다.
function displayImg(e){
console.log(e.target.files) // 사용자가 업로드한 파일 배열
const file = e.target.files[0]
if(!isValid(file.type)){
imgBox.innerHTML = 'File type not valid !'
return;
} // 파일 타입 검증
const img = document.createElement('img') // 이미지 태그 동적으로 생성
img.src = URL.createObjectURL(file) // 업로드한 파일의 임시경로 (이미지 경로)
imgBox.innerHTML = '' // 다음 이미지를 보여주기 위해서 imgBox 초기화
imgBox.appendChild(img) // 이미지 보여주기
}
사용자가 파일을 업로드할때 실행되는 이벤트핸들러 함수이다.
console.log(e.target.files)
e.target.files 는 아래와 같이 사용자가 업로드하는 파일에 대한 정보를 담고 있는 배열이다.
const file = e.target.files[0]
사용자가 업로드한 파일 데이터를 조회한다.
if(!isValid(file.type)){
imgBox.innerHTML = 'File type is not valid !'
return;
}
사용자가 업로드하는 파일에 대한 유효성 검증을 하는 코드이다. 파일 유형(type)이 이미지가 아니면 유효성 검증은 실패하고, 화면에 'File type is not valid !' 라는 문구를 보여준다.
const img = document.createElement('img')
img.src = URL.createObjectURL(file)
imgBox.appendChild(img)
파일 유효성 검증을 통과하면 img 요소를 동적으로 생성한 후 화면에 보여준다. URL 객체의 createObjectURL 메서드는 file 데이터로부터 이미지 경로(URL) 를 생성해준다.
function isValid(type){
return type.split('/')[0] === 'image'
}
파일의 유효성을 검증하는 함수이다. 파일 유형(type) 에 대한 값을 입력으로 전달받고, 문자열의 split 메서드를 사용하여 해당 파일이 image 인지 검증한다.
이번에는 사용자가 업로드한 파일의 실제 데이터를 비동기적으로 읽어서 현재 얼마만큼 업로드가 진행이 되었는지 표시하는 프로그레스바(progress bar) 를 만들어보자!
<!DOCTYPE html>
<html lang="en">
<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="style.css">
</head>
<body>
<input id="file-input" type="file">
<div class='progress-bar'>
<div class="progress-indicator"></div>
<div class="progress-number">0%</div>
</div>
<script src='app.js'></script>
</body>
</html>
index.html 파일을 위와 같이 작성하자! 파일을 업로드할 수 있는 input 요소와 파일 업로드 진행사항을 표시할 progress-bar 요소가 있다.
body{
margin: 0;
padding: 0;
}
#file-input{
position: absolute;
left: 50%;
top: 100px;
transform: translate(-50%);
}
.progress-bar{
width: 500px;
height: 20px;
border: 2px solid orange;
border-radius: 12px;
position: absolute;
left: 50%;
top: 200px;
transform: translate(-50%);
}
.progress-indicator{
width: 0%;
height: 20px;
background: orange;
border-radius: 8px;
}
.progress-number{
float: right;
font-size: 1.5rem;
}
style.css 파일을 위와 같이 작성하자!
const fileInput = document.getElementById('file-input') // 파일 업로드를 처리하기 위한 요소
const progressIndicator = document.querySelector('.progress-indicator') // 파일 업로드 진행률을 표시하기 위한 바
const progressNumber = document.querySelector('.progress-number') // 파일 업로드 진행률(%)을 숫자로 표시하기 위한 요소
function handleFileUpload(e){
const file = e.target.files[0]
console.log(file)
const fileReader = new FileReader(); // 파일 데이터를 비동기적으로 읽어들이기 위한 객체
fileReader.readAsDataURL(file); // 파일 데이터를 base64 문자열 형식으로 읽어들이기 시작함
fileReader.onloadstart = () => { // 파일을 읽기 시작할때 처리할 이벤트핸들러 함수
console.log("loadstart");
};
fileReader.onprogress = (e) => { // 파일을 읽는동안 처리할 이벤트핸들러 함수
console.log("onpregress");
console.log("progress: ", e.loaded / e.total * 100 )
const progress = parseInt(e.loaded / e.total * 100) + '%' // 파일 업로드 현재 진행률
progressIndicator.style.width = progress // 현재 진행률을 이용한 프로그레스바 설정
progressNumber.innerText = progress // 현재 진행률를 이용한 숫자 표시
};
fileReader.onloadend = (finishedEvent) => { // 파일 읽기가 끝났을때 처리할 이벤트핸들러 함수
console.log("onloadend");
};
fileReader.onload = function(e) { // 읽어들인 파일 데이터를 조회할때 실행할 이벤트핸들러 함수
console.log(e.target.result); // base64 인코딩된 값
}
}
fileInput.addEventListener('change', handleFileUpload)
app.js 파일을 위와 같이 작성하자!
const fileInput = document.getElementById('file-input') // 파일 업로드를 처리하기 위한 요소
파일 업로드에 대한 이벤트를 처리하기 위하여 input 요소를 가져온다.
const progressIndicator = document.querySelector('.progress-indicator') // 파일 업로드 진행률을 표시하기 위한 바
const progressNumber = document.querySelector('.progress-number') // 파일 업로드 진행률(%)을 숫자로 표시하기 위한 요소
파일 업로드에 대한 현재 진행률을 표시하기 위하여 관련된 요소들을 가져온다.
fileInput.addEventListener('change', handleFileUpload)
사용자가 업로드할 파일을 선택할때 실행할 이벤트핸들러를 연결한다.
function handleFileUpload(e){
const file = e.target.files[0]
console.log(file)
const fileReader = new FileReader(); // 파일 데이터를 비동기적으로 읽어들이기 위한 객체
fileReader.readAsDataURL(file); // 파일 데이터를 base64 문자열 형식으로 읽어들이기 시작함
fileReader.onloadstart = () => { // 파일을 읽기 시작할때 처리할 이벤트핸들러 함수
console.log("loadstart");
};
fileReader.onprogress = (e) => { // 파일을 읽는동안 처리할 이벤트핸들러 함수
console.log("onpregress");
console.log("progress: ", e.loaded / e.total * 100 )
const progress = parseInt(e.loaded / e.total * 100) + '%' // 파일 업로드 현재 진행률
progressIndicator.style.width = progress // 현재 진행률을 이용한 프로그레스바 설정
progressNumber.innerText = progress // 현재 진행률를 이용한 숫자 표시
};
fileReader.onloadend = (finishedEvent) => { // 파일 읽기가 끝났을때 처리할 이벤트핸들러 함수
console.log("onloadend");
};
fileReader.onload = function(e) { // 읽어들인 파일 데이터를 조회할때 실행할 이벤트핸들러 함수
console.log(e.target.result); // base64 인코딩된 값
}
}
사용자가 업로드할 파일을 선택하면 실행되는 이벤트핸들러 함수이다.
const file = e.target.files[0]
console.log(file)
업로드한 파일을 file 변수에 저장하고, 콘솔에 파일 정보를 출력한다.
const fileReader = new FileReader(); // 파일 데이터를 비동기적으로 읽어들이기 위한 객체
fileReader.readAsDataURL(file); // 파일 데이터를 base64 문자열 형식으로 읽어들이기 시작함
FileReader 는 사용자가 업로드한 파일 데이터를 비동기적으로 읽어들이는 객체이다. FileReader 객체의 readAsDataURL 메서드는 파일 데이터를 인자로 받아서 파일 데이터를 읽기 시작한다. 읽어들인 파일 데이터는 base64 문자열 형태이다.
fileReader.onprogress = (e) => { // 파일을 읽는동안 처리할 이벤트핸들러 함수
console.log("onpregress");
console.log("progress: ", e.loaded / e.total * 100 )
const progress = parseInt(e.loaded / e.total * 100) + '%' // 파일 업로드 현재 진행률
progressIndicator.style.width = progress // 현재 진행률을 이용한 프로그레스바 설정
progressNumber.innerText = progress // 현재 진행률를 이용한 숫자 표시
};
FileReader 객체는 파일을 읽어들이는 동안 발생하는 이벤트에 대하여 이를 처리할 이벤트핸들러 함수를 연결할 수 있다. onprogress 는 파일을 읽어들이는 중에 처리할 이벤트핸들러 함수이다. e.loaded 는 현재까지 읽어들인 파일 데이터의 크기이다. e.total 은 파일의 전체 크기이다. 이를 이용하여 파일 업로드의 현재 진행률(%)을 계산할 수 있다.
https://harunao.net/self-study/css-perspective-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0/
* 3D 카드 슬라이더
<!DOCTYPE html>
<html lang="en">
<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="style.css">
</head>
<body>
<div id='root'>
<div id='card-container'>
<div class='card'>
<img src="https://media.istockphoto.com/photos/forest-wooden-table-background-summer-sunny-meadow-with-green-grass-picture-id1353553203?b=1&k=20&m=1353553203&s=170667a&w=0&h=QTyTGI9tWQluIlkmwW0s7Q4z7R_IT8egpzzHjW3cSas="/>
<div class='card-text'>
<div>John Dow</div>
<div class='job'>Web Designer</div>
</div>
</div>
<div class='card'>
<img src="https://digitalassets-retail.cdn-apple.com/retail-image-server/a90/c85/1bd/c2d/1af/8ba/a2a/349/93e/fe2/11b8d32a-7f9f-372c-bca3-0a05d871e8c7_iMac_Wallpaper_4480x2520.jpg"/>
<div class='card-text'>
<div>John Dow</div>
<div class='job'>Web Designer</div>
</div>
</div>
<div class='card'>
<img src="http://file.instiz.net/data/file/20121125/5/6/0/5609249762358c2bcb65a46580549c99"/>
<div class='card-text'>
<div>John Dow</div>
<div class='job'>Web Designer</div>
</div>
</div>
<div class='card'>
<img src="https://s1.pearlcdn.com/KR/Upload/Community/434174d044320230412231123488.jpg"/>
<div class='card-text'>
<div>John Dow</div>
<div class='job'>Web Designer</div>
</div>
</div>
<div class='card'>
<img src='https://www.institutostrom.org/wp-content/uploads/2018/04/NZ.jpg'/>
<div class='card-text'>
<div>John Dow</div>
<div class='job'>Web Designer</div>
</div>
</div>
</div>
<div id='control-btns'>
<button id='prev' class='c-btn'>Prev</button>
<button id='next' class='c-btn'>Next</button>
</div>
</div>
<script src='app.js'></script>
</body>
</html>
index.html 파일을 위와 같이 작성하자!
body{
margin: 0;
padding: 0;
background: #0e1111;
}
#root{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
#card-container{
perspective: 200px;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 100px;
}
.card{
width: 300px;
height: 400px;
overflow: hidden;
user-select: none;
transition: all .3s ease-out;
background-color: white;
}
.card img{
width: 100%;
height: 80%;
background-color: white;
}
.card-text{
background-color: white;
padding: 10px;
text-align: center;
font-weight: bold;
}
.job{
color:red;
}
.card:nth-child(1){
transform: rotateY(40deg) ;
z-index: 1;
width: 200px;
height: 300px;
}
.card:nth-child(2){
transform: rotateY(20deg) ;
z-index: 2;
width: 250px;
height: 350px;
}
.card:nth-child(3){
transform:rotateY(0deg);
z-index: 3;
}
.card:nth-child(4){
transform: rotateY(-20deg) ;
z-index: 2;
width: 250px;
height: 350px;
}
.card:nth-child(5){
transform: rotateY(-40deg) ;
z-index: 1;
width: 200px;
height: 300px;
}
.c-btn{
all: unset;
width: 100px;
height: 50px;
border-radius: 10px;
border: 2px solid wheat;
color: wheat;
text-align: center;
cursor: pointer;
margin-right: 20px;
user-select: none;
}
style.css 파일을 위와 같이 작성하자!
const prev = document.getElementById('prev')
const next = document.getElementById('next')
const cardContainer = document.getElementById('card-container')
function moveToRight(){
console.log('right')
const cards = document.querySelectorAll('.card')
setTimeout(function(){
cardContainer.removeChild(cards[cards.length-1]) // 컨테이너 마지막 카드 제거
cardContainer.insertBefore(cards[cards.length-1], cardContainer.childNodes[0]) // 마지막 카드를 맨앞에 추가
}, 150)
}
function moveToLeft(){
console.log('left')
const cards = document.querySelectorAll('.card')
setTimeout(function(){
cardContainer.removeChild(cards[0]) // 컨테이너 첫번째 카드 제거
cardContainer.appendChild(cards[0]) // 첫번째 카드를 마지막에 추가
}, 150)
}
prev.addEventListener('click', moveToRight)
next.addEventListener('click', moveToLeft)
app.js 파일을 위와 같이 작성하자!
body{
margin: 0;
padding: 0;
background: #0e1111;
}
#root{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
#card-container{
perspective: 200px;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 100px;
}
.card{
width: 300px;
height: 400px;
overflow: hidden;
user-select: none;
transition: all .3s ease-in;
background-color: white;
}
.card img{
width: 100%;
height: 80%;
background-color: white;
}
.card-text{
background-color: white;
padding: 10px;
text-align: center;
font-weight: bold;
}
.job{
color:red;
}
.card:nth-child(1){
transform: rotateY(40deg) ;
z-index: 1;
width: 200px;
height: 300px;
}
.card:nth-child(2){
transform: rotateY(20deg) translateZ(20px);
z-index: 2;
width: 250px;
height: 350px;
}
.card:nth-child(3){
transform:rotateY(0deg) translateZ(60px);
z-index: 3;
}
.card:nth-child(4){
transform: rotateY(-20deg) translateZ(20px);
z-index: 2;
width: 250px;
height: 350px;
}
.card:nth-child(5){
transform: rotateY(-40deg) ;
z-index: 1;
width: 200px;
height: 300px;
}
.c-btn{
all: unset;
width: 100px;
height: 50px;
border-radius: 10px;
border: 2px solid wheat;
color: wheat;
text-align: center;
cursor: pointer;
margin-right: 20px;
user-select: none;
}
각각의 카드에 translateZ 설정을 추가하면 조금더 역동적인 느낌이 난다. 문제는 appendChild 메서드로 첫번째 카드를 마지막에 추가하면서 마지막에 추가된 카드가 나중에 렌더링된다. 그래서 바로 앞부분의 카드보다 위쪽에 덮는 현상이 발생하고 있다.
한가지 문제점은 다음 버튼 클릭시 첫번째 사진이 마지막으로 이동하면서 앞에 있는 사진을 가리는 현상이 발생한다.
const prev = document.getElementById('prev')
const next = document.getElementById('next')
const cardContainer = document.getElementById('card-container')
function moveToRight(){
console.log('right')
const cards = document.querySelectorAll('.card')
setTimeout(function(){
cardContainer.removeChild(cards[cards.length-1]) // 컨테이너 마지막 카드 제거
cardContainer.insertBefore(cards[cards.length-1], cardContainer.childNodes[0]) // 마지막 카드를 맨앞에 추가
}, 150)
}
function moveToLeft(){
console.log('left')
const cards = document.querySelectorAll('.card')
setTimeout(function(){
cards[0].style.opacity = '0' // 첫번째 카드 숨기기
cardContainer.removeChild(cards[0]) // 컨테이너 첫번째 카드 제거 (카드 이동)
cardContainer.appendChild(cards[0]) // 첫번째 카드를 마지막에 추가 (카드 이동)
setTimeout(function(){
cards[0].style.opacity = '1' // 첫번째 카드 보여주기
}, 100)
}, 150)
}
prev.addEventListener('click', moveToRight)
next.addEventListener('click', moveToLeft)
첫번째 사진을 마지막에 추가할때 잠깐 화면에서 가렸다가 이동이 끝난 다음 보여주는 식으로 처리하여 문제를 해결했다.
#card-container{
border: 1px solid red;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 100px;
perspective: 200px;
height: 400px; /* 버튼 위치가 변경되는 문제 해결 */
}
슬라이드가 이동할때 버튼이 아래 위로 움직이는데 이는 슬라이드를 감싸고 있는 컨테이너 높이가 계속 변경되기 때문이다. 이를 해결하기 위하여 컨테이너 높이를 가장 큰 슬라이드의 높이로 설정하였다.
* 연습과제 1
사용자 입력 처리하기 예제코드를 참고하여 사용자가 뭔가를 입력할때 랜덤으로 배경색상이 변경되도록 해보자!
* 연습과제 2
파일 입력 처리하기 예제코드에서 파일을 업로드하는 input 요소의 디자인이 별로 좋지가 않다. 그래서 input 요소는 숨기고 버튼을 추가한 다음 버튼을 클릭했을때 input 요소가 클릭되도록 해보자!
fileInput.click()
input 요소를 자바스크립트를 사용하여 동적으로 클릭하는 코드는 위와 같다.
* 연습과제 3
파일 입력 처리하기 예제 코드에서 하나의 파일이 아니라 여러개의 파일을 업로드하고 화면에 디스플레이해보자! 여러개의 파일을 업로드하려면 input 의 속성에 multiple 을 추가하면 된다. (단, 업로드한 사진을 보여줄때 아래 캡쳐화면과 같은 레이아웃을 잡아서 보여주세요!)
1. 이미지의 높이는 고정폭으로 설정한다.
2. 플럭스박스를 사용한다.
* 연습과제 4
위아래 화살표키로 색상을 선택해서 하이라이트가 적용되어 있는 상태에서 마우스 호버로 색상을 선택하면 기존에 화살표키로 선택되어 있던 하이라이트는 해제될 수 있도록 해보자!
* 연습과제 5
슬라이드 예제에서 확대 축소를 키 이벤트를 사용해서 해보세요! e 키를 누르면 확대되고, esc 키를 누르면 축소되도록 한다.
연습과제 6
회원가입이나 로그인에서 사용자 입력을 처리할때는 유효성 검증이 필요하다. 예를 들어 비밀번호를 입력받을때 특수문자나 숫자를 포함하도록 한다. 그래서 입력창과 버튼을 추가하고, 만약 사용자가 비밀번호 입력후 로그인 버튼을 클릭했을때 특수문자나 숫자를 포함하지 않으면 경고창을 띄워서 사용자에게 안내하는 프로그램을 작성해보자! (단, 정규표현식을 사용하지 말고 구현하기)
연습과제 7
회원가입이나 로그인에서 사용자 입력을 처리할때는 유효성 검증이 필요하다. 그래서 사용자에게 비밀번호를 입력받을때 특수문자나 숫자가 포함되어 있지 않으면 안전하지 않다고 판단하여 색상으로 표시해준다. 사용자가 비밀번호를 입력하고 로그인 버튼을 클릭했을때 아래 4가지 경우에 대하여 조건을 충족하는지 체크하여 해당 비밀번호의 안전도를 화면에 표시해보자!
1. 영문자 소문자를 포함하고 있는가?
2. 영문자 대문자를 포함하고 있는가?
3. 숫자를 포함하고 있는가?
4. 특수문자를 포함하고 있는가?
1개의 조건을 만족하면 붉은색, 2~3개의 조건을 만족하면 주황색, 4개의 조건을 모두 만족하면 초록색으로 안전도를 프로그레스바로 화면에 표시하도록 한다.
'프론트엔드 > Javascript' 카테고리의 다른 글
자바스크립트 문법 7 - 배열(Array) 의 다양한 메서드 1 (검색과 조회) (0) | 2021.12.27 |
---|---|
자바스크립트 문법 5 - 이벤트(Event) 처리하기 3 (0) | 2021.12.23 |
자바스크립트 문법 3 - 이벤트(Event) 처리하기 (0) | 2021.11.30 |
에러 처리 (Error handling) (0) | 2021.10.09 |
자바스크립트 문법 14 - 비동기 처리 (Asynchronouse) (0) | 2021.10.09 |