[모던JS: 브라우저] 문서 Document (4)

KG·2021년 6월 11일
0

모던JS

목록 보기
30/47
post-thumbnail

Intro

본 포스팅은 여기에 올라온 게시글을 바탕으로 작성되었습니다.
파트와 카테고리 동일한 순서로 모든 내용을 소개하는 것이 아닌, 몰랐거나 새로운 내용 위주로 다시 정리하여 개인공부 목적으로 작성합니다.
중간중간 개인 판단 하에 필요하다고 생각될 시, 기존 내용에 추가로 보충되는 내용이 있을 수 있습니다.

요소 사이즈와 스크롤

자바스크립트는 요소의 너비나 높이 같은 기하 정보 관련 프로퍼티를 지원한다. 이런 프로퍼티는 요소를 움직이거나 특정 좌표에 위치시킬 때 사용할 수 있다.

해당 프로퍼티 사용법을 알아보기 위해 하나의 샘플요소를 만들어보자.

<div id="example">
  ...텍스트...
</div>
<style>
  #example {
    width: 300px;
    height: 200px;
    border: 25px solid #E8C48F;
    padding: 20px;
    overflow: auto;
  }
</style>

이로 인해 만들어지는 UI는 아래와 같다.

스크롤바가 생기게 되면 브라우저마다 그 처리가 조금씩 상이하다. 몇몇 브라우저는 콘텐츠 영역 너비 일부를 빌려 스크롤바를 위치시키기 때문이다. 샘플 예시에서도 만약 스크롤바가 없었다면 너비는 300px이었을 것이다. 하지만 스크롤바가 16px을 차지하기 때문에 콘텐츠 영역 너비가 284px로 재조정된 것을 알 수 있다.

1) 기하 프로퍼티

기하 프로퍼티를 그림으로 나타내면 다음과 같다.

기하 프로퍼티의 값은 숫자인데, 그 단위는 픽셀이다. 따라서 기하 프로퍼티는 모두 픽셀 단위로 측정된다. 요소 밖에서부터 안으로 기하 프로퍼티의 세부사항을 살펴보도록 하자.

2) offsetParent와 offsetLeft, offsetTop

잘 쓰이는 프로퍼티는 아니지만 가장 바깥에 위치한 기하 프로퍼티이다. 여기서 사용되는 offset이라는 용어의 기본적인 의미는 요소가 화면에서 차지하는 영역 전체 크기를 나타내는데, 요소의 너비와 높이에 패딩,스크롤바, 테두리를 합친 크기이며 이때 마진은 포함되지 않는다.

offsetParent 프로퍼티는 해당 요소를 렌더링할 때 좌표 계산에 사용되는 가장 가까운 조상 요소의 참조를 반환한다. 조상 요소는 CSS position에 영향을 받기 때문에 반환되는 가장 가까운 조상 요소는 아래 셋 중 하나에 해당한다.

  1. CSS position 프로퍼티가 absolute, relative, fixed, sticky인 가장 가까운 조상 요소
  2. <td>, <th> 또는 <table>
  3. <body>

offsetLeftoffsetTop 프로퍼티는 offsetParent를 기준으로 각각 요소가 오른쪽, 그리고 아래쪽으로 얼마나 떨어져 있는지 나타낸다.

아래 예시에서는 offsetParent<main> 태그이고 offsetLeftoffsetTop은 각각 180px이다.

<main style="position: relative" id="main">
  <article>
    <div id="example" style="position: absolute; left: 180px; top: 180px">...</div>
  </article>
</main>
<script>
  alert(example.offsetParent.id); // main
  // (주의: 문자열 '180px'이 아닌 숫자 180이 반환)
  alert(example.offsetLeft); // 180 
  alert(example.offsetTop); // 180
</script>

다음과 같은 요소를 기준으로는 offsetParentnull이 될 수도 있다.

  • 화면에 보이지 않는 요소(display 프로퍼티가 none이거나 문서 내에 있지 않은 요소)
  • <body><html>
  • position 프로퍼티가 fixed인 경우

