반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리

JQuery Mobile - Scripting pages

2012. 7. 5. 00:22 | Posted by 솔웅


반응형

Scripting pages in jQuery Mobile


jQuery Mobile은 Ajax-powered navigation system을 사용하고 있어서 여러분의 content를 다루는 script를 write 할 때 알아두면 좋을 몇가지가 있습니다. 좀 더 자세한 사항은  global configuration options, events, and methods 같은 mobile API를 읽어보시던가 Ajax navigation model의 technical detail 부분을 파 보시면 도움이 될 겁니다.


Scripts & styles in the head


jQuery Mobile-driven site에서 유저가 클릭할 때 navigation system의 디폴트 behavior는 Ajax request를 formulate 하기 위해 링크의 href를 사용하는 것입니다. (request의 디폴트 link behavior인 requesting에 대한 full page load 하는 href 를 다루는 것을 하는 대신에..). 그 Ajax request가 완료되고 나면 프레임워크는 전체 text content를 받을 겁니다. 하지만 그것은 response 의 body element content 만 inject 할 겁니다. (혹은 특별한 경우 data-role="page"가 제공될 경우 그 엘리먼트를 inject 할 겁니다). 그런데 페이지의 head에는 별 특별한것이 없기 때문에 사용될게 없습니다. (페이지 타이틀 같은게 있기는 하지만 페이지에 표시할 별 다른 특별한 것은 없습니다.)  스크립트는 이런 상황에서 다이나믹하게 로드 됩니다. 이 스크립트가 일반 http request를 통해 로드된다면 로드되는 순서가 항상 같다는 보장은 없습니다. 


이 의미는 페이지의 헤드부분에 있는 어떤 스크립트나 스타일이라도 ajax를 통해서 로드되는 페이지일 때 어떤 effect도 없을거라는 겁니다. 다만 http를 통해 일반적으로 request될 때 excute 될 겁니다. jQuery Mobile 싸이트의 스크립트인 경우는 두 경우 모두를 고려해야 합니다. Ajax를 통해서 request 될 경우 head 부분이 무시될거라는 것은 이런 자바스크립트가 다시 excute 될 가능성이 아주 높다는 겁니다. (같은 스크립트를 모든 페이지에서 참조하는게 일반적인 구조이기 때문이죠.) 이것 때문에 작업하는데 좀 복잡한 점이 있기 때문에 우리는 page-specific script는 개발자에게 excuting 하는 작업을 미루고 있습니다. 그리고 head script는 브라우저 세션별로 딱 한번 execute 될 거라고 가정한 상황에서 작업을 합니다.


jQuery Mobile 싸이트로 만들 때 가장 손 쉬운 접근법은 모든 페이지의 head 부분에서 같은 스타일쉬트와 스크립트 셋트를 참조하는 것입니다. 특정 페이지에서는 특정 스크립트나 특정 스타일 쉬트를 로드해야 한다면 pageInit 이벤트에 로직을 binding 하는 방법(자세한 것은 아래에 있습니다.)을 추천합니다.  이렇게 하면 특정 페이지가 생성될 때 필요한 코드를 run할 수 있습니다. (id attribute나 다른 방법으로 determined 될수 있습니다.) 아래있는 예제는 페이지가 직접 로드되던가 Ajax를 통해 보여지고 pull in 되는 경우 code가 execute될 겁니다.


해당 페이지에만 적용되는 스크립트를 구현하기 위한 다른 접근법은 아무런 data-role=page 엘리먼트가 정의되지 않은 bodey element 의 끝에 script를 include 시키는 방법이 있을 겁니다. 만약 이 방법으로 여러분의 스크립트를 include 시킨다면 이 스크립트는 Ajax나 일반 HTTP를 통해 페이지가 로드될 때 execute 될 겁니다. 그래서 이 스크립트가 매 페이지마다 있으면 문제가 일어날 수도 있습니다. 오직 그 페이지에만 unique한 스크립트는 Ajax를 통해 fetch되는 페이지일 때 실행하도록 그 element 안에 위치시킬 수 있습니다.


pageinit = DOM ready


$(document).ready() 함수를 이용해서 DOM이 준비 되자마자 DOM-specific code를 execute 시키기는 것이 jQuery를 사용하려는 사람들이 처음 배우는 것입니다. jQuery Mobile 사이트와 앱 그리고 페이지들은 유저가 navigate 하는 것과 같은 DOM으로 request 되고 inject 됩니다. 그래서 DOM ready 이벤트를 단지 첫번째 페이지를 execute 하기위해 사용하는 것은 그다지 유용하지는 않습니다. jQuery Mobile에서 새로운 페이지가 로드되고 생성될 때마다 코드를 실행시키기위해 여러분은 pageinit 이벤트에 bind 해 주시면 됩니다.

