hacking/web2009/09/15 20:26
크리에이티브 커먼즈 라이선스
Creative Commons License
사진작가들이 쓰는 Lightbox 얘기가 아니다. 웹개발자나 디자이너에게 Lightbox는 웹 페이지 상에서 이미지들을 뽀대나게 보여주는 일종의 Modal Dialog 형태의 UI 컴포넌트다.


대표적인 Lightbox 라이브러리로는 (유행이 좀 지난 느낌이 있지만) prototype / script.aculo.us 를 사용하여 만든 Lightbox2를 보면 어떤 것인지 쉽게 이해 된다. 요즘 유행하는 jQuery용 플러그인은 넘쳐난다.

하지만, 개인적으로 prototype이나 jQuery 보다는 dojo를 선호하기 때문에(대부분의 경우엔 별 차이가 없지만, 마이너리티 체질의 똥고집이랄까...) dojo로 Lightbox를 만들기로 했는데... 웬걸 -.-; dojox.image.Lightbox라는 녀석이 이미 있더라. 그냥 쓸까 하다가... 이 녀석이 dijit(dojo의 위젯 시스템; jQuery UI 쯤에 해당하는 모듈이다)에 의존하는 관계로 버림받고 있길래 간단히 하나 맹그러봤다.

솔직히... 만만하게 보고 시작했는데... 생각보다는 많이 복잡하더라. -,.-;;;

이름은 Lightbox와 비슷하면서도 dojo로 만들었다는 느낌이 들도록 Delightbox라고 지었다 ^^;

각설하고, 사용법을 보자면 대충 이런 식이다:

1. (jQuery빠들이 좋아하는) unobtrusive 스타일

iolo.ui.Delightbox.create(dojo.query('#content a.delightbox'));

html 페이지의 모양새가 대충 이런식이라고 하면:
...
<div id="content">
...
<a href="원본이미지url" title="제목" class="delightbox"><img src="썸네일이미지url" /></a>
<a href="원본이미지url" class="delightbox">제목</a>
...


위의 예는 html 페이지 내에서 id가 content인 태그 안쪽에서 "delightbox"라는 css 클래스를 가진 모든 a 태그를(dojo.query는 jQuery의 $의 여러가지 기능 중의 하나라고 보면 된다) delightbox 형태로 바꾼다.

이 방식의 장점은 javascript를 쓸 수 없는 환경에서도 그럭저럭 보이는 거라고 하는데... 글쎄.. 과연 그럴까? 아무튼, unobtrusive javascript에 대해서는 예전에 쓴 글을 참고하시라.

2. (dojo빠들이 좋아하는) json datastore 스타일

iolo.ui.Delightbox.fetch('http://iolo.pe.kr/screenshots/screenshots.json');

json 파일은 대충 이런 식인데, thumbnail이 없으면 title을 사용한다:
{
items: [
{ url: '원본 이미지 url', title:'제목', thumbnail:'썸네일 url' },
...
]
}

위의 예는 주어진 url에서 json으로 된 데이터를 ajax로 읽어와서 그 안에 있는 내용을 토대로 동적으로 delightbox가 결합된 html을 만든다.

3. 코딩으로 처리하기

이건 소스 코드를 참고해서 알아서 하시면 되겠다. :p 소스

소스 코드는 여기, 실제 돌아가는 페이지는 여기.

덧1. dojo의 크로스 도메인 빌드를 지원하도록 만들었으므로, 내 웹서버에는 이것과 관련해서는 js 파일이 하나도 필요없다. 여기에 대해서는 테스트 페이지의 소스 코드를 참조하시면 되겠다.

덧2. 아! 빼먹을 뻔 했는데... IE에선 안된다. CSS만 좀 손봐주면 될것 같은데... 귀찮아서-,.-;;;


Posted by iolo
hacking/web2009/01/07 16:31
크리에이티브 커먼즈 라이선스
Creative Commons License

Dojo Objective Harness라니... 참 이름도 잘 짓는다. 원래는 그냥 Dojo Helper의 약자였는데... -.-;

아무튼 자바스크립트 단위테스트로 고민하고 있는 분들이라면 한번 쯤 읽어봐도 좋을 튜토리얼이다.
dojo와 무관하게 함수들을 동기/비동기로 테스트할 수 있다.
어느 정도 자동화도 가능하지만, 이 경우엔 rhino를 사용하기 때문에 실제 브라우져에서와 다른 결과가 나올 수 있으므로 그다지 유용하다고 할 수 없다. 대부분의 경우엔 수시로 브라우져를 통해서 테스트를 한번씩 돌리는 것이 더 확실하다.

간단한 대체품으로는 JsUnit이 있긴 하지만... 그닥 -.-;
dojo는 죽어도 싫다거나, 더욱 강력한 녀석을 원한다면 Selenium을 검토해보는 것이 좋을 듯 ~.~

Posted by iolo
hacking/web2008/10/24 17:08
크리에이티브 커먼즈 라이선스
Creative Commons License
한국 developerWorks에 기고한 번역문

원문: Develop AJAX applications like the pros, Part 3: Use DWR, Java, and the Dojo Toolkit to integrate Java and JavaScript
번역: 전문가다운 Ajax 애플리케이션 개발, Part 3: DWR, 자바, Dojo 툴킷을 사용하여 자바와 자바스크립트 통합하기

전형적인 디벨로퍼웍스 스타일의 제목... 원츄! -.-)b~

제목과는 달리 dojo 얘기는 없다.
대신, 자바 개발자들을 위한 거져 먹는 Ajax! DWR이 있다.
정말~ 쉽고! 정말~ 편하다!
장담하건데... 기존에 자바로 구축된 웹 사이트에 간단한 Ajax를 추가하는 거라면 이 보다 더 좋은 솔루션은 없다.

百問而不如一見~

Posted by iolo
hacking/web2008/10/08 00:39
크리에이티브 커먼즈 라이선스
Creative Commons License
아싸~ 아기다리고기다러던~~ (이런 개그하지 말랬지~ +-.-)==@ (*..*) 퍼퍽!)

*경* Dojo Toolkit 1.2 정식 버전 릴리즈~~ *축*

Alex Russell의 뒤를 이어 Pete Higgins가 메인테이너를 맡은 뒤 첫 릴리즈다.
아무튼, 뭐가 달라졌나 볼까나~.~

새로운 데이터스토어들
  • dojox.data.JsonRestStore
  • dojox.data.CouchDBRestStore
  • dojox.data.GoogleFeedStor
  • dojox.data.GoogleSearchStore
  • dojox.data.PersevereStore - Perservere... 쓰는 사람이 있구나... -.-;
  • dojox.data.S3Store
JsonRestStore는 어디에나 써먹을 수 있을 정도고, 나머지도 나름대로 유용할 듯.

dojox.analytics.Urchin
구글 애널리틱스 스크립트 지연 로딩하는 모듈.
몇 바이트 되지도 않는 자바스크립트 때문에 가끔 먹통되는 상황을 경험해 본 개발자라면 유용하겠다.
내가 다니는 회사에서는 loginside라는 솔루션을 쓰는데, 이 녀석 조금 고치면 될 듯^^;

audio/video/object/embed 지원 모듈들
  • dojox.av.FLVideo
  • dojox.av.widget.Player
  • dojox.embed
  • dojox.embed.Flash
  • dojox.embed.Flash.proxy
  • dojox.embed.Quicktime
  • dojox.embed.Object
뭐~ 보시는 대로... dijit 위젯으로 만들어져서 일관성도 있고, 쓰기도 편해졌다.

엽기적인 크로스도메인 Ajax. 보면... 왜 엽기적이라고 하는 지 알 수 있다. -.-;
세삼 확인하는 사실...
세상은 넓고 꼴통은 많다...

dojox.io.xhrPlugins
dojo.xhr을 여러가지 방법으로 확장할 수 있는 방법을 제공한다.
예를 들면 크로스도메인일 경우엔만  IE8의 XDomainRequest나 서버측 프록시를 사용한다거나... Loading 메시지를 표시한다거나...
개인적으론, 1.2에 추가된 최고의 기능이 아닐까 한다.

dojo 0.x 시절에도 있었던 AOP 기능을 깔끔하게 새로 만들었다. 이런 거 보면 dojo가 너무 많이 앞서 갔다. -.-;

dojox.lang.observable
객체의 속성을 읽거나, 쓰거나, 메소드를 호출하는 것을 (비동기로) 감시하는 멋진 기능인데... IE에서 문제가 쪼금~ 있다는...-.-;

보안 관련 모듈들
  • dojox.secure.capability: 자바스크립트 코드를 검증한다. 그래도 eval은 여전히 evil~
  • dojox.secure.DOM: DOM 노드의 접근을 제한한다. 쉬운 말로... 자바스크립트로 DOM API의 래퍼를 다 만들었다는... -.-;;
  • dojox.secure.sandbox: 위의 모듈들을 사용하여 XHR을 통해 다른 도메인에서 로드된 HTML, JSON, 자바스크립트들의 접근을 제한한다. 옥상옥이라는게 이런건가...-.-;;;

덤으로...(알만한 사람들은 이미 알고 있겠지만)
기존에 AOL에서 제공하던 CDN 외에 구글에서 제공하는 CDN을 통해서 다운로드/설치 없이 바로 쓸 수 있다.

더 자세한 얘기는 공식 릴리즈 노트에~~

