[Info]Tags categorized posts and contents patterns..

[AJAX] Ajax Code E xamples.. [Book] About the book.. [CSS] CSS Code E xamples.. [DB] Sql Code E xamples.. [DEV] All development stor...

레이블이 dom:loaded인 게시물을 표시합니다. 모든 게시물 표시
레이블이 dom:loaded인 게시물을 표시합니다. 모든 게시물 표시

2016년 3월 2일 수요일

[JS]IE에서 dom:loaded(domready Event)의 잘못된 호출..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

포스팅 제목을 짓는데 좀 고민을 했네요. 결과로 보면 제목을 저렇게 짓기는 좀 애매한 부분이 있기는 하지만 해답까지 다 알게 된 후에는 검색할 필요가 없으니 그냥 문제를 겪은 상황으로 제목을 정했습니다. ㅎ

prototype.js를 아주 잘 쓰진 않지만 그래도 어느정도 사용해왔었는데 오늘 처음보는 현상을 겪었습니다. prototype.js에는 dom:loaded라는 이벤트가 있습니다. 이 이벤트는 DOM이 모두 로드되면 발생하는 이벤트이고 보통 많이 쓰는 window.onload와는 약간 다릅니다. window.onload보다는 좀 더 빠른 타이밍이라고 할 수 있습니다. DOM만 다 로드되면 호출되니까요. 저는 prototype.js라서 dom:loaded이지만 통칭 domready Event라고 할 수 있고 대개의 자바스크립트 프레임워크에는 이런 이벤트 리스너가 제공되고 있습니다.

이 이벤트에 대해서 단 한번도 문제를 겪은 적이 없었는데 오늘은 황당한 문제를 겪었습니다. dom:loaded 이벤트가 호출되었는데 엘리먼트를 호출하면 null이 나오면서 못찾는 것이었습니다. 약간의 텀을 주고 나면 당연히 다시 다 로드가 되고요. 즉 dom이 실제 다 로드되지 않았는데 dom:loaded가 호출되었다는 것이지요. 소스가 상당히 꼬여있는 상태였고 그걸 정리할 수도 없는 상황이라 고생을 좀 했는데 어쨌든 이유는 찾았습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<html>  
    <head>  
        <title>Untitled Document</title>  
        <script src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.2/prototype.js"></script>   
        <script type="text/javascript">  
        //<![CDATA[ 
            document.observe('dom:loaded', function() {
                alert($("testElement2"));
            });
        //]]>  
        </script>  
    </head>  
    <body>  
        <div id="testElement1"></div>
        <script type="text/javascript">  
        //<![CDATA[ 
            $("testElement1").innerHTML = "It's New Text.";
        //]]>  
        </script>  
        <div id="testElement2"></div>
    </body>  
</html> 

간단히 보자면 위의 상황이라고 할 수 있습니다.

상황을 설명하자면 DOM중간에 스크립트를 넣어서 DOM이 다 로드되지 않은 상태에서 DOM을 동적으로 핸들링시도한 것입니다. 어찌된 일인지 이렇게 할 경우 실제 DOM이 로드되는 타이밍이 아니라 DOM을 동적으로 핸들링하는 시점에서 dom:loaded가 발생해 버립니다. 물론 IE에서만 그러고(역쉬!!) 파이어폭스, 사파리, 오페라, 크롬 등에서는 위와같은 현상이 발생하지 않습니다.

물론 위와같은 코드는 일단 사용법자체가 잘못되어 있습니다. DOM은 기본적으로 완전히 로드된 후에 다루는 것이 기본이지요. DOM이 load되지 않았는데 다룰려고 잘못하면 실패할 가능성이 높기 때문입니다. 위 코드는 그런 기본적인 룰(강제는 아니죠)을 어긴 것이긴 하지만 실제 사이트 소스들을 보면 동적으로 핸들링은 하지는 않더라도 body곳곳에 script태그를 삽입하는 경우를 많이 보곤 합니다. 현업에서 Obtrusive Javascript를 지향하는 것은 거의 보기가 힘들죠 ㅠ..ㅠ


위 예제는 간단히 볼 수 있게 만든 테스트입니다. dom:loaded가 호출되었지만 반이상정도는 null로 찍히는 것을 볼 수 있습니다. 물론 IE에서 테스트했을 때만 발생합니다. 너무 간단한 DOM이라서 위에 설명한 현상이 잘 발생하지 않아서 하단에 스크립트를 약간 두어서 dom load를 지연되도록 했습니다.(로컬에선 반정도만 발생하는데 확실히 웹상에 올리면 거의 발생하는군요.)

