블로그 내 검색

2012. 2. 11.

CSS Object Model - clientWidth, offsetHeight, scrollLeft...

자바스크립트로 페이지의 요소를 컨트롤하려면 자주 보는 속성들이 있습니다

innerWidth, clientWidth, offsetHeight, scrollLeft ...

막상 처음 자바스크립트를 활용해서 페이지를 멋지게 꾸미려는사람을 혼돈의 카오스로 인도하는 속성들입니다.

뭐 이렇게 많은지...잘 정리해서 적재 적소에 써먹어 봅시다.

위에 나열한 속성들은 문서의 시각적 요소 (visual view) 에 대한 속성들로서, 사실 오래전부터 브라우저별로 구현되어 있었고 불문율같은 반표준 프로퍼티지만, 정식 표준은 아니며 지금 정규 표준이 제안되고 있다고 합니다.

CSS Object Model

W3C에서는 이렇게 명시하고 있습니다.

This is a draft document and may be updated, replaced or obsoleted by other documents at any time
(이것은 초안이며 갱신되거나 교체 혹은 언제든 무용지물이 될 수 있다.)

하지만 대다수의 브라우저가 지원하고 있고, 실제 쓰이고 있어서 크게 바뀔 일은 없을것 같습니다. http://www.w3.org/TR/cssom-view/에 가면 해당 설명을 확인할 수 있습니다.
영어에 자신이 있는 사람은 이 포스트를 읽기 보다 표준제안 문서의 정보를 참고하길 권합니다.

Window 관련

