안녕하세요.
이 공간은 제가 프론트엔드를 처음 접하며 배운 것을 주저리주저리 쓰는 공간입니다.
이번 내용은 javascript 9편입니다.
지금까지는 아래 코드처럼 이벤트 내부에서 문서 객체를 사용해 정보를 추출했는데요.
document.addEventListener('DOMContentLoaded'. () => {
const textarea = document.querySelector('textarea');
const h1 = document.querySelector('h1');
textarea.addEventListener('keyup', (event) => {
const length = textarea.value.length;
h1.textContent = `글자 수:${length}`;
})
})
상황에 따라서는 eventListner 내부에서 변수에 접근할 수 없는 경우도 있습니다.
아래 코드에서는 listener() 함수 내부에서 textarea 변수에 접근하지 못해 오류가 발생합니다.
const listener = (event) => {
// 현재 블록에서는 textarea 변수 사용이 불가능합니다.
const length = textarea.value.length;
h1.textarea = `글자 수: ${length}`;
};
// addEventListener() 메소드가 밖으로 분리되었습니다.
document.addEventListener('DOMContentLoaded', () => {
const textarea = document.querySelector('textarea');
const h1 = document.querySelector('h1');
textarea.addEventListener('keyup', listener);
})
코드의 규모가 커지면 커질수록 addEventListener() 메소드를 외부로 분리하는 경우가 많아집니다.
이러한 경우, 두 가지 방법으로 이벤트를 발생시킨 객체에 접근할 수 있을까요?
화살표 함수, 일반 함수 모두 사용 가능합니다.
일반 함수에서 사용 가능합니다.
화살표 함수가 등장하기 전 2번 위주로 사용했지만, 현재는 두 가지 모두 범용적으로 사용됩니다.
물론, 라이브러리와 프레임워크를 사용하는 경우 어떤 형태가 더 일반적으로 사용되는지는 달라집니다.
첫번째로 evnet.currebtTarget 속성을 사용하는 예제입니다.
const listener = (event) => {
// event.currentTarget이 textarea가 됩니다.
const length = event.currentTarget.value.length
h1.textarea = `글자 수: ${length}`
}
document.addEventListener('DOMContentLoaded', () => {
const textarea = document.querySelector('textarea')
const h1 = document.querySelector('h1')
textarea.addEventListener('keyup', listener)
})
event.currentTarget 속성은 실제 이벤트가 일어나는 textarea를 가리키게 됩니다.
두번째로 this 키워드를 사용하는 예제입니다.
const listener = function (event) {
const length = this.value.length
h1.textContent = `글자 수: ${length}`
}
document.addEventListener('DOMContentLoaded', () => {
const textarea = document.querySelector('textarea')
const h1 = document.querySelector('h1')
textarea.addEventListener('keyup', listener)
})
사용자로부터 입력을 받을 때 사용하는 요소를 입력양식이라 합니다.
HTML에서는 input, textarea, button, select 태그 등이 입력 양식입니다.
아래 예시는 이러한 입력 양식을 기반으로 inch를 cm 단위로 변환하는 프로그램입니다.
<!-- HTML -->
<body>
<input type="text" /> inch<br>
<button>계산</button>
<p></p>
</body>
// JS
document.addEventListener('DOMContentLoaded', () => {
const input = document.querySelector('input')
const button = document.querySelector('button')
const p = document.querySelector('p')
button.addEventListener('click', () => {
const inch = Number(input.value)
if (isNaN(inch)) {
p.textContent = '숫자를 입력하세요!'
return
}
const cm = inch * 2.54
p.textContent = `${cm} cm`
})
})