덧. 1.2 릴리즈 소식을 듣고 다운받으러 갔다가 무심결에 맨 위에 있는 링크를 눌렀더니... dojo.js -.-;;;;
prototypejquery 덕분에(?) 크기에 대한 압박이 심했나 보다. 아무튼 prototype.js나 jquery.js 대용이라면 dojo.js 파일 하나면 충분하고... dijit을 포함한 dojo 모든 것을 맛보려면 역시 풀버전~ ^^; 그 전에 눈요기가 필요하다면 Dojo Feature Explorer~ =3=333


Posted by iolo
hacking/web2008/08/20 23:06
크리에이티브 커먼즈 라이선스
Creative Commons License
여름 휴가도 있고... 밥먹고 사는 일도 있고 해서... 한참 쉬었으니 다시 시작해보자.

dojo의 객체 시스템의 핵심은 dojo.declare다:
  • dojo.declare(className, superClass, { members... }) : 주어진 이름을 가진 클래스를 정의한다. 클래스 이름은 문자열로, 수퍼클래스는 클래스 객체로 지정해야 한다. 수퍼클래스가 없으면 null 또는 [ ] (빈 배열)을 지정하면 된다. 하나 이상의 수퍼클래스(정확히는 하나의 수퍼클래스와 여러 개의 믹스인을 배열로 지정할 수도 있다.
먼저, 단순한 클래스를 정의하는 예를 보면:
dojo.declare('Point', null, {
    x: 0,
    y: 0,
    constructor: function(x, y) {
        this.x = x || 0;
        this.y = y || 0;
    },
    moveTo: function(x, y) {
      this.x = x;
      this.y = y;
   },
    equals: function(point) {
      return (this.x == point.x) && (this.y == point.y);
    }
});

위의 예는 Point라는 이름의 클래스를 정의한다. 이 클래스는 수퍼클래스가 없으며, 하나의 생성자(constructor)와 x, y 두 개의 속성(멤버 변수)을 가지며, moveTo, equals 세 개의 메소드(멤버함수)를 가진다.
키워드는 declare(선언)이지만, 실제로 하는 일은 선언이 아니라 정의(define)다. 오히려 dojo.provide가 선언에 가깝다. 이 글의 주제에서 벗어나는 얘기니 궁금하면 구글신께 물어보시라... 나중에 시간나면 따로 쓰기로 하자~

이 클래스를 사용하는 예를 보면:
var p1 = new Point(1, 2);
var p2 = new Point();
p2.moveTo(3, 4);
alert(p1.equals(p2)); // false

보다시피 자바 등의 다른 언어와 별 차이가 없다.

계속해서, 상속의 예를 보면:
dojo.declare('Rect', [ Point ], {
    w: 0,
    h: 0,
    constuctor: function(x, y, w, h) {
       this.w = w || 0;
       this.h = h || 0;
    },
    getRight: function() {
      return x + w;
    },
    getBottom: function() {
      return y + h;
    },
    resize: function(w, h) {
      this.w = w;
      this.h = h;
    },
    equals: function(rect) {
      return this.inherited(arguments) && (this.w == w) && (this.h == h);
    }
});


위의 예는 Rect라는 이름의 클래스를 정의한다. 이 클래스는 Point를 상속받으며(x, y 두 개의 속성과 moveTo, equals 두 개의 메소드), 추가로 w, h 두 개의 속성과 getRight, getBottom, resize 세개의 메소드를 정의하며, equals를 오버라이딩(재정의)하고 있다.
equals에서 this.inherited(arguments)라는 생소한 구문이 보이는데 부모클래스에서 정의한 메소드를 호출하는 것이다. 자바로치면 super(rect)와 비슷하다고 보면 되겠다.
생성자에서 super를 호출하지 않는다는 점도 주의해야한다. 아무말 안해도 무조건 super를 호출한다. 자바스크립트는 함수 오버로딩(중복정의)을 지원하지 않기 때문에 생성자를 하나밖에 가질 수 없다.

객체 관련 유틸리티 함수들이 여러가지 있지만, 개인적으로 한 번이라도 써 본적이 있는 녀석들만 간단히 알아보면:
  • dojo.delegate(obj, { members...}) : 주어진 객체를 상속받은 객체를 만든다. dojo.declare와 달리, 클래스가 아니라 인스턴스 객체를 만든다. 간단하게 몇 가지 속성이나 메소드만 재정의한 객체를 만들때 편리하다.
  • dojo.clone(obj) : 주어진 객체의 deep-copy 객체를 만든다.
dojo는 하나 이상의 클래스를 하나의 .js 파일에 넣어두고, 이를 동적으로 로딩하는 기능을 제공하는데, dijit 설명할 때 자세히 알아보기로 하고... 여기서는 dojo 관련 소스들을 보다가 자주 보게될 함수 몇가지만 알아보자:
  • dojo.require(className) :  현재 모듈(나중에 설명한다)에서 주어진 이름의 클래스를 참조(사용)한다. 자바의 import와 비슷하지만, 자바와 달리 와일드카드(*)를 사용할 수 없다(dojo 0.4.x 쓰시던 분들도 주의!). 일반적으로 dojo.require('dijit.form.TextBox')라고 하면 dojo디렉토리/dijit/form/TextBox.js를 로딩한다고 이해해도 무방하다.
  • dojo.provide(className) : 주어진 이름의 클래스를 선언한다. 즉, 현재 모듈(나중에 설명한다)에서 주어진 이름의 클래스를 정의할 것임을 미리 알린다. 실제로는 빈 객체를 정의하기 때문에 네임스페이스 선언을 위해서 사용한다.
위의 두 함수는 dojo custom build에서도 매우 중요한 역할은 한다.

오늘은 여기까지~

다음에는 아기다리고기다리던 dijit(dojo widget)이다.

덧. 자바스크립트로 진지한 코딩을 해 본 개발자라면 IE의 trailing comma error의 공포를 알고 있을 것이다. dojo.delcare도 조심해서 쓰지 않으면 좌절을 맛보게 된다. 조만간 동의하실 것이라개인적으로 즐겨쓰는 방법은:
  1. 무조건! jslint로 검사한다. 예전에 썼던 웹 프로젝트에서 jslint로 자바스크립트 검증하기: maven 또는 ant 를 참조하시라~
  2. 무조건! 각 멤버 변수/함수를 쉼표로 끝내고, 불필요한 comma를 넣는 실수를 방지하기 위해 더미로 속성을 하나 더 넣는다. 예를 들면:
dojo.declare('Circle', [ Point ], {
    attr1: 'test',
    attr2: null,
    func1: function() {
        ...
    },
    func2: function() {
        ....
    },
    _dummy: null
});


Posted by iolo
hacking/web2008/08/10 17:13
크리에이티브 커먼즈 라이선스
Creative Commons License
자바스크립트로 코딩 좀 했다는 분들도 IE(참고자료)와 불여우, 그리고 W3C의 표준 DOM 이벤트 모델간의 차이에 대해서 제대로 이해하기보다는 까이꺼~ 대충~ 통밥으로~ 처리하는 경우를 많이 본다. 누가 좋으냐 나쁘냐는 논쟁은 끝이 없으니... 넘어가기로 하고... 골치아픈 브라우져간의 차이점을 신경쓰지 않고 개발할 수 있는 dojo의 이벤트 시스템을 알아보자.
  • dojo.connect(이벤트객체, 이벤트이름, 이벤트핸들러함수) : 지정한 객체에서 지정한 이름의 이벤트가 발생하면 이벤트 핸들러 함수를 호출하도록 "연결"한다. 핸들을 리턴한다.
  • dojo.disconnect(핸들) : dojo.connect()함수의 리턴값으로 받은 핸들을 사용하여 이벤트와 이벤트 핸들러 함수의 연결을 끊는다.
뭔 얘긴지 더 헷갈린다. 간다한 예제를 보자:
<html>
<head>
<script type="text/javascript" djConfig="isDebug:true" src="http://o.aolcdn.com/dojo/1.1.1/dojo/dojo.xd.js"></script>
<script type="text/javascript">
</script>
</head>
<body>
<button id="helloBtn">Click Me</button>
<script type="text/javascript">
var helloBtn_onclick = function(evt) {
    alert('hello, world!');
    if (helloBtn_onclick_handler) {
       dojo.disconnect(helloBtn_onclick_handler);
       helloBtn_onclick_handler = null;
    }
};
var helloBtn = dojo.byId('helloBtn');
var helloBtn_onclick_handler = dojo.connect(helloBtn, 'onclick', helloBtn_onclick);
</script>
</body>
</html>

보시다시피 단순하다.

기존 방식대로 하면:
helloBtn_onclick = function(evt) {
    alert('hello, world!');
    helloBtn.onclick = null;
};
var helloBtn = document.getElementById('helloBtn');
helloBtn.onclick = helloBtn_onclick;

어랏?! 더 단순하자나! 그러나 이 코드가 항상 동작하는 것이 아니다라는 점이 문제다. 기존의 방식에 익숙하다면 지금 당장 바꿀 필요는 없다. dojo.connect가 있다는 점만 기억해두면 머지않아 필요할때가 올 것이다. 하나 이상의 이벤트 핸들러를 연결해야할 때, 이벤트 핸들러에서 이벤트의 전달을 중단해야 할 때, 또는 기본 동작(폼 submit같은)을 막고 싶을 때... 등등...
dijit(dojo의 위젯 시스템)을 쓴다면 별도로 disconnect를 해줄 필요가 없지만, 그 외의 경우에는 꼼꼼하게 disconnect해주는 것이 좋다. DOM 노드를 제거할때 dojo._destroyElement()를 사용하면 자동으로 연관된 이벤트 핸들러들을 disconnect 해주므로 편리하게 쓸 수 있다.

script태그가 body태그 끄트머리에 들어가있는 것이 이상하다고 생각한 분 계신가? 그렇다. 일부러 그렇게 한거다. 저렇게 안하면 브라우져와 주변 상황에 따라(!) 예상치 못한 결과를 만나게 된다. 그래서 dojo에서 이런 방법을 제공한다:
  • dojo.addOnLoad(핸들러함수) : 페이지를 열고 난 직후에 호출될 핸들러 함수를 추가한다.
  • dojo.addOnUnload(핸들러함수) : 페이지를 닫기 직전에 호출될 핸들러 함수를 추가한다.
백문이불여일견~
<html>
<head>
<script type="text/javascript" djConfig="isDebug:true" src="http://o.aolcdn.com/dojo/1.1.1/dojo/dojo.xd.js"></script>
<script type="text/javascript">
var helloBtn_onclick = function(evt) {
    alert('hello, world!');
};
var byeBtn_onclick = function(evt) {
    alert('bye~ world!');
    location.href = 'http://google.com/';
};
var handlers = [];
var init = function() {
    handlers.push(dojo.connect(dojo.byId('helloBtn'), 'onclick', helloBtn_onclick));
    handlers.push(dojo.connect(dojo.byId('byeBtn'), 'onclick', byeBtn_onclick));
};
var destroy = function() {
  dojo.forEach(handlers).disconnect();
}
dojo.addOnLoad(init);
dojo.addOnUnload(destroy);
</script>
</head>
<body>
<button id="helloBtn">Click Me</button>
<button id="byeBtn">Bye~</button>
</body>
</html>

예제가 좀 길어졌지만, 기존에 자바스크립질에 익숙한 분들은 dojo.addOnLoad/Unload가 body의 onload/onunload핸들러와 크게 다르지 않다는 것을 알아챘을 것이다. 즉, <body onload="init()" onunload="destroy()"> 이라고 하면 된다는 얘기~ 그런데 왜 저렇게 복잡하게 하느냐고? 예전에도 한번 포스팅한 적이 있는데... onload의 타이밍이 미묘해서 브라우져마다 조금씩 다르다.
좀 다른 얘기지만, 위의 예제 코드에서 init()/destroy()에서 사용된 배열에 이벤트 핸들들을 보관해두고 일괄 해제하는 패턴은 자주 사용되므로 익해두면 편하다.

보너스로 한가지 더:
  • dojo.stopEvent(이벤트객체) : 이벤트의 전파와 기본 동작을 모두 차단한다.
예를 들면:
<form id="loginForm" action="login.do" method="post">
    <input type="text" id="usernameText" name="username" value="" />
    <input type="password" id="passwordText" name="password" value="" />
    <button type="submit">Login</button>
</form>
<script>
dojo.connect(dojo.byId('loginForm'), 'onsubmit', function(evt) {
    if (!dojo.byId('usernameText').value || !dojo.byId('passwordText').value) {
      dojo.stopEvent(evt);
    }
});
</script>

자바스크립트에 익숙한 분들은 return false; 이런 짓(!)을 하시는데... 이거 생각처럼 잘 안된다. 이 기회에 링크한 문서들을 참고해서 왜 되고 또 왜 안되는지 좀 더 파 보시는 것도 괜찮을 듯...

다소 뜬금없지만, 이벤트 시스템의 일부이기도 하고, 유용한 녀석들이니 언급은 하고 넘어가야 할 것 같다:
  • dojo.subscribe(토픽이름, 토픽핸들러함수) : 지정한 토픽이 발생하면 호출할 핸들러함수를 추가한다.
  • dojo.publish(토픽이름, 파라메터배열) : 지정한 토픽을 발생시킨다. 그러면, 위의 dojo.subscribe()를 통해 등록한 핸들러 함수들이 모두 호출된다.
AJAX의 특성상 비동기 동작이 많고, 이를 처리하기위해서 코드가 꼬이고, 이렇게 꼬인 코드를 풀기 위해 전역변수를 많이 쓰게된다. dojo.publish/subscribe()를 사용하면 이러한 상황을 많이 줄일 수 있다. C언어를 오랜동안 쓰신 분이라면 setjmp/longjmp와 비슷하다고 생각할 수 있겠다. 제대로된 예제를 만들려면 꽤 긴 코드가 필요하니, 나중에 따로 설명하기로 하자.

다음은 dojo의 객체 시스템에 대해서 알아보기로 하고...
오늘은... 이만...
사용자 삽입 이미지
정말... 덥다...

Posted by iolo
hacking/web2008/08/02 21:28
크리에이티브 커먼즈 라이선스
Creative Commons License
이번에 알아볼 내용은 애니메이션/효과를 위한 기능이다.

기본 효과
간단한 효과들이지만 dojo base에 포함되어 있어서 dojo.js 파일 하나만 있으면 편하게 쓸 수 있다.
  • dojo.fadeIn(args) : 지정한 노드를 서서히 불투명하게 만든다. args는 여러가지 파라메터를 묶은 해시다. dojo에서는 이런 식의 파라메터를 즐겨쓴다.
  • dojo.fadeOut(args) : 지정한 노드를 서서히 투명하게 만든다.
  • dojo.animateProperty(args) : 지정한 노드의 (CSS) 속성을 연속적으로 변화시킨다. 이 함수만 활용하면 대부분의 효과를 만들 수 있다.
간단한 예를 들어 보면:

dojo.fadeOut(
    node: 'title',
   duration: 1000,
   delay: 250
  }).play();

