IT/development

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

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

목차

    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