Rev. 2.73

굵직한 자바스크립트 관련 웹 프로젝트들을 진행하다 보면 개발자간 협업에서 부디치는 문제가 상당수 있습니다. 뜻하지 않게 네임스페이스를 비효율적으로 사용하거나 이벤트가 무작위로 생성되기도 하고 심지어 중복된 결과를 낳는 함수를 각자 개발하는 사례도 있습니다. 특히, window객체에 할당하는 이벤트 처리는 전역에 걸쳐 발동하게 되기 때문에 코드 자체를 작업자간에 공유하여 작업하거나 window.onload와 같은 이벤트를 중복해서 할당해 버리는 경우도 많습니다. 이 문제를 조금 더 효율적으로 처리할 방법을 강구하다가 아래와 같은 이벤트의 재사용 방법을 모색하게 되었습니다.

/**
 * Dynamic event observation
 *
 * @author firejune <to [at] firejune [dot] com>
 * 
 * @usage : 
 *   - Event.addObserve['load'].initializeTooltip = new Tooltip;
 *   - Event.addObserve['resize'].accordionResize = function() { 
 *       Accordion.resize(); 
 *     };
 * 
 * @requires 
 *   - prototype.js <prototypejs.org>
 * 
 */

Event.addObserve = {
  load : {},
  scroll: {},
  resize: {}
};

// Align global event-oservers
for (var evt in Event.addObserve) {
  var eventObj = Event.addObserve[evt];
  var attachFunc = Prototype.emptyFunction;

  if (!eventObj) continue;

  // Call the functions
  var callFunctions = function(bind, obj) {
    var report = [];
    for (var func in obj) {
      if (func && typeof obj[func] == 'function') {
        obj[func](bind);
        report.push(func);
      } else {
        //debug(obj[func], 'is not a function');
      } 
    }
    //debug('called functions: ' + report);
  };

  if (evt == 'load') {
    attachFunc = callFunctions.bindAsEventListener(this, eventObj);
  } else {

    // Better performance for IE
    attachFunc = function() {
      if (typeof Event.oserveTimeOut == "number") {
        window.clearTimeout(Event.oserveTimeOut);
        delete Event.oserveTimeOut;
      }
      Event.oserveTimeOut = setTimeout(function(){
        callFunctions(this, eventObj);
      }.bind(this), 100);
    }.bindAsEventListener(this);
  }

  // Observing Events
  Event.observe(window, evt, attachFunc, false);
}

이로써 window객체로 할당되는 이벤트를 재사용하여 함수를 동적으로 관리할 수게 되었습니다. 그리고 윈도 리사이즈와 스크롤 이벤트에는 성능향상을 위해 100 밀리세컨즈 뒤에 실행되도록 setTimeout이 적용되어 있습니다. 유저의 무리한 컨트롤에 대응하고 쓸대없는 계산을 줄이기 위해서 입니다. 위 코드를 적절한 곳에서 한번만 실행되게 하면 아래처럼 필요한 때에 추가/대체/무력화 할 수 있습니다. 단, 라이브러리(Open source, Open API)와 같은 비의존성 겸손한(Unobtrusive) 자바스크립트 프로그래밍과는 거리가 멀기 때문에 항상 유용한 것은 아니라는 점 참고하시기 바랍니다.

// A개발자의 Tooltip 클래스가 초기화 되기위한 이벤트 핸들러
 // 단, 온로드 이벤트가 발생하기 전에 선언해야 한다. 
 Event.addObserve['load'].initializeTooltip = Tooltip = new Tooltip;

 // B개발자의 Accordion 클래스가 초기화 되기위한 추가 이벤트 핸들러
 Event.addObserve['load'].initializeAccordion = Accordion = new Accordion;

 // B개발자의 Accordion 리사이즈 이벤트 핸들러
 Event.addObserve['resize'].accordionResize = Accordion.resize; 

 // A개발자의 Tooltip 클래스에 필요한 문서크기 재계산 추가 이벤트 핸들러
 Event.addObserve['resize'].tooltipResize = function() { 
   var element = $('element1') || $('element2');
   Tooltip.setDocumentPositions(element); 
 };

 // B개발자가 추가했던 Accordion 리사이즈 이벤트 핸들러 무력화
 Event.addObserve['resize'].accordionResize = null; 

Comments

gebcnwebkitonly.png

사파리에서 사용되고 있는 웹브라우저 오픈소스 프로젝트인 WebKit은 getElementsByClassName 메서드를 기본으로 제공한다고 밝혔습니다. 메서드 이름에서 유추할 수 있듯이 Element를 class명으로 찾아 배열타입으로 반환하는 매우 활용가치가 높은 메서드이며, HTML5의 DOM API Draft문서에 포함되어 있기도 합니다. 지금까지는 유틸리티 함수로써 자체적으로 구현해 사용하거나 Prototype과 같은 자바스크립트 라이브러리에서 임의로 제공되는 형태로 사용되어 왔습니다. 이 메서드가 네이티브로 제공되었을 때의 이점은 더이상 자바스크립트로 구현하지 않아도 된다는 것 외에도 위 벤치마크 자료에서 보는 것과 같이 엄청난 성능향상을 기대할 수 있습니다.

Comments

아이팟 터치와 아이폰의 모바일 사파리 브라우저에서 중력센서에 의해 화면이 회전하였을 때 이벤트를 발생시키고 어느 방향으로 회전 했는지를 자바스크립트에서 알아낼 수 있는 방법이 공개되었습니다. 이 이벤트를 응용한 재미있는 홀리데이 페이지도 만들어 졌군요. 아래의 예제코드를 참고하세요.

window.onorientationchange = function() {
  var orientation = window.orientation;
  switch(orientation) {
    case 0:
        document.body.setAttribute("class","portrait");
        document.getElementById("currentOrientation").innerHTML = "Now in portrait orientation (Home button on the bottom).";
        break;  
        
    case 90:
        document.body.setAttribute("class","landscapeLeft");
        document.getElementById("currentOrientation").innerHTML = "Now in landscape orientation and turned to the left (Home button to the right).";
        break;
    
    case -90:  
        document.body.setAttribute("class","landscapeRight");
        document.getElementById("currentOrientation").innerHTML = "Now in landscape orientation and turned to the right (Home button to the left).";
        break;
  }
}

Comments