'Javascript'에 해당되는 글 112건

  1. 2012.07.03 자바스크립트 장식자 패턴(Decorator pattern) 구현
  2. 2012.07.03 자바스크립트 옵져버 패턴(Observer Pattern) 구현
  3. 2012.07.02 Onunload 이벤트 할당 하기
  4. 2012.07.01 DOM 사용 최소화 하기 2
  5. 2012.06.04 자바스크립트 안티패턴(Antipatterns)의 유형과 사용법 2
  6. 2012.05.04 AJAX API
  7. 2012.05.04 AJAX 란?
  8. 2012.05.03 Form 유효성 검사 API
  9. 2012.04.18 Dimension API
  10. 2012.04.18 자바스크립트 Dimension (Screen, Viewport, Scroll, Element, Mouse Event)

자바스크립트 장식자 패턴(Decorator pattern) 구현



Decorator(장식자) Pattern

- 런타임 시 해당 객체에 원하는 기능(메서드)을 추가하는 패턴.

장식자 패턴은 더글라스 아저씨가 만드신 method() 함수에서도 찾아 볼 수 있다.


- javascript decorator pattern:

http://lostechies.com/jimmybogard/2009/01/20/javascript-decorator-pattern/


- 자바스크립트 method() 함수:

http://mohwaproject.tistory.com/archive/201202?page=2



아래 코드는 런타임 시 해당 객체(obj1)에 원하는 기능(method1, method2)을 추가 시킨다.


  1. // Decorator(장식자) 패턴
  2. var Decorator = new (function () {
  3.  
  4.     var Decorator = function () {
  5.         return this;
  6.     }
  7.  
  8.     Decorator.fn = Decorator.prototype = {
  9.  
  10.         add: function (obj, n, fn) {
  11.  
  12.             obj[n] = function () { fn.apply(obj, Array.prototype.slice.call(arguments)); };
  13.  
  14.             return this;
  15.         },
  16.         remove: function (obj, n) {
  17.  
  18.             delete obj[n];
  19.  
  20.             return this;
  21.         }
  22.     }
  23.  
  24.     return Decorator;
  25.  
  26. } ())();
  27.  
  28. function obj1() {
  29.     return this;
  30. }
  31.  
  32. obj1.prototype.msg = function (str) {
  33.     alert(str);
  34. }
  35.  
  36. function obj2() {
  37.     return this;
  38. }
  39.  
  40. obj2.prototype.method1 = function (str) {
  41.     alert(str);
  42. }
  43.  
  44. function obj3() {
  45.     return this;
  46. }
  47.  
  48. obj3.prototype.method1 = function (str) {
  49.     alert(str);
  50. }
  51.  
  52. var obj1 = new obj1();
  53. // add
  54. Decorator.add(obj1, 'obj2_method1', new obj2().method1).add(obj1, 'obj3_method1', new obj3().method1);
  55. // remove
  56. //obj = Decorator.remove(obj1, 'obj3_method1');
  57.  
  58. obj1.msg('str1');
  59. obj1.obj2_method1('str2');
  60. obj1.obj3_method1('str3');



자바스크립트 옵져버 패턴(Observer Pattern) 구현



Observer Pattern



흔히 발행/구독 패턴이라 불리는 이 패턴은 객체의 상태 변화를 관찰하는 관찰자(옵져버)를 객체에 등록(구독)하여, 객체에 상태 변화가 있을때마다 등록된 목록의 관찰자들에게 통지(발행) 하도록 하는 디자인 패턴이다.


쉽게 말해 Broadcast System을 구축할 수 있으며, 각 모듈간의 밀접한 커플링을 조율하여, 모듈의 재사용성을 높인다.



아래 코드는 모든 유저(user1, user2, user3)가 현재 까지 등록(구독)된 유저 현황에 대한 정보를 통지(발행)를 받을 수 있다.


  1. // 옵져버 패턴
  2. var Observer = new (function () {
  3.  
  4.     var Observer = function () {
  5.         this.observers = [];
  6.         return this;
  7.     }
  8.  
  9.     Observer.fn = Observer.prototype = {
  10.  
  11.         subscribe: function (type, fn) {
  12.  
  13.             type = type || '';
  14.  
  15.             this.observers.push({ id: this.observers.length - 1, type: type, fn: fn, isStop: false });
  16.  
  17.             return (this.observers.length - 1);
  18.         },
  19.         remove: function (type, id) {
  20.  
  21.             type = type || '';
  22.  
  23.             if (typeof id === 'number') {
  24.                 this.observers[id].type === type && delete this.observers[id];
  25.             }
  26.             else {
  27.                 for (var t in this.observers) {
  28.                     if (type) {
  29.                         if (this.observers[t].type === type) {
  30.                             delete this.observers[t];
  31.                         }
  32.                     }
  33.                     else {
  34.                         delete this.observers[t];
  35.                     }
  36.                 }
  37.             }
  38.  
  39.             return this;
  40.         },
  41.         abort: function (type, id) {
  42.  
  43.             type = type || '';
  44.  
  45.             if (typeof id === 'number') {
  46.                 if (this.observers[id].type === type) this.observers[id].isStop = true;
  47.             }
  48.             else {
  49.                 for (var t in this.observers) {
  50.                     if (type) {
  51.                         if (this.observers[t].type === type) {
  52.                             this.observers[t].isStop = true;
  53.                         }
  54.                     }
  55.                     else {
  56.                         this.observers[t].isStop = true;
  57.                     }
  58.                 }
  59.             }
  60.  
  61.             return this;
  62.         },
  63.         restore: function (type, id) {
  64.  
  65.             type = type || '';
  66.  
  67.             if (typeof id === 'number') {
  68.                 if (this.observers[id].type === type) this.observers[id].isStop = false;
  69.             }
  70.             else {
  71.                 for (var t in this.observers) {
  72.                     if (type) {
  73.                         if (this.observers[t].type === type) {
  74.                             this.observers[t].isStop = false;
  75.                         }
  76.                     }
  77.                     else {
  78.                         this.observers[t].isStop = false;
  79.                     }
  80.                 }
  81.             }
  82.  
  83.             return this;
  84.         },
  85.         publish: function (type, id) {
  86.  
  87.             type = type || '';
  88.  
  89.             if (typeof id === 'number') {
  90.                 typeof this.observers[id].fn === 'function' && this.observers[id].fn.apply(this.observers[id].type, Array.prototype.slice.call(arguments));
  91.             }
  92.             else {
  93.                 for (var t in this.observers) {
  94.                     if (type) {
  95.                         if (this.observers[t].type === type && !this.observers[t].isStop) {
  96.                             typeof this.observers[t].fn === 'function' && this.observers[t].fn.apply(this.observers[t].type, Array.prototype.slice.call(arguments));
  97.                         }
  98.                     }
  99.                     else {
  100.                         if (!this.observers[t].isStop) {
  101.                             typeof this.observers[t].fn === 'function' && this.observers[t].fn.apply(this.observers[t].type, Array.prototype.slice.call(arguments));
  102.                         }
  103.                     }
  104.                 }
  105.             }
  106.  
  107.             return this;
  108.         }
  109.     }
  110.  
  111.     function marge() {
  112.  
  113.         var parent = this
  114.     , target = {};
  115.  
  116.         for (var i = 0, length = arguments.length; i < length; i++) {
  117.  
  118.             target = arguments[i];
  119.  
  120.             for (var n in parent) {
  121.                 if (typeof target[n] === 'undefined') target[n] = parent[n];
  122.             }
  123.         }
  124.  
  125.         return target;
  126.     }
  127.  
  128.     return Observer;
  129.  
  130. } ())();
  131.  
  132. function Users(name, callback) {
  133.     this.name = name;
  134.  
  135.     Observer.subscribe(this, callback);
  136.  
  137.     return this;
  138. }
  139.  
  140. Users.prototype.broadCast = function () {
  141.     alert(this.name + '님 안녕하세요.');
  142. }
  143.  
  144. var user1 = new Users('전성균1', Users.prototype.broadCast);
  145. var user2 = new Users('전성균2', Users.prototype.broadCast);
  146. var user3 = new Users('전성균3', Users.prototype.broadCast);
  147. var uid = Observer.subscribe(user3, Users.prototype.broadCast);
  148.  
  149. // 구독 삭제
  150. //Observer.remove();
  151. //Observer.remove(user1);
  152. //Observer.remove(user3, uid);
  153.  
  154. // 구독 정지
  155. //Observer.abort();
  156. //Observer.abort(user3);
  157. //Observer.abort(user3, uid);
  158.  
  159. // 구독 복구
  160. //Observer.restore();
  161. //Observer.restore(user3);
  162. //Observer.restore(user3, uid);
  163.        
  164. // 통지
  165. //Observer.publish();
  166. //Observer.publish(user3);
  167. //Observer.publish(user3, uid);




