[JavaScript] SelectBox를 사용하는 날짜 검색창 제작[JavaScript] SelectBox를 사용하는 날짜 검색창 제작

Posted at 2020. 7. 12. 20:31 | Posted in JavaScript & jQuery/JavaScript
반응형







■ 셀렉트 박스를 이용한 캘린더 제작







위와 같이 <SELECT> 태그를 통해 날짜값을 지정하여 검색하는 페이지를 제작하게 되었다.


jQuery UI의 DatePicker등을 사용하면 그냥 손쉽게 해결 할 수 있고,


보통은 다 이렇게 하지만, 또 막상 일을 하다보면 위와 같은 검색을 선호하는 클라이언트가 또 있을 수 있다라는 생각이들어


추가적으로 실제 업무에서 많이 쓰이게 되는 GET 방식을 통한 검색조건 전달의 경우를 포함하여


내용을 정리해보았다.





※ 예외 처리해야할 사항들


 ① 매월의 마지막 날의 날짜는 항상 일치하지 않기에, 그달의 마지막 날보다 큰 날짜는 자동적으로 선택할 수 없어야 한다.

 ② 처음 접속시 해당월의 첫째날( 01일 )과 마지막날( 28일, 29,일 30일, 31일 등 )이 자동 세팅되어야 한다.

 ③ GET방식을 통해 검색 시작일과 종료일의 날짜 데이터가 전달되는 경우의 수를 처리해야 한다.

 ④ 사용자가 날짜를 선택하는 과정에서 31일이 선택되어 있는 가운데 31일이 존재하지 않는 년, 월을 선택한 경우

     해당 월의 마지막 날로 자동 변경시켜주어야 한다.




위 경우를 상정하여 아래 코드를 작성하게 되었다.




# 소스코드

<html>

<head>

<title>:: JavaScript 매월 주차 구하기 ::</title>

