얼마전 알바로 들어온 회사에서 프로젝트를 살짝 끝냈다.
개발자의 필수코스인 주말출근에 밤도 새면서(ㅠㅠ) 열심히 만들었는데,
예전엔 mootools같은 js라이브러리를 썼다가 안쓰고 만들려니, 크로스브라우징이 내 발목을 잡았다.

이번 프로젝트의 목표 브라우저는 Internet Explorer(이하 ie)와 Firefox(이하 ff).
그런데 내가 짠 자바스크립트가 둘중하나에서 동작을 안했던 경우가 있어
다른 개발자분들의 삽질을 조금이나마 줄여보고자. 이 포스트를 남긴다.


첫째. 동적으로 input을 생성할때.

우선 element를 만드는 명령어는 널리 알려져있다.
var input = document.createElement('input');

이렇게 하면 input라는 태그가 만들어진다.
<input />가 만들어진다고 생각하면 된다.

그런데 ie에서는 아래 명령어가 먹지않는다.
input.type = 'hidden';

그래서 해결하고자 ie는 아래와 같은 방법을 썼다.
var input = document.createElement('<input type="hidden" />');

단, 조심할것은 위 코드는 ie에서만 되는것 같다. ff에서는 확실히 안된다.



둘째. 공백을 노드로 인식하느냐 안하느냐 하는 문제.

우선 아래와 같은 HTML 코드가 있다고 하자.
<div id="div1">
    <span id="span1">내용1</span>
   <span id="span2">내용2</span>
   <span id="span3">내용3</span>
    ...(span이 동적이라 몇개가 될지 모름)
</div>



위와같은 상황에서. 모든 span에 특정 동작을 줘야할때.
난 보통 아래처럼 코딩한다.
var spans = document.getElementById('div1').childNodes;
for(var i=0; i<spans.length; i++){
    var span = spans.item(i);
    (span에 이짓저짓)
}


그런데 ff에서 문제가 발생했다.
난 이유를 알 수 없었다. 당연히 되야될 것 같았다.

그러나 저 이짓저짓부분에
alert(span.nodeType);

을 넣어보고 원인을 알 수 있었다.

ie에서는 whitespace(공백,엔터,탭 등)만으로 이루어진 공간을 무시하는데 비해
ff에서는 하나의 textnode로 취급하기때문에 생긴 에러였다.

그러니 이짓저짓 부분에 span.id 따위의 접근을 하면 에러가 날수밖에 없었다.
그래서 해결책으로 아래처럼 if를 추가했다.

var spans = document.getElementById('div1').childNodes;
for(var i=0; i<spans.length; i++){
    var span = spans.item(i);
   if(span.nodeType == 1){
       (span에 이짓저짓)
    }
}

저렇게 if문을 넣어놓으면 textnode를 피해갈 수 있다.
참고로 textnode의 타입은 3이다.

이 프로젝트를 하면서 결심했다.
자바스크립트 노가다에 시간을 쓰느니 걍 prototype쓰자. ㄷㄷㄷ

이 포스트가 여러분의 웹표준 개발에 조금이나마 도움이 되길 바라면서....
사용자 삽입 이미지



License
달리 정하지 않는 한, 이 저작물 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
Except where otherwise noted, this content is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 2.0 Korea License
Mootools에는 Tips라는 클래스가 있다.
이 객체는 Drag.Move클래스처럼 new 키워드로 생성만 해주면 작동하는 방식인데
지정된 객체위에 마우스가 올라가면 선언할때 생성된 div를 마우스 옆에 따라다니도록 만든다.
사용법은 아래와 같다.

var myTips = new Tips(Element들이 들어있는 리스트, 옵션);
예)
var myTips = new Tips($$('img.toolTip'), {maxTitleChars: 50, offsets: {'x': 16, 'y': 16} });





하지만 이번에 Tips를 사용하는 중에 스크롤해도 툴팁은 스크롤을 따라오지못하는 문제를 발견했다.
소스를 공개하자면,

1. 툴팁을 띄울 이미지.
<img id="mapimg" class="toolTip" title="클릭하면 큰화면으로 볼 수 있습니다." src="images/handbook/map.jpg">


2. 스크립트(body의 onload에 실행됨)
var myTips = new Tips($$('img.toolTip'), {
     offsets: {'x': 0, 'y': 20}
});


3. css파일에 툴팁스타일 추가(툴팁의 className은 디폴트가 tool-tip이다)
.tool-tip{
     padding: 3px;
     border: 1px solid rgb(102,102,102);
     background-color: rgb(255,255,153);
}