3) offsetWidth와 offsetHeight

offsetWidthoffsetHeight는 가장 간단한 프로퍼티이며, 각각 요소의 가장 바깥 부분이 차지하는 너비와 높이를 제공한다. 즉 테두리를 포함한 요소 전체의 사이즈 정보를 제공한다.

샘플 요소를 대상으로 offsetWidthoffsetHeight를 계산하면 다음과 같다.

  • offsetWidth = 390 : 컨텐츠 너비(300) + 테두리 너비(25 * 2) + 패딩 너비(20 * 2)
  • offsetHeight = 290 : 컨텐츠 높이(200) + 테두리 너비(25 * 2) + 패딩 너비(20 * 2)

이때 화면에 표시되지 않는 요소의 기하 프로퍼티 값은 0 또는 null이다. 기하 프로퍼티는 항상 보이는 요소를 대상으로만 계산된다. 따라서 요소 혹은 이 요소의 조상 요소 중 어떤 것이든 화면에 표시되지 않는다면 모든 기하 프로퍼티의 값은 0이 된다. (offsetParent 프로퍼티는 null)

요소를 만들기는 했지만 아직 문서에 삽입하지 않았다던지, 아니면 새롭게 만든 요소가 숨겨져 있다면 기하 프로퍼티의 값은 0과 같다. 이러한 특징을 이용하면 요소의 숨김 상태 여부를 아래와 같은 방법으로 확인할 수 있다.

function isHidden(elem) {
  return !elem.offsetWidth && !elem.offsetHeight;
}

그런데 isHidden 함수는 요소가 화면에 있기는 하지만 사이즈가 0일때 (비어있는 <div> 등) 역시 true를 반환한다는 점을 주의해야 한다.

4) clientTop과 clientLeft

마진(margin)과 달리 테두리(border)는 요소 내에 존재한다. 이때 clientTopclientLeft를 사용하면 테두리의 두께를 측정할 수 있다. 샘플 예시에서 테두리 두계를 계산하면 다음과 같다.

  • clientTop = 25 : 위쪽 테두리 높이
  • clientLeft = 25 : 왼쪽 테두리 너비

하지만 clientTopclientLeft 프로퍼티는 특정 경우에 테두리 높이와 너비와 정확하게 일치하지 않을 수 있다. 이는 사실 엄밀히 말하면 테두리 바깥을 기준으로 한 테두리 안 상대좌표를 나타낸다.

예를 들어 아랍어나 히브리어의 경우에서는 글이 오른쪽에서 왼쪽으로 전개된다. 따라서 이에 따라 스크롤바 역시 대부분 언어와는 달리 왼쪽에 배치가 되는데, 이 경우에는 clientLeft에 스크롤바 너비가 포함된다.

위 그림처럼 이 경우엔 clientLeft는 기존 25에 스크롤 너비 16을 더한 41이 된다.

5) clientWidth와 clientHeight

clientWidthclientHeight 프로퍼티는 테두리 안 영역의 사이즈 정보를 제공한다. 테두리 안에는 콘텐츠 너비와 패딩이 포함되는데 이때 스크롤바 너비는 포함되지 않는다.

먼저 clientHeight의 경우에는 가로 스크롤바가 없기 때문에 테두리 안 영역 전체를 더한 값이 된다. 이는 요소 자체의 높이 200px과 패딩으로 지정된 20px * 2 = 40px의 합으로 총 240px이 된다.

clientWidth의 경우에는 세로 스크롤바로 인해 콘텐츠 너비에 변경이 생긴다. 처음 설정된 콘텐츠 너비는 300px로 지정되었으나 스크롤바의 너비 16px은 제외하기 때문에 이는 284px로 계산된다. 위와 마찬가지로 패딩값을 더한 최종 값은 총 324px이 된다.

따라서 패딩이 없는 경우에 clientWidthclientHeight는 테두리와 스크롤바 안쪽에 있는 콘텐츠 영역의 너비 및 높이와 정확히 일치할 것이다.