<script type="text/javascript">


    document.addEventListener("DOMContentLoaded", function() {


        const stdDays = document.querySelector("span:nth-child(1)");     // @param 시작일 영역지정

        const endDays = document.querySelector("span:nth-child(2)");    // @param 종료일 영역지정


        let stdFirstDate = null;    // @param 시작일의 첫날

        let endFirstDate = null;   // @param 종료일의 첫날

        let stdLastDate = null;    // @param 시작일의 전체일수

        let endLastDate = null;   // @param 종료일의 전체일수



        // @param GET방식으로 전달된 시작일, 종료일의 지정값이 존재하지 않는경우 ( ※ 맨처음 시작시에 실행 )

        if(nullPointerException(getParameter("stdDate")) == false && nullPointerException(getParameter("endDate")) == false) {


            const toDate = new Date();


            stdFirstDate = new Date(toDate.getFullYear(), (toDate.getMonth() + 1));

            stdLastDate = new Date(stdFirstDate.getFullYear(), (stdFirstDate.getMonth() + 1), 1);  // @param 다음달의 첫째날을 지정

            stdLastDate.setDate(0);    // @param 다음달에서 하루를 빼서 원하는 월의 마지막 날로 맞춤


            endFirstDate = new Date(toDate.getFullYear(), (toDate.getMonth() + 1));

            endLastDate = new Date(endFirstDate.getFullYear(), (endFirstDate.getMonth() + 1), 1);  // @param 다음달의 첫째날을 지정

            endLastDate.setDate(0);    // @param 다음달에서 하루를 빼서 원하는 월의 마지막 날로 맞춤


        }


        // @param GET방식으로 전달된 시작일, 종료일의 지정값이 존재하는 경우

        else {


            const stdDate = getParameter("stdDate");

            stdFirstDate = new Date(

                  Number(stdDate.substring(0, 4))

                , Number(stdDate.substring(6, 8))

                , Number(stdDate.substring(4, 6))

            );

            stdLastDate = new Date(stdFirstDate.getFullYear(), stdFirstDate.getMonth(), 1);  // @param 다음달의 첫째날을 지정

            stdLastDate.setDate(0);    // @param 다음달에서 하루를 빼서 원하는 월의 마지막 날로 맞춤


            const endDate = getParameter("endDate");

            endFirstDate = new Date(

                  Number(endDate.substring(0, 4))

                , Number(endDate.substring(4, 6))

                , Number(endDate.substring(6, 8))

            );

            endLastDate = new Date(endFirstDate.getFullYear(), endFirstDate.getMonth(), 1);  // @param 다음달의 첫째날을 지정

            endLastDate.setDate(0);    // @param 다음달에서 하루를 빼서 원하는 월의 마지막 날로 맞춤


        }


        // @param 년도( Year ) - 선택

        for(let year = 0; stdDays.querySelector("select:nth-child(1)").length > year; year++) {

            if(stdDays.querySelector("select:nth-child(1)").options[year].value == stdFirstDate.getFullYear()) {

                stdDays.querySelector("select:nth-child(1)").options[year].selected = true;

            }


            if(endDays.querySelector("select:nth-child(1)").options[year].value == stdFirstDate.getFullYear()) {

                endDays.querySelector("select:nth-child(1)").options[year].selected = true;

            }

        }


        // @param 월( Month ) - 선택

        for(let month = 0; stdDays.querySelector("select:nth-child(2)").length > month; month++) {

            if(stdDays.querySelector("select:nth-child(2)").options[month].value == stdFirstDate.getMonth()) {

                stdDays.querySelector("select:nth-child(2)").options[month].selected = true;

            }


            if(endDays.querySelector("select:nth-child(2)").options[month].value == endFirstDate.getMonth()) {

                endDays.querySelector("select:nth-child(2)").options[month].selected = true;

            }

        }


        // @param 일( Day ) - 생성 및 선택

        // @details - 날짜는 매월 마지막일이 다르게 끝나기 때문에 직접 생성하고,

        //                선택한 날( 기본값 시작일 - 01일, 종료일은 - 말일 )을 자동으로 selected 한다.

        for(let date = 1; stdLastDate.getDate() >= date; date++) {

            if(stdFirstDate.getDate() == date) {

                stdDays.querySelector("select:nth-child(3)").innerHTML

                    += "<option value='" + date + "' selected>" + autoLeftPad(date, 2) + "</option>";

            } else {

                stdDays.querySelector("select:nth-child(3)").innerHTML

                    += "<option value='" + date + "'>" + autoLeftPad(date, 2) + "</option>";

            }

        }


        for(let date = 1; endLastDate.getDate() >= date; date++) {

            if((nullPointerException(getParameter("endDate")) == true) ? endFirstDate.getDate() : endLastDate.getDate() == date) {

                endDays.querySelector("select:nth-child(3)").innerHTML

                    += "<option value='" + date + "' selected>" + autoLeftPad(date, 2) + "</option>";

            } else {

                endDays.querySelector("select:nth-child(3)").innerHTML

                    += "<option value='" + date + "'>" + autoLeftPad(date, 2) + "</option>";

            }

        }

    });



    /**

     * @brief   시작일( 년, 월 ), 종료일 ( 년, 월 )의 값이 변경된 경우 실행

     * @details 시작일( 년, 월 ), 종료일 ( 년, 월 )의 값이 변경에 따른 해당 년월의 전체일수를 다시 체크,

     *             변경된 년월의 일보다 선택된 일이 큰경우 마지막 날로 선택일 자동 변경

     */

    function changeConditionPeriod(cal) {


        const searchDays = cal.parentNode;

        const checkDate = searchDays.querySelector("select:nth-child(3)").value;


        // @param 다음달의 첫째날을 지정

        let lastDate = new Date(

              searchDays.querySelector("select:nth-child(1)").value

            , searchDays.querySelector("select:nth-child(2)").value

            , 1

        );

        lastDate.setDate(0);    // @param 하루를 뺌


        // @param 일정보 초기화

        searchDays.querySelector("select:nth-child(3)").innerHTML = "";


        // @param 일정보 재삽입

        for(let date = 1; lastDate.getDate() >= date; date++) {

            if(checkDate == date) {

                searchDays.querySelector("select:nth-child(3)").innerHTML

                    += "<option value='" + date + "' selected>" + autoLeftPad(date, 2) + "</option>";

            } else {

                searchDays.querySelector("select:nth-child(3)").innerHTML

                    += "<option value='" + date + "'>" + autoLeftPad(date, 2) + "</option>";

            }

        }


        // @param 일정보 예외처리 선택되어있는 마지막 날이 바꾼 월의 날보다 큰경우의 예외처리

        if(checkDate > lastDate.getDate()) {

            searchDays.querySelector("select:nth-child(3) > option:last-child").selected = true;

        }

    }


    /**

     * @brief   강제로 앞에 0을 붙여서 두자릿수 숫자로 변경한다.

     * @param   num     앞에 0을 붙일 숫자 값

     * @param   digit   자릿수를 지정

     */

    function autoLeftPad(num, digit) {

        if(String(num).length < digit) { num = new Array(digit - String(num).length + 1).join("0") + num; }

        return num;

    }


    /**

     * @brief   GET으로 넘어오는 값의 존재 여부를 체크

     */

    function nullPointerException(worth) {

        if(

               worth == "" 

            || worth == null 

            || worth == undefined 

            || (worth != null && typeof worth == "object" && !Object.keys(worth).length == "")

        ) {

            return false;

        } else {

            return true;

        }

    }


    /**

     * @brief   GET방식으로 전달된 값을 추출

     * @param   param     추출할 key의 명칭

     */

    var getParameter = function(param) {

        let returnValue;

        let url = location.href;

        let parameters = (url.slice(url.indexOf("?") + 1, url.length)).split("&");

        for(let i = 0; i < parameters.length; i++) {

            let varName = parameters[i].split("=")[0];

            if (varName.toUpperCase() == param.toUpperCase()) {

                returnValue = parameters[i].split("=")[1];

                return decodeURIComponent(returnValue);

            }

        }

    };

