반응형
11-24 05:55
Today
Total
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
관리 메뉴

개발하는 고라니

[Javascript] 이벤트 기반의 윈도우 프로그래밍 본문

Languages/JS

[Javascript] 이벤트 기반의 윈도우 프로그래밍

조용한고라니 2021. 4. 14. 16:30
반응형

스크립트 코드 작성 영역

<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() - Web APIs | MDN

Window.getComputedStyle() The Window.getComputedStyle() method returns an object containing the values of all CSS properties of an element, after applying active stylesheets and resolving any basic computation those values may contain. Individual CSS prope

developer.mozilla.org

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
Comments