Onunload 이벤트 할당 하기



Onunload 이벤트 할당 하기


페이지 로드 이벤트인 onload 이벤트와 달리 페이지 종료 시(Refrash, Redirect, window.close) 발생하는 onunload 이벤트와 같은 경우 브라우저 벤더 및 버전까지 분기 처리해야 하는 이슈가 존재한다.

특히, Chrome 브라우저 같은 경우는 테스트 해본 결과 종료 시 onunload 이벤트가 발생하지 않아 onbeforeunload 이벤트를 이용하여 처리해야 한다.

이를 해결하기 위해 아래 코드의 onUnload() 함수를 "onbeforeunload" 이벤트에 할당하여, onunload 이벤트 발생 브라우저(Chrome 브라우저 제외)와 그렇지 않은 브라우저(Chrome)를 분기처리 하여 모든 브라우저에서 onunload 시 해당 이벤트 handler를 발생 시킨다.

  1. function getBrowserInfo() {
  2.  
  3.     var msie = /(msie) ([\d]{1,})./gi.exec(window.navigator.userAgent.split(';'));
  4.     var firefox = /(firefox)\/([\d]{1,})./gi.exec(window.navigator.userAgent.split(' ')[6]);
  5.     var safari = /(safari)\/([\d]{1,})./gi.exec(window.navigator.userAgent.split(' ')[9]);
  6.     var chrome = /(chrome)\/([\d]{1,})./gi.exec(window.navigator.userAgent.split(' ')[8]);
  7.  
  8.     var name = '';
  9.     var version = 0;
  10.  
  11.     if (msie) {
  12.         name = msie[1].toLowerCase();
  13.         version = msie[2];
  14.     }
  15.     else if (firefox) {
  16.         name = firefox[1].toLowerCase();
  17.         version = firefox[2];
  18.     }
  19.     else if (safari && !chrome) {
  20.         name = safari[1].toLowerCase();
  21.         version = /(version)\/([\d]{1,})./gi.exec(window.navigator.userAgent.split(' ')[8])[2];
  22.     }
  23.     else if (safari && chrome) {
  24.         name = chrome[1].toLowerCase();
  25.         version = chrome[2];
  26.     }
  27.  
  28.     return {
  29.         name: name,
  30.         version: version
  31.     }
  32. }
  33.  
  34. function onUnload(e, callback) {
  35.  
  36.     var ag = window.navigator.userAgent.toLocaleLowerCase()
  37.       , binfo = getBrowserInfo();
  38.  
  39.     e = window.event || e;
  40.     callback = typeof callback === 'function' ? callback : function () { ; };
  41.  
  42.     // IE
  43.     if (binfo.name === 'msie') {
  44.         if (confirm("callback 함수가 실행 됩니다.")) {
  45.             bind(window, 'unload', function (e) { callback() });
  46.         }
  47.     }
  48.     else {
  49.         if (binfo.name === 'firefox') {
  50.             if (confirm("callback 함수가 실행 됩니다.")) {
  51.                 bind(window, 'unload', function (e) { callback(); });
  52.             }
  53.             else {
  54.                 // window loading stop
  55.                 windiw.setTimeout(function () { window.stop(); }, 1);
  56.             }
  57.         }
  58.         else if (binfo.name === 'safari') {
  59.             if (window.confirm('callback 함수가 실행 됩니다.')) {
  60.                 bind(window, 'unload', function (e) { callback(); });
  61.             }
  62.         }
  63.         else if (binfo.name === 'chrome') {
  64.             return callback();
  65.         }
  66.     }
  67. }
  68.  
  69. function callback() {
  70.     // window unload 이벤트 핸들러
  71.     document.title = 'title';
  72.     alert(document.title);
  73.     return 'callback 함수가 실행 됩니다.';
  74. }
  75.  
  76. // 이벤트 등록 함수
  77. function bind(elem, type, handler, capture) {
  78.     type = typeof type === 'string' && type || '';
  79.     handler = typeof handler === 'function' ? handler : function () { ; };
  80.  
  81.     if (elem.addEventListener) {
  82.         elem.addEventListener(type, handler, capture);
  83.     }
  84.     else if (elem.attachEvent) {
  85.         elem.attachEvent('on' + type, handler);
  86.     }
  87.  
  88.     return elem;
  89. };
  90.  
  91.  
  92. bind(window, 'beforeunload', function (e) {
  93.     return onUnload.apply(this, [e, callback]);
  94. });


위 코드와 같이 Chrome 브라우저를 제외한 나머지 브라우저는 onunload 이벤트가 정상적으로 발생하므로 onbeforeunload 이벤트 발생 시 해당 이벤트를 할당 시킨다.



DOM 사용 최소화 하기



DOM 사용 최소화 하기



노드 조각(document.createDocumentFragment), 노드 사본(elem.cloneNode), 문자 배열([])을 활용한 노드 추가 시 아래와 코드와 같이 DOM 접근을 최소화 하여 비용을 줄일 수 있다.




1. 기본적인 엘리먼트 추가 방법.



function notReflow() {

    var elem = document.getElementById('container');

    for (var i = 0; i < 10; i++) {
        var a = document.createElement('a');
        a.href = '#';
        a.appendChild(document.createTextNode('test' + i));
        elem.appendChild(a);
    }

    return false;
}




2. 노드 조각을 활용한 엘리먼트 추가 방법 



function notReflow() {

    var frag = document.createDocumentFragment();

    for (var i = 0; i < 10; i++) {
        var a = document.createElement('a');
        a.href = '#';
        a.appendChild(document.createTextNode('test' + i));
        frag.appendChild(a);
    }

    document.getElementById('container').appendChild(frag);

    return false;
}




3. 노드 사본을 활용한 엘리먼트 추가 방법 



function notReflow() {
 
    var elem = document.getElementById('container');
    var clone = elem.cloneNode(true);
 
    for (var i = 0; i < 10; i++) {
        var a = document.createElement('a');
        a.href = '#';
        a.appendChild(document.createTextNode('test' + i));
        clone.appendChild(a);
    }
 
    elem.appendChild(clone);
 
    return false;
}




4. 문자 배열을 활용한 엘리먼트 추가 방법 



function notReflow() {

    var h = [];

    for (var i = 0; i < 10; i++) {

        h.push('test' + i + '');
    }

    document.getElementById('container').innerHTML = h;

    return false;
}



상황별 테스트 결과:


첫 번째 상황: 153ms

두 번째 상황: 136ms

세 번째 상황: 129ms

네 번째 상황: 127ms



- 첫 번째 상황을 제외한 나머지 상황들은 성능 상 큰 차이를 보이지 않았지만, 객체 맴버(조각, 사본)를 사용한 방법보다 엘리먼트 속성인 innerHTML을 활용한 문자 배열 추가 방식이 좀 더 빠른 결과를 가져왔다.





자바스크립트 안티패턴(Antipatterns)의 유형과 사용법

 


자바스크립트 안티패턴(Antipatterns)의 유형과 사용법.




정의: 


코드 작성 시 문제에 대한 해결책으로 자주 사용되는 패턴이지만 예상치 못한 결과를 가져올 수 있는 패턴이기도 합니다.


또한, 언어에 대한 지식과 경험이 부족한 상황에 더 많은 문제가 발생하곤 하지만, 코드 작성 시 적재적소에 제대로만 사용한다면 코드 가독성 및 성능을 높일 수 있는 많은 방법을 제시하기도 합니다.





1. 암묵적 변수 선언으로 인한 충돌:


전역 변수는 웹페이지 내 모든 코드에서 공유됩니다. 즉, 모든 전역 변수는 동일한 객체(window) 맴버에 생성되기 때문에 목적이 다른 동일한 이름의 전역 변수를 재 정의 시 먼저 정의된 변수를 덮어쓰게 되어 예상치 못한 오류가 발생하게 됩니다.



