JavaScript 강의가 계속되고 있습니다.
오늘은 DOM 강의와 Ajax 강의가 일부 진행됐습니다.
사실상 강의는 거의 끝났다고 보면 될 것 같습니다. 배운거 잘 이용해야죠^^
기존의 포스팅들은 교안을 정리하고, 제 생각들을 추가 하는 정도로 진행했는데
오늘은 교안, 실습한 코드, 강사님 코드를 분석해 가면서 작성해 볼까 합니다.
시작해보겠습니다.^^


캡처링 과 버블링

<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <article class="parent">
        <button class="btn" type="button">버튼</button>
    </article>

    <script>
        const parent = document.querySelector('.parent');
        const btnFirst = document.querySelector('.btn');

        window.addEventListener('click', () => {
            console.log("window bubble!");
        });

        btnFirst.addEventListener('click', (event) => {
            console.log("btn bubble!");
        })

        parent.addEventListener('click', () => {
            console.log("parent bubble!");
        });

        document.addEventListener('click', () => {
            console.log("document bubble!");
        });


        btnFirst.addEventListener('click', (event) => {
            console.log("btn capture!");
        }, true);

        window.addEventListener('click', () => {
            console.log("window capture!");
        }, true); // true : 캡처링 단계의 이벤트가 발생하도록 합니다.

        document.addEventListener('click', () => {
            console.log("document capture!");
        }, true);

        parent.addEventListener('click', () => {
            console.log("parent capture!");
        }, true);

    </script>
</body>

</html>

브라우저가 이벤트 대상을 찾아갈때는 가장 상위의 window 객체부터 document, body 순으로
DOM 트리를 따라 내려가는데 이를 캡처링 단계 라고 합니다.
이벤트 대상을 찾아가는 과정에서 브라우저는 중간에 만나는 모든 캡처링 이벤트 리스너를 실행시킵니다.
그리고, 이벤트 대상을 찾고, 캡처링이 끝나면 DOM 트리를 따라 올라가며 만나는 모든 버블링 이벤트
리스너를 실행합니다. 이를 이벤트 버블링 단계라고 합니다.
예제의 내부 리스너들의 위치를 바꿔도 캡처링, 버블링 순서로 진행됩니다.
addEventListener 의 마지막 매개변수를 true값을 주면 캡처링 단계의 이벤트가 발생하도록 합니다.
기본디폴트 값이 false 이니 버블링이 기본이라고 할 수 있습니다.


과제

    <script>
        // 버튼 이벤트를 위함
        const btnCommon = document.querySelector('.btn-common');
        // 입력받는 input 값 핸들링을 위함
        const txtFieldInput = document.querySelector(".txt-field input");
        const txtTimeInput = document.querySelector(".txt-time input"); 
        // 결과를 보여주기 위함
        const target = document.querySelector(".cont-result .result");

        // 버튼 눌렀을때        
        btnCommon.addEventListener('click', (event) => {

            // input 에 데이터가 없을때 
            if (txtFieldInput.value == '' || txtTimeInput.value == ''){
                // 오류창 실행
                alert("입력되지 않았습니다.")
                // input을 다시 빈칸으로 만들어줌
                txtFieldInput.value = '';
                txtTimeInput.value = '';
                // 결과화면 닫음
                target.setAttribute('style', 'display: none;');
                return   
            }
            else{
                // input으로 입력받은 값을 target에서 수정함
                target.childNodes[1].textContent  = txtFieldInput.value;
                target.childNodes[5].textContent  = parseInt(10000 / txtTimeInput.value); 
                // 결과화면 open
                target.style.removeProperty('display');        
            }           
        });            
    </script>

기존에 완수하지 못했던 과제에서 JavaScript 부분 추가 해보라는 과제가 다시 주어졌습니다.
이것저것 찾아가면서 완성했는데 강의시간중에 리뷰를 해주셨습니다.
들을때는 열심히 들었는데 다시 생각하면서 수정해보려고 하니 또 잘 안되더라고요.ㅎ
그래도 기억나는 부분들을 정리해보면 앞쪽에 태그 class 들을 변수로 할당한 부분에서
document 부터 querySelector 하는게 성능?에 부담이 된다는 말씀을 해주셨습니다.
중간에 form 태그 들이 있을때 거기서 부터 querySelector 사용하면 부하를 줄일 수 있다고
해주셨습니다.


