
이벤트 핸들러를 태그에 직접 호출 : 디자인과 코드가 섞여버림
좋은 방법: 동적(프로그래밍적)으로 이벤트 핸들러 연결
addEventListener( ): js의 프로그래밍적 방법으로 이벤트를 연결하는 방법
익명함수: 재사용성이없는 함수는 함수명 없애기
addEventListener() 메서드(함수)에 두번째 매개변수값으로는 함수가 올 수 있는데, 이 함수는 딱 그 이벤트 발생 시에만 호출되는 용도이므로 재사용성이 없음 = 익명함수 처리
addEventListener("load", function(){...});
이때 function()이 익명함수
addEventListener("load", function(){ ... }) 방식으로 쓰는 건 아주 좋은 습관
addEventListener("load", ...)을 쓰는 이유:
HTML이 전부 다 로드된 다음에 실행
그래서 document.getElementById(...) 같은 코드가 실패하지 않음
코드가 더 유연하고 깔끔해짐
<body> 같이 HTML에 직접 쓰는 방식보다,
자바스크립트 코드와 HTML을 분리해서 관리할 수 있어서 더 깔끔함
여러 개의 이벤트를 붙일 수 있음
addEventListener는 같은 이벤트를 여러 개 등록 가능해.
반면 window.onload = function() {} 방식은 한 번만 등록 가능해서 덮어써짐

썸네일에서 선택한 이미지 #content에서 크게 보기, scroll기능 구현
<style>
#wrapper{
width: 700px;
height: 550px;
margin: auto;
background-color: beige;
}
#aside{
width: 120px;
height: 100%;
background-color: skyblue;
float: left;
text-align: center; /* 내부의 컨텐츠 가운데 수평 정렬 */
overflow: scroll; /* 현재 영역을 넘쳐흐르는 컨텐츠가 있다면 스크롤 처리 */
}
#content{
width: 580px;
height: 100%;
background-color: rgb(199, 228, 240);
float: left;
text-align: center;
}
#aside img{
width: 100px;
/* 너비만 부여하면, html에서 해당 너비에 대한 비율을 유지하여 높이를 알아서 설정 */
margin-top: 5px;
/* 나의 외부영역에 있는 있는 윗부분에서 5px 떨어지기 */
}
#content img{
width: 100%;
}
</style>
<script>
function createThumb(){
for(let i=1; i<=9;i++){
// 태그로 이미지 하나씩 작성x(정적코드X)
// 프로그램 실행시(동적으로) 이미지 생성하여 aside에 자식요소로 부착하기
let img=document.createElement("img"); //<img>작성과 동일 (단 화면에 미부착)
img.src="../../images/geographic/animal"+i+".jpg"; //<img src="">과 동일
//이미지에 클릭 이벤트 연결 (프로그래밍적 이벤트 연결)
// <img>과 같은 역할
img.addEventListener("click", function(){
//우측의 content 영역에 지금 누른 썸네일 이미지의 큰 버전이 나오게 하자
let content=document.getElementById("content");
document.querySelector("#content img").src=this.src; // 길게 표시할 필요없이 나!로 지정
});
let aside=document.getElementById("aside"); //부모얻기
aside.appendChild(img); //img 요소를 자식으로 부착
}
}
// 이벤트 핸들러 구현을 태그에 직접 작성하는 방법도 있지만,
// js의 프로그래밍적 방법으로 이벤트를 연결하는 방법도 있다
addEventListener("load", function(){ //앞에 window. 가 생략됐다고 생각하기
createThumb(); //썸네일 생성하는 함수 만들어봄
});
</script>
<body>
<!-- <button id="btn">나 눌러봐</button> -->
<!-- <button id="btn">나 눌러봐</button> -->
<!-- 똑같음 - this활용하기 -->
<div id="wrapper">
<div id="aside"></div>
<div id="content"><img></div>
</div>
</body>
overflow: scroll; :현재 영역을 넘쳐흐르는 컨텐츠가 있다면 스크롤 처리
this : 현재 코드가 실행되고 있는 "자기 자신"을 참조하는 키워드
<script>
let img=document.createElement("img"); //<img>작성과 동일 (단 화면에 미부착)
img.src="../../images/geographic/animal1.jpg"; //<img src="">과 동일
img.addEventListener("click", function(){
let content=document.getElementById("content");
document.querySelector("#content img").src=this.src; // 길게 표시할 필요없이 나!로 지정
});
</script>
<body>
<div id="content"><img></div>
</body>
→ #content 영역에 이미 img 태그가 임시로 존재하고, 사용자가 썸네일 이미지를 클릭하면 그 img 태그의 src 속성을 클릭한 썸네일 이미지의 src로 변경하는 방식
(src는 이미지 파일이 어디 있는지 경로를 알려주는 역할)
"mousemove" "click"<script>
addEventListener("mousemove", function(){
//사용자가 일으킨 모든 이벤트는 event 객체가 알고있다
//저번 예제에서는 키보드 이벤트도 얻어왔었다 ex) keyCode로 아스키코드 얻어오기
let x = event.clientX;
let y = event.clientY;
console.log(`x좌표=${x}, y좌표=${y}`);
});
</script>
clientX: 브라우저 화면 기준 X좌표
clientY: 브라우저 화면 기준 Y좌표