아래 prototype() 함수 내부의 암묵적 변수 선언(안티패턴)으로 인해 라이브러리 코드의 전역 변수($)가 새로운 객체로 제 정의되어 라이브러리 객체 맴버(setName)에 접근할 수 없게 되었습니다.


  1. // 생성된 라이브러리
  2. var $ = (function () { return { name: 'j', setName: function () { return this.name } }; })();
  3. alert($.setName()); // j
  4.  
  5.  
  6. // 인라인 스크립트
  7. (function prototype() {
  8.    
  9.     var f = function () {
  10.         this.name = 'p'
  11.         return this;
  12.     };
  13.  
  14.     // 암묵적 변수 선언(안티패턴)
  15.     $ = new f();
  16.  
  17. }());
  18.  
  19. alert($.setName); // undefined
  20. alert($.name); // p
  21.  
  22.  
  23. // 아래 코드는 명시적 변수 선언을 통해 변수 충돌을 해결한 예제입니다.
  24.  
  25. // 생성된 라이브러리
  26. var $ = (function () { return { name: 'j', setName: function () { return this.name } }; })();
  27. alert($.setName()); // j
  28.  
  29.  
  30. // 아래와 같은 즉시 실행 함수 패턴((function(){ ; }());)은 표현식 안의 모든 코드를 지역 유효 범위로 묶으며(무의미한 전역변수 생성을 막는다), 함수를 선언과 동시에 실행하게 만든다.
  31.  
  32. // 인라인 스크립트
  33. var p = (function prototype() {
  34.    
  35.     var f = function () {
  36.         this.name = 'p'
  37.         return this;
  38.     };
  39.  
  40.     // 명시적 변수 선언을 사용하여 라이브러리 전역 변수 와의 충돌을 피했다.
  41.     var $ = new f();
  42.  
  43.     return $;
  44.  
  45. }());
  46.  
  47. alert($.setName); // function(){ ; }
  48. alert($.name); // j
  49.  
  50. alert(p.name); // p





2. parseInt() 함수를 통한 형 변환 오류


아래와 같이 parseInt() 함수의 매개변수 생략 시 예상치 못한 결과를 반환합니다.

이유는 parseInt() 함수는 매개변수 생략 시 기본적으로 8진수로 형 변환하여 반환하기 때문입니다.


  1. var x = '09';
  2.  
  3. alert(parseInt(x)); // 0(9(8진수 11))
  4. alert(parseInt(x, 10)); // 9(10진수)





3. 네이티브 프로토타입 확장


자바스크립트 네이티브 객체인 Object 프로토타입 맴버 확장 시 생성된 모든 객체는 확장 프로토타입 맴버를 모두 상속받게 됩니다.


즉, 생성되 모든 객체는 확장 프로토타입 맴버에 접근할 수 있다는 얘기입니다.

하지만 이 과정을 통해 객체 상속에서의 문제와 같은 여러 가지 예상치 못한 결과가 나올 수 있습니다.



아래는 그에 대한 예제 코드입니다.


  1. // name, setName(s) 맴버를 가진 a 객체를 생성합니다.
  2. var a = { name: 'x', setName: function (s) { return s; } }
  3.   , ms = [];
  4.  
  5. for (var n in a) {
  6.     // a객체의 맴버를 ms 배열에 추가한다.
  7.     ms.push(n);
  8. }
  9.  
  10. // a 객체에 할당된 맴버(name, setName(s))를 가져옵니다.
  11. alert(ms) // name, setName
  12.  
  13. // a.setName(s) 함수를 호출하고 전달된 매개변수 'test'를 반환받습니다.
  14. alert(a.setName('test')) // test
  15.    
  16. // Object 프로토타입 맴버를 확장합니다.
  17. Object.prototype.setName = function (){ return this.name; }
  18.  
  19. ms = [];
  20. for (var n in a) {
  21.     // a객체의 맴버를 ms 배열에 추가한다.
  22.     ms.push(n);
  23. }
  24.  
  25. // a 객체에 할당된 맴버(name, setName(s))를 가져옵니다.
  26. alert(ms) // name, setName
  27.  
  28. // 전과 동일하게 a.setName(s) 함수를 호출하고 전달된 매개변수 'test'를 반환받습니다.
  29. alert(a.setName('test')) // test
  30.  
  31. // a 객체의 setName(s) 프로토타입 맴버를 삭제 합니다.
  32. delete a.setName;
  33.  
  34. // a 객체의 프로토타입 맴버(setName(s))를 제거하더라도, 프로토타입 체인 구조 상 동일 이름의 맴버로 확장된 Object 프로토타입 맴버(setName()) 함수에 접근할 수 있습니다.
  35. // 즉 Object.prototype.setName() 함수가 호출되며, 객체의 name 맴버값(x)이 반환됩니다.
  36. alert(a.setName()) // x
  37.  
  38. ms = [];
  39. var b = { name: 'y' };
  40. for (var n in b) {
  41.     // b 맴버를 ms 배열에 추가한다.
  42.     ms.push(n);
  43. }
  44.  
  45. // b 객체 맴버(name)와 위의 설명과 같이 확장된 Object 프로토타입 맴버(setName())를 가져옵니다.
  46. alert(ms) // name, setName
  47.  
  48. // Object.prototype.setName() 함수가 호출되며, 객체의 name 맴버값(y)이 반환됩니다.
  49. alert(b.setName()); // y
  50.  
  51.  
  52. // 객체 탐색 시 Object.hasOwnProperty() 함수를 사용하여 자신이 확장한 맴버만 가져올 수 있습니다.
  53. // 즉 a 객체에 포함된 맴버(setName(s) 맴버가 제거된 후)만 가져오며, 확장된 Object 프로토타입 맴버는 가져오지 않습니다.
  54. ms = [];
  55. for (var n in a) {
  56.     if (Object.hasOwnProperty.call(a, n)) {
  57.         ms.push(n);
  58.     }
  59. }
  60.  
  61. // a 객체의 맴버(name)를 가져옵니다.(setName(s) 맴버 함수는 위의 delete 키워드로 인해 삭제 되었음.)
  62. alert(ms) // name





4. 여러개의 변수 선언 시 var 생략법


아래 함수와 같은 연속적인 변수 선언 시 var 키워드를 생략하고 쉼표(,)로 구분하여 선언할 시 코드 가독성을 높일 수 있습니다.


즉 쉼표를 변수 앞에 작성하면 변수의 추가/삭제/주석 처리 시 용이 합니다.


  1. function a() {
  2.     var x = 1;
  3.     var y = 2;
  4.     var z = 3;
  5. }
  6.  
  7. // Do this
  8. function b() {
  9.     var x = 1
  10.       , y = 2
  11.       , z = 3;
  12. }





5. 순회문


순회문은 카운트를 거꾸로 내려가서 0과 비교하는 것이 배열의 length 또는 0이 아닌 값과 비교하는 것보다 빠르게 수행됩니다.


아래는 순회문의 일반적인 처리를 비교한 결과입니다.


  1. var arr = ['1', '2', '3'];
  2.  
  3.  
  4. // 배열의 length를 비교:
  5.  
  6. // 1. 실행 조건을 비교(i < length)
  7. // 2. 실행 조건이 true로 평가되는지 확인하기 위한 비교(i < length === true)
  8. // 3. 증감 연산 한번(i++)
  9. // 4. alert(i);
  10. for (var i = 0, length = arr.length; i < length; i++) {
  11.     alert(i); // 0 ~ 2
  12. }
  13.  
  14.  
  15. // 0과 비교:
  16.  
  17. // 1. 실행 조건이 true로 평가되는지 확인하기 위한 비교(i === true)
  18. // 2. 차감 연산 한번(i--)
  19. // 3. alert(i);
  20. for (var i = arr.length; i--; ) {
  21.     alert(i); // 2 ~ 0
  22. };
  23.  
  24.  
  25. // 1. 배열 검색 한번(arr[i])
  26. // 2. 실행 조건이 true로 평가되는지 확인하기 위한 비교(curr === true)
  27. // 3. 증감 연산 한번(i++)
  28. // 4. alert(i);
  29. for (var i = 0, curr; curr = arr[i]; i++) {
  30.     alert(i); // 0 ~ 2
  31. };
  32.  
  33.  
  34. // 1. 실행 조건이 true로 평가되는지 확인하기 위한 비교(curr === true)
  35. // 2. 차감 연산 한번(i++)
  36. // 3. 산술 연산 한번(length - 1)
  37. // 4. alert(i);
  38. var length = arr.length - 1;
  39. for (var i = arr.length; i--; ) {
  40.     alert(length - i); // 0 ~ 2
  41. };


아래에서 2번째 코드부터는 0과 비교할 시 정방향(일반적인) 정렬을 수행하는 방법입니다.
하지만 이 2가지 코드의 성능상 차이점으로는 배열 검색과 산술 연산에 대한 차이가 존재합니다.

일반적으로 자바스립트에서의 객체와 배열 접근은 다른 연산보다 비교적 더 느립니다. 

즉 산술 연산을 통한 순회 방법이 더 효율적일 거로 생각합니다.




6. 객체 리터널를 활용한 Switch문 대체하기

사용 시 코드 가동성을 높일 수 있으며, 조건 연산과 비교하여 객체 검색이 보다 효율적입니다.

  1. function Swich(n)
  2. {
  3.     switch (n) {
  4.         case 1:
  5.         {
  6.             return n;
  7.             break;
  8.         }
  9.     }
  10. }
  11.  
  12. alert(Swich(1)); // 1
  13.  
  14.  
  15. function objSwitch(n)
  16. {
  17.     return { '1': 1 }[n];
  18. }
  19.  
  20. alert(objSwitch(1)); // 1