이 pageinit 이벤트는 그것이 initialized 될 때 페이지에 trigger 됩니다. initialization 된 바로 직후에 실행되죠. 대부분의 jQuery Mobile의 공식 위벳들은 이 이벤트에 근거해서 자동적으로 initialize 됩니다. 여러분의 코드에 대해서도 같은 방식으로 적용하시면 됩니다. 


$( document ).delegate("#aboutPage", "pageinit", function() {
  alert('A page with an ID of "aboutPage" was just created by jQuery Mobile!');
}); 


만약 pageinit 이벤트가 fire 되고 위젯들이 auto-initialized 되기 전에 페이지의 content들을 다루고 싶다면 pagebeforecreate 이벤트를 바인드 하시면 됩니다.


$( document ).delegate("#aboutPage", "pagebeforecreate", function() {
alert('A page with an ID of "aboutPage" is about to be created by jQuery Mobile!'); });


Important note: pageCreate() vs pageInit()


이전의 Beta2 버전에서는 jQuery Mobile의 페이지와 child widget markup을 다루는 방법으로 권장됐던 것이 pagecreate 이벤트를 바인드 하는 것이었습니다. Beta2에서 internal change는 위벳 메소드에 direct call 하는 장소에 pagecreate를 bind 함으로서 각각의 위젯들을 decouple 하도록 만든 것이었습니다. 결과적으로 mobileinit에서 pagecreate로 유저가 바인딩하는 것은 각각의 플러그인에 의해서 enhance 돼있는 markup 이전에 바인딩을 실생시키게 됩니다. jQuery UI 위젯 Factory의 lifecycle을 유지하기위해 initialization 메소드는 create 메소드 이후에 invoke 됩니다. 그래서 pageinit 이벤트는 DOM과 자바스크립트 객체들의 post enhancement manipulation에대해 적절한 타이밍을 제공하게 되는 겁니다.  간단히 말하면 이전에 여러분이 페이지가 보여지기 이전에 enhanced markup을 다루기위해 pagecreate를 사용했다면 지금 버전에서는 대신 pageinit을 사용하면 된다는 얘기입니다.


Changing pages


현재의 active page를 바꾸기를 원하신다면 여러분은 changePage 메소드를 사용하시면 됩니다. 페이지를 바꾸기위한 방법에는 아주 많은 메소드와 프로퍼티들이 있을 수 있습니다. 그중 두가지 예제를 아래에서 다루겠습니다.


//transition to the "about us" page with a slideup transition
$.mobile.changePage( "about/us.html", { transition: "slideup"} );

//transition to the "search results" page, using data 
//from a form with an ID of "search""
$.mobile.changePage( "searchresults.php", { type: "post", data: $("form#search").serialize() });


Loading pages

외부 페이지와 그 콘텐츠 enhance를 로드하기위해 그리고 그럿을 DOM에 넣기 위해 loadPage method를 사용합니다. 페이지를 로딩할 때 여러분이 세팅할 수 있는 메소드와 프로퍼티들은 무수히 많지만 아래 간단한 한가지 예제만 보여 드리겠습니다.


//load the "about us" page into the DOM
$.mobile.loadPage( "about/us.html" );


Enhancing new markup


페이지 플로그인은 pageInit 이벤트를 dispatch 합니다. 대부분의 위젯들은 auto-initialize 하기위해 이것을 이용하죠. 위젯 플러그인 스크립트가 참조 되면 그 페이지에서 찾은 위젯들의 instance를 자동적으로 enhance 하게 됩니다.


만약 여러분이 클라이언트 사이드에서 새로운 markup을 generate 시킨다던가 Ajax를 통해서 콘텐트를 로드한다던가 그 콘텐트를 페이지에 inject 하려고 한다면 create 이벤트를 trigger 하시면 됩니다. 그러면 새로운 markup안에 있는 모든 플러그인들에 대한 auto-initialization을 처리할 수 있습니다. 이것은 어떤 엘리먼트에서도 trigger 될 수 있습니다. (page의 div에서도 가능합니다.) 그렇게 하면 매뉴얼로 각각의 플러그인(listview 버튼, select 등)을 initializing 하는 번거로움을 덜 수가 있습니다.



예를 들어 HTML markup 블럭이 Ajax를 통해서 로드됐다면 enhanced version으로 모든 위젯들을 (login form 이라면 inputs and buttons ) 자동적으로 transform 하기 위해 create 이벤트를 트리거 합니다. 예제는 아래에 있습니다.


$(...new markup that contains widgets...).appendTo(".ui-page").trigger("create");


Create vs. refresh: An important distinction