250ms를 기다린 후, 1000ms(1초)동안 id가 title인 노드를 천천히 표시한다(불투명). fadeOut대신 fadeIn을 사용하면 숨긴다(투명).

dojo.animateProperty({
    node: 'title',
    properties: {
        width:{ start:10, end:500, unit:'px' },
        opacity: { start:0, end:1 },
        backgroundColor: { start: '#ff0000', end: '#00ff00' }.
        fontSize: { start:10, end:20, unit:'pt'},
   },
    duration:500
});

500ms(0.5초) 동안 domNode의 너비를 10px에서 500px로, 투명에서 불투명으로,  배경색을 빨강에서 초록으로, 글꼴 크기를 10pt에서 20pt로... 부드럽게~ 바꾼다. 즉, 위의 dojo.fadeIn/fadeOut()은 dojo.animateProperty()로 쉽게 구현할 수 있다.

좀 더 복잡한(그래봐야 길어 보일 뿐~) 예를 만들어보자:
<html>
<head>
<script type="text/javascript" src="http://o.aolcdn.com/dojo/1.1.1/dojo/dojo.xd.js"></script>
<style type="text/css">
#welcome { display:none; position:absolute; top:50px; left:50px; width:300px; height:200px; border:2px solid #cc0000; background-color:#00ff00; color:#0000ff; }
</style>
<script type="text/javascript">
var showWelcome = function() {
  // fadeIn이 효과가 있으려면 먼저 투명하게 만들어둬야 한다.
  dojo.style('welcome', { opacity: '0', display: 'block' });
  dojo.fadeIn({
    node: 'welcome',
    duration: 1000,
    delay: 1000
  }).play();
};

var flyWelcome = function() {
  var bodyBox = dojo.contentBox(dojo.body());
  var welcomeBox = dojo.contentBox('welcome');
  var goArgs = {
    node: 'welcome',
    properties: {
      left: bodyBox.w - welcomeBox.w,
      top: bodyBox.h - welcomeBox.h
    },
    duration: dojo.byId('flyTime').value * 1000, // 밀리초
  };
  var goAnim = dojo.animateProperty(goArgs);
  goAnim.play();
};

var hideWelcome = function() {
  dojo.fadeOut({
    node: 'welcome',
    duration: 1000,
    delay: 100,
    onEnd: function() { dojo.style('welcome', 'display', 'none'); }
  }).play();
  // 애니메이션은 비동기로 일어난다.
  // 여기에 dojo.style('welcome', 'display', 'none');를 넣으면
  // 애니메이션을 시작하자마자 바로 사라져버리므로(display:none),
  // 스르륵 사라지는(투명도를 조절하는) 효과를 볼 수 없게된다.
};

dojo.addOnLoad(showWelcome);
</script>
</head>
<body>
<h1>Hello, World!</h1>
<p>Blah Blah...</p>

<div id="welcome">
<h1>Welcome!</h1>
<p>Blah Blah...</p>
<p>
<button onclick="flyWelcome()">Fly me to the moon</button>
during <input type="text" id="flyTime" value="1" size="5" /> seconds.
</p>
<button onclick="hideWelcome()">Let it be</button>
</div>

</body>
</html>

페이지가 로딩되면 잠시 뒤에 Welcome 창(?)이 스르륵~ 나타난다. 여기서 Let it be를 누르면 창이 스르륵~ 사라지고, Fly me to the moon을 누르면 지정한 시간동안 창이 아래쪽 구석으로 날아간다.
예제 자체는 별거 없지만, 중간에 코멘트 중에 중요한 내용이 있다. 애니메이션은 항상 비동기로 진행된다는 사실을 명심하자. 동기를 맞추려면 그래서 onBegin, onEnd 등의 이벤트를 이용해야 한다.