7. 배열 순회 시 for - in 순회문 사용하기

자바스크립트 배열은 객체이므로 for-in 순회문을 통한 처리가 가능하지만 이를 통해 코드 가독성이 떨어지며, 성능상 불필요한 비용이 추가되는 단점이 있습니다.

즉, 객체 탐색 시에만 사용하는 것이 효율적이라 생각합니다.

  1. var a = [1, 2];
  2. for (var i in a)
  3. {
  4.     alert(a[i]); // 1, 2
  5. }
  6.  
  7. // 불필요한 코드 사용으로 인해 가독성과 성능이 떨어진다.
  8. var a = [1, 2]
  9.   , length = a.length;
  10.  
  11. for (var i in a) {
  12.     alert(a[--length]); // 1, 2
  13. }
  14.    
  15. var b = {1: 1, 2: 2};
  16. for (var n in b) {
  17.     alert(b[n]); // 1, 2
  18. }




8. 배열과 객체 정의 시 리터널 패턴 사용하기.

- 코드 가독성을 높이기 위해 리터널 패턴을 사용한다.

  1. var a = new Array([1])
  2.   , h1 = new Object()
  3.     h1.a = 1
  4.     h1.b = 2
  5.     h1['c'] = 3
  6.     h1['d'] = 4;
  7.  
  8. alert(a); // 1
  9. alert(h1); // [object Object]
  10.  
  11.  
  12.  
  13.  
  14.  
  15. var b = [1]
  16.  
  17. , h2 = {
  18.     a: 1,
  19.     b: 2,
  20.     'c': 3,
  21.     'd': 4
  22. };
  23.  
  24. alert(b); // 1
  25. alert(h2); // [object Object]




9. 즉시 실행 패턴을 활용한 자바스크립트 모듈화.

즉시 실행 패턴의 () 표현식은 ()안의 모든 코드를 지역 유효 범위로 묶으며(무의미한 전역변수 생성을 막는다), 함수를 선언과 동시에 실행하게 만든다.

  1. var a;
  2.  
  3. (function() {
  4.     alert(a); // undefined
  5. })();
  6.  
  7. a = function () {
  8. }();
  9.  
  10. alert(a); // undefined
  11.  
  12.  
  13. a = (function () {
  14.  
  15.     function $() {
  16.         return this;
  17.     };
  18.  
  19.     $.prototype = {
  20.     };
  21.  
  22.     return $;
  23.  
  24. })();
  25.  
  26. alert(a); // function $(){ return this; };
  27.  
  28.  
  29. a = (function () {
  30.  
  31.     function $() {
  32.         return this;
  33.     };
  34.  
  35.     $.prototype = {
  36.     };
  37.  
  38.     return new $();
  39.  
  40. })();
  41.  
  42.  
  43. alert(a); // object
  44.  
  45.  
  46. // 아래 실행 패턴은 제이쿼리 프레임웍에서 실제 사용중인 패턴이다.
  47. a = (function () {
  48.  
  49.     var $ = function (opt) {
  50.         return new $.fn.init(opt);
  51.     };
  52.  
  53.     $.fn = $.prototype = {
  54.         init: function (opt) {
  55.             return this;
  56.         },
  57.         bind: function () { }
  58.     };
  59.  
  60.     $.fn.init.prototype = $.prototype;
  61.  
  62.     $.ajax = function () {
  63.     };
  64.  
  65.     return $;
  66.  
  67. })();
  68.  
  69.  
  70. alert(a.ajax); // static method
  71. alert(a()); // object
  72. alert(a().bind); // prototype member method
  73.  
  74.  
  75. a = (function () {
  76.  
  77.     var $ = function(opt) {
  78.         return new (function () {
  79.  
  80.             function init() {
  81.                 this.opt = opt;
  82.                 return this;
  83.             };
  84.  
  85.             init.prototype = {
  86.                 bind: function () { }
  87.             };
  88.  
  89.             return init;
  90.         } ());
  91.     };
  92.  
  93.     $.ajax = function () {
  94.     };
  95.  
  96.     return $;
  97.  
  98. })();
  99.  
  100. alert(a.ajax); // static method
  101. alert(a()); // object
  102. alert(a().bind); // prototype member method


AJAX API



이전 포스트에서 우리는 AJAX 대한 정의와 XHR 객체 맴버에 대해 알아보았습니다.


이번 시간에는 그 내용을 바탕으로 AJAX API 코드를 작성하고 코드에 설명해 드리도록 하겠습니다.




아래는 작성된 AJAX API 전체 소스입니다.


  1. var Ajax = (function(win, doc){
  2.    
  3.     var ua = window.navigator.userAgent.toLowerCase();
  4.  
  5.     return { request: function(opt){ return new request(opt); } };
  6.        
  7.     // 요청
  8.     function request(opt)
  9.     {
  10.  
  11.         this.options = {
  12.            
  13.             url: '',
  14.             type: 'html',
  15.             method: 'post',
  16.             headers: {},
  17.             data: {},              
  18.             callback: function(){ ; }
  19.         };
  20.  
  21.         extend.call(this.options, opt);
  22.         start.call(this.options);
  23.    
  24.         return this;
  25.     };
  26.  
  27.    
  28.     function start(){
  29.        
  30.         if (!this.url) return false;
  31.  
  32.         var xhr = getXHR()
  33.           , params = getParamsSerialize(this.data);
  34.  
  35.         var method = this.method = this.method.toLowerCase();
  36.        
  37.         var url = (method === 'get' && params) ? this.url + '?' + params + '&s=' + encodeURIComponent(new Date().toUTCString()) : this.url;
  38.        
  39.         xhr.open(method, url, true);
  40.    
  41.         setHeaders.call(xhr, this.headers);
  42.  
  43.         if (xhr.overrideMimeType) setHeaders.call(xhr, { 'Connection': 'close' });
  44.  
  45.         (function($this){ xhr.onreadystatechange = function(){ handler.call($this, xhr); }; })(this);
  46.  
  47.        
  48.         if (method === 'get') xhr.send(null);
  49.         else if (method === 'post') xhr.send(params + '&s=' + encodeURIComponent(new Date().toUTCString()));
  50.     }
  51.  
  52.  
  53.     // xhr 객체 가져오기
  54.     function getXHR(){
  55.          /*
  56.              ie5 : Microsoft.XMLHTTP,
  57.              ie6+ : Msxml2.XMLHTTP,
  58.              비 IE : XMLHttpRequest
  59.          */
  60.          if (!window.XMLHttpRequest) return new ActiveXObject(ua.indexOf('msie 5') > -1 ? 'Microsoft.XMLHTTP' : 'Msxml2.XMLHTTP');
  61.          else if (window.XMLHttpRequest) return new XMLHttpRequest;
  62.  
  63.     };
  64.  
  65.    
  66.     // 파라메터 직렬화
  67.     function getParamsSerialize(data) {
  68.      
  69.         var ret = [];
  70.  
  71.         for (var p in data) ret.push(p + '=' + encodeURIComponent(data[p]));
  72.  
  73.         return ret.join('&');
  74.     };
  75.  
  76.     // 요청 헤더 추가
  77.     function setHeaders(heders){
  78.  
  79.         for (var h in heders) if (heders[h] !== '') this.setRequestHeader(h, heders[h]);
  80.  
  81.     };
  82.  
  83.     // 응답 헨들러
  84.     function handler(x){
  85.            
  86.         if (x.readyState === 4){
  87.  
  88.             if (error(x.status)) alert('request Error' + x.status);
  89.             else this.callback.apply(x, [getXhrData.call(this, x), x.status]); 
  90.         }
  91.     };
  92.  
  93.  
  94.     // 서버 에러 유/무
  95.     function error(s){
  96.    
  97.         return !s && window.location.protocol === 'file:' ? false : s >= 200 && s < 300 ? false : s === 304 ? false : true;
  98.     };
  99.  
  100.     // 수신받은 결과값 가공 함수
  101.     function getXhrData(x)
  102.     {
  103.  
  104.         var contentType = x.getResponseHeader('content-type')
  105.           , xml = contentType && contentType.indexOf('xml') > -1
  106.           , json = contentType && contentType.indexOf('json') > -1;
  107.  
  108.         var type = this.type.toLowerCase();
  109.  
  110.         if (xml && type === 'xml') return x.responseXML;        
  111.         else if (json  && type === 'json') return eval('(' + x.responseText + ')');
  112.         else if (type === 'text') return x.responseText.replace(/<(\/)?([a-zA-Z]*)(\s[a-zA-Z]*=[^>]*)?(\s)*(\/)?>/g, '');
  113.         else return x.responseText; // html */*
  114.  
  115.     };
  116.  
  117.     // 객체 상속 함수
  118.     function extend(){
  119.        
  120.         var target = this
  121.           , opts = []
  122.           , src = null
  123.           , copy = null;
  124.  
  125.         for (var i = 0, length = arguments.length; i < length; i++) {
  126.            
  127.             opts = arguments[i];
  128.            
  129.             for (var n in opts) {
  130.                 src = target[n];
  131.                 copy = opts[n];
  132.  
  133.                 if (src === copy) continue;
  134.                 if (copy) target[n] = copy;
  135.             }
  136.         }
  137.     };
  138.  
  139. })(window, document);





