IT/development

[html] button에 type을 명시하자(feat. default submit)

알 수 없는 사용자 2023. 7. 9.
반응형

목차

    ajax로 페이징을 구현 했는데 페이지 이동이 되었다가 다시 같은 페이지가 호출이 되었다.

    콘솔 보면 2페이지 데이터를 정상적으로 가져왔다가 다시 1페이지가 호출이 되고 있다.

    간단한 원인 이었는데 꽤 많은 시간 삽질 끝에 나중에 다시 봐야지 하고 넘어 갔었다가 다시 봐서 해결 했다.

    before source 🙂

    /**
         * 페이지네이션을 그린다.
         * @param paging
         */
        function drawPagination(paging) {
    
        	let pageHtml = "";
        	pageHtml += "<ul class='pagination'>";
    
        	//first
        	const first = parseInt(paging.firstPage);
        	pageHtml += "<li class='paginate_button page-item first'>";
    
        	if (paging.currentPage === paging.firstPage) {
        		pageHtml += "<button class='page-link' disabled>";
        	} else {
        		pageHtml += "<button class='page-link' style='cursor: pointer;' onclick='movePage("+ first +")'>";
        	}
        	pageHtml += "<span class='hidden'>first page</span>";
        	pageHtml += "</button>";
        	pageHtml += "</li>"
    
        	//prev
        	const prev = parseInt(paging.currentPage) -1;
        	pageHtml += "<li class='paginate_button page-item prev'>";
    
        	if (paging.firstPage === paging.currentPage) {
        		pageHtml += "<button class='page-link' disabled>";
        	} else {
        		pageHtml += "<button class='page-link' style='cursor: pointer;' onclick='movePage("+ prev +")'>";
        	}
        	pageHtml += "<span class='hidden'>prev page</span>";
        	pageHtml += "</button>";
        	pageHtml += "</li>";
    
        	//① ~ ⑩
        	for (let i = paging.firstPageNo; i <= paging.lastPageNo; i++) {
        		pageHtml += "<li class='paginate_button page-item";
        		//현재 페이지가 인덱스와 같으면 active class 추가
        		if (paging.currentPage === i) {
        			pageHtml += " active'>";
        			pageHtml += "<button class='page-link' style='cursor: pointer;' onclick='movePage("+ i +")'>";
        			pageHtml += i;
        			pageHtml += "</button>";
        			pageHtml += "</li>"
        		} else {
        			pageHtml += "'>";
        			pageHtml += "<button class='page-link' style='cursor: pointer;' onclick='movePage("+ i +")'>";
        			pageHtml += i;
        			pageHtml += "</button>";
        			pageHtml += "</li>"
        		}
        	}
    
        	//next
        	const next = parseInt(paging.currentPage) + 1;
        	pageHtml += "<li class='paginate_button page-item next'>";
    
        	if (paging.currentPage === paging.lastPage) {
        		pageHtml += "<button class='page-link' disabled>";
        	} else {
        		pageHtml += "<button class='page-link' style='cursor: pointer;' onclick='movePage("+ next +")'>";
        	}
        	pageHtml += "<span class='hidden'>next page</span>";
        	pageHtml += "</button>";
        	pageHtml += "</li>"
    
        	//last
        	const last = parseInt(paging.lastPage);
        	pageHtml += "<li class='paginate_button page-item last'>";
    
        	if (paging.currentPage === paging.lastPage) {
        		pageHtml += "<button class='page-link' disabled>";
        	} else {
        		pageHtml += "<button class='page-link' style='cursor: pointer;' onclick='movePage("+ last +")'>";
        	}
        	pageHtml += "<span class='hidden'>last page</span>";
        	pageHtml += "</button>";
        	pageHtml += "</li>"
    
        	pageHtml += "</ul>";
        	//페이지네이션 영역에 반영
        	$(".tbl-paging").html(pageHtml);
        }

    after source 😎

    /**
         * 페이지네이션을 그린다.
         * @param paging
         */
        function drawPagination(paging) {
    
        	let pageHtml = "";
        	pageHtml += "<ul class='pagination'>";
    
        	//first
        	const first = parseInt(paging.firstPage);
        	pageHtml += "<li class='paginate_button page-item first'>";
    
        	if (paging.currentPage === paging.firstPage) {
        		pageHtml += "<button type='button' class='page-link' disabled>";
        	} else {
        		pageHtml += "<button type='button'  class='page-link' style='cursor: pointer;' onclick='movePage("+ first +")'>";
        	}
        	pageHtml += "<span class='hidden'>first page</span>";
        	pageHtml += "</button>";
        	pageHtml += "</li>"
    
        	//prev
        	const prev = parseInt(paging.currentPage) -1;
        	pageHtml += "<li class='paginate_button page-item prev'>";
    
        	if (paging.firstPage === paging.currentPage) {
        		pageHtml += "<button type='button'  class='page-link' disabled>";
        	} else {
        		pageHtml += "<button type='button'  class='page-link' style='cursor: pointer;' onclick='movePage("+ prev +")'>";
        	}
        	pageHtml += "<span class='hidden'>prev page</span>";
        	pageHtml += "</button>";
        	pageHtml += "</li>";
    
        	//① ~ ⑩
        	for (let i = paging.firstPageNo; i <= paging.lastPageNo; i++) {
        		pageHtml += "<li class='paginate_button page-item";
        		//현재 페이지가 인덱스와 같으면 active class 추가
        		if (paging.currentPage === i) {
        			pageHtml += " active'>";
        			pageHtml += "<button type='button'  class='page-link' style='cursor: pointer;' onclick='movePage("+ i +")'>";
        			pageHtml += i;
        			pageHtml += "</button>";
        			pageHtml += "</li>"
        		} else {
        			pageHtml += "'>";
        			pageHtml += "<button type='button'  class='page-link' style='cursor: pointer;' onclick='movePage("+ i +")'>";
        			pageHtml += i;
        			pageHtml += "</button>";
        			pageHtml += "</li>"
        		}
        	}
    
        	//next
        	const next = parseInt(paging.currentPage) + 1;
        	pageHtml += "<li class='paginate_button page-item next'>";
    
        	if (paging.currentPage === paging.lastPage) {
        		pageHtml += "<button type='button'  class='page-link' disabled>";
        	} else {
        		pageHtml += "<button type='button'  class='page-link' style='cursor: pointer;' onclick='movePage("+ next +")'>";
        	}
        	pageHtml += "<span class='hidden'>next page</span>";
        	pageHtml += "</button>";
        	pageHtml += "</li>"
    
        	//last
        	const last = parseInt(paging.lastPage);
        	pageHtml += "<li class='paginate_button page-item last'>";
    
        	if (paging.currentPage === paging.lastPage) {
        		pageHtml += "<button type='button'  class='page-link' disabled>";
        	} else {
        		pageHtml += "<button type='button'  class='page-link' style='cursor: pointer;' onclick='movePage("+ last +")'>";
        	}
        	pageHtml += "<span class='hidden'>last page</span>";
        	pageHtml += "</button>";
        	pageHtml += "</li>"
    
        	pageHtml += "</ul>";
        	//페이지네이션 영역에 반영
        	$(".tbl-paging").html(pageHtml);
        }

    달라진 부분은 동적으로 만든 button에 type을 button으로 설정했다.

    이번에 알게된 사실인데 button에 type을 명시하지 않을 경우 default값은 submit이라고 한다.

    그러니 버튼을 누를 때마다 ajax로 2페이지를 가져온 다음 form submit을 한번 더 하게 된거다.

    form action에 아무값도 주지 않았기에 현재 페이지로 submit을 태워서 계속 같은 페이지가 로드 된 거다.

    사실을 알게 된 후 form action에 존재하지 않는 주소를 줬더니 예상대로 404 에러가 발생했다.

    이 삽질을 통해 느낀 교훈: button을 만들 때 명시적으로 항상 type을 선언하도록 하자.

    전체 소스(샘플용)

    리팩토링 X

    <html xmlns:th="http://www.thymeleaf.org">
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="https://code.jquery.com/jquery-latest.min.js"></script>
    <style>
        li {
            list-style: none;
            float: left;
        }
    </style>
    </head>
    <body>
    <form id="searchForm">
        <select name="type" id="type">
            <option value="">전체</option>
            <option value="T">제목</option>
            <option value="C">내용</option>
            <option value="W">작성자</option>
        </select>
        <input type="text" name="keyword" id="keyword">
        <button id="searchBtn">검색</button>
        <a th:href="@{/board/reg}">글쓰기</a>
        <div id="count"></div>
    <table>
        <thead>
            <th>번호</th>
            <th>제목</th>
            <th>내용</th>
            <th>작성자</th>
            <th>등록일</th>
        </thead>
        <tbody id="boardBody"></tbody>
    </table>
        <div class="tbl-paging"></div>
    </form>
    
    <script th:inline="javascript" type="text/javascript">
        //구분
        const menu = "board";
        //검색조건
        let searchData = "";
    
        $(function () {
    
            getList("", 1, menu);
        });
    
        //검색
        $("#searchBtn").on("click", function () {
            searchData = $('#searchForm').serialize();
            getList(searchData, 1, menu);
        });
    
        /**
         * 페이지 이동
         * @param currentPage
         */
        function movePage(currentPage){
        	//검색조건이 있을 경우에만 검색조건 추가(검색조건 유지한 채 페이지 이동)
        	(searchData != null) ? getList(searchData, currentPage, menu) : getList("", currentPage, menu);
        }
    
        /**
         * ajax 통신으로 리스트를 조회한다.
         * @param searchParam 검색조건
         * @param currentPage 현재 페이지
         * @param type 메뉴타입
         */
        function getList(searchParam, currentPage, type){
            console.log("getList!!!!");
        	$.ajax({
        		url: "/api/" + type +"?currentPage=" + currentPage,
        		type:"get",
        		//IE 브라우저 사용 안한다는 가정하에 주석처리
        		// cache: false,
        		data: searchParam,
        		success: function (data){
        			//draw tbody
        			drawTbody(data, type);
        			//draw pagination
        			drawPagination(data.paging);
        		},
        		error:function(e){
        		}
        	});
        }
    
        /**
         * tbody에 html을 덮어쓴다.
         * @param data
         * @param gubun
         */
        function drawTbody(data, gubun) {
            //drawHtml, draw 대상 tbody
            let htmlData, targetTbody = "";
            targetTbody = "boardBody";
    
            // foreach start
            for (let i = 0; i < data.list.length; i++) {
                htmlData += "<tr ";
                htmlData += "data-sno=" + data.list[i].boardSno + ">";
                htmlData += "<td>";
                htmlData += data.list[i].no;
                htmlData += "</td>";
                htmlData += "<td>";
                htmlData += "<a href='#' onclick='detail(\"" + data.list[i].boardSno + "\")'>";
                htmlData += data.list[i].title;
                htmlData += "</a>";
                htmlData += "</td>";
                htmlData += "<td>";
                htmlData += data.list[i].content;
                htmlData += "</td>";
                htmlData += "<td>";
                htmlData += data.list[i].userId;
                htmlData += "</td>";
                htmlData += "<td>";
                htmlData += data.list[i].regDate;
                htmlData += "</td>";
                htmlData += "<td>";
                htmlData += "<a href='#' onclick='delBoard(\"" + data.list[i].boardSno + "\")'>삭제</a>";
                htmlData += "</td>";
                htmlData += "</tr>";
            }
            // foreach end
            //tbody에 반영
            $("#" + targetTbody + "").html(htmlData);
            countHtml = "";
            countHtml += data.count + "건";
            $("#count").text(countHtml);
        }
        // end function getList()
    
        //상세보기
        function detail(sno) {
            location.href = "/board/mod/" + sno;
        }
    
        function delBoard(sno) {
            $.ajax({
                url: "/api/board/" + sno,
                type:"delete",
                //IE 브라우저 사용 안한다는 가정하에 주석처리
                // cache: false,
                success: function (data) {
                    if(data.code === "1") alert("삭제 되었습니다.");
                    location.href = "/board"
                },
                error:function(e){
                    console.log(e);
                }
            });
        }
    
        /**
         * 페이지네이션을 그린다.
         * @param paging
         */
        function drawPagination(paging) {
    
        	let pageHtml = "";
        	pageHtml += "<ul class='pagination'>";
    
        	//first
        	const first = parseInt(paging.firstPage);
        	pageHtml += "<li class='paginate_button page-item first'>";
    
        	if (paging.currentPage === paging.firstPage) {
        		pageHtml += "<button type='button' class='page-link' disabled>";
        	} else {
        		pageHtml += "<button type='button'  class='page-link' style='cursor: pointer;' onclick='movePage("+ first +")'>";
        	}
        	pageHtml += "<span class='hidden'>first page</span>";
        	pageHtml += "</button>";
        	pageHtml += "</li>"
    
        	//prev
        	const prev = parseInt(paging.currentPage) -1;
        	pageHtml += "<li class='paginate_button page-item prev'>";
    
        	if (paging.firstPage === paging.currentPage) {
        		pageHtml += "<button type='button'  class='page-link' disabled>";
        	} else {
        		pageHtml += "<button type='button'  class='page-link' style='cursor: pointer;' onclick='movePage("+ prev +")'>";
        	}
        	pageHtml += "<span class='hidden'>prev page</span>";
        	pageHtml += "</button>";
        	pageHtml += "</li>";
    
        	//① ~ ⑩
        	for (let i = paging.firstPageNo; i <= paging.lastPageNo; i++) {
        		pageHtml += "<li class='paginate_button page-item";
        		//현재 페이지가 인덱스와 같으면 active class 추가
        		if (paging.currentPage === i) {
        			pageHtml += " active'>";
        			pageHtml += "<button type='button'  class='page-link' style='cursor: pointer;' onclick='movePage("+ i +")'>";
        			pageHtml += i;
        			pageHtml += "</button>";
        			pageHtml += "</li>"
        		} else {
        			pageHtml += "'>";
        			pageHtml += "<button type='button'  class='page-link' style='cursor: pointer;' onclick='movePage("+ i +")'>";
        			pageHtml += i;
        			pageHtml += "</button>";
        			pageHtml += "</li>"
        		}
        	}
    
        	//next
        	const next = parseInt(paging.currentPage) + 1;
        	pageHtml += "<li class='paginate_button page-item next'>";
    
        	if (paging.currentPage === paging.lastPage) {
        		pageHtml += "<button type='button'  class='page-link' disabled>";
        	} else {
        		pageHtml += "<button type='button'  class='page-link' style='cursor: pointer;' onclick='movePage("+ next +")'>";
        	}
        	pageHtml += "<span class='hidden'>next page</span>";
        	pageHtml += "</button>";
        	pageHtml += "</li>"
    
        	//last
        	const last = parseInt(paging.lastPage);
        	pageHtml += "<li class='paginate_button page-item last'>";
    
        	if (paging.currentPage === paging.lastPage) {
        		pageHtml += "<button type='button'  class='page-link' disabled>";
        	} else {
        		pageHtml += "<button type='button'  class='page-link' style='cursor: pointer;' onclick='movePage("+ last +")'>";
        	}
        	pageHtml += "<span class='hidden'>last page</span>";
        	pageHtml += "</button>";
        	pageHtml += "</li>"
    
        	pageHtml += "</ul>";
        	//페이지네이션 영역에 반영
        	$(".tbl-paging").html(pageHtml);
        }
    
    
    </script>
    
    </body>
    </html>

    개인 스터디 기록을 메모하는 공간이라 틀린점이 있을 수 있습니다.

    틀린 점 있을 경우 댓글 부탁드립니다.

    반응형

    'IT > development' 카테고리의 다른 글

    [ChatGPT] ChatGPT 고맙다.  (0) 2023.07.22
    [React.js] state 사용법  (0) 2023.07.13
    [dbeaver] dbeaver DDL, DML 생성  (0) 2023.07.08
    [JavaScript] 동적 엘리먼트 onclick 문자열  (0) 2023.07.05
    [JavaScript] validation  (0) 2023.06.25

    댓글