인터넷에 보면 javascript로 프린트를 하는데 동적인 TD의 높이 때문에 자칫 페이지가 넘어갈 때 화면이 짤리는 상황을 경험하게 된다. 이를 완벽하게 해결한 쉬운 소스를 공개한다. 보통 폼은 첫번째 장에는 헤더가 있고 다음장부터는 헤더가 없는 경우가 많다. 본 포스팅은 이러한 경우까지 대응하고 있다. 말은 거창하지만 사실 그냥 그만큼 자리 차지하고 있고 그 아랫부분부터는 동적으로 알아서 잘라주고 이어주고 알아서 잘 되니 신경 안써도 된다.
4. 첫 페이지의 헤더를 위치시킨다. 내용은 각자 알아서 마음대로 교체하면 됨 id는 SECTIONPAGE1으로 하였다. 실질적인 출력 영역은 DIVTOPAREA class로 지정하였고 내용을 담당하는 DIV의 class는 DIVDATAAREA 끝으로 다음 tr의 높이가 페이지를 넘어서서 짤리게 될 경우 페이지 하단에 공백을 계산하여 넣어줘야 하므로 공백 DIV인 DIVBUFFERAREA가 추가되었다. 제목헤더인 DIVTOPAREA는 1페이지만 출력시키므로 SECTIONPAGE1에만 추가되어 있다. 참고로 여기서 거론한 class를 제외한 tdPrintWhite 클래스 등은 그냥 내 소스의 공통 css에서 사용되는 클래스이므로 무시하면 됨 <sectionclass="sheet padding-10mm"id="SECTIONPAGE1"> <divclass="DIVTOPAREA"> <tableclass=""width="100%"border="0"cellspacing="0"cellpadding="10"> <tr> <tdclass="tdPrintWhite"width="100%;"align="center"><h1>제목헤더영역</h1> <ahref="#"class="btnblue"id="BTNPRINT">인쇄</a> </td> </tr> </table> </div> <br/> <divclass="DIVDATAAREA"></div> <divclass="DIVBUFFERAREA"></div> </section>
5.데이터를 출력시킬 SECTIONPAGE영역을 여유있게 미리 만들어 둔다. 동적으로 그때그때 생성해도 되지만 이게 편하다. 난 총 4페이지까지면 충분하므로 2부터 4까지 for문을 돌려 생성하였다. <% for iSEL = 2to4 %>
여기까지 진행했으면 일단 1차 준비는 끝났다. 이제 실질적으로 큰 영역에 모든 데이터들을 뿌려주고 페이지가 모두 로딩이 된 후 이 영역의 데이터들을 계산하여 앞서 만들어둔 SECTIONPAGE section의 DIVDATAAREA에 붙여넣기를 진행할 것이다.
6.보면 div에 id가 부여되었고 class도 편의상 id와 동일하게 부여하였다. 특이한 점은 rowspan이 2이다. 즉 아래와 같은 표형태이다. 굳이 왜 rowspan을 예로 들었냐면 그 이유는 잠시 후 아래에서 설명할 것이다. 이것 때문에 약간 고생을 한 경험이 있기 때문이다.
</tr> ......for문을 통해 모든 데이터들을 각자 알아서 출력...... </table> </div>
7.여기까지 HTML소스 끝이다. 이제 jQuery, javascript로 DIVITEMBODY의 데이터들을 계산해서 복사 붙여넣기를 하면 된다. 소스는 그냥 복사해서 붙여넣기 해보면 잘 동작할 것이다. 응용하려면 로직이 비교적 간단하니 바보 아니면 이해 가능한 수준이기도 하고 ;p 위에서 설명하기로 했던 내용으로 중요한 부분인 rowspan이 없는 헤더인 경우 "//헤더TR이 2줄 이므로 if (index == 0) 대신 if (index<= 1)로 사용"하라고 주석까지 달아 두었다. SECTION을 4개가 아닌 10개를 만들었다면 for (nSectionPage = 2; nSectionPage <= 4; nSectionPage++)의 4를 10로 바꾸면 된다. 이 정도면 정말 이유식을 떠먹여줬다 본다. 나머진 복사 붙여넣기 하면 끝
<script> $(document).ready(function() { var bAppendEnd = false; var nLastTrIndex = 0; var nSectionPage = 1;
var objSECTIONPAGE; var objTopArea; var objDataArea; var objBufferArea;
1. contents() 먼저 iframe에 접근하기 위해선 그냥 find로 접근하면 안된다. contents().find()로 접근해야 한다.
$("#부모창의IFRAME").contents().find("#자식창의DIV")
2. on load 1번까지 하고 접근해보면 null을 리턴하거나 undefine을 리턴한다. 본창의 document가 ready되었을 뿐이지 iFrame의 내용까지 모두 불러와진게 아니기 때문이다. 따라서 1번 코드는 이렇게 호출되어야만 한다. 부모창의IFRAME의 contents에서 자식창의DIV란 요소를 획득하겠다란 이야기인데 언제? iFrame이 모두 불러와져서 온전하게 자식요소들에 접근이 가능할 때 접근하겠다.
$("#부모창의IFRAME").on("load", function() { var objIframeChild = $("#부모창의IFRAME").contents().find("#자식창의DIV); $("#부모창의DIV").height(objIframeChild.height()); /* objIframeChild로 iFrame의 자식을 획득하였으니 이녀석을 가지고 놀면 된다. objIframeChild.height() 등등*/ }
여기까지 한다면 내 도메인 내에선 아무 문제 없이 주고 받고가 가능하지만 다른 도메인이라면 이야기가 달라진다.
망할 cross oigin
Uncaught DOMException: Blocked a frame with origin "http://~~~~" from accessing a cross-origin frame. 크롬에서 개발자 도구로 지켜보면 이 에러를 내뱉으며 동작하지 않는다.
iFrame 소스의 on load 또는 jQuery $(document).ready 아래에 부모로의 통신용 postMessage를 구현해준다. 구조는 간단하다. window.parent.postMessage() 안에 {키, 값} 형태로 넣어주고 콤마(,) 이후 전달 되는 도메인이나 IP주소를 입력해주면 된다. 도메인이나 IP주소 대신 *도 가능하지만 해킹당할 우려가 있으니 가급적 지양해야 할 것이고 만약 여러 도메인으로 통신을 해야겠다면 도메인이나 IP주소만 바꿔가며 window.parent.postMessage를 반복하면 된다.
window.parent.postMessage({키값: 보내고 싶은 값 }, "허용되는 도메인 또는 아이피");
보내는 곳인 자식쪽 소스 window.parent.postMessage({ childHeight: $("자식창의DIV").height() }, "http://......");
받는 곳인 부모쪽 그냥 리스너로메세지 받겠다 하면 끝이다. window.addEventListener('message', function(e) { $("#부모창의DIV").height(e.data.childHeight); });