</script>

</head>

<body>

    <h1>■ SelectBox를 이용한 캘린더</h1>

    <hr/>

    <!-- 년( Year )은 자동으로 생성시 너무 많이 늘어날 수 있어서 -->

    <!-- 월( Month )은 01 ~ 12라는 고정값을 알고 있기에 직접 값을 지정 -->

    <!-- 일( Day )은 마지막 일이 항상 변하기 때문에 자동 생성 한다. -->

    <div>

        <span>

            시작일&nbsp;:&nbsp;

            <select onChange="changeConditionPeriod(this);">

                <option value="2018">2018</option>

                <option value="2019">2019</option>

                <option value="2020">2020</option>

                <option value="2021">2021</option>

                <option value="2022">2022</option>

                <option value="2023">2023</option>

                <option value="2024">2024</option>

                <option value="2025">2025</option>

            </select>

            &nbsp;년&nbsp;&nbsp;

            <select onChange="changeConditionPeriod(this);">

                <option value="1">01</option>

                <option value="2">02</option>

                <option value="3">03</option>

                <option value="4">04</option>

                <option value="5">05</option>

                <option value="6">06</option>

                <option value="7">07</option>

                <option value="8">08</option>

                <option value="9">09</option>

                <option value="10">10</option>

                <option value="11">11</option>

                <option value="12">12</option>

            </select>

            &nbsp;월&nbsp;&nbsp;

            <select class="choiceDay"></select>

            &nbsp;일

        </span>

        &nbsp;&nbsp;~&nbsp;&nbsp;

        <span>

            종료일&nbsp;:&nbsp;

            <select onChange="changeConditionPeriod(this);">

                <option value="2018">2018</option>

                <option value="2019">2019</option>

                <option value="2020">2020</option>

                <option value="2021">2021</option>

                <option value="2022">2022</option>

                <option value="2023">2023</option>

                <option value="2024">2024</option>

                <option value="2025">2025</option>

            </select>

            &nbsp;년&nbsp;&nbsp;

            <select onChange="changeConditionPeriod(this);">

                <option value="1">01</option>

                <option value="2">02</option>

                <option value="3">03</option>

                <option value="4">04</option>

                <option value="5">05</option>

                <option value="6">06</option>

                <option value="7">07</option>

                <option value="8">08</option>

                <option value="9">09</option>

                <option value="10">10</option>

                <option value="11">11</option>

                <option value="12">12</option>

            </select>

            &nbsp;월&nbsp;&nbsp;

            <select class="choiceDay"></select>

            &nbsp;일

        </span>

    </div>

</body>

</html> 



이제 위 코드를 브라우저를 통해 실행시켜 보면




# 출력결과




위와 같이 예외처리해야할 사항들이 정상적으로 반영되어 기능하는 모습을 확인 할 수 있다.








반응형
//

[JavaScript] 매월의 월요일 기준, 주차 구하기[JavaScript] 매월의 월요일 기준, 주차 구하기

Posted at 2020. 6. 29. 18:14 | Posted in JavaScript & jQuery/JavaScript
반응형




참고 : https://blog.naver.com/z1004man/220936098651

참고 : https://matthew-jo.tistory.com/8





매월 월요일을 기준으로하는 주차 및 해당주차의 날짜 구하기


( 부제 : 일요일을 마지막으로 하는 주차 구하기 )