6) scrollWidth와 scrollHeight

scrollWidthscrollHeight 프로퍼티는 clientWidthclientHeight 유사한데, 스크롤바에 의해 감춰진 영역까지 모두 포함한다는 점에서 차이가 있다.

  • scrollWidth = 324 : 수평 스크롤바가 없기에 clientWidth와 동일
  • scrollHeight = 723 : 세로 스크롤바에 가려진 부분 모두 포함하는 콘첸트 영역 안쪽 전체의 높이

이러한 특성을 통해 scrollWidthscrollHeight는 요소 크기를 콘텐츠가 차지하는 만큼 늘리고자 할 때 사용할 수 있다.

elem.style.height = `${elem.scrollHeight}px`;

7) scrollLeft와 scrollTop

scrollLeftscrollTop은 가로 스크롤이 오른쪽, 세로 스크롤이 아래로 움직임에 따라 가려진 영역의 너비와 높이를 나타낸다.

세로 스크롤바를 아래로 조금 내린 경우를 가정한 그림을 살펴보자. 이때 scrollHeight에서 scrollTop이 얼마만큼의 영역을 차지하는지 볼 수 있다.

이처럼 scrollTop은 세로 스크롤바에 의해 가려져 보이지 않는 위쪽 콘텐츠의 높이를 말한다.

또 하나 특이한 점은 대부분의 기하 프로퍼티가 읽기 전용인 반면 scrollLeftscrollTop의 경우 변경이 가능하다는 점이다. 스크립트로 이 값을 조정하면 자동으로 요소 내 스크롤 위치가 변하게 된다. 이를 통해 브라우저의 스크롤 위치를 맨위 또는 맨아래 등으로 옮기는 등의 작업 또한 가능하다.

8) CSS를 사용한 너비와 높이

기하 프로퍼티를 이용하면 컨텐츠 영역을 포함한 요소의 사이즈를 얻을 수 있다. 그러나 우리는 이전 챕터에서 getCouputedStyle 메서드를 사용해 CSS가 적용된 요소의 높이와 너비를 구할 수 있음을 살펴보았다. 하지만 CSS를 사용해 너비와 높이에 접근하는 것은 추천하지 않는다.

먼저 CSS widthheight는 다른 CSS 프로퍼의 영향을 받는다. 요소 너비와 높이 계산 방법을 지정하는 box-sizing이 대표적인 예시인데, 따라서 box-sizing이 변경되면 getCouputedStyle을 통해 구한 값이 부정확해질 수 있다.

또한 CSS widthheightauto일 수 있다. 이 경우 화면에 보여지는 요소의 사이즈는 픽셀 단위로 부여되어 있지만 getCouputedStyle을 통해 구해오는 값은 auto가 되기 때문에 픽셀 단위의 숫자값을 구할 수 없다.

또 다른 이유는 스크롤바의 유무이다. 스크롤바가 없다면 정상적으로 동작하는데, 만약 스크롤바가 생기게 되면 의도한 대로 동작하지 않을 수 있다. 스크롤바가 있는 경우 getCouputedStyle을 통해 너비와 높이를 구할 때는 브라우저마다 상이한 결과를 출력한다. 크롬 브라우저의 경우에는 스크롤바 너비를 제외한 진짜 내부 너비를 반환하지만, 파이어폭스 브라우저의 경우에는 스크롤바를 무시하고 CSS로 설정한 너비를 반환한다. 이렇게 브라우저 간 차이로 서로 다른 값을 출력하기 때문에 일정한 값을 보장하는 기하 프로퍼티로 접근하는 것이 권장된다.

브라우저 창 사이즈와 스크롤

브라우저 창이 차지하는 너비와 높이는 어떻게 구할 수 있을까? 스크롤 때문에 보이지 않는 영역을 포함해 문서 전체가 차지하는 너비와 높이에 대한 수요가 있을 수 있다. 이때 자바스크립트를 사용해 페이지를 스크롤 할 수 있을까?

