- Today
- Total
개발하는 고라니
[Javascript] 이벤트 기반의 윈도우 프로그래밍 본문
스크립트 코드 작성 영역
<script>
...
</script>
스크립트 코드는 스크립트 태그 안에서 작성할 수 있다.
<input onXXX=" "/>
<input onclick=" ",>
<input onmouseover=" "/>
...
하지만 이처럼 다른 태그 안에서도 작성될 수 있다.
<h1 id="aside-title" onclick="alert('click!!');">고객센터</h1>
<h1 id="aside-title" onclick="
var x = parseInt(prompt('x: '));
var y = parseInt(prompt('y: '));
alert(y + x);
">고객센터</h1>
<script>
function printSum(){
var x = parseInt(prompt('x: '));
var y = parseInt(prompt('y: '));
alert(y + x);
}
</script>
...
<body>
...
<h1 id="aside-title" onclick="printSum()">고객센터</h1>
객체 생성의 시기와 함수 대입의 시기
<script>
function printSum(){
var x = parseInt(prompt('x: '));
var y = parseInt(prompt('y: '));
asideTitle.innerText = x + y;
}
asideTitle.onclick = printSum;
</script>
<body>
<h1 id="asideTitle">제목</h1>
...
</body>
위 코드처럼 두고 페이지를 요청하면 오류가 발생한다. asideTitle이 define 되지 않았다는 오류이다. 그럼 어떻게 해야할까?
window라는 전역 객체를 이용하는 것이다. window가 "load"될 때, 즉 모든 DOM이 생성되고 난 이후에 asideTitle을 이용하려 한다면 에러가 발생하지 않을 것이다.
이는 다음과 같이 처리한다.
<script>
function printSum(){
var x = parseInt(prompt('x: '));
var y = parseInt(prompt('y: '));
asideTitle.innerText = x + y;
}
window.onload = function (){
asideTitle.onclick = printSum;
};
</script>
window라는 객체에 onload 되었을 때 함수를 실행하는데, 그 때 asideTitle의 onclick 속성에 printSum 함수를 대입하는 것이다. 여기서 중요한 것은, printSum()처럼 괄호를 넣지 않도록 한다. 괄호를 넣는다는 것은 함수를 호출하는 것 이므로, 함수의 반환 값을 onclick에 대입한다는 것이다. 우리는 함수를 대입하길 원하므로 ()를 넣지 않는다.
이를 좀 더 깔끔하게 정리하자면 아래와 같다.
<script>
window.onload = function (){
asideTitle.onclick = function (){
var x = parseInt(prompt('x: '));
var y = parseInt(prompt('y: '));
asideTitle.innerText = x + y;
}
}
</script>
JS 파일 외부에 두기
html 문서 내에서 <script> 코드 블럭을 사용하지 않고, CSS처럼 외부에 파일을 둘 수 있다.
//html
<script src="list.js"></script>
<body>
...
</body>
//list.js
window.onload = function (){
asideTitle.onclick = function (){
var x = parseInt(prompt('x: '));
var y = parseInt(prompt('y: '));
asideTitle.innerText = x + y;
};
};
그런데 보통 dom 객체를 위에처럼 바로 꺼내서 쓰지 않고 getElementById나 querySelector 같은 것을 사용해서 쓴다.
//html
<script src="list.js"></script>
<body>
...
</body>
//list.js
window.onload = function (){
var asideTitle = document.getElementById("asideTitle");
asideTitle.onclick = function (){
var x = parseInt(prompt('x: '));
var y = parseInt(prompt('y: '));
asideTitle.innerText = x + y;
};
};
여러 스크립트 파일을 함께 사용할 때 초기화 함수의 문제
//first.js
window.onload = function() {
...
}
//second.js
window.onload = function() {
...
}
위의 파일들을 동시에 사용한다면, first.js에서 window가 load됬을 때의 정의한 것들은 사라지게 될 것이다. second.js의 내용들로 덮어쓰여지기 때문이다. 그렇다면 first.js와 second.js의 모든 것을 사용하려면 어떻게 해야할까?
//first.js
window.addEventListener("load", function() {
...
}
//second.js
window.addEventListener("load", function() {
...
}
위처럼 onload 에 직접 대입하는 것이 아닌, EventListener를 추가해서 사용하면 기존의 내용이 overwrite되지 않는다.
Selector API
기존에는 getElementById, getElementsByClassName 밖에 없었지만, CSS 셀렉터를 이용해 객체를 가져올 수 있는 메서드가 추가되었다.
var btn = document.querySelector(".btn");
setTimeout() / setInterval() + clearInterval()
window.setTimeout( Login func(), ms );
//btnCountDown을 누르면 1초후 spanTime 안의 갑이 1 감소해서 나옴 (1번 클릭 시)
btnCountDown.addEventListener("click", function(e) {
setTimeout(() => {
var count = parseInt(spanTime.innerHTML);
spanTime.innerHTML = --count;
}, 1000);
});
setInterval을 시작하면 종료 시퀀스가 없는 한 계속 루프를 돌게 된다. 이를 도중에 멈추게 하려면 어떻게 해야할까? setInterval을 시작함과 동시에 타이머의 ID를 반환하는데, 이를 변수에 저장하고, 종료하고 싶을 때 조건문을 주어 'clearInteval()'메서드를 주면 되겠다. 다음 예제 코드를 보자.
var sec4 = document.querySelector("#ex4");
var container = sec4.querySelector(".container");
var boxes = container.querySelectorAll(".box");
var btnClick = sec4.querySelector("button");
btnClick.addEventListener("click", function(e) {
var boxStyle = getComputedStyle(boxes[0]);
var left = parseInt(boxStyle.getPropertyValue('left'));
var tid = setInterval(function() {
if(left > 400)
clearInterval(tid);
boxes[0].style.left = left + 'px';
left++;
}, 17);
});
left가 400이 넘어가면 타이머를 종료하도록 하는 코드이다.
window.setInterval( Login func(), ms )
//1번 클릭시 1초 후 값이 1 감소해서 출력됨 (계속)
btnCountDown.addEventListener("click", function(e) {
setInterval(() => {
var count = parseInt(spanTime.innerHTML);
spanTime.innerHTML = --count;
}, 1000);
});
태그로 감싼 컨텐츠
- textContent : 결과 = 제목 : 안녕하세요 (표준이긴 하나, 옛 브라우저에서는 적용이 안된다.)
- innerText : 결과 = 제목 : 안녕하세요
- innerHTML : 결과 = 제목 : <a href="">안녕하세요</a>
태그 객체의 Style 속성 가져오기
window.getComputedStyle( 태그 객체 );
var 속성의 값 = getPropertyValue( attribute );
btnClick.addEventListener("click", function(e) {
/* */
var boxStyle = getComputedStyle(boxes[0]);
var left = parseInt(boxStyle.getPropertyValue('left').replace('px', ''));
/* */
setInterval(function() {
if(left <= 400){
boxes[0].style.left = left + 'px';
left++;
}
}, 17);
});
노드 조작하기
※ 노드 선택
- parentNode : 부모 노드
- firstChild : 첫번째 자식 노드
- lastChild : 마지막 자식 노드
- previousSibling : 이전 이웃한 형제 노드 (텍스트 포함)
- nextSibling : 다음 이웃한 형제 노드 (텍스트 포함)
- parentElement : 부모 엘리먼트
- firstElementChild : 첫째 자식 엘리먼트
- lastElementChild : 마지막 자식 엘리먼트
- previousElementSibling : 이전 이웃한 형제 엘리먼트
- nextElementSilbling : 다음 이웃한 형제 엘리먼트
<section id="ex5">
<style>
</style>
<h1>ex5 - 노드 조작하기</h1>
<div>
<input type="button" class="btn btn-add" value="추가">
<input type="button" class="btn btn-del" value="삭제">
<input type="button" class="btn btn-clone" value="복제">
</div>
<div class="container">
</div>
</section>
노드 추가하기
var sec5 = document.querySelector("#ex5");
var container = sec5.querySelector(".container");
var btnAdd = sec5.querySelector(".btn-add");
var btnDel = sec5.querySelector(".btn-del");
var btnClone = sec5.querySelector(".btn-clone");
btnAdd.addEventListener("click", function(e){
//엘리먼트 객체 생성
var img = document.createElement('img');
//엘리먼트 객체 속성 설정
img.src = '../images/1.jpg';
//엘리먼트 객체를 문서에 추가
//container.appendChild(img);
container.append(img);
});
노드 제거하기
btnDel.addEventListener("click", function(e){
var div = container.querySelector('div:first-child');
div.remove();
});
노드 변경하기
btnChange.addEventListener("click", function(e) {
/* var e1 = container.querySelector("div:nth-child(1)");
var e2 = container.querySelector("div:nth-child(2)"); */
var e1 = container.querySelector("div:first-child");
var e2 = container.querySelector("div:last-child");
var oldNode = container.replaceChild(e1, e2);
container.insertBefore(oldNode, e1);
});
※ insertAdjacentElement
targetElement.insertAdjacentElement(position, element);
/*
beforebegin : 타겟 자신의 전에 element를 붙인다
beforeend : 타겟 자신의 자식의 마지막 자리에 element를 붙인다
afterbegin : 타겟 자신의 자식의 첫번째 자리에 element를 붙인다
afterend : 타겟 자신의 이후에 element를 붙인다
*/
이벤트 버블링과 Event target 속성
위 컨테이너에서 파란색 원을 눌렀을 때 선택이 되도록, 그리고 삭제버튼을 누르면 선택이 된 것을 삭제하도록 하는 것을 해보자.
var sec6 = document.querySelector("#ex6");
var container6 = sec6.querySelector(".container");
var btnDel6 = sec6.querySelector(".btn-del");
var btnClone6 = sec6.querySelector(".btn-clone");
var btnChange6 = sec6.querySelector(".btn-change");
var boxes = container6.querySelectorAll(".box");
var selected = null;
for(var i=0; i<3; i++){
boxes[i].addEventListener("click", function(){
selected = this;
console.log(selected);
});
}
/* boxes.forEach(function(box, idx) {
box.addEventListener("click", function() {
selected = box;
console.log(idx+'번째 box selected');
});
}); */
btnDel6.addEventListener("click", (e) => {
if(selected !== null)
selected.remove();
})
이를 좀 더 간단하게 할 수 없을까?
이벤트가 발생하면 부가적으로 발생하는 객체가 있는데 이를 'e'라고 하자. 이 'e'를 이용해서 내가 클릭한 객체가 어떤건지 알 수 있다.
btnDel6.addEventListener("click", (e) => {
if(selected !== null)
selected.remove();
})
container6.addEventListener("click", function(e){
var selected = e.target;
if(selected.className === 'box')
selected.remove();
});
Event 트리거
파일을 업로드하기 위해서는 <input type="file">을 이용해서 하는 방법이 있는데 이는 브라우저마다 모양이 제각각이고, css도 먹히지 않는다. 따라서 보통 <span>이나 <button>에 모양을 입히고 이 클릭 이벤트를 <input type="file">로 전달하는 방법을 택한다.
var sec9 = document.querySelector('#ex9');
var btnUpload = sec9.querySelector('.btn-upload');
var inputFile = sec9.querySelector('input[type="file"]');
btnUpload.addEventListener('click', function(e) {
var event = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true
});
inputFile.dispatchEvent(event);
});
'Languages > JS' 카테고리의 다른 글
[Javascript] Ajax (0) | 2021.04.29 |
---|---|
[Javascript] Drag & Drop 파일 업로드 (0) | 2021.04.27 |
[JQuery] checkbox (0) | 2021.02.23 |
[Javascript] 문자열 찾기 (0) | 2021.01.04 |
[Javascript] 참조와 복제 (0) | 2020.12.09 |