4. imgElement.href를 하면 src의 내용이 나오기때문에 이미지에서도 주소가 나타나는 문제가 있어서 build메소드에 if 추가
(아마 IE문제인듯.)
...
...
build: function(el){
     if(el.getTag() != 'img') el.myTitle = el.href ? el.href.replace('http://', '') : (el.rel || false);
...
...

*변경결과:
사용자 삽입 이미지

변경 전




사용자 삽입 이미지

변경 후




하지만 스크롤시 문제가 발생! 참고로 브라우저는 IE7이다.

사용자 삽입 이미지

스크롤 전

























사용자 삽입 이미지

스크롤 후














원인을 찾아보니 Tips 클래스에서 마우스 이동시마다 툴팁의 위치를 바꿔주는 locate메소드가 문제였다.
코드를 보면,

locate: function(event){
     var win = {'x': window.getWidth(), 'y': window.getHeight()};
     var scroll = {'x': window.getScrollLeft(), 'y': window.getScrollTop()};
     var tip = {'x': this.toolTip.offsetWidth, 'y': this.toolTip.offsetHeight};
     var prop = {'x': 'left', 'y': 'top'};
     for (var z in prop){
          var pos = event.page[z] + this.options.offsets[z];
          if ((pos + tip[z] - scroll[z]) > win[z]) pos = event.page[z] - this.options.offsets[z] - tip[z];
          this.toolTip.setStyle(prop[z], pos + 'px');
     };
     event.stop();
},


위에 진하게 표시한 부분이 현재 스크롤 위치를 알아내는 부분인데,
window.getScrollTop()의 코드를 보니
getScrollTop: function(){
     return this.pageYOffset || document.documentElement.scrollTop;
},

인데, 테스트해보니 document.documentElement.scrollTop은 항상 0을 리턴하고,
document.body.scrollTop을 써야 제대로 리턴이 되는 것이었다.
(이건 혹시 IE7의문제???)
그래서 일단 위의 getScrollTop메소드를
getScrollTop: function(){
     return this.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;
},


로 바꾸어서 body를 먼저 참조하고 undifined일때 documentElement를 참조하도록 했다.
그리고 windows내의 documentElement를 사용하는 모든 메소드에 body참조를 추가했다
(window.getWidth(), window.getHeight(), window.getScrollLeft(), window.getScrollTop() 등등..)

바꾸고나니까 잘 작동한다. 후후후...

하지만,
가면갈수록 싫어지는 IE..
dlrtmvmffhfj wnrdjfk~


License
달리 정하지 않는 한, 이 저작물 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
Except where otherwise noted, this content is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 2.0 Korea License
난 태그를 자바스크립트로 동적 생성할때는 아래처럼 쓰는 버릇이 있었다.

...
var input = document.createElement('input');
input.name = 'id';
input.type = 'text';
form.appendChild(input);
...


MooTools(이하 무툴)를 쓰게 된 이후로는 아래처럼 됐다.


...
var input = new Element('input');
input.setProperties({name: 'id', type: 'text'});
form.adopt(input);
...


이렇게 잘 쓰고 있는데 이상하게 ie6에서만 name이 지정 안되길래 무툴의 소스를 봤더니,


...
setProperties: function(source){
    for (var property in source) this.setProperty(property, source[property]);
    return this;
}

setProperty: function(property, value){
    switch (property){
        case 'class': this.className = value; break;
        case 'style': this.setStyles(value); break;
        case 'name': if (window.ie6){
            var el = $(document.createElement('<'+this.getTag()+' name="'+value+'" />'));
            $each(this.attributes, function(attribute){
                if (attribute.name != 'name') el.setProperty(attribute.name, attribute.value);
            });
            if (this.parentNode) this.replaceWith(el);
            return el;
        }
        default: this.setAttribute(property, value);
    }
    return this;
}
...


이 소스를 보면 name속성 코드가 ie6을 위해 따로 제작되어있다.
하지만 ie6용 코드에서는 자기자신을 바꾸지 않는다. 부모노드를 가진 객체일경우만 새로 생성해서 바꿔준다.
그러나 위에 내가 짠 코드는 속성을 설정한 후에 부모에 붙이기때문에 ie에서 작동하지 않았다..

저 오류를 발견한건 이미 저런 코딩이 몇개 더 있는 상황이라 무툴의 소스를 살짝 고쳐봤다.


setProperty: function(property, value){
    switch (property){
        case 'class': this.className = value; break;
        case 'style': this.setStyles(value); break;
        case 'name': if (window.ie6){
            if (this.parentNode){
                var el = $(document.createElement('<'+this.getTag()+' name="'+value+'" />'));
                $each(this.attributes, function(attribute){
                    if (attribute.name != 'name') el.setProperty(attribute.name, attribute.value);
                });
                this.replaceWith(el);
                return el;
            }else{
                this.name = value;
                break;
            }
        }
        default: this.setAttribute(property, value);
    }
    return this;
}
...


이렇게 바꿔서 잘 되기는 하는데 뭔가 문제가 생길지도..

그런데 이문제는 내 코딩습관이 잘못된건가.. 아님 무툴에서 구현을 잘못한건가...
나도 잘 모르겠다.


License
달리 정하지 않는 한, 이 저작물 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
Except where otherwise noted, this content is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 2.0 Korea License
자바 스크립트 라이브러리중에 MooTools라는게 있다.
알아본 바로는 moo.fx라는 라이브러리를 어떤 이유로 고친 느낌이다.
내가 현재 진행중인 프로젝트에서 살짝 써봤는데
매우 만족하고 있다.

그전까지 난 prototype1.4를 쓰고 있던중에 1.5의 소식을 듣게되었고
그때 mootools의 존재를 함께 알았다.

mootool의 장점은 prototype를 기본적으로 본받으면서 Fx로 시작하는 클래스들이 꽤나 유용하다.

단점은 prototype1.5랑 함께 로드했더니 일부 메소드에서 에러가 난다.
같은 이름의 다른 메소드가 있어서 그런 듯 하다.
둘다 풀버전을 써서 그런건지..
prototype1.5랑 mootool의 모든 기능을 다 쓸수는 없는걸까.
아직은 그다지 불편하지 않으므로 패스...

앞으로는 mootool의 사용법도 차차 올려야겠다.


License
달리 정하지 않는 한, 이 저작물 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
Except where otherwise noted, this content is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 2.0 Korea License

+ Recent posts