이번 챕터에서는 루트 문서 요소인 document.documentElement(= <html>)에서 지원하는 프로퍼티와 메서드를 살펴보도록 하자.

1) 브라우저 창의 너비와 높이

창이 차지하는 너비와 높이를 알기 위해서는 document.documentElementclientWidthclientHeight를 사용하면 된다.

이때 window 객체가 아닌 document.documentElement를 사용하는 이유가 있다. window 객체는 브라우저 호스트 환경에서 최상단에 위치한 루트 객체이고, 해당 객체에서도 innerWidthinnerHeight를 제공하기 때문에 이를 사용해서 창 크기에 접근할 수 있다.

하지만 스크롤바가 생기는 경우엔 앞서 살펴본 바와 같이 clientWidthclientHeight는 스크롤바가 차지하는 공간을 제외하여 너비와 높이를 계산한다. 따라서 눈에 보이는 문서에서 콘텐츠가 실제 들어가게 될 영역 너비와 높이 값을 계산한다.

그러나 window.inner... 프로퍼티의 경우에는 스크롤바가 차지하는 영역을 포함해 값을 계산한다. 따라서 스크롤바가 있는 경우엔 실제 콘텐츠 영역이 차지하는 사이즈와 다른 값을 가지게 된다. 보통 창 사이즈가 필요한 경우엔 스크롤 바 안쪽에 무언가를 배치하거나 그리는 경우가 많기 때문에, 창 크기를 구하는 목적이 이와 부합한다면 document.documentElement를 사용하는 것이 좋다.

기하 관련 프로퍼티는 DOCTYPE이 명시되어 있지 않은 경우 제대로 동작하지 않는 경우가 있다. 따라서 항상 HTML 문서에 이를 정의해주는 것이 필요하다.

2) 문서의 너비와 높이

이론상 document.documentElement는 문서의 루트 요소에 상응하고 루트 요소엔 콘텐츠 전부가 들어가기 때문에 문서 전체 크기는 document.documentElementscrollHeightscrollWidth를 사용하면 구할 수 있을 것으로 보인다.

그런데 전체 페이지를 대상으로 했을 때 document.documentElement 프로퍼티는 간혹 예상과는 다르게 동작할 때가 있다. 크롬, 사파리, 오페라에서 스크롤이 없는 경우에는 document.documentElement.scrollHeightdocument.documentElement.scrollWidth 보다 작은 경우가 종종 발생한다. 같은 값이어야 함에도 불구하고 미세한 차이가 있을 때가 있다.

따라서 정확한 문서 전체 높이 값을 얻으려면 아래 여섯 프로퍼티가 반환하는 값 중에 최댓값을 구해야 한다.

let scrollHeight = Math.max(
  document.body.scrollHeight, document.documentElement.scrollHeight,
  document.body.offsetHeight, document.documentElement.offsetHeight,
  document.body.clientHeight, document.documentElement.clientHeight
);

3) 스크롤 정보 얻기

DOM 요소의 현재 스크롤 상태(스크롤에 의해 가려진 영역에 대한 정보)는 scrollLeftscrollTop 프로퍼티를 통해 구할 수 있다.

대부분의 브라우저에서 문서의 스크롤 상태는 document.documentElementscrollLeft 또는 scrollTop을 이용해 구할 수 있다. 다만 구버전 Webkit을 기반으로 하는 브라우저에서는 버그 때문에 documet.documentElement가 아닌 document.body를 사용해야 원하는 값을 구할 수 있다.

따라서 스크롤 포지션 정보를 위해 브라우저별 예외처리까지 모두 고려해야 하는 생각이 들 수 있다. 그러나 다행히 window 객체에서 제공하는 pageXOffsetpageYOffset을 사용하면 브라우저 상관없이 스크롤 정보를 구할 수 있다.

alert('세로 스크롤에 의해 가려진 위쪽 영역 높이: ' + window.pageYOffset);
alert('가로 스크롤에 의해 가려진 왼쪽 영역 너비: ' + window.pageXOffset);

4) scrollTo, scrollBy로 스크롤 상태 변경