선택한 매월의 월요일을 기준으로하는 해당월의 주차수와 각 주차별 월요일, 일요일 날짜를 구하는 함수




통계 프로그램을 만들면서 JSP, PHP, Oracle, MySQL등 여러가지 개발 언어를 사용하여


매월의 주차를 구하는 프로그램을 구현해야 하는 일이 종종 발생 하였다.


그런데 저렇게 다양한 환경에서 매번 그 언어에 맞게 다시 조립 및 분해하는 과정이 생기는게 싫어서.


이번에 아예 한번 자바스크립트로 클라이언트 영역에서 넘겨줄 값을 미리 다 생성해서 넘겨주면


좀더 환경에 자유로울 수 있을 것 같아 순수 자바스크립트로 매월의 ( 월요일을 시작을 기준 )으로 함수를 만들어 정리해 보았다.





# 소스코드

<html>

<head>

<title>:: JavaScript 매월 주차 구하기 ::</title>

<script type="text/javascript">


    // 기준요일에 따른 주차구하는 함수.

    // 해당 주차 / 해당주차 시작날짜 / 해당주차 끝나는날짜를 리턴.

    function searchPeriodCalculation() {


        let cYear = document.getElementById("choiceYear").value;

        let cMonth = document.getElementById("choiceMonth").value.replace(/(^0+)/, "") - 1;


        // 날짜형으로 데이트 포맷

        let date = new Date(cYear, cMonth);


        // 월요일을 중심으로한 주차 구하기( JS기준 : 일요일 0 월요일 1 ~ 토요일 6 )

        let firstDay = new Date(date.getFullYear(), date.getMonth(), 1);

        let lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0);


        let weekObj = null;

        let weekObjArray = new Array();

        let weekStand = 8;  // 월요일 고정

        let firstWeekEndDate = true;

        let thisMonthFirstWeek = firstDay.getDay();


        for(var num = 1; num <= 6; num++) {


            // 마지막월과 첫번째월이 다른경우 빠져나온다.

            if(lastDay.getMonth() != firstDay.getMonth()) {

                break;

            }


            weekObj = new Object();


            // 한주의 시작일은 월의 첫번째 월요일로 설정

            if(firstDay.getDay() <= 1) {


                // 한주의 시작일이 일요일이라면 날짜값을 하루 더해준다.

                if(firstDay.getDay() == 0) { firstDay.setDate(firstDay.getDate() + 1); }


                weekObj.weekStartDate =

                      firstDay.getFullYear().toString()

                    + "-"

                    + numberPad((firstDay.getMonth() + 1).toString(), 2)

                    + "-"

                    + numberPad(firstDay.getDate().toString() , 2);

            }


            if(weekStand > thisMonthFirstWeek) {

                if(firstWeekEndDate) {

                    if((weekStand - firstDay.getDay()) == 1) {

                        firstDay.setDate(firstDay.getDate() + (weekStand - firstDay.getDay()) - 1);

                    }

                    if((weekStand - firstDay.getDay()) > 1) {

                        firstDay.setDate(firstDay.getDate() + (weekStand - firstDay.getDay()) - 1)

                    }

                    firstWeekEndDate = false;

                } else {

                    firstDay.setDate(firstDay.getDate() + 6);

                }

            } else {

                firstDay.setDate(firstDay.getDate() + (6 - firstDay.getDay()) + weekStand);

            }


            // 월요일로 지정한 데이터가 존재하는 경우에만 마지막 일의 데이터를 담는다.

            if(typeof weekObj.weekStartDate !== "undefined") {


                weekObj.weekEndDate =

                      firstDay.getFullYear().toString()

                    + "-"

                    + numberPad((firstDay.getMonth() + 1).toString(), 2)

                    + "-"

                    + numberPad(firstDay.getDate().toString(), 2);

                    

                weekObjArray.push(weekObj);

            }


            firstDay.setDate(firstDay.getDate() + 1);

        }


        console.log( weekObjArray );

    }


    // 월, 일 날짜값 두자리( 00 )로 변경

    function numberPad(num, width) {

        num = String(num);

        return num.length >= width ? num : new Array(width - num.length + 1).join("0") + num;

    }

</script>

</head>