API 실행 함수


  1. function ajax()
  2. {
  3.     Ajax.request({
  4.         url: 'http://sof.fpscamp.com',
  5.         type: 'text',
  6.         method: 'post',
  7.         header: {
  8.             'content-type': 'application/x-www-form-urlencoded'
  9.         },             
  10.         data: {
  11.             id: 'id1'
  12.         },
  13.         callback: function(data, status){
  14.             //alert(data);
  15.             alert(status);
  16.         }
  17.     });
  18.  
  19.     Ajax.request({
  20.         url: 'http://sof.fpscamp.com',
  21.         type: 'text',
  22.         method: 'post',
  23.         header: {
  24.             'content-type': 'application/x-www-form-urlencoded'
  25.         },             
  26.         data: {
  27.             id: 'id2'
  28.         },
  29.         callback: function(data, status){
  30.             //alert(data);
  31.             //alert(status);
  32.             alert(this.abort);
  33.         }
  34.     });
  35. };







API 맴버 설명:


- 호환 가능한 XHR 객체를 가져오는 함수이며, 즉, 브라우저(버전 포함)에 따라 반환되는 객체가 달라집니다.


  1. function getXHR(){
  2.      /*
  3.          ie5 : Microsoft.XMLHTTP,
  4.          ie6 ~ ie? : Msxml2.XMLHTTP,
  5.          표준 브라우저(IE 브라우저 제외) : XMLHttpRequest
  6.      */
  7.      if (!window.XMLHttpRequest) return new ActiveXObject(ua.indexOf('msie 5') > -1 ? 'Microsoft.XMLHTTP' : 'Msxml2.XMLHTTP');
  8.      else if (window.XMLHttpRequest) return new XMLHttpRequest;
  9.  
  10. };




- 전송 될 데이터 객체를 파라메터 문자열로 직렬화 시키는 함수


  1. function getParamsSerialize(data) {
  2.  
  3.     var ret = [];
  4.  
  5.     for (var p in data) ret.push(p + '=' + encodeURIComponent(data[p]));
  6.  
  7.     return ret.join('&');
  8. };
  9.  




- 요청 HTTP Header 정의 함수


  1. function setHeaders(heders){
  2.  
  3.     for (var h in heders) if (heders[h] !== '') this.setRequestHeader(h, heders[h]);
  4.  
  5. };




- 응답에 대한 상태값을 검증하여 완료 시 전달받은 callback 함수를 호출합니다.


  1. function handler(x){
  2.        
  3.     if (x.readyState === 4){
  4.  
  5.         if (error(x.status)) alert('request Error' + x.status);
  6.         else this.callback.apply(x, [getXhrData.call(this, x), x.status]); 
  7.     }
  8. };




응답에 대한 상태값을 확인 후 에러 유/무를 반환합니다.


  1. function error(s){
  2.  
  3.     return !s && window.location.protocol === 'file:' ? false : s >= 200 && s < 300 ? false : s === 304 ? false : true;
  4. };




- 전달받은 요청 타입과 응답 Content-Type을 비교하여 해당 Content-Type으로 가공하여 반환합니다.


  1. function getXhrData(x)
  2. {
  3.  
  4.     var contentType = x.getResponseHeader('content-type')
  5.       , xml = contentType && contentType.indexOf('xml') > -1
  6.       , json = contentType && contentType.indexOf('json') > -1;
  7.  
  8.     var type = this.type.toLowerCase();
  9.  
  10.     if (xml && type === 'xml') return x.responseXML;        
  11.     else if (json  && type === 'json') return eval('(' + x.responseText + ')');
  12.     else if (type === 'text') return x.responseText.replace(/<(\/)?([a-zA-Z]*)(\s[a-zA-Z]*=[^>]*)?(\s)*(\/)?>/g, '');
  13.     else return x.responseText; // html */*
  14.  
  15. };





AJAX 란?



AJAX(Asynchronous JavaScript and XML)란? 



정의: 


AJAX(Asynchronous JavaScript and XML)는 동적 웹 애플리케이션을 구현하는 데 필요한 기술입니다. 


또한, 그와 관련된 모든 전송 처리는 비동기로 이루어지며, 이 방식은 거의 모든 브라우저에서 제공하는 XMLHttpRequest 객체를 통해 구현됩니다.


하지만 XMLHttpRequest 객체는 W3C 표준이 아니므로 브라우저마다 설계 방식에 대한 차이가 존재합니다. (다행히도 현재 대부분의 브라우저가 XHR 객체를 서로 비슷하게 구현하고 있다.)





AJAX 사용 시 장/단점



장점: 


1. 비동기 처리로 페이지 새로고침 없이 받은 데이터를 가공하여 DOM을 조작할 수 있다.


2. 갱신시킬 페이지에 소량의 데이터 블록만 서버로부터 수신하기 때문에 서버 및 네트웍 부하를 줄일 수 있다.




단점:


1. 거의 모든 브라우저가 XHR 객체를 지원하고 있지만 그렇지 않은 경우도 존재하므로 호환성 문제가 야기된다.


2. 비동기 요청은 페이지 URL을 따로 생성해서 가져가야 한다. 하지만 그를 보완하기 위한 방법으로 하나의 URL을 가공하여 각기 다른 형식의 데이터로 응답할 수 있다.


3. AJAX 비동기 요청은 보안 정책으로 크로스 도메인 접근이 허용되지 않는다.





기존 동기 처리 방식:






비동기 처리 방식:








XMLHttpRequest 객체 맴버:




1. void open(string method, string url, boolean asynch): HTTP 요청 함수


  1. xhr.open(method, url, true);


method: 서버에 데이터를 전송하기 위한 메시지 전송 방식을 지정하거나 반환합니다. 지정 가능한 method로는 head, trace, put, delete, connect가 있습니다.

url: 서버로 전송되는 주소를 지정합니다.

asynch: 비동기 / 동기 요청




2. void setRequestHeader(string header, string value): 요청 HTTP Header 값 지정


  1. xhr.setRequestHeader(key, value);


** 반드시 xhr.open() 메소드 호출 후 지정해야 한다.


header: 헤더 key

value: 헤더 value




3. onreadystatechange (응답 이벤트): xhr.readyState 상태값 변경 시 마다 이벤트에 할당된 handler를 호출한다.


  1. (function($this){ xhr.onreadystatechange = function(){ handler.call($this, xhr); }; })(this);


  1. function handler(xhr){
  2.        
  3.     if ( xhr .readyState === 4){
  4.  
  5.         if (error( xhr .status)) alert('request Error' +  xhr .status);
  6.         else this.callback.apply( xhr, [getXhrData.call(this,  xhr ),  xhr .status]); 
  7.     }
  8. };




아래는 xhr.readyState 상태값을 나타냅니다.


0: uninitialize: open 메소드가 호출되지 않은 상태


1: loading: send 메소드가 호출되지 않은 상태


2: loaded: send 메소드가 호출된 상태, header 와 status 사용 가능 / status와 헤더는 도착하지않은상태


3: interactive: reponseText 에 부분적인 데이터가 저장 됨


4: completed




4. void send(string arg): 서버 전송함수(arg: GET 전송 방식인 경우 null / POST 전송 방식인 경우 해당 파라메터 문자열 지정)


  1. if (method === 'get') xhr.send(null);
  2. else if (method === 'post') xhr.send(params + '&s=' + encodeURIComponent(new Date().toUTCString()));




5. xhr.responseText / responseXML: 응답 데이터


  1. function getXhrData(x)
  2. {
  3.  
  4.     var contentType = x.getResponseHeader('content-type')
  5.       , xml = contentType && contentType.indexOf('xml') > -1
  6.       , json = contentType && contentType.indexOf('json') > -1;
  7.  
  8.     var type = this.type.toLowerCase();
  9.  
  10.     if (xml && type === 'xml') return x.responseXML;        
  11.     else if (json  && type === 'json') return eval('(' + x.responseText + ')');
  12.     else if (type === 'text') return x.responseText.replace(/<(\/)?([a-zA-Z]*)(\s[a-zA-Z]*=[^>]*)?(\s)*(\/)?>/g, '');
  13.     else return x.responseText; // html */*
  14.  
  15. };


xhr.responseText: 반환된 일반 텍스트 문자열

xhr.responseXML: 반환된 XML - DOM 객체