고급 효과(dojo.fx 패키지)
dojo base에 효과를 자꾸 추가하면 dojo.js 파일의 크기가 너무 커지므로, dojo.fx 라는 별도의 패키지를 만들었다. 따라서 아래의 효과들을 사용하기 전에 dojo.require('dojo.fx');를 실행해야 한다.
  • dojo.fx.chain([애니메이션객체들...]) : 여러 개의 효과을 순서대로 실행하는 복합 효과.
  • dojo.fx.combine([애니메이션객체들...]) : 여러 개의 애니메이션을 동시에 실행하는 복합 효과.
  • dojo.fx.slideTo({ node: 노드, top: 세로좌표, left: 가로좌표 }) : 노드를 지정한 좌표로 이동하는 효과.
  • dojo.fx.wipeIn({ node: 노드 }) : 노드의 높이를 점점 크게하면서 나타나는 효과.
  • dojo.fx.wipeOut({ node: 노드 }) : 노드의 높이를 점점 줄이면서 사라지는 효과.
  • dojo.fx.Toggler(args) : 지정한 효과를 사용해서 노드를 show/hide() 하는 도우미 클래스. 동일한 효과로 show/hide()를 반복할 경우 편리하게 쓸 수 있다.
백문이불일견, 백견이불여일런!
말로 설명하려고 해도 말재주가 부족하니... 소스코드로 때우기로 하자.
<html>
<head>
<script type="text/javascript" djConfig="isDebug:true" src="http://o.aolcdn.com/dojo/1.1.1/dojo/dojo.xd.js"></script>
<style type="text/css">
#overlay { z-index:999; position:absolute; overflow:hidden; top:0; left: 0; right:0; bottom: 0; width:100%; height:100%; background:#000; }
#lightbox { z-index:1000; position:absolute; overflow:hidden; top:0; left:0; background:#fff; }
#lightbox div { border:1px solid #000; padding:20px; }
</style>
<script type="text/javascript">
dojo.require('dojo.fx');

var showLightbox = function() {
  var s1 = dojo.fadeIn({ node:'overlay', end: 0.5 });
  var s2 = dojo.fx.wipeIn({ node:'lightbox' });
  var s3 = dojo.fx.slideTo({ node:'lightbox', top:100, left:0 });
  var s4 = dojo.fx.slideTo({ node:'lightbox', top:200, left:300 });
  fx = dojo.fx.chain([s1, s2, s3, s4]);
  dojo.connect(fx, 'onBegin', function() {
    dojo.style('overlay', 'display', 'block');
    dojo.style('lightbox', 'display', 'block');
    fx.stop();
  });
  fx.play();
};

var hideLightbox = function() {
  var h1 = dojo.fx.slideTo({ node:'lightbox', top:200, left:0 });
  var h2 = dojo.fx.slideTo({ node:'lightbox', top:0, left:0 });
  var h3 = dojo.fx.wipeOut({ node:'lightbox' });
  var h4 = dojo.fadeOut({ node:'overlay' });
  var fx = dojo.fx.combine([h1, h2, h3, h4]);
  dojo.connect(fx, 'onEnd', function() {
    dojo.style('overlay', 'display', 'none');
    dojo.style('lightbox', 'display', 'none');
    fx.stop();
  });
  fx.play();
};

dojo.addOnLoad(function() {
  dojo.style('overlay', 'display', 'none');
  dojo.style('lightbox', 'display', 'none');
});
</script>
</head>
<body>

<p>Click the picture to show in the light box:</p>
<img src="photo.jpg" width="80" onclick="showLightbox()" />

<div id="overlay"></div>

<div id="lightbox">
  <div>
    <img src="photo.jpg" onclick="hideLightbox()" />
  </div>
</div>

</body>
</html>

페이지가 로딩된 다음, 작은 이미지를 클릭하면 뭔가 어수선한 애니메이션이 나오고 창(레이어)에 큰 이미지가 표시된다. 큰 이미지를 클릭하면 역시 어수선한 에니메이션이 나오고 창(레이어)이 닫힌다. 쉽게 얘기해서 어수선한 LightBox 다. 나도 안다... 하나도 안 비슷하다는 거... 그래도 조금만 손을 보면... -.-;;;;
중간에 보면 dojo.fx.combine()dojo.fx.chain()을 쓴 부분이 있는데... 위의 코드를:
dojo.fx.chain([f1, dojo.fx.combine([f2, f3]), f4]); 처럼 바꾸면 f1이 먼저 진행되고, f2와 f3는 동시에 진행되고, 그 다음에 f4가 진행된다. 예를 들려다보니 저렇게 됐지만, 잘 엮으면 멋진 효과를 쉽게~ 만들 수 있다. 정말 잘~ 엮어야 된다. 뭔 소린지는 엮어 보면 안다. -.-;;;
그리고 dojo.addOnLoad()를 사용해서 페이지가 로딩된 직후 overlay와 lightbox노드를 숨기고(display: none)있는데, 이는 보이지 않는 노드에 대해서 높이 계산을 안하는 불여우의 버그(또는 기능)를 피하기 위한 꼼수다.

혹시 prototype을 사용하고 있다면 script.aculo.us를 사용해서 비슷한 효과(더욱 현란한 효과)를 낼 수 있다. prototype과 scrit.aculo.us에 관심이 있는 분들은 인사이트에서 번역 출판한 프로토타입과 스크립타큘러스를 참조하시라(절때! 내가 추천글을 썼다거나 교정을 봤다거나 해서 이러는 건 아니고...-.-;;;; 프로토타입 관련 책 중에서는 원서도 제일 좋고, 번역도 제일 좋다).

오늘은 이 정도로 하고... 다음은 이벤트 시스템~

Posted by iolo
hacking/web2008/07/29 00:43
크리에이티브 커먼즈 라이선스
Creative Commons License
할 수 있는데 까지 해보자.

DOM 노드 위치/좌표 관련 함수들
CSS만으로 모든 레이아웃을 정확하게 계산하기란 쉽지 않다. 디자이너의 현란한 디자인이 IE의 더블마진 버그와 만나는 순간... 때문에 레이아웃 잡는게 여간 골치아픈게 아니다. 그래서 이런게 필요하다:
  • dojo.marginBox(domNode) : 지정한 DOM 노드의 margin box를 얻는다.
  • dojo.marginBox(domNode, {l:가로좌표, t:세로좌표, w:너비, h:높이}) : 지정한 DOM 노드의 위치/크기를 margin box로 설정한다. 위치는 부모 노드의 위치에 대한 상대좌표다(즉, CSS의 position:absolute 다). margin/padding/border는 바뀌지 않지만, content box의 크기는 바뀐다. 기본 단위는 픽셀(px)이다. 각 속성은 생략할 수 있다.
  • dojo.contentBox(domNode) : 지정한 DOM 노드의 content box를 얻는다.
  • dojo.contentBox(domNode, {l:가로좌표, t:세로좌표, w:너비, h:높이}) : 지정한 DOM 노드의 content box의 위치/크기를 설정한다. 위치는 부모 노드의 위치에 대한 상대좌표다(즉, CSS의 position:absolute 다) margin/padding/border는 바뀌지 않지만, margin box의 크기는 바뀐다. 기본 단위는 픽셀(px)이다. 각 속성은 생략할 수 있다.
사용자 삽입 이미지
위의 그림은 CSS의 박스모델을 설명하는 것인데, dojo의 contentBox는 위의 그림에서 맨 안쪽의 content를 둘러싼 사각형을, marginBox는 맨 바깥쪽의 margin까지 포함한 사각형을 의미한다. 이 결과는 IE이건 아니건, Quirks Mode이건 아니건 동일하다.
웬만하면 CSS로 처리하는 것이 바람직하지만, iframe이나 textarea같은 꼴통들에겐 dojo.contentBox/marginBox()가 도움이된다. 간단한 예를 들어보면(저번에 예제를 하나도 안만들어서 이번엔 좀 길게 만들어 봤다):
<html>
<head>
<script type="text/javascript" src="http://o.aolcdn.com/dojo/1.1.1/dojo/dojo.xd.js"></script>
<style type="text/css">
body { margin:0; padding:0; height:100%; overflow:hidden; }
#wrapper { width:100%; height:100%; }
#header { height:20px; background:#9cf; }
#content { width:100%; position:absolute; top:20px; bottom:0; background:#ccc; }
#frame { width:100%; height:100%; overflow:auto; }
</style>
<script type="text/javascript">
if (dojo.isIE) {
  var onResize = function() {
    var box1 = dojo.contentBox('wrapper');
    box1.t = 20;
    box1.h -= 20;
    dojo.marginBox('content', box1);
  };
  dojo.connect(dojo.global, 'resize', onResize);
  dojo.addOnLoad(onResize);
}
</script>
</head>
<body>
<div id="wrapper">
  <div id="header">Hello,World!</div>
  <div id="content"><iframe id="frame" src="http://dojotoolkit.org/" frameborder="no"></iframe></div>
</div>
</body>
</html>