<body>

    <div>

        <input type="text" id="choiceYear" style="text-align:center;" value="2020"/>

        &nbsp;년&nbsp;&nbsp;

        <select id="choiceMonth">

            <option value="01">01</option>

            <option value="02">02</option>

            <option value="03">03</option>

            <option value="04">04</option>

            <option value="05">05</option>

            <option value="06">06</option>

            <option value="07">07</option>

            <option value="08">08</option>

            <option value="09">09</option>

            <option value="10">10</option>

            <option value="11">11</option>

            <option value="12">12</option>

        </select>

        &nbsp;월&nbsp;&nbsp;

        <button type="button" onClick="searchPeriodCalculation( );">검색</button>

    </div>

</body>

</html>




위 코드의 결과는 아래와 같다.



# 출력 결과















반응형
//

[JavaScript] Table을 Excel 처럼 즉시 수정가능하게 만들기[JavaScript] Table을 Excel 처럼 즉시 수정가능하게 만들기

Posted at 2020. 6. 24. 09:46 | Posted in JavaScript & jQuery/JavaScript
반응형

 

 

 

주의 : 해당 포스팅의 샘플 코드에서 사용하는 addEventListener( ) 메서드의 경우 Internet Explorer에서는 정상적으로 작동하지 않습니다.

           호환되지 않는 Internet Explorer에서의 사용의 경우는 addEventListener( ) 메서드를 attachEven( )로 변경하여 작업을 진행하셔야 합니다.

 

 

 

 

 

contenteditable 속성을 이용한 텍스트 내용 수정

 

 

 

기본적으로 웹 환경에서 값을 변경하기 위해서는 <input type="text" value="값">이 기본 형태였지만.

 

HTML5가 나오면서 contenteditable을 이용해 좀더 재미있는 사용자 경험을 제공할 수 있게 되었다.

 

해당 포스팅에서는 웹 페이지에 출력된 텍스트 문구를 contenteditable 속성을 이용하여 수정해 보려 한다.

 

 

 

# 소스코드

<html>
<head>
<title>:: JavaScript Contenteditable ::</title>
</head>
<body>
<p contenteditable="false">
    이 단락은 편집을 원하는 경우 더블 클릭해 주세요
</p>
</body>
</html>
<script type="text/javascript">

    // @breif contenteditable 속성을 가진경우
    content = document.querySelector( "[contenteditable]" );
   
    document.addEventListener("DOMContentLoaded", function() {

        // @breif 더블클릭시 실행
        content.addEventListener("dblclick", function(event) {

            // @details contenteditable 속성이 수정 불가인 경우 실행( false )
            if(content.isContentEditable == false) {

                // @details 편집 가능 상태로 변경
                content.contentEditable = true;

                // @details 텍스트 문구 변경
                content.textContent = "편집 가능한 상태로 변경되었습니다.";

                // @details CSS 효과 추가
                content.style.border = "1px solid #FE7F9C";

                // @details 포커스 지정
                content.focus();
            }

            // @details contenteditable 속성이 수정 가능인 경우 실행( true )
            else {

                // 편집 불가 상태로 변경
                content.contentEditable = false;
                content.style.border = "0px";
            }
        });
    });
</script>

 

 

 

# 출력결과

 

 

 

 

 


 

 

 

 

 

■ 테이블을 엑셀처럼 즉시 변경가능하게 만들기

 

 

 

 

이제 contenteditable 태그를 좀더 심도있게 활용을 해보자.

 

바로 테이블에 출력되어 있는 내용을 엑셀처럼 즉시 수정을 하고 해당내용을 적용하는 것이다.

 

즉각적으로 수정된 내용을 데이타 베이스( DataBase )에 적용하는 예제는 아니지만.

 

해당 내용의 결과물을 본다면 어떻게 사용하는것이 좋을지 바로 감이 올 거라 생각한다.

 

 

# 소스코드

<html>
<head>
<title>:: JavaScript Contenteditable ::</title>
<style type="text/css">
    table { border-collapse:collapse;border:1px gray solid; }
    .rowColumn { border-radius:5px;margin:5px; }