6. xhr.status: 응답 코드


  1. // 서버 에러 유/무
  2. function error(s){
  3.  
  4.     return !s && window.location.protocol === 'file:' ? false : s >= 200 && s < 300 ? false : s === 304 ? false : true;
  5. };


200 계열 - 요청성공 / 200 : 요청성공

300 계열 - Redirect

400 계열 - 클라이언트측 오류 / 403 : 접근거부 / 404 : 페이지없음

500 계열 - 서버측 오류 / 500 : 서버오류


xhr.statusText: HTTP 응답 상태를 나타내는 문자열




7. void abort(): HTTP 요청을 취소



8. string getAllResponseHeaders(): 모든 헤더 정보를 반환






Form 유효성 검사 API


이전 포스트 주제인 FORM 태그에 관한 내용 중 태그에 관한 정의는 아래와 같다고 말씀드렸습니다.



정의: "웹상에서 사용자 정보를 입력하는 여러(text, button, checkbox, file, hidden, image, password, radio, reset, submit)방식의 영역을 제공하며, 사용자로부터 할당된 데이터를 서버로 전송하는 역활을 담당한다."



즉, 사용자가 입력한 데이터를 서버로 전송하여 사용한다는 말입니다.



하지만 사용자가 입력한 데이터에 대한 유효성 검사 없이 서버로 전송한다는 것은 사용자 접근성 및 데이터 무결성을 보장할 수 없는 방법이며, 만약 클라이언트 단의 유효성 검사를 마쳤다 할지라도 서버단 체크가 이루어지지 않는다면 그것 또한, 데이터 무결성을 보장할 수 없습니다.



즉, 클라이언트 단 검증과 서버 단 검증은 분리시켜 생각해야 한다는 말이며, 클라이언트 단 검증은 그 목적 자체가 사용자 접근성 향상에 있다고 봐도 무방할 것입니다.



아래는 가장 일반적인 형태의 유효성 검사만 다루고 있으므로, 새로운 기능에 대한 확장이 필요한 경우 코드를 추가 / 수정하여 사용하시기 바랍니다.





자바스크립트 소스:


  1. // Forms API
  2.  
  3. var Forms = (function(win, doc){
  4.  
  5.     return function(callbacks){
  6.        
  7.         callbacks = callbacks.constructor === Array ? callbacks : [callbacks];
  8.  
  9.         function init(){
  10.            
  11.             this.forms = document.forms;
  12.             this.$forms = getForms(this.forms, callbacks) || {};
  13.                    
  14.             appendSubmitEvent.apply(this, [this.$forms]);
  15.  
  16.             return this;
  17.         }
  18.  
  19.         Forms.fn = init.prototype = {
  20.            
  21.             getForms: getForms,
  22.             valids:
  23.             {
  24.                 // 빈값 체크
  25.                 empty: function empty(o, opt){
  26.  
  27.                     return new (function(){
  28.                    
  29.                         return function()
  30.                         {
  31.                             this.msg = '값을 입력해주세요.';
  32.                             this.exp = null;
  33.                        
  34.  
  35.                             extend.call(this, opt);
  36.                                            
  37.                             this.test = trim(o.value) !== '' ? true : false
  38.  
  39.                             return this;
  40.                         };
  41.  
  42.                     }());
  43.  
  44.                 },
  45.                
  46.                 // 한글 체크
  47.                 han: function(o, opt){
  48.  
  49.                     return new (function(){
  50.                        
  51.                         return function()
  52.                         {
  53.                             this.msg = '한글만 입력만 가능합니다.';
  54.                             this.exp = /^([가-힣ㄱ-ㅎ]| ){1,}$/i;
  55.  
  56.                             extend.call(this, opt);
  57.  
  58.                             var val = o.value;
  59.  
  60.                             this.test = (val !== '' && this.exp.test(val)) ? true : false
  61.  
  62.                             return this;
  63.                         };
  64.  
  65.                     }());
  66.                 },
  67.                
  68.                 // 영문 체크
  69.                 eng: function(o, opt){
  70.  
  71.                     return new (function(){
  72.                        
  73.                         return function()
  74.                         {
  75.                             this.msg = '영문만 입력만 가능합니다.';
  76.                             this.exp = /^([a-zA-Z]| ){1,}$/i;
  77.  
  78.                             extend.call(this, opt);
  79.  
  80.                             var val = o.value;
  81.  
  82.                             this.test = (val !== '' && this.exp.test(val)) ? true : false
  83.  
  84.                             return this;
  85.                         };
  86.  
  87.                     }());
  88.                 },
  89.                
  90.                 // 문자 체크
  91.                 str: function(o, opt){
  92.                    
  93.                     return new (function(){
  94.                        
  95.                         return function()
  96.                         {
  97.                             this.msg = '문자를 입력해주세요.';
  98.                             this.exp = /^([\w가-힣]| ){1,}$/i;
  99.  
  100.                             extend.call(this, opt);
  101.  
  102.                             var val = o.value;
  103.  
  104.                             this.test = (val !== '' && this.exp.test(val)) ? true : false
  105.  
  106.                             return this;
  107.                         };
  108.  
  109.                     }());
  110.                 },
  111.                
  112.                 // 숫자 체크
  113.                 num: function(o, opt){
  114.  
  115.                     return new (function(){
  116.                        
  117.                         return function()
  118.                         {
  119.                             this.msg = '숫자 입력만 가능합니다.';
  120.                             this.exp = /^([0-9]| ){1,}$/i;
  121.  
  122.                             extend.call(this, opt);
  123.  
  124.                             var val = o.value;
  125.  
  126.                             this.test = (val !== '' && this.exp.test(val)) ? true : false
  127.  
  128.                             return this;
  129.                         };
  130.  
  131.                     }());
  132.                 }
  133.  
  134.                 // 유효성 검사 기능 함수 추가.....
  135.             }
  136.         }
  137.  
  138.         return new init();
  139.  
  140.     };
  141.  
  142.  
  143.     function getForms(forms, callbacks){
  144.  
  145.         var $forms = {};
  146.  
  147.         for (var i = 0, ilength = forms.length; i < ilength; i++){                 
  148.            
  149.             var f = forms[i];
  150.            
  151.             $forms[f.id] = {
  152.                 f: f,
  153.                 inputs: [],
  154.                 pns: [],
  155.                 callback: callbacks[i]
  156.             };
  157.  
  158.        
  159.             for (var k = 0, klength = f.length; k < klength; k++){                                     
  160.                                
  161.                 var cs = f[k].className && f[k].className.split(' ');
  162.  
  163.                 for (var t = 0, tlength = cs.length; t < tlength; t++){
  164.                     if (Forms.fn.valids[cs[t]]){
  165.                         $forms[f.id].inputs.push(f[k]);
  166.                         $forms[f.id].pns.push(cs[t]);
  167.  
  168.                         continue;
  169.                     }
  170.                 }
  171.             }
  172.         }
  173.  
  174.         return $forms;
  175.     };
  176.  
  177.  
  178.     function appendSubmitEvent(forms){
  179.        
  180.         var $forms = forms || {};
  181.  
  182.         for (var n in $forms){
  183.            
  184.             (function($){
  185.  
  186.                 var f = $.$forms[n].f
  187.                   , inputs = $.$forms[n].inputs
  188.                   , pns = $.$forms[n].pns
  189.                   , callback = $.$forms[n].callback;
  190.                
  191.                 bind(f, 'submit', function(e){
  192.                                                    
  193.                     for (var i = 0, length = inputs.length; i < length; i++){
  194.                        
  195.                         var match = $.valids[pns[i]](inputs[i]);
  196.                        
  197.                         if (!match.test){
  198.                             alert(match.msg);
  199.                             inputs[i].focus();
  200.                             return false;
  201.                         }                      
  202.                     }
  203.                    
  204.                     if (typeof callback === 'function') callback.call(f);
  205.  
  206.                 });
  207.  
  208.             })(this);
  209.         }
  210.     }
  211.  
  212.     // 객체 상속 함수
  213.     function extend(){
  214.        
  215.         var target = this
  216.           , opts = []
  217.           , src = null
  218.           , copy = null;
  219.  
  220.         for (var i = 0, length = arguments.length; i < length; i++) {
  221.            
  222.             opts = arguments[i];
  223.            
  224.             for (var n in opts) {
  225.                 src = target[n];
  226.                 copy = opts[n];
  227.  
  228.                 if (src === copy) continue;
  229.                 if (copy) target[n] = copy;
  230.             }
  231.         }
  232.     };
  233.  
  234.  
  235.     function trim(s){
  236.        
  237.         return s.replace(/(\s)/g, '');
  238.     };
  239.  
  240. })(window, document);
  241.  
  242.  
  243. // submit callback 함수
  244. var Member = (function(){
  245.  
  246.  
  247.    return new (function(){
  248.      
  249.        function init(){  
  250.          
  251.            return this;
  252.        }
  253.  
  254.        init.prototype = {
  255.            login: login
  256.        };
  257.  
  258.  
  259.        return init;
  260.  
  261.    }());
  262.  
  263.  
  264.     function login(){    
  265.        
  266.         var id = document.getElementById('loginId');
  267.         var pass = document.getElementById('loginPass');
  268.  
  269.         if (!id.value || !pass.value) this.submit();
  270.     }
  271.  
  272. })();
  273.  
  274.    
  275. // 이벤트 할당
  276. function bind(elem, type, handler, capture)
  277. {    
  278.     type = typeof type === 'string' && type || '';
  279.     handler = handler || function(){ ; };
  280.    
  281.     if (elem.addEventListener){
  282.         elem.addEventListener(type, handler, capture);
  283.     }
  284.     else if (elem.attachEvent){
  285.         elem.attachEvent('on' + type, handler);
  286.     }
  287.  
  288.     return elem;
  289. };
  290.  
  291.  
  292.  
  293. // api 호출
  294. bind(window, 'load', function(){
  295.     // Forms([callback, callback, callback]);
  296.     Forms([
  297.         Member.login,
  298.         Member.login,
  299.         Member.login
  300.     ]);
  301. });
  302.  