마우스로 음식을 클릭하면 음식이 따라오고
다시 클릭하면 안 따라오게 구현
클릭 시 테두리 주기
script영역에서 접시 만들어보기
#aside에는 마우스 좌표 누적기록
<style>
body{
margin: 0px;
/* 좌표 계산 쉽게 하려고 */
}
#wrapper{
width: 850px;
height: 550px;
background-color: beige;
border: 5px solid black;
}
#content{
width: 700px;
height: 100%;
float: left;
background-color: beige;
position: relative;
}
#aside{
width: 150px;
height: 100%;
float: left;
background-color: orange;
position: relative;
overflow: scroll; /* 스크롤 주기 */
}
</style>
<script>
// 프로그램에서 자주 사용될 가능성이 있는 요소들을 초기화 작업 시, 전역변수화 시켜놓자
let content;
let aside;
let img; //다른 함수영역(scope)에서도 접근할 수 있도록 전역으로 빼놓자
let flag=false; //처음엔 안 따라다니게
function createFood(){
img=document.createElement("img"); //<img>
img.src="../../images/food/hamburger.png"; //<img src="">
img.style.width=150+"px";
img.style.position="absolute";
img.style.left=100+"px";
img.style.top=180+"px";
//음식이 따라다니고, 안따라다니고의 기준은 flag 변수이므로
//유저를 배려하여, 클릭 시마다 논리값을 뒤집어 버리자
img.addEventListener("click", function(){
flag=!flag;
if(flag){
this.style.border= "5px solid red"; //테두리는 flag true일때만 적용하자
this.style.borderRadius="25%";
} else {
this.style.border="none"; //마우스 클릭 안했을 때는 테두리x
}
});
content.appendChild(img);
}
function createDish(n,x,y){ //접시 만들기
let div=document.createElement("div");
div.style.width=n*10+"px";
div.style.height=n*10+"px";
div.style.backgroundColor="white";
div.style.borderRadius=50+"%";
div.style.position="absolute";
div.style.left=x+"px";
div.style.top=y+"px";
div.style.border = "5px solid black"; // 테두리에는 solid도 반드시 추가
content.appendChild(div);
}
function init(){ // 프로그래밍 가동되면, 초기화할 작업이 있을 경우 초기화 함수를 정의하는게 좋은 방법
//content요소에 대해 마우스 움직임 이벤트 연결하기
content=document.getElementById("content");
aside=document.getElementById("aside");
createDish(20,400,150); //접시 등장 시키기 ★함수 만들었으면 호출 필수★
createDish(10,450,200);
createFood(); //음식 등장 시키기
//문서 전체를 대상으로 마우스 이벤트를 연결하자
document.body.addEventListener("mousemove",function(){ //body는 1개, 굳이 아이디 주지 말자
console.log("지금 움직임?");
//마우스에 의한 x,y좌표를 **음식의 좌표와 일치시키면** 따라다니는 효과 가능
let x=event.clientX;
let y=event.clientY;
if(flag){ //아래의 조건식은 flag에 따라 수행될 지 말 지 결정된다.
if(x<=(750-110) && y<=(550-70)){ //img 박스 밖으로 안 튀어나가게 하려면?
img.style.left= (x-75)+"px" //(변수-이미지의 절반=마우스가 이미지 중심)
img.style.top= (y-75)+"px"
}
}
//aside 영역에 좌표를 출력하자
aside.innerHTML=aside.innerHTML+`x=${x}, y=${y}<br>`;
});
};
//문서가 로드된 이후에 작업 진행
addEventListener("load", function(){
init();
});
</script>
<body>
<div id="wrapper">
<div id="content"></div>
<div id="aside"></div>
</div>
</body>
document.body.addEventListener("mousemove",function(){ });
let flag=false;
flag=!flag;
<img> 생성img=document.createElement("img"); // html에서 <img>
img.src="../../images/food/hamburger.png"; // html에서 <img src="">
border: 5px solid black;
setInterval(함수,호출시간간격) - 지정한 함수를 지정된 간격으로 무한호출setTimeout(함수. 호출시간간격) - 지정한 함수를 지정된 시간이 흐른 후 호출 (1회 호출) function test(){
console.log("나 불렀어?");
setTimeout(test,100); // 1/1000초 표현
// 콘솔에서 test(); 호출 시 - 가벼운 무한루프 (재귀호출이용)
}
"test()" ❌ ← 문자열로 전달 (eval 방식, 비권장)
test() ❌ ← 즉시 실행돼버림 (실행 결과만 전달됨)
test ✅ ← 함수 참조 전달 (정확하고 안전함)
![]() | ![]() |
|---|
카운트 시작 버튼을 누르면 input창에 숫자 n++
버튼을 누르면 다시 못 누르게 비활성화
<style>
input{
width: 300px;
height: 300px;
font-size: 200px;
font-weight: bold;
color: red;
text-align: right;
}
</style>
<script>
let n=0; //프로그램이 가동되는 동안 누적시키기 위한 전역변수
function setCount(){ //1씩 증가시키는 카운트 함수 정의
n++
//증가된 데이터를 화면에 출력
document.querySelector("input").value=n; // 입력창 값이 변수로 바뀜
setTimeout(setCount,100); //재귀호출 이용한 무한루프=자동호출
};
addEventListener("load", function(){ //문서가 로드되면... </html>까지 읽어들이면... 이란뜻
let bt= document.querySelector("button");
bt.addEventListener("click", function(){
this.disabled=true; //버튼을 비활성화 시킴
setCount();
});
});
</script>
<body>
<button>카운트 시작</button>
<br>
<input type="text">
</body>
value가 의미하는 것
<input> 입력된 텍스트, 숫자 등
<textarea> 작성된 여러 줄 텍스트
<select> 선택된 옵션의 값
<button> 버튼의 값 (거의 안 씀
disabled 속성
disabled = true 는 '사용자가 클릭하거나 입력하는 걸 막는다'라는 뜻
this.disabled = true; 는
bt.disabled = true; 와 같은 의미
둘 다 해당 버튼을 비활성화한다는 뜻

이번엔 start/stop 기능도 구현
<style>
input{
width: 300px;
height: 300px;
text-align: center;
font-size: 200px;
color: palevioletred;
}
button{
width: 100px;
height: 50px;
font-size: 25px;
}
</style>
<script>
let n=0;
let timer;
function setCount(){
n++;
document.querySelector("input").value=n; //value는 input에 적혀있는 값
timer=setTimeout(setCount,100);
}
addEventListener("load", function(){
let start=document.getElementById("start");
let stop=document.getElementById("stop");
start.addEventListener("click", function(){
this.disabled=true;
stop.disabled=false;
// n=0; 이렇게 초기화 시켜주면 start 눌렀을 때 다시 1부터 시작
setCount();
});
stop.addEventListener("click", function(){
clearTimeout(timer); //카운트 멈춤
this.disabled=true;
start.disabled=false;
});
});
</script>
<body>
<input type="text">
<br>
<button id="start">Start</button>
<button id="stop">Stop</button>
</body>
감속도 운동
(내 위치) = (현재 내 위치) + a*(목표지점 - 현재 내 위치 =남은거리 )
x=x+a*(target-x)
a: 비율계수 (기울기). 숫자가 높을 수록 투박함
비율계수가 높다 = 1에 가깝다 (예: 0.95, 0.98)
매번 줄어드는 양이 아주 작아, 속도가 천천히 천천히 줄고
줄어드는 느낌이 너무 느려서, 끝없이 질질 끄는 느낌이 생김.
→ 결과적으로 "부드럽다"기보다는 답답하고, 끈적끈적하고, 투박한 느낌이 남
변화는 작지만 너무 느려서 깔끔하지 않고 투박하다는 뜻
비율계수가 낮다 = 0에 가깝다 (예: 0.1, 0.2)
매번 줄어드는 양이 커서 훅훅 줄어들고, 초반에는 변화가 빠르지만,
일정 순간 이후 부드럽게 멈추는 느낌이 남
→ 이게 오히려 더 깔끔하고 자연스러운 감속처럼 보임
let div;
let a=0.01; //비율계수 크기. 숫자가 높을 수록 투박함
let targetX=800; //도달할 목표지점
function move(){ // 물체 움직임 함수
//나의 위치=기존 나의 위치+a(비율계수/기울기)*(목표지점-나의 위치);
// 남은 거리에 정해진 비율로 증가할거야 = 야금야금 다가갈거야
div.style.left=parseInt(div.style.left)+a*(targetX-parseInt(div.style.left))+"px";
setTimeout(move(),10);
}
function creatRect(){ // 물체 생성용 함수 (Rectangle)
div=document.createElement("div"); // 동적으로 <div>생성
div.style.background="red";
div.style.width=50+"px";
div.style.height=50+"px";
div.style.position="absolute";
div.style.top=100+"px";
div.style.left=0+"px";
document.body.appendChild(div); //body는 1개. 별도 id 필요없음
}
addEventListener("load",function(){//프로그램 가동과 동시에 물체 생성
creatRect();//물체 생성 함수 호출
move();
});
appendChild: 부모요소.appendChild(자식요소);<script>
function move(){//물체 움직임 함수
div.style.left=parseInt(div.style.left)+a*(targetX-parseInt(div.style.left))+"px";
}
</script>
여기서 parseInt(div.style.left)를 하는 이유는,
div.style.left의 값이 "0px", "120px" 이런 식으로 문자열(string) 이기 때문
→ parseInt()는 "0px", "120px"처럼 글자가 붙은 문자열을
숫자 0, 120으로 바꿔서 수학 계산이 가능하게 해줌
함수의 개념을 정확히 모르고 그냥 강사님을 따라 코딩할 때는 이 코드를 왜 여기 넣어야하는지 이 메서드는 왜 쓰는 건지 이해를 못하고, 일단 쓰라고 해서 썼었다. 답답하기도 답답하고 이걸 다 외워야하는 건가? 막막했었다.
그런데 함수의 개념을 조금이나마 이해한 지금은 어떤 의도로 이런 함수를 써야하는 지 왜 이 위치에 이 메서드를 써야하는지 아주 조금 감이 올 것 같다. VScode만 켜면 까막눈이 된 것 같아서 많이 답답했었는데, 이제는 GPT한테 대들 수 있을 정도(?)로 코드의 흐름이 보이기 시작한다. 어제보다 오늘 더, 오늘보다 내일 더 성장할 내 모습이 기대가 된다. 화이팅 ㅠㅠ