다른 탭에서도 todo list를 사용할 수 있는 방법이 있을까요 ? - javascript

조회수 403회

todo list를 두 가지 목적(프로젝트, 개인)으로 사용할 수 있도록 javascript, html, css로 제작 중 인데요. 번갈아 가며 사용 가능해야 하는데 , 첫 번째 탭에서는 정상 작동하나, 두 번째 탭에서 작동하지 않습니다. 실행하자 마자 두번째 탭을 먼저 들어가도 작동하지 않습니다. 제 생각엔 실행 순서에 문제가 있는 것 같지만, 아닐 수도 있습니다... 어떻게 고칠 수 있을까요 ? 하단은 javascript의 코드입니다 !

const todoObjectList1 = [];
const todoObjectList2 = [];

// 탭 전환

$(".menu1").click(function(){
    $(this).addClass('on');
    $(".menu2").removeClass("on");
})

$(".menu2").click(function(){
    $(this).addClass('on');
    $(".menu1").removeClass("on");
})

// project todo

class Todo_Class1 {
    constructor(item){
        this.ulElement1 =item;
    }

    add() {
        const todoInput1 = document.querySelector("#myInput1").value;
        if (todoInput1 == "") {
            alert("You did not enter any item!") 
        } else {
            const todoObject1 = {
                id1 : todoObjectList1.length,
                todoText1 : todoInput1,
                isDone1 : false,
            }

        todoObjectList1.unshift(todoObject1);
        this.display();
        document.querySelector("#myInput1").value = '';

        }
    }

    done_undone(x) {
        const selectedTodoIndex1 = todoObjectList1.findIndex((item)=> item.id1 == x);
        console.log(todoObjectList1[selectedTodoIndex1].isDone1);
        todoObjectList1[selectedTodoIndex1].isDone1 == false ? todoObjectList1[selectedTodoIndex1].isDone1 = true : todoObjectList1[selectedTodoIndex1].isDone1 = false;
        this.display();
    }

    deleteElement(z) {
        const selectedDelIndex1 = todoObjectList1.findIndex((item)=> item.id1 == z);

        todoObjectList1.splice(selectedDelIndex1,1);

        this.display();
    }


    display() {
        this.ulElement1.innerHTML = "";

        todoObjectList1.forEach((object_item1) => {

            const liElement1 = document.createElement("li");
            const delBtn1 = document.createElement("i");

            liElement1.innerText = object_item1.todoText1;
            liElement1.setAttribute("data-id1", object_item1.id1);

            delBtn1.setAttribute("data-id1", object_item1.id1);
            delBtn1.classList.add("far", "fa-trash-alt");

            liElement1.appendChild(delBtn1);

            delBtn1.addEventListener("click", function(e) {
                const deleteId1 = e.target.getAttribute("data-id1");
                myTodoList1.deleteElement(deleteId1);
            })

            liElement1.addEventListener("click", function(e) {
                const selectedId1 = e.target.getAttribute("data-id1");
                myTodoList1.done_undone(selectedId1);
            })

            if (object_item1.isDone) {
                liElement1.classList.add("checked");
            }

            this.ulElement1.appendChild(liElement1);
        })
    }
} 

// personal todo

class Todo_Class2 {
    constructor(item){
        this.ulElement2 =item;
    }

    add() {
        const todoInput2 = document.querySelector("#myInput2").value;
        if (todoInput2 == "") {
            alert("You did not enter any item!") 
        } else {
            const todoObject2 = {
                id2 : todoObjectList2.length,
                todoText2 : todoInput2,
                isDone2 : false,
            }

        todoObjectList1.unshift(todoObject2);
        this.display();
        document.querySelector("#myInput2").value = '';

        }
    }

    done_undone(x) {
        const selectedTodoIndex2 = todoObjectList2.findIndex((item)=> item.id2 == x);
        console.log(todoObjectList2[selectedTodoIndex2].isDone2);
        todoObjectList2[selectedTodoIndex2].isDone2 == false ? todoObjectList1[selectedTodoIndex2].isDone2 = true : todoObjectList2[selectedTodoIndex2].isDone2 = false;
        this.display();
    }

    deleteElement(z) {
        const selectedDelIndex2 = todoObjectList2.findIndex((item)=> item.id2 == z);

        todoObjectList2.splice(selectedDelIndex2,1);

        this.display();
    }