사용자가 숫자를 입력하지 않는 경우를 대비해 if 조건문에 isNaN() 함수로 구별했습니다.
만약 isNaN() 함수의 결과가 true로 나오는 숫자가 아닌 경우 바로 return 키워드를 실행, 이후 코드는 실행되지 않습니다.
이처럼 else 키워드를 사용하지 않는 방식을 '조기 리턴'이라고 합니다.
만약 else 키워드를 사용한다면 를 사용한다면?
document.addEventListener('DOMContentLoaded', () => {
const input = document.querySelector('input')
const button = document.querySelector('button')
const p = document.querySelector('p')
button.addEventListener('click', () => {
const inch = Number(input.value)
if (isNaN(inch)) {
p.textContent = '숫자를 입력하세요!'
} else {
// return에서 변환되는 값이 else 반환 조건에 붙습니다.
const cm = inch * 2.54
p.textContent = `${cm} cm`
}
})
})
들여쓰기 단계가 하나 늘어나는 모습입니다.
아래 예시는 이메일 형식을 확인하는 예시입니다.
<!-- HTML -->
<body>
<input type="text" />
<br>
<p></p>
</body>
// JS
document.addEventListener('DOMContentLoaded', () => {
const input = document.querySelector('input')
const p = document.querySelector('p')
const isEmail = (value) => {
// 골뱅이를 가지고 있고 && 골뱅이 뒤에 점이 있다면 리턴합니다.
return (value.indexOf('@') > 1) && (value.split('@')[1].indexOf('.') > 1)
}
input.addEventListener('keyup', (event) => {
const value = event.currentTarget.value
if (isEmail(value)) {
p.style.color = 'green'
p.textContent = `올바른 이메일 형식입니다: ${value}`
} else {
p.style.color = 'red'
p.textContent = `잘못된 이메일 형식입니다: ${value}`
}
})
})


isEmail() 함수는 indexOf() 함수를 통해 @와 .이 있는지 확인합니다.
그렇게 전달된 값이 이메일인지 확인한 후, 위 사진처럼 true 혹은 false를 리턴하죠.
물론 javascript@JS. 이렇게만 입력해도 올바른 이메일 형식이라고 나옵니다.
일반적으로 이런 유효성 검사는 정규 표현식을 사용합니다.
드롭다운 목록은 select 태그로 구현됩니다.
<!-- HTML -->
<body>
<select>
<option>Alpha</option>
<option>Brovo</option>
<option>Charlie</option>
<option>Delta</option>
<option>Echo</option>
<option>Foxtrot</option>
</select>
<!-- 처음에는 Alpha가 출력되도록 초기값을 지정했습니다. -->
<p>선택: Alpha</p>
</body>
// JS
document.addEventListener('DOMContentLoaded', () => {
const select = document.querySelector('select')
const p = document.querySelector('p')
select.addEventListener('change', (event) => {
const options = event.currentTarget.options
const index = event.currentTarget.options.selectedIndex
// 선택한 option 태그를 추출합니다
p.textContent = `선택: ${options[index].textContent}`
})
})
코드를 실행하고 드롭다운 목록에서 항목을 선택하면,
options[index]에서 선택한 option 태그가 출력됩니다.


위 코드에서는 textContent 속성을 바로 추출해서 사용했지만,
option 태그에 다른 속성을 부여하고 속성을 활용할 수도 있습니다.
select 태그에 multiple 속성을 부여하면, ctrl 혹은 shift 키를 누르고 여러 항목을 중복선택할 수 있는 상자가 나옵니다.
multiple select 태그는 사용 방법이 다소 특이합니다.
<!-- HTML -->
<body>
<select multiple>
<option>Alpha</option>
<option>Brovo</option>
<option>Charlie</option>
<option>Delta</option>
<option>Echo</option>
<option>Foxtrot</option>
</select>
<p></p>
</body>
// JS
document.addEventListener('DOMContentLoaded', () => {
const select = document.querySelector('select')
const p = document.querySelector('p')
select.addEventListener('change', (event) => {
const options = event.currentTarget.options
const list = []
for (const option of options) {
if (option.selected) {
list.push(option.textContent)
}
}
p.textContent = `선택: ${list.join(',')}`
})
})
option 속성으로 모든 속성을 선택하고 반복문을 돌린 후에,
selected 속성으로 선택된 요소를 확인합니다.
저 코드를 실행한 후 ctrl 키를 누르고 요소를 선택하면, 다중으로 선택되는 모습을 볼 수 있습니다.