이 예제의 목적은 스크롤바 없이 화면에 꽉 채운 페이지를 만드는 것이다. IE에선 부모 노드의 크기가 %로 지정되면 right/bottom 속성이 동작하지 않기때문에 곤란하다. IE의 CSS expression을 써도 되겠지만, 개인적으로는 이 방법을 선호한다. CSS만으로 어떻게 할 수 있을 것 같긴한데... 내공 부족 -.-;;;(표준 CSS만으로 할 수 있는 좋은 방법이 있으면 알려주시면 콩다방 커피 한잔 대접합니다~). 예제를 위한 예제이다 보니 아직 설명한 것들이 잔뜩 있는데 핵심은 중간 쯤에 있는 자바스크립트 코드다. 중간에 있는 if (dojo.isIE) { ... } 스크립트(주황색 배경)를 지우고 IE에서 실행시켜보시라. 나중에 dijit layout widget을 사용하면 이것보다 훨씬 복잡한 작업을 한 방에 해치울 수 있다!

DOM 편의 함수
앞에서 알아본 dojo.attr()/dojo.style()dojo.*Class() 외에도 잡다한 편의함수들이 많다. 이 함수들의 완전한 목록은 dojo API Reference에서 찾아보시고~ 여기서는 맛보기로 생각나는 것 몇 개만 알아보자:
  • dojo.place(domNode, refDomNode, position) : domNode를 refDomNode를 기준으로 위치/순서를 변경한다. position은 인덱스(숫자) 또는 before/after/first/last 중의 하나다. position 인자에 따라 appendChild/insertBefore() 등의 DOM API를 적절히 호출한다. 처음엔 복잡해 보이겠지만, 익숙해지고 나면 DOM API가 얼마나 구린지(?), 그리고 dojo.place()가 얼마나 자연스러운지 알게 될 것이다.
  • dojo.isDescendant(domNode, ancestorDomNode) : domNode가 ancestorDomNode의 하위 노드인지 확인한다.
  • dojo._destroyElement(domNode) : 지정한 노드를 해제한다. IE에서는 이벤트 핸들러가 걸린 노드와 관련한 메모리가 새는 버그가 있어서 domNode.parentNode.removeChild(domNode) 또는 domNode.parentNode.innerHTML = '' 또는 domNode.ownerDocument.detachNode(domNode) 로는 충분하지 않다. domNode.parentNode.innerHTML'_'로 시작하는 private함수지만 public으로 해도 무방할 정도로 유용하다.
  • dojo.setSelectable(domNode, selectable) : domNode와 모든 자식노드들을 선택불가능하게(마우스로 긁을 수 없게) 만든다. 이걸 활용하면 블로그 본문 긁지 못하게 하는 것을 쉽게 할 수 있겠지만... 이런 짓 하지말자. 어차피 긁을려면 다 긁는다. 다만, 메뉴나 툴바 같은 녀석들을 이 함수를 사용해 긁지 못하게 하면 잡다한 오동작을 원천봉쇄할 수 있다.
  • dojo._getOpacity(domNode) : domNode의 투명도를 얻어온다. 0이면 완전 투명, 1이면 완전 불투명이다. 이 함수 없이 할려면 IE이냐 아니냐에 따라서 삽질을 좀 해야 한다.
  • dojo._setOpacity(domNode, opacity) : domNode의 투명도를 설정한다.
이 함수들을 직접 쓸 기회는 많지 않겠지만 알아두면 유용할 때가 금방 온다.

(중간에 dojo API Reference가 있다고 얘기했다. 아는 사람은 다 알겠지만... 이걸 지금까지 얘기안한 이유가 있다. 이거 보고 괜히 dojo를 오해하지 말기 바란다. 이렇게 느려터지게 만드는 것도 쉬운 일이 아니다. 굳이 API가 필요하다면 이것 보다는 dojo toolbox를 쓰길 권한다.)

사용자 삽입 이미지
유틸리티 함수는 일단 마무리~
다음에는 재미있는(?) 애니메이션/효과 관련 함수들을 간단히 알아보고,
이어서 이벤트 시스템, 클래스 시스템, 그리고 dijit... 갈 길이 멀구나...

Posted by iolo
hacking/web2008/07/24 17:49
크리에이티브 커먼즈 라이선스
Creative Commons License
남아 있는 유틸리티 함수들 중에서 정말 자주 쓰이는 몇가지만 더 알아보자.

dojo.byId(id)
document.getElementById(id)와 동일하며 프로토타입$(id)와 동일하다.
지정한 id를 가진 DOM 노드를 찾는다. 참고로 dijit.byId(id)와는 전혀 다른 녀석이니 헷갈리지 않기를... 그리고 다른 window(frame/iframe)의 노드를 찾고 싶을 때는 dojo.withGlobal(window,'byId',dojo,[id]) 처럼 하면 된다(뭐가 이렇게 복잡하냐고? 그래도 없는 것 보다는 낫다).
$()보다 길어서 타이핑하기 귀찮겠지만... 덕분에 프로토타입이나 jQuery와 섞어서 사용해도 아무런 문제를 읽으키지 않는다. 굳이 $()를 쓰고 싶으면 예전에 쓴 블로그를 참조하시길...

dojo.attr() - DOM 속성 관련 함수들
dojo.attr()은 getter/setter 두가지 역할을 겸하고 있으며, domNode 대신 해당 노드의 id를 문자열로 지정해도 된다.
  • dojo.attr(domNode, attrName) : 지정한 DOM 노드의 지정한 속성 값을 가져온다. domNode.getAttribute(attrName)와 비슷하다.
  • dojo.attr(domNode, attrName, attrValue) : 지정한 DOM 노드의 지정한 속성 값을 변경한다. domNode.setAttribute(attrName, attrValue)와 비슷하다. 참고로, dojo 1.1.1 이후 버전에서는 attrName/Value 쌍을 배열로 지정할 수 있다. 예를 들면: dojo.attr(domNode, { action: 'add.php', method: 'POST' });
  • dojo.removeAttr(domNode, attrName) : 지정한 DOM 노드의 지정한 속성을 삭제한다. domNode.removeAttribute(attrName)와 비슷하다.
domNode.setAttribute/getAttribute/removeAttribute를 사용하면되는데 왜 이런게 필요하냐고 물으신다면... 자바스크립트 닭질을 좀 더 하시길 권해드리는 바이다. 일단 dojo에서 이런 걸 제공한다는 사실만 기억해두면 조만간 고맙게 쓰게 될 날이 온다.

dojo.style() - DOM CSS 속성 관련 함수들
dojo.style()도 getter/setter 두가지 역할을 겸하고 있으며, domNode 대신 해당 노드의 id를 문자열로 지정해도 된다.
  • dojo.style(domNode, cssAttrName) : 지정한 DOM 노드의 스타일 중에서 지정한 CSS속성 값을 가져온다. 주의 할 점은 cssAttrName은 CSS 형식(font-size, left-padding ...)이 아닌 자바스크립트 형식(fontSize, leftPadding ...)으로 지정해야 한다는 것이다. 참고로, dojo 1.1.1 이후 버전에서는 CSS 형식과 자바스크립트 형식 둘 다 사용할 수 있으며, cssAttrName/Value 쌍을 배열로 지정할 수 있다. 예를 들면: dojo.style(domNode, { fontSize: '20pt', 'text-decoration': 'underline' });
  • dojo.style(domNode, cssAttrName, cssAttrValue) : 지정한 DOM 노드의 스타일 중에서 지정한 CSS속성 값을 변경한다.
dojo.style()함수는 계산된 스타일(computed style)을 대상으로 한다. 즉, 캐스캐이드된 여러가지 CSS속성들이 모두 적용된 상태를 기준으로 값을 얻어오거나 변경한다.

dojo.*Class() - DOM CSS 클래스 관련 함수들
별거 아닌거 같지만 짱 편한 함수 몇 개 소개한다. 이런 짓 해보신 적 없으신가?
domNode.className = domNode.className + ' ' + 'newClass'; // 공백 주의!
domNode.className = domNode.className.replace('oldClass', ' ');

이게 중간 중간 공백 신경 쓰려면 열라 귀찮다. 아무튼 이런 닭질은 이젠 안녕이다.
  • dojo.addClass(domNode, cssClassName) : 지정한 DOM 노드에 지정한 CSS 클래스를 추가한다. cssClassName에는 여러 개의 클래스 이름을 공백으로 구분하여 지정해도 된다.
  • dojo.removeClass(domNode, cssClassName) : 지정한 DOM 노드에서 지정한 CSS 클래스를 제거한다. cssClassName에는 여러 개의 클래스 이름을 공백으로 구분하여 지정해도 된다.
  • dojo.toggleClass(domNode, cssClassName) : 지정한 DOM 노드에서 지정한 CSS 클래스를 갖고 있으면 제거하고, 없으면 추가한다.
  • dojo.hasClass(domNode, cssClassName) : 지정한 DOM 노드가 지정한 CSS 클래스를 갖고 있는지 확인한다.
dojo.attr()/dojo.style()/dojo.*Class()함수들은 dojo.query()와 함께 쓰면 더욱 편리하다. dojo.query에 대해서는 다음에 따로 알아보기로 하고, 예를 하나만 들어보면:
dojo.query('tr').forEach(
    function(node, i) {
        dojo.addClass(node, (i % 2) ? 'even' : 'odd');
    }
);

유틸리티 함수는 세 번으로 끝낼려고 했는데... 생각보다 길어졌다.

