HTML 요소의 위치와 크기 정보를 얻기

자바스크립트 메서드 중 element.getBoundingClientRect() 메서드는 요소의 왼쪽 위 모서리 좌표와 오른쪽 아래 모서리 좌표 값을 가져오는 메서드입니다.

얻은 좌표값은 웹브라우저의 뷰포트 왼쪽 위 꼭짓점을 시작점(0, 0)으로 해서 계산된 값입니다.

아래 그림을 보면 이해가 조금 더 쉽습니다.

뷰포트 기준점을 기준으로 하는 상대 위치가 되기 때문에 화면이 스크롤되면 얻는 좌표값도 변합니다.

좌표를 얻게을 때 왼쪽에 붙어 있는 요소가 0이 아닌 8px를 반환하기도 하는데 이것은 도큐먼트 루트(body 태그)의 기본 마진(margin)이 8px로 설정되어 있기 때문입니다.

도큐먼트 루트의 마진으로 인해 불필요한 8px가 더해지는 문제를 없애고 싶으면 다음처럼 body 태그의 마진 속성을 없애주면 됩니다. 기본 마진 값은 웹 브라우저 종류에 따라, 또는 브라우저 설정에 따라 다를 수 있습니다.

body{
	margin: 0px;
}

대각 좌표를 알수 있으므로 크기 정보도 계산을 하면 알 수 있습니다.

let rect = elem.getBoundingClientRect();
console.log("너비 x 높이:", rect.right-rect.left, rect.bottom-rect.top);//엘리먼트 크기

단, 좌표가 뷰포트를 기준으로 한 좌표이기 때문에 도큐먼트(body 태그) 시작 위치를 기준으로 한 좌표를 얻으려면 마찬가지로 다른 정보를 추가로 얻어서 계산을 해야 합니다.

다음 그림은 도큐먼트 루트(body 태그)의 뷰포트 시작 위치를 기준점으로 한 상대 좌표를 표시합니다. 

document.body.getBoundingClientRect() 메서드로 좌표 위치를 얻을 수 있습니다.

getBoundingClientRect() 메서드로 가져오는 도큐먼트 루트 요소의 좌표에는 마진 값이 제외됩니다.

따라서 앞서 구한 엘리먼트의 상대 좌표값들에서 도큐먼트 루트의 상대 좌표값 중 왼쪽 위 시작 좌표값을 빼면 도큐먼트의 루트의 마진이 제외된 엘리먼트의 절대좌표를 알 수 있습니다.

let bodyRect = document.body.getBoundingClientRect();
let rect = elem.getBoundingClientRect();
console.log("도큐먼트 루트 기준 엘리먼트 위치:", rect.left-bodyRect.left, rect.top-bodyRect.top, rect.right-bodyRect.left, rect.bottom-bodyRect.left);//엘리먼트 절대 위치(마진 제외)

도큐먼트 루트의 마진을 0으로 설정했거나, 마진을 포함한 엘리먼트의 절대 좌표를 알고 싶으면 element.offsetTop, element.offsetLeft 속성으로 얻을 수 있습니다.

도큐먼트 루트(body 태그)를 기준으로 뷰포트 시작 위치까지 스크롤한 크기 정보를 얻으려면 다음 그림처럼 document.documentElement.scrollTop과 document.documentElement.scrollLeft 속성으로 위치 값을 얻을 수 있습니다.

앞서 엘리먼트의 절대 위치를 얻는 offfsetTop/offsetLeft 속성은 도큐먼트 루트의 scrollTop/scrollLeft 값에서 엘리먼트의 상대 좌표를 얻는 getBoundingClientRect() 메서드의 좌표값을 더한 것과 같습니다.

개발자도구 콘솔에 박스의 좌표들을 출력하는 간단한 함수를 만들어서 다음과 같이 출력해 볼 수 있습니다.

박스에는 클릭 이벤트 리스너를 추가해서 페이지를 스크롤하면서 박스를 클릭해서 달라지는 좌표 값을 콘솔에서 확인할 수 있습니다.

<div class="box"></div>

<script>
    document.addEventListener('DOMContentLoaded', ()=>{
        let elem = document.querySelector('.box');
        getInfo(elem);
        elem.addEventListener('click', (e)=>{
            getInfo(e.target);
        })
    });
    function getInfo(elem){
        console.log("document.documentElement scrollTop scrollLeft:", document.documentElement.scrollTop, document.documentElement.scrollLeft);//body 의 스크롤 이동 크기
        console.log("element offsetTop offsetLeft scrollTop scrollLeft:", elem.offsetTop, elem.offsetLeft);//element 의 body 기준 절대좌표
        //bounding client rectangle 은 뷰포트 왼쪽 위 모서리가 (0,0) 인 것을 기준으로 (x,y) 이동 좌표
        let bodyRect = document.body.getBoundingClientRect();
        console.log("body Bounding Rectangle top left:", bodyRect.top, bodyRect.left, bodyRect.bottom, bodyRect.right);//뷰포트 기준 body 상대좌표
        let rect = elem.getBoundingClientRect();
        console.log("element Bounding Rectangle top left bottom right:", rect.top, rect.left, rect.bottom, rect.right);//뷰포트 기준 element 상대좌표
    }
</script>