다음 메서드를 사용하기 위해선 한 가지 전제 조건이 필요하다. 자바스크립트를 사용해 스크롤을 움직이려면 DOM이 완전히 만들어진 이후에 적용되어야 한다.

일반 요소의 스크롤 상태는 scrollTop 이나 scrollLeft로 쉽게 변경할 수 있다. 페이지 전체의 스크롤 상태 역시 document.documentElementscrollTop/scrollLeft를 사용해 변경할 수 있다.

다만 사파리의 경우에는 document.bodyscrollTop/scrollLeft를 사용해야 한다.

그러나 이보다 더 편하고 브라우저 상관없이 쓸 수 있는 대안이 있다. 위에서 언급한 바와 마찬가지로 브라우저 호스트 환경의 최상단 객체인 window 객체를 이용하는 것이다. window 객체에서는 다음 두 가지 메서드를 제공한다.

  • window.scrollBy(x, y)
    • 페이지의 스크롤 상태를 현재 포지션을 기준으로 상대적으로 조정
    • scrollBy(0, 10)을 호출하면 문서의 스크롤 상태를 현재를 기준으로 아래로 10px 내린 것처럼 동작한다
  • window.scrollTo(pageX, pageY)
    • 페이지의 절대 좌표를 기준으로 페이지 스크롤 상태 변경
    • 눈에 보이는 영역의 왼쪽 위 모서리의 좌표가 문서 전체의 왼쪽 위 모서리를 기준으로 (pageX, pageY)가 된다.
    • 따라서 scrollLeft/scrollTop 값을 변경한 것 처럼 이동
    • scrollBy(0, 0)을 호출하면 스크롤 상태를 처음 상태로 이동

5) scrollIntoView

elem.scrollIntoView(top)을 호출하면 전체 페이지 스크롤이 움직여 elem 요소가 눈에 보이는 상태로 변경된다. 이때 사용되는 매개변수는 Boolean 값을 받는데 이에 따라 다른 동작을 보여준다.

  • top = true : 디폴트 값이며, elem이 페이지에 가장 상단에 보이도록 스크롤 상태 변경
  • top = false : elem이 페이지 가장 하단에 보이도록 스크롤 상태 변경

6) 스크롤 막기

때에 따라 문서의 스크롤바를 고정해두고 조작이 불가능하도록 막아야 할 경우가 생길 수 있다. 사용자가 반드시 읽어야 하는 메시지를 노출시켜야 하는 경우 또는 모달 창이 뜨는 경우 등이 그러한 경우이다.

이럴 때 단순하게 document.body.style.overflow = 'hidden'을 사용할 수 있다. 이는 화면에 보이는 영역을 넘어가는 부분은 모두 숨김처리를 함으로써 스크롤바를 없애는 기법이다. 스크롤바가 제거되기 때문에 당연히 스크롤 이동을 막을 수 있다. DOM 객체 프로퍼티 중 style을 조정하는 것이기 때문에 document.body 뿐만이 아니라 다른 요소에도 동일하게 적용할 수 있다.

하지만 이 방법은 스크롤바가 사라지기 때문에 해당 공간을 채우기 위해서 기존 콘텐츠의 위치가 재조정될 수 있다. 이 경우에는 모든 콘텐츠의 위치가 조금씩 바뀌면서 갑자기 움직이는 현상이 보인다. 이러한 현상은 사용자의 UX를 해칠 수 있다.

따라서 개발자는 스크롤바를 고정시키기 전과 후의 clientWidth 값을 비교해서 해당 증상을 보정해야 한다. 스크롤바가 사라질 때 clientWidth 값이 커지는데 이때 스크롤바가 차지했던 영역만큼 document.bodypadding을 주는 등의 기법으로 콘텐츠 전체의 너비를 스크롤바가 사라지기 전과 같은 값으로 유지할 수 있다.

좌표