그렇게 선택된 요소는 join 메소드를 통해 , 로 구분되어 화면에 표시되죠.
cm 단위를 여러 단위로 변환하는 예제를 살펴보겠습니다.
document.addEventListener('DOMContentLoaded', () => {
let presentValue // 현재값
let transmutationConstant = 10 // 변환상수
const select = document.querySelector('select')
const input = document.querySelector('input')
const span = document.querySelector('span')
const calculate = () => {
// toFixed() 메소드를 통해 소숫점 2번째 자리까지만 출력합니다.
span.textContent = ( presentValue * transmutationConstant ).toFixed(2)
}
select.addEventListener('change', (event) => {
const options = event.currentTarget.options
const index = event.currentTarget.options.selectedIndex
// 항목을 선택하면 항목의 value 값을 추출합니다.
transmutationConstant = Number(options[index].value)
calculate()
})
input.addEventListener('keyup', (event) => {
// 값을 입력하면 현재 값을 추출합니다.
presentValue = Number(event.currentTarget.value)
calculate()
})
})

빈 공란에 값을 입력하면 어떻게 될까요?



체크박스는 입력양식의 checked 속성을 사용합니다.
<!-- HTML -->
<body>
<input type="checkbox">
<span>타이머 활성화</span>
<h1></h1>
</body>
// JS
document.addEventListener('DOMContentLoaded', () => {
let [timer, timerId] = [0, 0]
const h1 = document.querySelector('h1')
const checkbox = document.querySelector('input')
checkbox.addEventListener('change', (event) => {
// 체크 상태를 확인합니다.
// checked 속성을 활용.
if (event.currentTarget.checked) {
timerId = setInterval(() => {
timer += 1
h1.textContent = `${timer}초`
}, 1000)
} else {
// 체크 해제 상태
clearInterval(timerId)
}
})
})
예제에서는 change 이벤트가 발생할 때 체크박스의 상태를 확인하고,
setInterval()함수 혹은 clearInterval()함수를 실행합니다.

처음에는 빈 화면이 나오지만, 체크박스를 클릭하면

1초마다 글자가 바뀌며 타이머가 작동합니다.
그리고 체크를 해제하면,

이렇게 타이머가 해당 초에서 정지합니다.
라디오 버튼도 체크 박스와 마찬가지로 checked 속성을 사용합니다.
<!-- HTML -->
<body>
<h3>좋아하는 애완동물을 선택해주세요!</h3>
<input type="radio" name="pet" value="강아지">
<span>강아지</span>
<input type="radio" name="pet" value="고양이">
<span>고양이</span>
<input type="radio" name="pet" value="고슴도치">
<span>고슴도치</span>
<input type="radio" name="pet" value="앵무새">
<span>앵무새</span>
<!-- radio 버튼을 하나씩 선택하게 하려면 name 속성을 통일해 그룹화해줍니다. -->
<hr>
<h3 id="output"></h3>
</body>
// JS
// 문서 객체 추출하기
document.addEventListener('DOMContentLoaded', () => {
const output = document.querySelector('#output')
const radios = document.querySelector('[name=pet]')
// 모든 라디오 버튼에
radios.forEach((radio) => {
// 이벤트 연결
radio.addEventListener('change', (event) => {
const current = event.currentTarget
if (current.checked) {
output.textContent = `좋아하는 애완동물은 ${current.value}이군요!`
}
})
})
})
이미지에서 마우스 오른쪽 버튼을 눌러보면 컨텍스트 메뉴가 등장하는데요.
이렇게 웹 브라우저가 기본적으로 처리해주는 것을 기본 이벤트라고 부릅니다.
링크 이동, 제출 클릭 이동 등 기본 이벤트를 제거할 땐 event 객체의 preventDefault() 메소드를 사용합니다.
<!-- HTML -->
<body>
<img src="https://imgnews.pstatic.net/image/477/2022/12/02/0000397906_001_20221202100102079.jpg?type=w647" alt="html">
</body>
// JS
document.addEventListener('DOMContentLoaded', () => {
const imgs = document.querySelector('img')
imgs.forEach((img) => {
img.addEventListener('contextmenu', (event) => {
event.preventDefault()
})
});
})