다음에 남은 유틸리티 함수들을 알아보고, 그 다음에 에니메이션 관련 함수들 알아보기로 하고... 오늘은 여기까지~

Posted by iolo
hacking/web2008/07/19 16:56
크리에이티브 커먼즈 라이선스
Creative Commons License
자바스크립트에는 두 가지 타입 검사 연산자가 있다: instanceoftypeof

그런데 이게 미묘하다. 예를 들어:
var hello = "hello"; 
alert(typeof hello); // string
var world = new String("world");
alert(typeof world); // object -,.-;;

dojo를 사용해보면 어떨까?
alert(dojo.isObject(hello)); // false
alert(dojo.isObject(world)); // true
alert(dojo.isString(hello)); // true
alert(dojo.isString(world)); // true

말나온 김에 한 가지만 더 예를 들어보면:
function test2() {
  alert(typeof arguments); // object
  // alert(arguments.join(',')); // 에러 -,.-;;;
  var args = []; // new Array();
  for (var i = 0; i < arguments.length; i++) { args[i] = arguments[i]; };
  alert(typeof args); // object typeof는 "array"를 모른다! -.-;
  alert(args.join(',')); // hello,world,1,2,3
}
test2('hello', 'world', 1, 2, 3);

dojo를 사용해보면 어떨까?
alert(dojo.isArray(arguments)); // false
alert(dojo.isArray(args)); // true
alert(dojo.isArrayLike(arguments)); // true
alert(dojo.isArrayLike(args)); // true

위의 것들은 생각나는 것을 예로 든건 뿐이고, 자바스크립트로 코딩하다보면 더 미묘한 경우도 많다. 그래서... dojo에는 이런 것들이 있다:

  • dojo.isString(arg) : arg가 문자열이면 true
  • dojo.isObject(arg): arg가 객체이면 true
  • dojo.isFunction(arg): arg가 함수이면 true
  • dojo.isArray(arg): arg가 배열이면 true
  • dojo.isArrayLike(arg): arg가 유사배열이면 true

그냥 익혀두고 쓰면 날로 편한케 하고져 할 따름이다. 찝찝하다면 그냥 알아만 두자. 자바스크립트로 먹고 살다보면 아쉬울 때가 곧 올 것이다.

이것만으로 끝내기 아까우니까 dojo의 브라우져 검사 기능을 간단히 알아보자:

  • dojo.isFF : 파이어폭스라면 메이저 버전 번호(숫자; 1.5, 2, 3)가 들어있다.
  • dojo.isIE: IE계열이라면 6, 7, 8 등의 메이저 버전 번호가 들어있다.
  • dojo.isSafari: 사파리라면 버전이 들어있다.
  • dojo.isOpera: 오페라라면 버전이 들어있다.
  • dojo.isMozilla: Gecko 기반 브라우져(Mozilla, Firefox, Camino 등)라면 Gecko 버전이 들어있다.
  • dojo.isKhtml: KHTML 기반 브라우져(Safari, Konqurer 등)라면 버전이 들어있다.
  • 기타 dojo.isBrowser, dojo.isQuirks, dojo.isGears, dojo.isAir, dojo.isRhino, dojo.isSpidermonkey 등이 있다.

이게 생각보다 편하다. 예를 들면:
if (dojo.isFF) {
  // FF workaround...
} else if(dojo.isIE) {
  // IE 공통 workaround...
  if (dojo.isIE === 6) {
    // IE6 전용 workaround...
  }
}
if (!dojo.isIE && !dojo.isMozilla) {
  alert('sorry unsupported!');
  return;
}
// 브라우져 공통 ...

직접(!) 제작한 짤방 하나 소개하며... 오늘은 여기까지~
사용자 삽입 이미지


덧 1. dijit을 사용하게 되면 위의 속성들을 참조해서 다음과 같은 css클래스를 html 태그에 추가해주므로, 브라우져별 css 핵을 적용할 때 편하게 사용할 수 있다:
dj_ie, dj_ie55, dj_ie6, dj_ie7, dj_iequirks, dj_opera, dj_opera8, dj_opera9, dj_khtml, dj_safari, dj_gecko, dj_ff2

덧 2. 나중에 얘기할 기회가 있겠지만(전에 했던가...-.-) 위의 예를 쉽게 테스트 해보려면 그냥 다음과 같은 태그를 다른 <script> 태그보다 앞에 추가해주면 된다:
<script type="text/javascript" src="http://o.aolcdn.com/dojo/1.1.1/dojo/dojo.xd.js"></script>


Posted by iolo
hacking/web2008/07/13 19:58
크리에이티브 커먼즈 라이선스
Creative Commons License
dojo를 알고 있는 ajax 개발자들 조차도 dojo하면 먼저 뽀대는 나지만 용량이 크기, 느리고, 무거운 위젯 시스템(일명 dijit)을 떠올린다. dijit도 많이 가벼워졌고, 더 가볍게 쓸 수 있는 방법들 많이 있지만, 일단 무시하고...

dojo의 기본 시스템(일명 dojo core), 그 중에서도 유틸리티 함수들에 대해서 알아보기로 하자.

서버에서 ajax로 다음과 같은 json 데이터를 받아 왔다고 치자:
items: [
  { link: 'http://www.googledocs.com/', title: 'Google Docs' },
  { link: 'http://www.zoho.com/', title: 'Zoho' },
  { link: 'http://www.thinkfree.com/', title: 'ThinkFree Online' }
]

(데이터의 의미가 뭔지 알려고 하지 말자... 다친다 -.-;;;)

이걸 일반적인 자바스크립트로 처리한다고 치면:
for(var i = 0; i < items.length; i++) {
  str += '<a href="' + items[i].link + '">' + items[i].title + '</a>';
}
document.getElementById('resultDiv').innerHTML = str;

dojo 없이도 개선해야 할 여지가 많이 있지만, 그건 이 글을 범위를 벗어나니 스킵~

백문이 불여일견! dojo로 바꿔보자.
dojo.forEach(result, function(item) {
  str += '<a href="' + item.link + '">' + item.title + '</a>';
});
dojo.byId('resultDiv').innerHTML = str;

뭐가 달라졌을까...
1. 맨 아랫줄을 보면 document.getElementById() 대신 dojo.byId()가 사용된 것을 볼 수 있는데, 이건 프로토타입의 $() 와 비슷하다. 그냥 타이핑을 줄여주는 것 뿐이다.
2. 고전적인 for 루프 대신 dojo.forEach() 가 사용되었다.
for (var item in items) { } 도 있으므로 별 필요가 없다고 생각할 수 있지만, 자바스크립트의 for...in...은 유명한 evil 중의 하나다. 믿어라. 크록포드옹께서 그리 말씀하셨다.
다음에 보게 되겠지만 dojo.forEach()는 dojo의 배열 지원 함수 중의 하나에 불과하다. 모두 합체하면 게키강가3가 나온다. 믿거나 말거나... -.-;;; 루리루리짱께서 그리 말씀하셨다.

두둥! 이것이 바로 게키강가3!

아무튼 다시 원래 길로 돌아와서...
별로 편한거 같지 않다는 분들이 계시다. 한가지만 더 해보자.
dojo.forEach(result, function(item) {
  str += dojo.string.substitute('<a href="${link}">${title}</a>', item);
});
dojo.byId('resultDiv').innerHTML = str;

dojo.string.substitute()라는 이름은 길지만, 의미가 명확한 함수를 사용했다.
멋지다! 결과는 달라지지 않았지만, 코드가 아름다워졌다! 자바스크립트와 HTML의 따옴표 사이에서 방황했던 개발자라면... 감동! 그 자체다.

그래도 별로 편한거 같지 않다는 분들이 계시다.
하지만... 너무 덥다. -.-;;;; 오늘은 그만 하자.
아쉬운 분들을 위해 선물 하나 준비했다. 클릭하면 크게 보이겠지만, 그냥 작게 보고 만족하자.=3=333
사용자 삽입 이미지

Posted by iolo
hacking/web2008/02/19 12:09
크리에이티브 커먼즈 라이선스
Creative Commons License
500+개의 버그를 고쳤다는데... 그럼 도대체 버그가 몇개였다는 얘기?

3월 1일 정식이 나오고, 4월 1일 첫번째 마이너 업데이트(1.1.1)가 나올 예정이니까, 실무에 적용하는건 4월 이후에나 가능하겠다.

바뀐 내용을 좀 살펴보면:
  • dijit 테마를 수정
  • CSS 최적화 옵션등 빌드 스크립트 개선
  • BorderContainerExpandoPane 레이아웃 컨테이너 추가
  • dojo.gfx에 애니메이션 추가
  • dojox.fx에 지우기 기능 추가
  • dojox.highlight에 문법 강조 기능 추가
  • dojox.dt에 Django 문법 추가 및
  • dojo.data와 grid/tree간의 연동 개선, dojox.data에 JsonPath, SnapLogic 등의 구현 추가.
  • Firefox3의 WAI-ARIA 지원을 포함한 a11y 개선
  • Adobe AIR 지원
  • dojo 1.0과의 하위 호환성 일부 포기
라고 하는데...

개인적으로 가장 눈에 띄는 것은:
  • 기존의 LayoutContainer와 SplitContainer를 대체할 수 있는 BorderContainer 추가
  • Dialog/TooltipDialog에 TabContainer등의 레이아웃 위젯을 포함할 수 없었던 버그 수정
  • dijit.form.* 위젯들의 개선 및 버그 수정
  • IE에서의 메모리 릭을 포함한 잡다한 버그 수정
  • dojo.xhrXXX() 함수들을 포괄하는 dojo.xhr() 함수 추가.