앞서서 요소의 크기와 스크롤 바를 이동시키는 방법들을 살펴보며 몇몇 메서드에서는 좌표(coordinates)가 사용되는 것을 볼 수 있었다. 따라서 요소를 움직이기 위해서는 좌표에 대해 익숙해져야 한다.

자바스크립트에서 메서드는 다음 두 좌표 체계 중 하나를 이용한다.

  1. 창 기준 : position: fixed와 유사하게 창(window) 맨 위 왼쪽 모서리를 기준으로 좌표 계산
    • 해당 좌표는 clientX/clientY로 표시
  1. 문서 기준 : 문서(document) 최상단(root)에서 position: absolute를 사용하는 것과 비슷하게 문서 맨 위 왼쪽 모서리를 기준으로 좌표 계산
    • 해당 좌표는 pageX/pageY로 표시

스크롤을 움직이기 전에는 창의 맨 왼쪽 모서리가 문서의 맨 위 왼쪽 모서리와 정확히 일치한다. 그런데 스크롤이 움직이면서 문서가 이동하면 문서 기준 좌표는 변경되지 않지만, 창 내의 요소는 움직이기 때문에 창 기준 요소 좌표가 변경된다.

  • pageY : 문서 기준 좌표는 문서 맨 위부터 계산되므로 스크롤 전후 값이 동일
  • clientY : 문서가 스크롤 되면서 해당 지점이 창 상단과 가까워 졌기 때문에 좌표 변동 발생

1) getBoundingClientRect로 요소 좌표 얻기

elem.getBoundingClientRect() 메서드는 elem을 감싸는 가장 작은 네모의 창 기준 좌표를 DOMRect 클래스의 객체 형태로 반환한다.

DOMRect의 주요 프로퍼티는 다음과 같다.

  • xy : 요소를 감싸는 네모의 창 기준 X,Y 좌표
  • widthheight : 요소를 감싸는 네모의 너비, 높이(음수도 가능)
  • topbottom : 요소를 감싸는 네모의 위쪽 모서리, 아래쪽 모서리의 Y 좌표
  • leftright : 요소를 감싸는 네모의 왼쪽 모서리, 오른쪽 모서리의 X 좌표

그림을 통해 x, y, width, height 만으로 네모 영역을 완전히 묘사할 수 있다는 점을 알 수 있다.

elem.getBoundingClientRect()를 사용할 때 주의사항은 다음과 같다.

  • 좌표는 10.5처럼 소수일 수 있다. 브라우저는 좌표 계산에 소수를 사용하기 때문에 이는 정상이다. 따라서 style.left/top을 사용할 때 값을 반올림할 필요가 없다.
  • 좌표는 음수일 수 있다. 페이지가 스크롤 되어 elem이 창 위로 밀려났을 때 elem.getBoundingClientRect().top은 음수가 된다.

right, bottom 좌표는 CSS position 스타일 속성과 다르다. 위 그림을 보면 그 차이를 한 눈에 알 수 있는데, CSS position의 경우는 right가 오른쪽으로 부터의 거리를 말하는 반면 좌표에서의 right는 항상 왼쪽 위 모서리를 기준으로 떨어진 거리를 말한다.

2) elementFromPoint(x, y)

document.elementFromPoint(x, y)를 호출하면 창 기준 좌표 (x, y)에서 가장 가까운 중첩 요소를 반환한다.

아래 예시를 실행하면 창 정중앙에 있는 요소의 태그가 붉은색으로 변하는 것을 볼 수 있다. 이때 창 정중앙에 있는 요소는 현재 스크롤 위치에 따라 달라질 수 있다.

let centerX = document.documentElement.clientWidth / 2;
let centerY = document.documentElement.clientHeight / 2;

let elem = document.elementFromPoint(centerX, centerY);

elem.style.background = 'red';

이때 창 밖에 있는 좌표를 대상으로 elementFromPoint를 호출하면 null이 반환된다. 해당 메서드는 항상 (x,y)가 보이는 영역 안에 있을 때만 동작한다.

3) 요소를 창 내 특정 좌표에 고정

