[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에 저장되어있는 기존값으로 다시 돌아가게 설계되어 있다.

 

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

 

 

 

 

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

 

 

 

 

 

반응형
//

퍼펫티어 request, response ( 작성중 )퍼펫티어 request, response ( 작성중 )

Posted at 2020. 5. 29. 22:38 | Posted in Node.js/Puppeteer
반응형






    await page.on("response", async(response) => {
        console.log(await response.url());
        console.log(await response.json());
    });




    await  page.on("request", request => {
        console.log("123");
        request.continue({
              url:"사이트 URL"
            , method:"GET"
            , postData: JSON.stringify({ test:"test" })
            , headers: { "Content-Type":"application/json" },
        });
    }); 





반응형
//

[Puppeteer] 퍼펫티어를 이용해 File Upload 하기[Puppeteer] 퍼펫티어를 이용해 File Upload 하기

Posted at 2020. 5. 29. 21:26 | Posted in Node.js/Puppeteer
반응형





■ 퍼펫티어를 이용해 파일 업로드하기






퍼펫티어를 통한 사무자동화 작업중 사이트의 파일 업로드를 해야하는 경우가 발생하였다.


문제는 헤드리스 모드( 작업화면이 사용자에게 보이지않는 ) 상황으로 완성품을 만들기위해서는.


업로드 버튼을 누르고 → 파일을 찾고 → 이미지를 업로드 하는 과정을


컴퓨터 화면을 보지 않고 진행을 해야한다는 것이다.









다행이 퍼펫티어에서는 waitForFileChooser( ) 메서드를 통해


해결할 수 있는 방법을 제공한다.


아래 코드를 통해 관련 작업을 진행해 보자.




# 소스코드 - node.js

 fileUpload.php

const puppeteer = require( "puppeteer" );


// 사용시 인위적인 딜레이를 주기위한 함수

function delay( timeout ) {

    return new Promise(( resolve ) => {

      setTimeout( resolve, timeout );

    });

}


puppeteer.launch({

      headless : false          // 헤드리스모드의 사용여부를 묻는다.

    , devtools : false          // 개발자 모드의 사용여부를 묻는다.

    , slowMo : 50          // 지정된 시간(ms)만큼 Puppeteer 작업 속도를 낮춘다.

}).then(async browser => {


    const page = await browser.newPage();

    await page.goto( "http://localhost/fileUpload.php", { waitUntil : "networkidle2" } );


    const [fileChooser] = await Promise.all( [

        page.waitForFileChooser( ),

        page.click( "#upFile" )         // 파일을 올릴 input type = "file" 태그를 지정

    ] );


    await fileChooser.accept( [ "puppeteer.png" ] );


    // 3초간딜레이를 준다.

    await delay( 3000 );


    page.waitForNavigation( ),page.click( "#upBtn" );


    // await browser.close( );

});




해당 포스팅은 운영중인 어느 사이트가 아닌


업로드 기능을 가진 페이지를 기준으로 테스트를 진행하려 한다.


아래 파일을 업로드할 웹 페이지를 간단히 제작하자.




# 소스코드 - PHP

 fileUpload.php

<?php

    echo "<pre>";

    print_r( $_FILES );

    echo "</pre>";

?>

<html>

<head>

<title>:: Puppeteer 업로드 속성 ::</title>

<script type="text/javascript">

    function fileUpload() {

        document.getElementById("upFrm").action = "./fileUpload.php";

        document.getElementById("upFrm").submit();

    }

</script>

</head>

<body>

    <form enctype="multipart/form-data" id="upFrm" method="post">

        <input type="file" id="upFile" name="upFile" accept="image/*">

        <br/><br/>

        <input type="button" onClick="fileUpload();" id="upBtn" value="업로드">

    </form>

</body>

</html>





# 출력결과




그럼 위의 이미지와 같이, 지정한 이미지를 자동으로 업로드 해주는 모습을 확인 할 수 있다.







반응형
//

[Flow.Txt] 언젠간 내 기술이 시대 흐름에 뒤쳐질지 모른다.(2020.05.29)[Flow.Txt] 언젠간 내 기술이 시대 흐름에 뒤쳐질지 모른다.(2020.05.29)

Posted at 2020. 5. 29. 15:39 | Posted in Flow.Txt
반응형





금일에서야 대충 마무리 단계에 들어간 프로젝트의 프로그램 코드중에서


다음에도 다시 재활용하게될 코드들이 있을것 같아 정리하던중


문득 그 내용에대해 관련 기사나 포스팅등을 검색을 해보던중


"Puppeteer vs Playwright. : 어느 쪽을 택할 것인가?"


라는 포스팅을 보게 되어서 드는 생각을 좀 정리해 보았다.






자동화( 브라우저 제어 ) 업무가.


어쩌다 보니 근1년가까이 주된 업무가 되었다.


지금은 내가 웹 개발자인지, node.js 개발자인지 좀 햇갈릴 정도로, 해당 업무비중이 높아진 상태다.








일단 파이썬은 내가 다시 배워야 했지만, node.js의 경우 이미 JavaScript는 자칭( ? ) 귀신의 레벨이기에... 꽤 자신 있었기에....






그렇게 노드를 혼자서 열심히 파다가...


디노( Deno )라는 노드의 단점을 보완하기 위한것이 나왔다는 것을 알게 되어서...


아 앞으로는 이것도 해야 하나 라는 함숨이 절로 나올때...


아래의 포스팅을 보게되었다.





원문 : https://blog.logrocket.com/playwright-vs-puppeteer/


검색결과

사이트 링크가 있는 웹 검색결과

내가 Node.js의 작업중 1/3에서 2/3정도 사이의 작업 비중이 퍼펫티어 라이브러리를 사용하는


자동화와 스크랩핑이다, 그런데 그 퍼펫티어의 개발자들이 구글(Google)을 나와서 마이크로 소프트( MicroSoft )로 옮겨


플레이라이트라는 새로운걸 만들었다니...




물론 관련내용을 읽어보니 MySQL ↔ MariaDB 처럼...


바로 사용할 수 있는 레벨이지만...

( 위 포스팅에는 아직 Ver1.0 버전때지만 버전이 높아지면서 점점 차이가 두드럴 질 수 있다는 내용이 포함되어 있다. )


그래도 좀 괜히 힘빠지고 귀찮아 지는 것은 사실이다.





코로나19 사태로 많은일자리가 줄어들고 있기에 반대로.


이런 사무자동화 프로그램을 짤 수 있는건 큰 장점이 되기에...


"아 그래도 나는 이 사태가 내 밥줄에 크게 영향을 끼치지 않는다." 라는 생각을 가지게 되면서 잊고있었다.


기술의 패러다임이 변화하는 싸이클은 점점더 가속화 되고 있다는걸...


ㅠ.ㅠ





그닥 공부머리는 아니여서, 그냥 끈기하나로 붙잡고 늘어져서 코딩하는 나이기에...


관련내용들에 대해서는 블로그에 또 포스팅으로 남기겠지만.


언젠간 내가 가지고 있는 기술이 어쩌면 무쓸모 해질지 모른다 라는 생각이 드는건 역시 조금 서글프지만...









PS. 글을 다 쓰고 나서 좀 너무 부정적으로 끝나는것 같아 좀 더 써본다.


세상에 무의미한 기술은 없다, 아직 엄청나게 활성화 되거나 그런것도 아니고, 당장 내 기술이 사장된것도 아니다.


개발자로서 망하진 않을곳까지 올라간다... 적어도 그거는 이미 달성하였는데, 그거면 되었지...








반응형
//