1. 상황
이미 존재하는 버튼에 click 이벤트를 JavaScript로 동적으로 넣을 경우 addEventListener를 사용할 수 있다.
이벤트 발생시 동작하는 function에 매개변수를 각 열마다 다르게 전달하기 위해, 매개 변수를 동적으로 할당해야한다.
그러나 JavaScript는 비동기로 동작한다는 점을 잘 고려하지 못하여 시행착오 끝에 해당 기능을 개발할 수 있었다.
기초적인 기능이라 그런지 오히려 자료들이 많지 않았기에 해결 과정을 정리해둔다.
2. 과정
버튼0 부터 버튼 3까지 존재하는 버튼i를 만들어서
버튼i을 클릭하면 "button click {i}"를 로그로 남기는 기능을 만드는 것이 목표이다.
2.1. 테스트 1
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>자바스크립트 테스트 페이지</title>
</head>
<body>
<script>
window.onload = function() {
for (var i = 0; i < 4; i++) {
let element = document.getElementById('button' + i);
console.log("i = " + i);
element.addEventListener('click', function() {
//핵심로직
console.log("button click " + i);
});
}
}
</script>
<button type="button" id="button0">버튼0</button>
<button type="button" id="button1">버튼1</button>
<button type="button" id="button2">버튼2</button>
<button type="button" id="button3">버튼3</button>
</body>
</html>
window.onload()를 보면 for문 안에 addEventListener를 적용하고 있다.
그러나 for문과 addEventListener가 비동기적으로 적용이 되는 점을 고려하지 못했다.
결과적으로 어떤 버튼을 누르던 "button click 4"를 반환해서 원하는데로 동작하지 않는다.
그렇다면 핵심 로직("button click {i}"를 로그로 남기기)을 별도의 function으로 감싸면
function이 따로 적용되어 위 문제가 해결되지 않을까?
2.2. 테스트 2
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>자바스크립트 테스트 페이지</title>
</head>
<body>
<script>
window.onload = function() {
for (var i = 0; i < 4; i++) {
let element = document.getElementById('button' + i);
console.log("i = " + i);
element.addEventListener('click', sendITest1(i));
}
}
function sendITest1(i) {
//핵심로직
console.log("button click " + i);
}
</script>
<button type="button" id="button0">버튼0</button>
<button type="button" id="button1">버튼1</button>
<button type="button" id="button2">버튼2</button>
<button type="button" id="button3">버튼3</button>
</body>
</html>
addEventListener에 들어가는 핵심 로직을 sendITest1 함수에 넣고,
sendITest1를 addEventListener에 전달하고 있다.
결과적으로 위 addEventListener가 실행되는 시점에 sendITest1 까지 바로 실행이 되어
모든 버튼들이 그때 클릭이 되는 것처럼 인식되고
html을 여는 시점에 "button click {i}" 로그가 전부 찍히고 만다.
그렇다면 addEventListener 자체를 별도의 함수로 감싸서 for문에서 이를 호출하는 것은 어떨까?
2.3. 테스트 3
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>자바스크립트 테스트 페이지</title>
</head>
<body>
<script>
window.onload = function() {
for (var i = 0; i < 4; i++) {
console.log("i = " + i);
sendITest2(i)
}
}
function sendITest2(i) {
let element = document.getElementById('button' + i);
element.addEventListener('click', function() {
//핵심로직
console.log("button click " + i);
});
}
</script>
<button type="button" id="button0">버튼0</button>
<button type="button" id="button1">버튼1</button>
<button type="button" id="button2">버튼2</button>
<button type="button" id="button3">버튼3</button>
</body>
</html>
for문에서는 단순히 sendITest2를 호출하기만 하고, sendITest2에서 addEventListener를 버튼에 추가한다.
결과적으로 위 코드가 내가 원하는데로 동작하였다!
addEventListener 자체를 for문과 분리하여,
원하는 매개변수가 제대로 전달되게 함과 동시에
addEventListener가 바로 실행되지 않게 하였다.
3. 번외
addEventListener뿐만 아니라 createElement를 이용하더라도 동일한 문제에 만난다.
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>자바스크립트 테스트 페이지</title>
</head>
<body>
<script>
window.onload = function() {
for (var i = 0; i < 4; i++) {
let btn = document.createElement("button");
btn.onclick = function() { testFunction(i); };
btn.innerHTML = "button" + i;
document.body.append(btn);
}
}
function testFunction(i) {
//핵심로직
console.log("button click " + i);
}
</script>
</body>
</html>
위의 경우에도 어떤 버튼을 누르던 "button click 4"를 반환한다.
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>자바스크립트 테스트 페이지</title>
</head>
<body>
<script>
window.onload = function() {
for (var i = 0; i < 4; i++) {
testFunction1(i);
}
}
function testFunction1(i) {
let btn = document.createElement("button");
btn.innerHTML = "button" + i;
btn.onclick = function() { testFunction2(i); };
document.body.append(btn);
}
function testFunction2(i) {
//핵심로직
console.log("button click " + i);
}
</script>
</body>
</html>
앞에서와 같이 function을 따로 호출하도록 감싸주면 원하는데로 동작한다!