같은 위젯이 가지고 있는 create 이벤트와 refresh 메소드는 서로 다른 중요한 부분이 있습니다. create 이벤트는 한개 이상의 위젯을 가지고 있는 raw markup에 대한 enhancing에 알맞습니다. 반면에 refresh 메소드는 프로그래밍적으로 처리되고 있고 해당 UI로 update될 필요성이 있는 이미 존재하고 enhance된 위젯을 사용하기 위해 필요합니다.


예를 들어 여러분이 페이지 생성 이후에 data-role=listview 어트리뷰트로 정렬돼 있지 않은 새로운 list를 다이나믹하게 추가해야하는 페이지가 있다면 그 리스트의 parent element에 create를 트리거 함으로서 listview 스타일 위젯으로 그것을 transform 하도록 만들겁니다. 그 다음에 좀 더 많은 아이템들이 프로그램적으로 추가된다면 listview의 refresh 메소드를 부름으로서 enhanced state로 그 새로운 리스트 아이템들을 업데이트하게 되고 이미 존재하는 리스트 아이템들을 untouched로 남아있게 됩니다.


Scrolling to a position within a page


Back button behavior를 유지하기 위해 URL hash를 사용하고 난 후 그 페이지의 어떤 포지션으로 jump down 하기 위해 anchor를 사용하는것은 일반적인 anchor link(#foo)를 사용해서 할 수 없습니다. 스크롤 이벤트 리스너를 trigger 하지 않고 특정 Y 포지션으로 스크롤하기 위해 silentScroll 메소드를 사용해야 합니다. Y 위치로 yPos 인자를 pass 합니다. 아래 예제가 있습니다.


//scroll to Y 300px
$.mobile.silentScroll(300);


Binding to mouse and touch events


모바일에서 또 한가지 중요하게 생각해야 할 것은 마우스와 터치 이벤트를 처리하는 것입니다. 이 이벤트들은 모바일 플랫폼에 따라 많은 차이가 있습니다. 그 중에서도 일반적이고 공통적인것은 click 이벤트가 있다는 겁니다. 대개 500~700ms 의 지연이 있게 되죠. 이 지연은 더블 탭이나 스크롤 혹은 extended hold tap 이벤트가 일어나는지 아닌지를 브라우저가 catch 할 수 있는시간입니다. 이 지연을 피하려면 터치이벤트로 바인드 할 수가 있습니다.(ex. touchstart). 하지만 이 접근법에서 주의해야 할 점은 어떤 모바일 플랫폼에서는 (WP7, Blackberry) touch를 지원하지 않는다는 것입니다. 이 이슈를 해결하기 위해 어떤 플랫폼들은 touch와 mouse 이벤트를 동시에 일으킵니다. 그래서 이 두개 타입을 바인드 한다면 하나의 동작에 duplicate event가 발생할 겁니다. 우리의 해결법은 일반화된 마우스와 터치이벤트인 virtual events 세팅을 생성하는 것입니다.



이렇게 하면 기본적인 마우스 이벤트에 대한 리스너를 register 할 수 있도록 해 줍니다. 예를 들어 mousedown, mousemove, mouseup, click 같은 거죠. 이 플러그인은 해당 디바이스에 맞는 가능한 가장 빠른 시간에 listener가 invoke 되도록 화면 뒤에서 적절한 리스너를 register 하도록 해 줍니다. 하지만 아직까지 일반적인 마우스 이벤트에서 일어나는 이벤트들의 순서 부분이 남아있습니다. 각각 다른 이벤트들에 대해 같은 엘리먼트가 register 되도록 multiple handler가 필요하게 되는 것이죠. virtual mouse system은 jQuery bind event에 대한 다음과 같은 virtual evnet들을 발생 시킵니다. vmouseover, vmousedown, vmousemove, vmouseup, vclick, and vmousecancel.


Passing parameters between pages


jQuery Mobile은 internal/embeded 페이지로 pass 하는 query parameter를 지원하지 않습니다. 예를 들어 프레임워크가 #somePage?someId=1로 된 링크를 보면 이것을 "#somePage" 로 해석하고 somPage의 ID로 페이지 div를 navigate 학 ㅔ됩니다. 다음에 #somePage?someId=1의 data-url을 페이지 컨테이너에 적용하게 됩니다. #somePage?someId=2 같은 다른 params에 대한 이차적인 call들은 같은 div에서 찾을 겁니다. 왜냐하면 jQuery Mobile은 이미 세팅된 div의 data-url을 참조할 것이기 때문입니다.  페이지들 사이에서 query parameter가 필요하다면 이를 지원하는 두가지 plugin 이 있습니다. backbone.js나 spine.js를 사용할 수 있도록 하는 가벼운 플러그인으로는  page params plugin가 있고 좀 더 다양한 기능들이 제공 되는 플러그인으로는 jQuery Mobile router plugin 이 있습니다.


반응형