정도일까?

자세한 내용은: http://www.dojotoolkit.org/book/dojo-1-1-release-notes

'hacking > web' 카테고리의 다른 글

Ajax와 XML: Ajax로 table 다루기  (1) 2008/04/12
IE8에 대처하는 우리의 자세...  (2) 2008/03/15
dojo 1.1 beta 릴리즈!  (0) 2008/02/19
dojo에서 $ 쓰기  (0) 2007/12/04
prototype사용자를 위한 dojo 입문 (5)  (0) 2007/11/24
prototype사용자를 위한 dojo 입문 (4)  (1) 2007/11/16
Posted by iolo
TAG Dojo
hacking/web2007/12/04 15:18
크리에이티브 커먼즈 라이선스
Creative Commons License
요렇게 해주면:
$ = dojo = dojo.mixin(function(){ return dojo.query.apply(dojo, arguments); }, dojo);

요런 식으로 쓸 수 있게 된다:
$.addOnLoad(function(){
   console.debug($("div"));
});

$("div").onclick(function() { alert("div clicked."); });

$.xhrPost({form: $("#myFormId")[0]});

흠... 너무 prototype스럽군... -.-;

위의 방법 말고도 다양한 변형들이 있다.

요는 prototype의 $가 그렇게 대단한 무언가가 아니라는 거다.

'hacking > web' 카테고리의 다른 글

IE8에 대처하는 우리의 자세...  (2) 2008/03/15
dojo 1.1 beta 릴리즈!  (0) 2008/02/19
dojo에서 $ 쓰기  (0) 2007/12/04
prototype사용자를 위한 dojo 입문 (5)  (0) 2007/11/24
prototype사용자를 위한 dojo 입문 (4)  (1) 2007/11/16
prototype사용자를 위한 dojo 입문 (3)  (0) 2007/11/15
Posted by iolo
hacking/web2007/11/16 00:16
크리에이티브 커먼즈 라이선스
Creative Commons License
제 4 장 이벤트

javascript로 심각한 코딩을 해본 사람이라면 누구나 사소한 브라우져들간의 차이에 좌절한 경험이 있을 것이다. 문제는 prototype이나 dojo같은 멋진 넘들이 넘쳐나는 세상에도 여전히 과거의 코드에 매여있는 사람들이 있다는 것이다.
 
일단, 이런 HTML이 있다고 생각하자:

...
<button id='okBtn'>OK!</button>
...

* DOM
var okBtn = document.getElementById('okBtn');
if (okBtn.addEventListener) {
  okBtn.addEventListener('click', ok_onclick, true);
} else {
  okBtn.attachEvent('onclick', ok_onclick);
  okBtn.captureEvents(Event.CLICK);
}
function ok_onclick(e) {
  if (!e) e = window.event;
  if (e.keyCode) code = e.keyCode;
  else if (e.which) code = e.which;
  ...
  e.cancelBubble = true;
  if (e.stopPropagation) e.stopPropagation();
}

* prototype
Event.observe($('okBtn'), 'click', function(event) { ... } )

* dojo
dojo.connect(dojo.byId('okBtn'), 'onclick', function(event) { ... } )

결정적인 차이는(?!) prototype과 달리 dojo는 'on'을 항상 붙여야 한다(예: onclick, onchange)는 것이다. dojo의 위젯(dijit)들은 이벤트 이름을 대소문자를 섞어서 사용해서(예: onClick, onChange) 표준 이벤트들과 구별한다.

사실 dojo.connect()는 이벤트 핸들러 연결보다 더 많은 일을 한다. 실제 구현은 AOP의 before advice에 가깝다고 볼 수 있다. 즉, 일반적인 함수에 대해서도 적용 가능하다.

dojo는 페이지 load/unload 이벤트를 다른 이벤트와는 조금 다르게 취급한다:

* BOM
window.onload = function() { ... }
window.onunload = function() { ... }

* prototype
Event.observe(window, 'load', function() { ... });
Event.observe(window, 'unload', function() { ... });

* dojo
dojo.addOnLoad(function() { ... });
dojo.addOnUnload(function() { ... });

엄밀하게 따지면, dojo.addOnLoad()는 DOMContentLoaded 이벤트 핸들러를 연결한다. 이게 뭔지 아시는 분이라면 따로 설명이 필요 없을테고, 그게 뭐냐고 하시는 분이라면 몰라도 되는 분일테니... 여기서 이러쿵 저러쿵 설명 안하겠다.

dojo.connect(window, 'onload', function() { ... }); 라고 해도 비슷한 결과를 얻겠지만... 미묘~~한 부분이 있다.

마지막으로 dojo에는 Topic 이벤트 시스템이라는 특이한 녀석이 하나 더 있다:
dojo.publish('/some/topic', value1, value2);
dojo.subscribe('/some/topic', function(arg1, arg2) { ... } );

"익명/공용/전역 메시지 큐" 비슷한 것인데, AJAX의 첫번째 A(Asynchronous) 때문에(덕분에?) 꽤 유용하게 써먹을 수 있다. 자세한 내용을 늘 그랬듯이 온라인 문서를 참고하시길...

다음에는 OOP 지원 기능에 대해서 알아보기로 하고... 오늘은 요기까지~.~/

Posted by iolo
hacking/web2007/11/15 23:34
크리에이티브 커먼즈 라이선스
Creative Commons License
제 3 장 XMLHttpRequest

계속해서, XMLHttpRequest 관련 함수들을 살펴 보자.
 
BOM(Browser Object Model) 코드는... 열라 길다. -.-; 생략할려다가... prototype이나 dojo가 얼마나 많은 노가다를 줄여주는지 확인하는 차원에서 주욱 적어봤다. prototype과 dojo 코드는 얼추 비슷해 보이지만, 가만히 살펴보면 자질구레한 부분에서 조금씩 다르다:

* BOM
var xhr;
try {
  xhr = new XMLHttpRequest();
} catch (e) {
  try {
    xhr = new ActiveXObject("Msxml2.XMLHTTP");
  } catch (e) {
    try {
      xhr = new ActiveXObject("Microsoft.XMLHTTP");
    } catch (e) {
      // ... Orz
    }
  }
}
xhr.onstatechange = function() {
  select (xhr.readyState) {
  case 4: // loaded
    select (xhr.statusCode) {
    case 200: // ok
      break;
    case ...
    }
  case ...
  }
}
xhr.open('GET','your/url', true);
xhr.send('paramName1=' + encodeURIComponent(paramValue1) + '&paramName2=' + encodeURIComponent(paramValue));
...

* prototype
new Ajax.Request('/your/url', {
  method: 'GET', /* POST, PUT, DELETE ... */
  asynchronous: true,
  parameters: {
    paramName1: paramValue1,
    paramName2: paramValue2,
    ...
  }
  onComplete: function(xhr) { },
  onFailure: function(xhr) { }
});

* dojo
dojo.xhrGet({ /* xhrPost, xhrPut, xhrRawPost, xhrRawPut ... */
  url: '/your/url',
  sync: false,
  handleAs: 'xml',  /* xml, json, text */
  content: {
    paramName1: paramValue1,
    paramName2: paramValue2,
    ...
  },
  load: function(response, xhr) { },
  error: function(response, xhr) { }
});

차이점을 요약하면:
  • new 연산자를 쓰지 않는다(그냥 함수 호출이다).
  • 요청 메소드를 파라메터로 처리하지 않고, 요청 메소드별로 함수가 따로 있다(코멘트 부분 참조).
  • 요청 url을 별도의 파라메터가 아니라 다른 옵션들과 똑같이 취급한다.
  • 동기/비동기 모드를 지정하는 파라메터가 거꾸로다(당연히 이름도 asynchronous가 아니고 sync다).
  • 응답 형식을 지정할 수 있으며, 응답이 바로 콜백함수로 전달된다.(즉, json 응답의 처리 방법이 다르다.)
prototype처럼 성공/실패 여부에 따라 콜백 함수를 지정할 수도 있지만, 내 경우에는 dojo.Deferred(twisted의 deferred와 유사한데...)를 선호한다. 여기서는 많은 옵션들을 생략했는데, 자세한 것은 각각의 온라인 문서를 찾아보시라.

Ajax.Updater / Ajax.PeriodicalUpdater에 해당하는 녀석은 없다. 그냥 window.setInterval()과 domNode.innerHTML을 쓰면 되니까 그다지 아쉽지 않다. 그리고, dojo의 위젯(dijit) 중에 ContentPane이라는 녀석을 쓰면 이 모든걸 한 줄로 끝낼 수 있다.

dojo에는 XHMLHttpRequest 대신 <script>태그와 <iframe>태그를 사용하는 dojo.io.script와 dojo.io.iframe이라는 두개의 특별한 녀석들이 있다. AJAX의 Cross Domain 제한을 경험해본 개발자라면 귀가 솔깃할 만한 녀석들이다. 자세한 것은 역시 온라인 문서를 찾아보시라.

다음에는 이벤트 핸들링에 대해서 알아보기로 하고... 요기까지~

Posted by iolo
hacking/web2007/11/13 00:52
크리에이티브 커먼즈 라이선스
Creative Commons License
제 2 장 utility functions