그리고, if문에서 == 보다 === 를 사용하는게 좋다고 해주셨습니다.
=== 이거 보고 ??? 이랬는데 1 == '1' 이게 참이 나온다고 합니다.
JavaScript 가 알아서 묵시적으로 형변환해서 비교해 준다고 하네요.ㅎㅎ
다른 프로그램처럼 정확하게 하려면 === 을 써야 한다고 합니다.


그리고, parseInt(10000 / txtTimeInput.value); 이 부분을
Math.ceil( 10000 / parseInt(txtTimeInput.value) ); 로 수정하는 것이 좋다고 하셨습니다.
제가 parseInt 쓴 것은 소수점자리 나오는 것 때문에 쓴 건데 그 부분은 Math 내의 메소드를
사용하고, 입력 받는 숫자가 문자열 일 수 있으니 그 부분을 parseInt 로 해주는 것을 추천해주셨습니다.


마지막으로

target.setAttribute('style', 'display: none;');
...
...
...
target.style.removeProperty('display');      

이 부분을 class 를 통해서 처리하는 것을 추천해 주셨습니다.
CSS 속성에 직접 접근해서 수정하는 것은 인라인 요소가 있어서 추천하지 않는다고 하셨습니다.
다 기억해서 적었는지 모르겠네요.^^


실습1

<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <a href="https://www.naver.com" class="link">네이버 링크입니다만..</a>  

    <form action="">
        <button type="submit" class="submit">제출</button>
    </form>
    <script>
        let flag = false; // 오른쪽 클릭 방지를 위한 플래그 초기화

        const btn = document.querySelector('.submit'); // 버튼 선택
        btn.addEventListener('click', function (event) {
            flag = true; // 버튼 클릭 시 오른쪽 클릭 방지 활성화
            console.log('Submit button clicked, right-click disabled.');
            event.preventDefault(); // 폼 제출 방지
        });

        document.addEventListener('contextmenu', function (event) {
            if (flag) { // 오른쪽 클릭 방지가 활성화되어 있으면
                console.log('Right-click prevented');
                event.preventDefault(); // 오른쪽 클릭 방지
                alert('해당 페이지에서는 오른쪽 클릭을 제한합니다.');
            }
        });
</script>
</body>
</html>

실습은 브라우저에서 오른쪽 마우스클릭 이벤트를 막고, 경고창을 띄워주는 건데 너무 간단해서
버튼 눌렀을때 오른쪽 마우스클릭 이벤트를 막고, 경고창 띄워주는 걸로 수정해봤습니다.
addEventListener 안에 addEventListener 넣는 형태로 처음에 만들었는데 아무리 봐도 이상하고,
버튼 눌렀을때마다 이미 적용된 오른쪽 마우스클릭에 관한 addEventListener 를 계속 실행하는게
마음에 안들어서 flag를 적용하는 걸로 수정해봤습니다. 어떤 더 좋은 방법이 있을까요? 음..


실습2

<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <h1>나의 todo list</h1>
    <p>1. 오늘 저녁에는 부대찌개를 끓여 먹겠다.<button type="button">삭제</button></p>
    <p>2. 후식으로 슈팅스타를 먹겠다.<button type="button">삭제</button></p>
    <p>3. 자기 전에 반드시 내일 아침 메뉴를 생각해두겠다.<button type="button">삭제</button></p>

    <script>
        const txts = document.querySelectorAll('p');
        const btns = document.querySelectorAll('button');

        txts.forEach((item) => {
            item.addEventListener('click', () => {
                // console.log(item.childNodes[0]);
                alert(item.childNodes[0].textContent);
            });
        });

        btns.forEach((item) => {
            item.addEventListener('click', (event) => {
                const result = confirm('삭제하시겠습니까?');
                event.stopPropagation();
                if (result){
                    item.parentElement.remove();
                }
            });
        });
    </script>
</body>

</html>