prototype.js쪽의 문제라고 해야할지 IE의 문제라고 해야할지 고민을 많이 했는데 이것저것 찾아보니 역시 IE의 문제(?)군요. IE and “Operation Aborted” 이 포스팅을 보면 해당 문제에 대해서 정리를 상당히 할 수 있습니다. 이 포스팅에 나온대로 MS에서도 비슷한 문제에 대해서 정리된 내용이 있지만(위의 포스팅 내용중에 나와 있습니다.) 위 포스팅내용대로 정확히 맞아떨어지지는 않습니다.

정확한 현상은 DOM이 다 로드되지 않았는데 innerHTML이나 appendChild등을 이용해서 동적으로 핸들링을 시도했을 경우 문제가 발생하며 IE는 이를 알려주지도 않습니다. 정확히 어떻게 해결하는지는 모르겠지만 다른 브라우저에서는 원하는대로 잘 작동합니다. IE 6, 7, 8이 다 그런걸 보면 의도적인 걸까요 ㅠ..ㅠ

document.body.appendChild(document.createElement('div'));

오직 dom로드 종료전에 위 코드처럼 dom핸들링이 연속적으로 사용되었을 때만 error 표시를 해줍니다.

사용자 삽입 이미지

IE 8에서는 아주 친절히 알려줍니다. (그냥 단순 innerHTML만 했을때도 알려주면 좀 좋아.. ㅡ..ㅡ) 제가 다 짠 소스는 아니었지만 역시 일반적으로 권장하는 룰은 따라주는게 좋다는 교훈을 다시한번 되새기게 되는군요. 사실 그냥 window.onload를 쓰면 문제 없습니다. 복잡한 소스에 조금이라도 고급(?)기법을 써보려고 하다보니 꼬인 것이긴 하죠. 덕분에 새로운걸 하나 알았지만요.

2016년 2월 23일 화요일

[JS]prototype.js에서 DOM이 로드되었는지 확인하는 Event 처리하기..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

아마 이런 얘기는 계속 하게 될텐데 여태까지는 HTML안에 속성을 이용해서 자바스크립트를 호출했지만(대표적인 예로 onclick="") 이제는 자바스크립트를 분리해서 HTML 외부에서 이벤트리스너를 등록하는 것이 일반적인 추세이다. 그중에서도 자바스크립트작업을 할 때 가장 시작점이 되는 것이 DOM이 로드되었는지 확인하는 것이다. DOM이 로드된 후에야 이벤트 리스너를 등록하던지 DOM을 핸들링 하던지 할테고 DOM이 로드되기 전에 핸들링하려고 하면 에러메시지나 나올테니까.....

<body load="init()">

위와 같은 형태가 가장 기본적인 형태의 DOM이 로드되는 것을 확인하는 형태이다. 위에 말했듯이 이런 식으로 HTML과 자바스크립트를 혼용하는 것은 보기도 좋지 않고 점점 사용하지 않으려는 추세이다.

window.onload = function() {
    // 초기화 코드
}

이런 형태가 자바스크립트를 이용해서 할 수 있는 DOM이 로드되었는지를 확인할 때 사용하는 코드이다. 이렇게 사용하면 HTML에 자바스크립트 호출을 섞지않고 사용할 수 있다. 하지만 프로토타입은 이벤트 리스너를 등록할 수 있는 메서드를 제공하고 있다.

Event.observe(element, eventName, handler)


이것을 이용해서 DOM이 로드 되었는지를 확인할 수 있는 이벤트를 지원하고 있다.

document.observe('dom:loaded', function() {
    // 초기화 코드
});

dom:loaded라는  이벤트를 프로토타입 1.6부터는 지원해서 DOM 로드완료를 확인할 수 있다. 여기서 dom:loaded는 순수하게 DOM이 로드되었는지만을 확인한다. img등 다른 리소스들은 다 로드되지 않았을 수도 있지만 DOM자체는 모두 로드되었다. 이렇기 때문에 초기화 코드(초기화를 위한 특별한 기능이나 다른 이벤트리스너의 등록....)를 사용하기에는 딱 좋다.

그리고 window.onload의 경우는 한 문서에서 단 한번밖에 사용할 수 없다. 자바스크립트의 입장에서 본다면 window객체에 onload변수라고 볼 수 있기 때문에 2번 정의하면 2번째 정의한 것이 첫번째 것을 덮어써버리고 첫번째 정의한 것은 동작하지 않는다.(변수라고 생각하면 당연한 얘기다...) 하지만 dom:loaded를 사용하면 이런 일이 없다. 정의된 순서대로 여러번 정의하더라도 덮어쓰지 않고 모두 동작한다. 외부 자바스크립트를 많이 불러쓸 경우에 서로 덮어쓰는 문제를 방지할 수 있다.

덧) 별거 아닌 코드인데 할때마다 좀 헷갈린단 말야...