유틸리티 함수들을 살펴보자. dojo에는 아쉽게도(?) prototype사용자들이 제일 좋아하는 함수 $()가 없다. 대신 dojo.byId()가 있다. 이게 귀찮다고 생각하시는 분이 계시겠지만 dojo 개발팀에서도 꽤 오랜 고민과 토론 끝에 내린 결론이다. 관심있는 분은 메일링을 뒤져보시라.

* 지정한 id를 가진 DOM node를 검색

* dom
var node = document.getElementById('abc');

* prototype
var node = $('abc');

* dojo
var node = dojo.byId('abc');

너무 길다고? 익숙해지면 별 차이없다. document.getElementById()보다는 짧지않은가! 거기다 나는 $ 기호에 알르레기반응이 있다. per~~~~~l 씨러=3=3=3=33

* 지정한 노드 이름(태그 이름) / CSS 셀렉터를 가진 DOM node들(배열)을 검색

* dom
var nodes = document.getElementsByTagName('div');

* prototype
var nodes = $$('div');
var nodes = $$('.item');
var nodes = $$('#title);
var nodes = $$('div.item');
...

* dojo

var nodes = dojo.query('div');
var nodes = dojo.query('.item');
var nodes = dojo.query('#title');
var nodes = dojo.query('div.item');
...

dojo.query()도 prototype이나 jquery와 비슷한 CSS 2.1 셀렉터를 지원한다. 더 자세한 내용은 온라인 문서를 찾아보시라.

나중에 얘기하겠지만 prototype에서 $$와 each()의 결합을 많이 쓰는 것처럼, dojo.query()도forEach()와 함께 쓰면 편리하다.

일단 자자~ -,.-;;;;

Posted by iolo
hacking/web2007/11/13 00:48
크리에이티브 커먼즈 라이선스
Creative Commons License

dojo 1.0 릴리즈 기념으로 몇 번에 걸쳐 잡담을 좀 하려고 한다.

제목은 거창하게 "prototype 사용자를 위한 dojo 입문"이지만... 글쎄... 그냥... dojo 0.4 이전 버전의 dojo에서 실망하거나 좌절하고, 두번 다시는 dojo를 거들떠 보지 않는 분들에게... dojo도 꽤 쓸만하다는(쓸만해졌다는?) 얘기를 하고 싶었다.

제대로 될지는 모르겠지만, 일단 시작해보자!


제 1 장 getting dojo

prototype은 홈페이지에서 prototype.js를 받아서 로컬 웹서버에 올려놓고 참조했다. dojo도 마찬가지다.

dojo도 홈페이지에서 http://download.dojotoolkit.org/release-1.0.0/dojo-release-1.0.0.zip 파일을 다운 받아서 웹 디렉토리에 적당히 풀고, (그 디렉토리를 js라고 하면) 다음과 같이 쓰면 된다:

<script type="text/javascript" src="js/dojo/dojo.js></script>

주의: 할 점은 dojo가 두 번 들어간다는 것이다. 내 경우엔 js/dojo에 dojo를 풀었더니 무려 js/dojo/dojo/dojo.js가 되었다. -.-;;;

그러나 120k짜리 js파일(1.6은 120K)만 받으면 되는 prototype에 익숙한 개발자들에게 3M를 훌쩍 넘는 dojo의 zip파일(1.0은 3.9M)은 압박(!) 그 자체이다. 내가 알고 있는 많은 개발자들이 여기에서 좌절하고... prototype으로 돌아간다.

그런 분들을 위해 미리 말해두면 prototype 대체용의 dojo라면 잡다한 다른 파일 필요없이 아래의 파일 중에 하나만 받아서 쓰면 된다:

http://o.aolcdn.com/dojo/1.0.0/dojo/dojo.js.uncompressed.js (압축안된 버전; 224K)
http://o.aolcdn.com/dojo/1.0.0/dojo/dojo.js (압축된 버전; 71K)

이것도 귀찮은 개발자를 위해서 한가지 방법을 더 제공한다. 다운로드고 나발이고 필요없다. 그냥 다음과 같이 하면 된다:

<script type="text/javascript" src="http://o.aolcdn.com/dojo/1.0.0/dojo/dojo.xd.js"></script>

이렇게 하는 또하나의 장점은 AOL의 CDN이 엄청나게 빠르다는 것이다. 개발하는 동안에는 로컬에 있는 웹서버보다 빠르지 않겠지만, 최종적으로는 웹을 통해서 서비스할텐데, 그때도 AOL의 CDN보다 빠를까?

저렇게 하면 AJAX로 내 웹서버에도 접속 못하는 거 아니냐고 걱정하는 분이 계시다면(오! 고수!), 걱정 붙들어매도 되시겠다. dojo가 알아서 한다. 열라 신기하다. 정말! 정말! 정말! 궁금할때만 소스를 보시라. -.-;;;;

흠냐... 1시 다 됐네-.-; 자자~
Posted by iolo
hacking/web2007/09/17 16:27
크리에이티브 커먼즈 라이선스
Creative Commons License
http://sitepen.com/pressReleases.php?item=20070917

dojo 0.9에서 가장 황당한 이슈 중의 하나가... grid위젯(dojo 0.4의 SortableTable/FilteringTable의 대체품)이 없다는 것이었는데...

결국, 모질라 재단, SitePen, Nexaweb, Redfin, SnapLogin 등이 공동으로 TurboAjax의 Grid를 사들여서 dojo 재단에 기증하기로 했다는 소식.

TurboAjax는 dojo초창기부터 잘 다듬어서 상용으로 팔아먹던 회사였는데... 버틴 보람이 있군.

대충 훑어보면:
  • General Features
    • Browser support matching Dojo 1.0: IE6/7, Firefox 2/3, and Safari 3 on Windows, Safari 3 and Firefox 2/3 on Mac, and Firefox 2/3 on Linux. Opera support will follow.
    • Subgrids, nested grids, and tree grids
    • In-place editing
    • Context menus and tooltips specific to a particular row or column
    • Data sorting through sort functions or custom sort filter functions
    • Cell formatters for separation of data from layout
    • Documentation and unit tests
  • Dijit Features
    • Standard Dijit with markup and declarative instantiation
    • Data binding between data columns and grid columns, and dojo.data provider support
    • il8n and a11y
    • Grid events for easy manipulation and event handling for event-driven application development
    • Keyboard support, including cell selection
    • Theme infrastructure with structured CSS in dijit.css and tundra.css
  • Rows, Columns, formatting, and more
    • Virtual scrolling: 100,000+ rows without performance lags
    • User sizable columns
    • Complex rows of various widths and heights, just like HTML tables
    • Cells, rows, and columns may be patterned based on pattern or state
    • Fixed position columns
    • Column dimensions specified in a variety of CSS units, not just pixels
    • dojo.connect calls to selectively show/hide portions of rows
    • Optional auto-sizing to fill content or a container node
    • Auto-adjust dimensions in response to user-controlled font-size adjustments
    • Variable row height
    • Selection by row and multirows
    • Column layout may be changed on the fly by users and code
    • Resizing of the grid on the fly by users and code
좋은 얘기만 가득 @..@

10월 말 정도에 볼 수 있을꺼라고 하니... 기대만땅~

알렉스의 말빨이 무섭긴 무섭다. 열라 툴툴거리니까 옆에서 돈으로 해결해 주는 군-.-;

'hacking > web' 카테고리의 다른 글

DOMContentLoaded revisited  (0) 2007/09/27
RIA 전성 시대? AJAX 독주 체제!  (2) 2007/09/22
dojo 1.0을 위한 grid 위젯은 TurboGrid  (0) 2007/09/17
SVG for All  (2) 2007/09/14
When innerHTML isn't Fast Enough  (0) 2007/09/14
bloglines 3.0beta  (0) 2007/09/07
Posted by iolo
TAG Ajax, Dojo, GRID
hacking/web2007/09/07 11:12
크리에이티브 커먼즈 라이선스
Creative Commons License
블로그라인에서 새 버전dojo만들고 있다고 해서...
후다닥 들어가 봤는데...
이게 생각보다 꽤 쓸만~ @..@
구글 리더를 버리기로 결정~

P.S. C+U를 눌렀더니(직업병?)... 요런게 보인다~
  <body id="bloglines-read">
<div id="theconsole" style="display:none">
<div id="theconsolelog">
<pre>Welcome to
_ _ _ _
| |__ | | ___ __ _| (_)_ __ ___ ___
| '_ \| |/ _ \ / _` | | | '_ \ / _ \/ __|
| |_) | | (_) | (_| | | | | | | __/\__ \
|_.__/|_|\___/ \__, |_|_|_| |_|\___||___/
|___/ v3.0 (beta)</pre>
<i>Proudly Made On Earth!</i><br/>
<br/></div>
  <div>

나도 따라해봐야쥐~

'hacking > web' 카테고리의 다른 글

SVG for All  (2) 2007/09/14
When innerHTML isn't Fast Enough  (0) 2007/09/14
bloglines 3.0beta  (0) 2007/09/07
Web 2.0 ... The Machine is Us/ing Us  (0) 2007/02/26
FireBug 1.0  (0) 2007/01/28
IE6? IE7? 어느 것을 우선 타겟으로 삼을 것인가?  (0) 2007/01/03
Posted by iolo