먼저 윈도우 요소 속성을 나타내는 인터페이스입니다.
(http://www.w3.org/TR/cssom-view/#extensions-to-the-window-interface)
partial interface Window {
    MediaQueryList matchMedia(DOMString media_query_list);
    readonly attribute Screen screen;

    // viewport
    readonly attribute long innerWidth;
    readonly attribute long innerHeight;

    // viewport scrolling
    readonly attribute long scrollX;
    readonly attribute long pageXOffset;
    readonly attribute long scrollY;
    readonly attribute long pageYOffset;
    void scroll(long x, long y);
    void scrollTo(long x, long y);
    void scrollBy(long x, long y);

    // client
    readonly attribute long screenX;
    readonly attribute long screenY;
    readonly attribute long outerWidth;
    readonly attribute long outerHeight;
};
innerHeight, innerWidth
브라우저의 뷰포트(viewport), 즉 사용자에게 보이는 영역을 나타냅니다. 하지만 특정 브라우저에서는 저 속성을 지원하지 않아 직접 구해야 합니다.
document 의 최상위 루트 엘리먼트(즉, html)의 넓이로 뷰포트를 구합니다.

다음과 같은 코드가 나오겠지요.
function getViewport() {      
    if(window.innerWidth !== undefined && window.innerHeight !== undefined) {
        return [ window.innerWidth, window.innerHeight ];
    }
    // 해당 속성이 없을 경우, document.documentElement
    // 혹은 document.body의 치수로 대체
    var doc = document.documentElement || document.body;  
    return [ doc.clientWidth, doc.clientHeight ];
}

지금 이 블로그를 보는 당신의 브라우저의 뷰포트 넓이,높이는 [0, 0]입니다.

scrollX(Y), pageX(Y)Offset
뷰포트가 실제 볼 수 있는 페이지에서 얼마나 이동했는지 알 수 있는 스크롤링 계열 속성들도 필요합니다.
드래그 앤 드롭 구현시에 화면 중앙 레이어 표시, 화면 강제 스크롤링등에 쓰이는 속성 및 함수들도 window 인터페이스에 정의되어 있습니다.
뷰포트가 얼마나 X축으로 스크롤 되었는지 알려주는 scrollX, pageYOffset 두개인데 이것들은 언제나 같습니다.
Y축의 scrollY, pageYOffset도 마찬가지입니다.
하지만 IE는 개성이 강해서 위 두 속성을 쓸 수 없고, 직접 계산으로 구해야 합니다.
function getPageOffset() {
    if(window.pageXOffset !== undefined  && window.pageYOffset !== undefined ) {
        return [ window.pageXOffset, window.pageYOffset ];
    }
    var doc = document.documentElement || document.body;
    return [ doc.scrollLeft, doc.scrollTop ];
}

지금 이 블로그를 보는 당신의 브라우저 뷰포트 스크롤 X,Y 은[0, 0] 입니다.

scroll, scrollTo, scrollBy
scroll(x, y), scrollTo(x, y): 같은 기능으로 윈도우를 해당 좌표로 스크롤 시키는 함수입니다.
scrollBy(x, y): 현재 스크롤에 인자값을 더한 위치로 스크롤시킵니다.



screenX, screenY (혹은 screenLeft, screenTop)
현재 스크린(사용자 기본 출력)에서 브라우저가 위치한 곳을 나타냅니다. IE, opera, safari에서는 screenX, screenY를 지원하지 않고 대신 screenLeft, screenTop를 지원합니다.

지금 이 블로그를 보는 당신의 브라우저의 스크린에서의 X,Y 좌표는 [0, 0]입니다.

outerWidth, outerHeight
브라우저 자체의 크기, 즉 상태바 주소입력창 메뉴 테두리...등을 모두 합한 크기를 리턴하는 속성입니다. 이번에도 IE는 outerWidth, outerHeight를 지원하지 않으므로, 직접 구해야 합니다.
offsetHeight라는 속성은 객체의 테두리(border)까지 합한 실제 크기를 가진 속성입니다.
위의 함수에서 사용한 방법처럼 문서 최상위 루트를 구한 뒤 offsetWidth, offsetHeight 속성으로 구할 수 있습니다.

지금 이 블로그를 보는 당신의 브라우저의 실제 크기는 [0, 0] 입니다.

Screen 관련

다음은 screen 객체의 인터페이스입니다.
(http://www.w3.org/TR/cssom-view/#the-screen-interface)
interface Screen {
      readonly attribute unsigned long availWidth;
      readonly attribute unsigned long availHeight;
      readonly attribute unsigned long width;
      readonly attribute unsigned long height;
      readonly attribute unsigned long colorDepth;
      readonly attribute unsigned long pixelDepth;
};
출력 장치. 즉, 모니터가 됩니다.

availWidth, availHeight : 윈도우 작업표시줄을 제외한 실제 스크린 크기
width, height : 실제 스크린 크기
colorDepth, pixelDepth : 스크린에서 색상, 해상도 수치를 반환.

0, 0, 0, 0

Element 관련

다음은 HTMLElement에 대한 것들입니다.
(http://www.w3.org/TR/cssom-view/#extensions-to-the-element-interface)

친숙한 프로퍼티가 많습니다.
이 포스트를 쓰게 된 원인중 하나인 clientLeft, offsetWidth 이런 것들을 가진 태그들의 속성이죠.

본래 W3C문서에는 Element와 HTMLElement를 구분짓고 있으나 편의상 합쳐 쓰겠습니다.
HTMLElement는 Element를 상속한 인터페이스지만, IE에서는 (IE8버전기준) HTMLElement를 완벽히 지원하고 있지 않습니다.
interface Element {
 
    ClientRectList getClientRects();
    ClientRect getBoundingClientRect();
 
    // scrolling
    void scrollIntoView(optional boolean top);
 
    attribute long scrollTop;   // scroll on setting
    attribute long scrollLeft;  // scroll on setting
 
    readonly attribute long scrollWidth;
    readonly attribute long scrollHeight;
 
    readonly attribute long clientTop;
    readonly attribute long clientLeft;
    readonly attribute long clientWidth;
    readonly attribute long clientHeight;
 
    readonly attribute Element offsetParent;
    readonly attribute long offsetTop;
    readonly attribute long offsetLeft;
    readonly attribute long offsetWidth;
    readonly attribute long offsetHeight;
};

getBoundingClientRect()
자신의 오프셋 정보를 객체형태로 반환합니다. 이때 계산은 뷰포트에 대한 상대 위치입니다.
top, bottom, left, right, width, height를 가지고 반환됩니다.

scrollIntoView()
스크롤을 이 메서드를 실행한 엘리먼트가 보일 때까지 스크롤 해 줍니다.

scrollWidth, scrollHeight
엘리먼트가 부모의 overflow 속성 지정 등에 가려져 실제 크기가 보이지 않더라도(스크롤바 등으로 감춰져 있더라도) 그 크기를 나타내 줍니다.

cilentTop, clientLeft
offsetTop 속성, 즉 기준이 되는 오프셋과 실제 박스의 left, top 수치의 차이, border의 픽셀값과 같습니다.

clientWidth, clientHeight
실제 객체의 크기를  나타냅니다.
크기 계산 시 padding은 포함하고, margin, border, 스크롤바는 제외합니다.

offsetTop, offsetLeft
offsetParent로 찾을 수 있는 객체에 대해 얼마나 떨어져 있는가 계산된 수치.
offsetParent는 부모의 position이 static이 아닌 속성일 때입니다.
부모중에 위치 지정 포지션(position: static이 아닌 것)이 없을 땐 offsetParent는 body를 기준으로 계산됩니다.

offsetWidth, offsetHeight
offsetParent로 찾을 수 있는 객체에 대해 상대적으로 크기가 계산된 수치입니다.
쉽게, 스크롤바와 border까지 포함한 개체의 크기라고 생각하시면 됩니다.


부록
아래 DIV 영역의 width, height는 200px 이며, border는 10px입니다.
안 이미지의 width, height는 300px 이고 margin, padding, border 는 0px 입니다.
버튼을 눌러보세요.

* 모바일, 동적 뷰 환경 등 특수한 환경에서는 이미지 크기가 자동조절되거나 스크롤바가 나오지 않아 원하는 대로 테스트가 되지 않을 수 있습니다.
















참고 이미지입니다.
출처는 인터넷에 떠도는 이미지를 가져왔습니다.


댓글 없음:

댓글 쓰기