</style>
</head>
<body>
<table border="1">
    <thead>
        <tr>
            <th style="width:50px;">번호</th>
            <th style="width:120px;">가수</th>
            <th style="width:350px;">노래</th>
            <th style="width:100px;">발매일</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td style="text-align:center;">1</td>
            <td>
                <p class="rowColumn" contenteditable="false" data-default="윤하">윤하</p>
            </td>
            <td>
                <p class="rowColumn" contenteditable="false" data-default="오렌지 첫사랑">오렌지 첫사랑</p>
            </td>
            <td>
                <p class="rowColumn" contenteditable="false" data-default="2007-10-23">2007-10-23</p>
            </td>
        </tr>
        <tr>
            <td style="text-align:center;">2</td>
            <td>
                <p class="rowColumn" contenteditable="false" data-default="도원경">도원경</p>
            </td>
            <td>
                <p class="rowColumn" contenteditable="false" data-default="다시 사랑한다면">다시 사랑한다면</p>
            </td>
            <td>
                <p class="rowColumn" contenteditable="false" data-default="2001-05-01">2001-05-01</p>
            </td>
        </tr>
        <tr>
            <td style="text-align:center;">3</td>
            <td>
                <p class="rowColumn" contenteditable="false" data-default="더더">더더</p>
            </td>
            <td>
                <p class="rowColumn" contenteditable="false" data-default="it's You">it's You</p>
            </td>
            <td>
                <p class="rowColumn" contenteditable="false" data-default="1998-11-01">1998-11-01</p>
            </td>
        </tr>
        <tr>
            <td style="text-align:center;">4</td>
            <td>
                <p class="rowColumn" contenteditable="false" data-default="박기영">박기영</p>
            </td>
            <td>
                <p class="rowColumn" contenteditable="false" data-default="시작">시작</p>
            </td>
            <td>
                <p class="rowColumn" contenteditable="false" data-default="1999-04-07">1999-04-07</p>
            </td>
        </tr>
        <tr>
            <td style="text-align:center;">5</td>
            <td>
                <p class="rowColumn" contenteditable="false" data-default="이소은">이소은</p>
            </td>
            <td>
                <p class="rowColumn" contenteditable="false" data-default="오래오래">오래오래</p>
            </td>
            <td>
                <p class="rowColumn" contenteditable="false" data-default="2002-12-21">2002-12-21</p>
            </td>
        </tr>
    </tboyd>
</talbe>
</body>
</html>
<script type="text/javascript">


    // @breif contenteditable 속성을 가진경우
    contents = document.getElementsByClassName("rowColumn");


    document.addEventListener("DOMContentLoaded", function() {


        // @breif rowColumn 클래스의 갯수 만큼 반복문을 실행한다.
        Array.from(contents).forEach(function(content) {


            // @breif 마우스로 해당영역을 더블클릭 한경우
            content.addEventListener("dblclick", function(event) {


                // @breif 전체 테이블 컬럼( td > p )에서 현재 사용중인 값의 존재여부를 확인한다.
                Array.from(contents).forEach(function(defaultVal) {


                    /*
                    // @details 빈값( null )이 존재하는지 체크한다.
                    if(
                           defaultVal.textContent == ""
                        || defaultVal.textContent == null
                        || defaultVal.textContent == undefined
                        || (defaultVal.textContent != null
                        && typeof defaultVal.textContent == "object"
                        && !Object.keys(defaultVal.textContent).length == ""))
                    {

                        // @details 내용이 존재하지 않다면 data 태그의 기본값으로 되돌린다.
                        defaultVal.textContent = defaultVal.dataset.default;
                    }
                    */

                    // @details 저장하지 않은 내용이라고 판단하여 data 태그의 기본값으로 되돌린다.
                    defaultVal.textContent = defaultVal.dataset.default;

                    // @breif 수정 불가 상태로 되돌린다.
                    defaultVal.contentEditable = false;
                    defaultVal.style.border = "0px";
                });


                if(content.isContentEditable == false) {


                    // @details 편집 가능 상태로 변경
                    content.contentEditable = true;


                    // @details 텍스트 문구 변경
                    // content.textContent = "";


                    // @details CSS 효과 추가
                    content.style.border = "1px solid #FFB6C1";


                    // @details 포커스 지정
                    content.focus();
                }
            });


            // @breif 키보드 입력이 방생한 경우 실행
            content.addEventListener("keypress", function(event) {


                // @breif Enter키 입력시 실행
                if(event.key === "Enter") {


                    // @details 입력된 값이 빈값( null )인지 체크한다.
                    if(
                           content.textContent == ""
                        || content.textContent == null
                        || content.textContent == undefined
                        || (content.textContent != null
                        && typeof content.textContent == "object"
                        && !Object.keys(content.textContent).length == ""))
                    {


                        // @details 내용이 존재하지 않다면 data 태그의 기본값으로 되돌린다.
                        content.textContent = content.dataset.default;
                    } else {


                        // @details 내용의 수정이 완료되었다면 data 태그의 기본값도 바꿔준다.
                        content.dataset.default = content.textContent;
                    }


                    // @breif 수정 불가 상태로 되돌린다.
                    content.contentEditable = false;
                    content.style.border = "0px";
                }
            });
        });
    });