HTML 소스:


  1. <form id="login1" method="post" target="login_frm1" action="http://naver.com" enctype="application/x-www-form-urlencoded">
  2.     <fieldset>
  3.         <legend>폼 1</legend>
  4.         <input type="text" id="loginId" name="loginId" value="아이디" class="empty han" />
  5.         <input type="password" id="loginPass" name="loginPass" class="empty class"  />     
  6.     </fieldset>
  7.     <input type="submit" value="폼제출" />
  8. </form>
  9.  
  10. <iframe id="login_frm1" name="login_frm1" width="0" height="0" frameborder="0"></iframe>
  11.  
  12. <!-- 두 번째 폼 -->
  13. <form id="login2" method="post" target="login_frm2" action="http://naver.com" enctype="application/x-www-form-urlencoded">
  14.     <fieldset>
  15.         <legend>폼 2</legend>
  16.         <input type="text" id="loginId" name="loginId" value="아이디" class="han class" />
  17.         <input type="password" id="loginPass" name="loginPass" class="empty class"  />
  18.         <input type="text" id="loginId" name="loginId" value="아이디" class="eng class" />
  19.         <input type="password" id="loginPass" name="loginPass" class="empty"  />
  20.         <input type="text" id="loginString" name="loginPass" class="str"  />
  21.         <input type="text" id="loginNum" name="loginPass" class="num"  />
  22.     </fieldset>
  23.     <input type="submit" value="폼제출" />
  24. </form>
  25.  
  26. <iframe id="login_frm2" name="login_frm2" width="0" height="0" frameborder="0"></iframe>
  27.  
  28. <!-- 세 번째 폼 -->
  29. <form id="login3" method="post" target="login_frm3" action="http://naver.com" enctype="application/x-www-form-urlencoded">
  30.     <fieldset>
  31.         <legend>폼 3</legend>
  32.         <input type="text" id="loginId" name="loginId" value="아이디" class="han class" />
  33.         <input type="password" id="loginPass" name="loginPass" class="empty class"  />
  34.         <input type="text" id="loginId" name="loginId" value="아이디" class="eng class" />
  35.         <input type="password" id="loginPass" name="loginPass" class="empty"  />
  36.         <input type="text" id="loginString" name="loginPass" class="str"  />
  37.         <input type="text" id="loginNum" name="loginPass" class="num"  />
  38.     </fieldset>
  39.     <input type="submit" value="폼제출" />
  40. </form>
  41.  
  42. <iframe id="login_frm3" name="login_frm3" width="0" height="0" frameborder="0"></iframe>





폼 화면:










Dimension API



Dimension API



이전 포스트에서 자바스크립트를 통해 DOM Dimension 수치를 가져오는 방법에 대해 설명하였습니다.



아래는 그와 관련된 함수들을 모듈화한 소스 코드입니다.


  1. var Dimension = (function(win, doc){
  2.        
  3.     return new (function(){
  4.        
  5.         function init(){
  6.                              
  7.             return this;
  8.         };
  9.  
  10.         init.prototype =
  11.         {
  12.             screen: _screen,
  13.             doc: doc,
  14.             scroll: scroll,
  15.             mouse: mouse,
  16.             elem: elem
  17.         };
  18.  
  19.  
  20.         return init;
  21.  
  22.     }());
  23.  
  24.    
  25.     function _screen(){
  26.        
  27.         return {
  28.             w: screen.width,
  29.             h: screen.height
  30.         };
  31.     };
  32.  
  33.     function doc()
  34.     {
  35.         var page = {
  36.            
  37.             w: top.window.scrollMaxX ? top.window.innerWidth + top.window.scrollMaxX : top.document.documentElement.scrollWidth || top.document.body.scrollWidth || 0,
  38.             h: top.window.scrollMaxY ? top.window.innerHeight + top.window.scrollMaxY : top.document.documentElement.scrollHeight || top.document.body.scrollHeight || 0
  39.         }
  40.          
  41.           , win = {
  42.  
  43.             w: top.window.innerWidth ? top.window.innerWidth : top.document.documentElement.clientWidth || top.document.body.clientWidth || 0,
  44.             h: top.window.innerHeight ? top.window.innerHeight : top.document.documentElement.clientHeight || top.document.body.clientHeight || 0
  45.         };
  46.  
  47.         return {
  48.             page: page,
  49.             win: win
  50.         };
  51.     };
  52.    
  53.     function scroll(){
  54.        
  55.         return {
  56.             x: top.window.scrollX || top.window.pageXOffset || top.document.documentElement.scrollLeft || top.document.body.scrollLeft || 0,
  57.             y: top.window.scrollY || top.window.pageYOffset || top.document.documentElement.scrollTop || top.document.body.scrollTop || 0
  58.         };
  59.     };
  60.  
  61.     function elem(elem){
  62.  
  63.         elem = elem || top.document.documentElement;
  64.  
  65.         var round = Math.round
  66.           , pFloat = parseFloat
  67.           , w = round(pFloat(Element.css(elem, 'width'))) || 0
  68.           , h = round(pFloat(Element.css(elem, 'height'))) || 0
  69.           , x = round(pFloat(getElementX(elem))) || 0
  70.           , y = round(pFloat(getElementY(elem))) || 0
  71.           , r = x + w || 0
  72.           , rx = round(pFloat(getParentRelativeX(elem))) || 0
  73.           , ry = round(pFloat(getParentRelativeY(elem))) || 0;
  74.  
  75.        
  76.         return {
  77.             w: w,
  78.             h: h,
  79.             x: x,
  80.             y: y,
  81.             r: r,
  82.             rx: rx,
  83.             ry: ry
  84.         };
  85.     }
  86.  
  87.  
  88.     function getElementX(elem){
  89.  
  90.         elem = elem || top.document.documentElement;
  91.         var x = 0;
  92.  
  93.         while (elem.offsetParent){
  94.            
  95.             x += elem.offsetLeft;
  96.  
  97.             elem = elem.offsetParent;
  98.         };
  99.  
  100.         return x;
  101.     };
  102.  
  103.     function getElementY(elem){
  104.    
  105.         elem = elem || top.document.documentElement;
  106.         var y = 0;
  107.  
  108.         while (elem.offsetParent){
  109.            
  110.             y += elem.offsetTop;
  111.  
  112.             elem = elem.offsetParent;
  113.         }
  114.  
  115.         return y;
  116.     };
  117.  
  118.     // 해당 엘리먼트와 직속 부모 엘리먼의 상대적인 X 위치
  119.     function getParentRelativeX(elem){
  120.    
  121.         elem = elem || top.document.documentElement;
  122.  
  123.         return elem.parentNode === elem.offsetParent ? elem.offsetLeft :
  124.  
  125.         (getElementX(elem) - getElementX(elem.parentNode));
  126.     };
  127.    
  128.  
  129.     // 해당 엘리먼트와 직속 부모 엘리먼의 상대적인 Y 위치
  130.     function getParentRelativeY(elem){
  131.    
  132.         elem = elem || top.document.documentElement;
  133.  
  134.         return elem.parentNode === elem.offsetParent ? elem.offsetTop :
  135.  
  136.         (getElementY(elem) - getElementY(elem.parentNode));
  137.     };
  138.  
  139.  
  140.    
  141.     function mouse(e){
  142.        
  143.         e = top.window.event || e;
  144.  
  145.         var scroll = this.scroll();
  146.  
  147.         // x, y: 현재 마우스 위치
  148.         // rx, ry: 이벤트가 발생한 해당 엘리먼트와 마우스의 상대적인 x, y 위치
  149.         var x = e.pageX || e.clientX + scroll.x || 0
  150.           , y = e.pageY || e.clientY + scroll.y || 0
  151.           , rx = e.layerX || e.offsetX || 0
  152.           , ry = e.layerY || e.offsetY || 0;
  153.  
  154.  
  155.         return {
  156.             x: x,
  157.             y: y,
  158.             rx: rx,
  159.             ry: ry
  160.         };
  161.     };
  162.  
  163.  
  164.  
  165.  
  166. }(window, document));