좌표는 대부분 무언가를 위치시키려는 목적으로 사용한다. 요소 근처에 무언가를 표시할 때엔 getBoundingClientRect를 사용해 요소의 좌표를 얻고 CSS positionleft/top/right/bottom과 함께 사용해서 표시할 수 있다.

let elem = document.getElementById("coords-show-mark");

function createMessageUnder(elem, html) {
  // 메시지가 담길 요소를 생성
  let message = document.createElement('div');

  message.style.cssText = "position:fixed; color: red";

  // 좌표 지정 : style 프로퍼티는 px가 함께 기재된 문자열을 입력받음
  let coords = elem.getBoundingClientRect();

  message.style.left = coords.left + "px";
  message.style.top = coords.bottom + "px";

  message.innerHTML = html;

  return message;
}

let message = createMessageUnder(elem, 'hello World!');
document.body.append(message);
setTimeout(() => message.remove(), 5000);

위 예시를 실행시키면 페이지 내에 id="coords-show-mark" 속성을 가지고 있는 HTML 요소가 있다면 그 하단 부에 hello World! 텍스트가 5초간 출력되는 것을 확인할 수 있다.

하지만 이때 스크롤을 하게 되면 메시지가 요소와 떨어지는 것을 볼 수 있다. 왜냐하면 메시지 요소가 position: fixed이기 때문에 페이지가 스크롤 되더라도 메시지의 위치는 창 기준으로 적용되어 동일한 위치에 머무르기 때문이다. 스크롤을 하더라도 계속 요소 밑에 메시지가 위치할 수 있게끔 하려면 position: absolute를 활용할 수 있다.

4) 문서 기준 좌표

문서 기준 좌표는 창이 아닌 문서 왼쪽 위 모서리부터 시작한다. CSS와 비교하면 창 기준 좌표는 position: fixed에 해당하고 문서 기준 좌표는 맨 위 기준 position: absolute와 비슷하다고 볼 수 있다.

따라서 문서 내 특정 좌표에 무언가를 위치시키고자 할 땐 position: absolutetop, left 등의 속성을 이용해 스크롤 이동에 상관없이 한 좌표에 머물게 할 수 있다. 그러려면 먼저 정확한 좌표를 얻어야 한다.

그런데 요소의 문서 기준 좌표를 제공하는 표준 메서드가 아직 없다. 하지만 두 좌표 체계를 통해 쉽게 관련 수식을 구해줄 수 있다.

  • pageY = clientY + 문서에서 세로 방향 스크롤에 의해 밀려난 부분의 높이
  • pageX = clinetX + 문서에서 가로 방향 스크롤에 의해 밀려난 부분의 너비

따라서 다음의 함수 getCoords(elem)elem.getBoundingClientRect()를 사용해 창 기준 좌표를 얻어와 여기에 스크롤에 의해 가려진 영역의 너비 및 높이를 더해준다.

// 문서 기준 좌표 반환
function getCoords(elem) {
  let box = elem.getBoundingClientRect();
  
  return {
    top: box.top + window.pageYOffset,
    right: box.right + window.pageXOffset,
    bottom: box.bottom + window.pageYOffset,
    left: box.left + window.pageXOffset
  };
}

getCoords(elem) 함수를 통해 문서 기준 좌표를 구하고 이를 통해 해당 메시지를 문서 기준으로 고정시키도록 위에서 구현한 createMessageUnder 함수를 다음과 같이 수정해주자.

function createMessageUnder(elem, html) {
  let message = document.createElement('div');
  message.style.cssText = "position:absolute; color: red";

  let coords = getCoords(elem);

  message.style.left = coords.left + "px";
  message.style.top = coords.bottom + "px";

  message.innerHTML = html;

  return message;
}

이는 좌표 체계 기준으로만 배치를 해결하려는 시도에서의 접근 방안이다. 단순히 요소 노드를 하나 생성하여 HTML 문서 내 하단 태그로 배치시킨다면 간단하게 해결할 수도 있다.

References

  1. https://ko.javascript.info/document
profile
개발잘하고싶다

0개의 댓글