</script>

 

 

 

위 코드의 결과는 아래와 같이 직접 확인을 진행해 보자.

 

 

 

# 출력결과

번호

가수

노래

발매일

1 윤하 오렌지 첫사랑 2007-10-23
2 도원경 다시 사랑한다면 2001-05-01
3 더더 it's You 1998-11-01
4 박기영 시작 1999-04-07
5 이소은 오래오래 2002-12-21

주의해당 출력결과Internet Explorer에서는 정상적으로 작동하지 않습니다.

 

 

위에 가수, 노래, 발매일 항목을 마우스로 더블클릭( dblclick )하면 내용을 수정하고, 키보드 Enter 키를 눌러 변경한 내용을 반영할 수 있다.

( 단 공란( null )은 사용할 수 없게 처리되어 있다. )

 

 

또한 하나의 컬럼을 수정하던중 다른 컬럼을 더블클릭( dblclick )한 경우 수정중이던 컬럼영역은

 

data-default에 저장되어있는 기존값으로 다시 돌아가게 설계되어 있다.

 

이와같은 예외처리는 용도에 맞게 코드를 조금 손보면 바로 적용할 수 있을 것이다.

 

 

 

 

자체과제 : 심화코드 만들어보기

 

 

 

 

 

반응형
//

[JavaScript] Canvas로 이미지 서버에 전송하기 - Sample[JavaScript] Canvas로 이미지 서버에 전송하기 - Sample

Posted at 2020. 3. 6. 17:38 | Posted in JavaScript & jQuery/JavaScript
반응형




참고 : https://bugsdb.com/_ko/debug/14292f07a628fd12f3b6afc4a751f956






■ 캔버스로 생성한 이미지 파일 서버에 업로드 하기




# 소스코드 - 클라이언트 페이지 ( 이미지 전송 )

 canvas_upload.php

<html>

<head>

<meta charset="UTF-8">

<title>:: JavaScript 캔버스 이미지 업로드 ::</title>

<meta name="viewport" content="width=device-width">

<link type="text/css" rel="stylesheet" href="./css/modern.css"/>

<style type="text/css">

    .imgArea { text-align:center; }

    canvas, #uploadFile, #submitBtn { display:none; }

</style>

<script type="text/javascript">


    // @breif AJAX 통신 함수

    function getXMLHTTPRequest() {

        let request = false;

        try { request = new XMLHttpRequest(); }

        catch(err01) {

            try { request = new ActiveXObject("Msxml2.XMLHTTP"); }

            catch(err02) {

                try { request = new ActiveXObject("Microsoft.XMLHTTP"); }

                catch(err03) { request = false; }

            }

        }

        return request;

    }


    const ajax = getXMLHTTPRequest();


    // @breif 이미지 업로드 함수

    function uploadImgFilePrinted() {


        // @details 업로드 파일 정보를 받아온다.

        let fileInfo = document.getElementById("uploadFile").files[0];

        let reader = new FileReader();


        reader.onload = function() {


            // @details 업로드 버튼 이미지의 넓이와 높이에 맞게 썸네일을 생성을 위해 그 값을 받아온다.

            const btnWidth = document.getElementById("uploadImg").width;

            const btnHeight = document.getElementById("uploadImg").height;


            // @details 업로드 이미지 출력

            document.getElementById("uploadImg").src = reader.result;

            document.getElementById("uploadImg").width = btnWidth;

            document.getElementById("uploadImg").height = btnHeight;


            // @details 등록버튼 노출

            document.querySelector("#submitBtn").style.display = "inline";

        };


        if(fileInfo) {

            // @details readAsDataURL을 통해 업로드한 파일의 URL을 읽어 들인다.

            reader.readAsDataURL(fileInfo);

        }

    }


    function registryUploadImg() {

        if(confirm("업로드한 이미지를 업로드 하시겠습니까?") == true) {


            let upImage = new Image();

            upImage.src = document.getElementById("uploadImg").src;


            upImage.onload = function() {


                // @breif 캔버스 위에 이미지 그리기

                let canvas = document.querySelector("canvas");

                let canvasContext = canvas.getContext("2d");


                // @details 리사이즈할 이미지의 크기는, 현재 클라이언트에게 노출되어 있는 이미지 크기로 지정

                const btnWidth = document.getElementById("uploadImg").width;

                const btnHeight = document.getElementById("uploadImg").height;


                // @details 캔버스 생성 및 이미지 그리기

                canvas.width = btnWidth;

                canvas.height = btnHeight;

                canvasContext.drawImage(this, 0, 0, btnWidth, btnHeight);


                // @details AJAX 전송 데이터 설정

                const arrData =  {

                      "mode" : "imgFileUpload"

                    , "imgBase64" : canvas.toDataURL("image/jpeg")

                    , "imgFileName" : document.getElementById("uploadFile").files[0]['name']

                };


                ajax.open("POST", "./upload_image.php", true); // @details 비동기 방식 사용

                ajax.setRequestHeader("Content-Type", "application/json;charset=UTF-8;");

                ajax.send(JSON.stringify(arrData));


                ajax.onreadystatechange = function() {

                    if(ajax.readyState == 4) {

                        if(ajax.status == 200) {

                            const json = JSON.parse(ajax.responseText);

                            if(json.ret == "success") {

                                alert(json.message);

        } else {

                                alert(json.message);

                            }

                        } else {

                            alert(json.message);

                        }

                    }

                };

            };


        } else {

            return false;

        }

    }