    display() {
        this.ulElement2.innerHTML = "";

        todoObjectList2.forEach((object_item2) => {

            const liElement2 = document.createElement("li");
            const delBtn2 = document.createElement("i");

            liElement2.innerText = object_item2.todoText2;
            liElement2.setAttribute("data-id2", object_item2.id2);

            delBtn2.setAttribute("data-id2", object_item1.id2);
            delBtn2.classList.add("far", "fa-trash-alt");

            liElement2.appendChild(delBtn2);

            delBtn2.addEventListener("click", function(e) {
                const deleteId2 = e.target.getAttribute("data-id2");
                myTodoList2.deleteElement(deleteId2);
            })

            liElement2.addEventListener("click", function(e) {
                const selectedId2 = e.target.getAttribute("data-id2");
                myTodoList1.done_undone(selectedId2);
            })

            if (object_item2.isDone) {
                liElement2.classList.add("checked");
            }

            this.ulElement2.appendChild(liElement2);
        })
    }
}

////-----MAIN PROGRAM------------

const listSection1 = document.querySelector("#myUL1");

myTodoList1 = new Todo_Class1(listSection1);

// project todo add

document.querySelector(".addBtn1").addEventListener("click", function() {
    myTodoList1.add()
})

document.querySelector("#myInput1").addEventListener("keydown", function(e) {
    if (e.keyCode == 13) {
        myTodoList1.add()
    }
})

// personal todo add

const listSection2 = document.querySelector("#myUL2");

myTodoList2 = new Todo_Class2(listSection2);


document.querySelector(".addBtn2").addEventListener("click", function() {
    myTodoList2.add()
})

document.querySelector("#myInput2").addEventListener("keydown", function(e) {
    if (e.keyCode == 13) {
        myTodoList2.add()
    }
})

1 답변

  • 일단, 작동하지 않는 이유는, Todo_Class2에서 todoObjectList1을 참조하려는 데가 있기 때문입니다. 잘 찾아보세요. 찾기 어려우실 겁니다. 이런 식으로 코딩을 하고 계신 한, 앞으로도 그 어려운 버그 찾기 작업은 계속될 거구요.

    이참에 가만히 생각해 봅시다. 왜 Todo_Class2가 따로 새로 정의돼야 하나요? Todo_Class1Todo_Class2의 차이는 오직 사용할 속성명들뿐이지 않나요? data-id1이며 #myInput1이며 하는 것들 말이죠.

    기왕에 클래스 개념을 도입할 거라면, 이런 식으로 되어야 하는 거 아닐까요?

    const TodoApp = function (container) {
        // React에서 "state"라고 부르는 그런 것을 정의한다
        let items = [];
    
        // 기능들을 잔뜩 정의한다
        let appendItem = function (item) {
            items.push(item); this.render();
        };
        let render = function () {
            container.innerHTML = '<ul class="list">' + this.getItems() + '</ul>';
        };
        let getItems = function () {
            /* items를 가지고 li 태그의 목록을 만들어야 함 */
        };
    
        // 이벤트에 기능을 연결한다
        container.queryElementsAll('li').addEventListener('click', function () {
            /* 입력을 받아 this.appendItem을 실행해야 함 */
        });
    
        // 필요한 것만 노출한다
        return {
            render: this.render
        };
    };
    
    // 그러면 얘가 관리하는 items 배열이 따로 있고
    let TodoAppFoo = new TodoApp(document.getElementById('#foo'));
    TodoAppFoo.render();
    
    // 얘가 관리하는 items 배열이 따로 있을 것
    let TodoApp2 = new TodoApp(document.getElementById('#todo2'));
    TodoApp2.render();
    

    핵심은, container는 한번만 지정하게 하고, 나머지는 모두 그 container에 대한 상대적인 요소/기능으로서 작동하도록 만들자는 겁니다. 리액트 같은 프레임워크들이 대체로 이런 아이디어를 갖고 있죠. 그렇게 하면 클래스는 정말 일반론적인 행동 규칙만 갖게 되므로 어떤 container에 대해서든지 작업을 할 수 있고, 어떤 탭에서든지 독립적으로 작동할 겁니다.

    참고가 되면 좋겠네요. 한번 연구해 보세요!

    • 그렇군요 ! 나중에 DB를 연결하여 두 가지 todo를 따로 관리 할 예정이라 두 개를 만들어야만 하는 줄 알았습니다. 정말 좋은 예시가 될 것 같습니다. 적어주신 코드 바탕으로 수정해 볼게요 ! 정말 감사합니다 !! 알 수 없는 사용자 2021.10.7 00:28
    • 기왕 하실 거라면 처음부터 React나 Vue로 하시는 쪽을 강력 추천 드립니다. 애초에 지금 적은 저 코드는 테스트도 안해봤거든요 😅 엽토군 2021.10.7 00:33
    • 그러게요... 수정할 엄두가 안 나네요 ㅠㅠ 😶 이참에 React로 갈아타야겠습니다... 정말 감사드려요 ! 알 수 없는 사용자 2021.10.7 00:45

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)