이번 실습은 p태그 3개와 그 옆에 button 이 하나 있는 HTML 코드에서 p태그를 클릭했을때
p태그의 글자를 경고창에 띄우고, 삭제버튼을 눌렀을때 confirm 창을 띄워서 확인 누르면
p 태그와 button을 같이 삭제 하는 코드를 만들어 보는 것입니다.
위의 코드는 제가 만든게 아니고, 강사님이 간단하게 만드신 건데 사실 포스팅 할때는 제 코드랑
같이 올려서 비교하려고 했는데 코드가 너무 길어서 제외했습니다. 사실 볼 것도 없습니다.
태그마다 class 줘서 했습니다.^^ 리스너도 많이 쓰고, 코드도 길어지니 별로라서
반복문 써서 해야 한다고 생각은 했는데 익숙하지 않으니 뭐 써야 하지? 라는 생각만
들었습니다. 예제 코드들 많이 봐야할 것 같습니다.


실습3

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        function searchUser(userName, passWord, onSuccess) {

            const requestObj = new XMLHttpRequest();
            requestObj.open('GET', 'user.json');
            requestObj.onreadystatechange = () => {
                if (requestObj.readyState === 4 && requestObj.status === 200) {

                    // json 데이터를 자바스크립트 객체로 만드는 과정
                    const result = JSON.parse(requestObj.responseText);

                    console.log(result);

                    // json 데이터와 일치하는 검색하는 메소드
                    const info = result.user.find((item) => {
                        console.log(item.userName);
                        return item.userName === userName && item.password === passWord;
                    });

                    console.log(info);

                    if(info) {
                        onSuccess(info);
                    }else {
                        console.error('일치하는 유저가 없습니다.!');
                    }
                    //console.log(result); // 콘솔에 무엇이 찍히는지 확인해봅시다.
                }
            };
            requestObj.send();
        }

        function sayHi(user) {

            const requestObj = new XMLHttpRequest();
            requestObj.open('GET', 'greeting.json');
            requestObj.onreadystatechange = () => {
                if (requestObj.readyState === 4 && requestObj.status === 200) {

                    // json 데이터를 자바스크립트 객체로 만드는 과정
                    const result = JSON.parse(requestObj.
                    responseText)

                    // json 데이터와 일치하는 검색하는 메소드
                    const info = result.greetings.find((item) => {
                        return item.userName === user.userName 
                    });

                    if(info) {
                        alert(info.greetings);        
                    } else {
                        console.error('일치하는 유저가 없습니다.!');
                    }
                    //console.log(result); // 콘솔에 무엇이 찍히는지 확인해봅시다.
                }
            };
            requestObj.send();
        }

        const userName = prompt('이름을 입력하세요');
        const passWord = prompt('비밀번호를 입력하세요');

        searchUser(userName, passWord, (info) => {
            sayHi(info);
        });
    </script>
</body>
</html>

Ajax 강의하고, 콜백함수 사용하는 예제입니다. 전체적으로 어떻게 사용하는지 이해할 수 있었습니다.
json 파일 2개 이름을 일부러 강사님과 다르게 해서 따라하는 형태로 만들었는데 안되더라고요.^^
json 파일 이름은 greeting.json 안에 파일에는 greetings, password, passWord 이렇게
이름이 다르니 인식을 못하고 계속 일치하는 유저가 없다고만 나왔습니다.
덕분에 수정하면서 정확한 흐름을 캐치할 수 있었습니다.^^
그외 공부하면서 조금 까다로웠던 것은 화살표 함수쓰면서 부모요소와 얽히는 문제와 this 가 화살표
함수로 쓰면서 가리키는 대상이 달라지는 것 등 아직 익숙하지 않은 문법들이 까다로웠습니다.
역시 공부가 더 필요합니다.


오늘은 교안의 내용을 덜 옮겨오려고 했습니다.
포스팅 목적은 배운 것들 다시 한번 복습하는 것이라 그것도 나쁘지는 않았지만 어제 포스팅할때
약간 받아쓰기 하는 것 같은 느낌이 들어서 바꿔봤습니다.
그때 그때 다양하게 포스팅해보도록 하겠습니다.