</script>

<link rel="import" href="/edit_img.html">

</head>

<body>

<div class="windowFilm"></div>

<form id="uploadFrom" method="post">

    <input type="file" id="uploadFile" onChange="uploadImgFilePrinted();" accept="image/*"/>

    <div class="contents">

        <h1>캔버스&nbsp;업로드<span>샘플</span></h1>

        <div class="imgArea">

            <a href="javascript:;" onClick="document.getElementById('uploadFile').click();">

                <img id="uploadImg" src="./user-anonymous.png"/>

            </a>

            <br/><br/>

            <input id="submitBtn" type="button" onClick="registryUploadImg();" value="등록">

        </div>

        <canvas></canvas>

        <div class="copyright" style="bottom:0;">

            <p>Producer &copy; 사악미소</p>

        </div>

    </div>

</form>

</body>

</html> 




# 소스코드 - 서버 ( 이미지 파일 저장 )

 upload_image.php

<?php

$RetVal = array("ret"=>"fail", "error"=>"", "error_msg"=>"", "message"=>"");

$backUrl = $_SERVER['HTTP_REFERER'];


// @breif php://input은 내용의 유형에 관계없이 요청 HTTP 헤더 뒤에 모든 원시 데이터를 반환한다.

$data = json_decode(file_get_contents("php://input"), true);


switch($data['mode']) {


    // @breif ajax 이미지 업로드

    case "imgFileUpload" :


        // @breif base64로 암호화된 이미지 파일명 디코드

        $uploadCanvas = base64_decode(preg_replace("#^data:image/\w+;base64,#i", "", $data['imgBase64']));


        // @breif 파일명과 확장자를 분리 하여 담는다.

        $fileName = array_shift(explode(".", $data['imgFileName'])); // @details 이미지 파일 이름


        // @breif 이미지 파일명 변경

        $uploadImgName = $fileName."_".time().".jpg";


        // @breif 업로드할 이미지 경로 설정

        $uploadSrc = "./upload/".$uploadImgName;


        // @breif 이미지 파일 저장

        file_put_contents($uploadSrc, $uploadCanvas);


        // @breif 성공 여부 확인

        if(file_exists($uploadSrc) == TRUE) {

            $RetVal['ret'] = "success";

            $RetVal['message'] = "파일을 업로드 하는데 성공하였습니다.\n축하합니다.";

        } else {

            $RetVal['message'] = "파일을 업로드 하는데 실패하였습니다.\n다시 시도하여 주시기 바랍니다.";

        }


// @return 업로드 성공 / 실패 유무를 json 방식으로 웹 페이지에 전달한다.

        print json_encode($RetVal);

        return;


    break;


    default :


    $RetVal['message'] = "서버와의 연결 실패";

    print json_encode($RetVal);

        return;


    break;

}


header("location:".$backUrl);

?>





# 출력결과







관련포스팅#01 : [JavaScript] 이미지 파일 썸네일 생성하기

관련포스팅#02 : [JavaScript] Canvas를 통한 이미지 회전

관련포스팅#03 : [JavaScript] Canvas로 이미지 서버에 전송하기 - Sample

관련포스팅#04 : [jQuery] JCROP을 이용한 이미지 자르기 - Sample










반응형
//