자바스크립트 Dimension (Screen, Viewport, Scroll, Element, Mouse Event)



자바스크립트 Dimension



이번 포스트에서는 자바스크립트를 이용하여 DOM 상의 모든 위치값을 알아내는 여러 가지 방법에 대해 상세히 알아보도록 하겠습니다.


즉, 자바스크립트 Dimension에 관한 내용이며, 이는 자바스크립트 통한 DOM 조작 시 매우 많이 쓰이는 부분이기도 합니다.


관련 코드및 설명, 해당 영역에 대한 이미지는 아래와 같습니다.




1. Screen


  1. function _screen(){
  2.    
  3.     return {
  4.         w: screen.width,
  5.         h: screen.height
  6.     };
  7. };



Dimension.screen(): 사용자 디스플레이의 가로 / 세로 폭


- 모든 브라우저 지원


screen.width

screen.height











2. Viewport (page, window)


  1. function doc()
  2. {
  3.     var page = {
  4.        
  5.         w: top.window.scrollMaxX ? top.window.innerWidth + top.window.scrollMaxX : top.document.documentElement.scrollWidth || top.document.body.scrollWidth || 0,
  6.         h: top.window.scrollMaxY ? top.window.innerHeight + top.window.scrollMaxY : top.document.documentElement.scrollHeight || top.document.body.scrollHeight || 0
  7.     }
  8.      
  9.       , win = {
  10.  
  11.         w: top.window.innerWidth ? top.window.innerWidth : top.document.documentElement.clientWidth || top.document.body.clientWidth || 0,
  12.         h: top.window.innerHeight ? top.window.innerHeight : top.document.documentElement.clientHeight || top.document.body.clientHeight || 0
  13.     };
  14.  
  15.     return {
  16.         page: page,
  17.         win: win
  18.     };
  19. };



Dimension.doc().page: 스크롤 사이즈를 포함한 페이지 가로 / 세로 폭(ViewPort)


- 파이어 폭스만 지원


top.window.scrollMaxX: 스크롤 X 사이즈

top.window.scrollMaxY: 스크롤 Y 사이즈


top.window.innerWidth(사용자 가시폭(Dimension.doc().win.w)) + top.window.scrollMaxX:  문서의 전체 가로폭

top.window.innerHeight(사용자 가시폭(Dimension.doc().win.h)) + top.window.scrollMaxY  문서의 전체 세로폭



- 모든 브라우저 지원


top.document.documentElement.scrollWidth

top.document.documentElement.scrollWidth


top.document.body.scrollHeight

top.document.body.scrollHeight



Dimension.doc().win: 사용자의 가시폭



- IE9+ 및 표준 브라우저 지원


window.innerWidth 

window.innerHeight



- 모든 브라우저 지원


top.document.documentElement.clientWidth

top.document.documentElement.clientWidth


top.document.body.clientHeight

top.document.body.clientHeight






page:







window:












3. Scroll


  1. function scroll(){
  2.    
  3.     return {
  4.         x: top.window.scrollX || top.window.pageXOffset || top.document.documentElement.scrollLeft || top.document.body.scrollLeft || 0,
  5.         y: top.window.scrollY || top.window.pageYOffset || top.document.documentElement.scrollTop || top.document.body.scrollTop || 0
  6.     };
  7. };



Dimension.scroll(): 스크롤 X / Y 사이즈


- IE 브라우저를 제외한 브라우저 지원


top.window.scrollX

top.window.scrollY


top.window.pageXOffset

top.window.pageYOffset



- 모든 브라우저에서 지원


(DocType(문서타입)의 명시적 선언 유/무에 따라 document.body.scrollTop(Left) 반환 값이 달라짐. 

즉, DocType(문서타입)이 선언되지 않았을 경우는 document.body.scrollTop의 값을 정상적으로 반환하며, 반대의 경우에는 무조건 0을 반환합니다.)





top.document.documentElement.scrollLeft

top.document.body.scrollLeft


top.document.documentElement.scrollTop

top.document.body.scrollTop












4. Element


  1. function elem(elem){
  2.  
  3.     elem = elem || top.document.documentElement;
  4.  
  5.     var round = Math.round
  6.       , pFloat = parseFloat
  7.       , w = round(pFloat(Element.css(elem, 'width'))) || 0
  8.       , h = round(pFloat(Element.css(elem, 'height'))) || 0
  9.       , x = round(pFloat(getElementX(elem))) || 0
  10.       , y = round(pFloat(getElementY(elem))) || 0
  11.       , r = x + w || 0
  12.       , rx = round(pFloat(getParentRelativeX(elem))) || 0
  13.       , ry = round(pFloat(getParentRelativeY(elem))) || 0;
  14.  
  15.    
  16.     return {
  17.         w: w,
  18.         h: h,
  19.         x: x,
  20.         y: y,
  21.         r: r,
  22.         rx: rx,
  23.         ry: ry
  24.     };
  25. }
  26.  
  27.  
  28. function getElementX(elem){
  29.  
  30.     elem = elem || top.document.documentElement;
  31.     var x = 0;
  32.  
  33.     while (elem.offsetParent){
  34.        
  35.         x += elem.offsetLeft;
  36.  
  37.         elem = elem.offsetParent;
  38.     };
  39.  
  40.     return x;
  41. };
  42.  
  43. function getElementY(elem){
  44.  
  45.     elem = elem || top.document.documentElement;
  46.     var y = 0;
  47.  
  48.     while (elem.offsetParent){
  49.        
  50.         y += elem.offsetTop;
  51.  
  52.         elem = elem.offsetParent;
  53.     }
  54.  
  55.     return y;
  56. };
  57.  
  58. // 해당 엘리먼트와 직속 부모 엘리먼의 상대적인 X 위치
  59. function getParentRelativeX(elem){
  60.  
  61.     elem = elem || top.document.documentElement;
  62.  
  63.     return elem.parentNode === elem.offsetParent ? elem.offsetLeft :
  64.  
  65.     (getElementX(elem) - getElementX(elem.parentNode));
  66. };
  67.  
  68.  
  69. // 해당 엘리먼트와 직속 부모 엘리먼의 상대적인 Y 위치
  70. function getParentRelativeY(elem){
  71.  
  72.     elem = elem || top.document.documentElement;
  73.  
  74.     return elem.parentNode === elem.offsetParent ? elem.offsetTop :
  75.  
  76.     (getElementY(elem) - getElementY(elem.parentNode));
  77. };


Element.offsetParent: 


지정된 엘러먼트의 부모 엘리먼트를 가리킵니다.


단, 여기서 부모 엘리먼트들의 포지션이 상대(relative) 또는 절대(absolute) 포지션이 아니라면, .offsetParent 속성값은 무조건 document.body를 가리킵니다.









5. Mouse Event


  1. function mouse(e){
  2.    
  3.     e = top.window.event || e;
  4.  
  5.     var scroll = this.scroll();
  6.  
  7.     // x, y: 현재 마우스 위치
  8.     // rx, ry: 이벤트가 발생한 해당 엘리먼트와 마우스의 상대적인 x, y 위치
  9.     var x = e.pageX || e.clientX + scroll.x || 0
  10.       , y = e.pageY || e.clientY + scroll.y || 0
  11.       , rx = e.layerX || e.offsetX || 0
  12.       , ry = e.layerY || e.offsetY || 0;
  13.  
  14.  
  15.     return {
  16.         x: x,
  17.         y: y,
  18.         rx: rx,
  19.         ry: ry
  20.     };
  21. };


Dimension.mouse(e): 페이지의 현재 마우스 X / Y위치



- IE를 제외한 모든 표준 브라우저


e.pageX, e.pageY


- IE 브라우저


window.event.clientX + scroll.x(스크롤 X 사이즈)

window.event.clientY + scroll.y(스크롤 Y 사이즈)




이벤트가 발생한 엘리먼트와 마우스의 상대 X, Y 거리



- IE를 제외한 모든 표준 브라우저


e.layerX, e.layerY


- IE 브라우저


window.event.offsetX, window.event.offsetY















참고로 아래는 각 종 Dimension 수치를 볼 수 있는 페이지 입니다. 





http://13thparallel.com/archive/viewport/example6.htm




prev 1 2 3 4 5 6 7 8 ··· 12 next