본문 바로가기
  • Seizure But Okay Developer
FrontEnd/CSS 관련자료

코드스피츠 76-2 (Box model & Absolute Position)

by Sky_Developer 2019. 10. 8.
  • 개요
  • Box model
  • Box-sizing
  • Box shadow
  • 예제
  • Offset
  • Offset Parent
  • Offset Value
  • 예제

개요

코드스피츠 CSS 수업 2강을 듣고 핵심적인 내용을 정리하고, 추 후에도 보고 기억이 날 수 있도록 기록하기 위해서 정리하였습니다. Box model과 Absolute Position이 주된 내용인데, 한번 같이 살펴보겠습니다.

(이 블로그 내용을 보실 때 코드스피츠 76-2 교안을 같이 보시면 내용 진행에 대한 이해가 더욱 잘 되실 수 있습니다)

 

다음 두 가지 키워드를 염두해두면서 보시면 학습에 더욱 도움이 될 수 있습니다.

  • geometry 단계 : 영역을 잡는 단계
  • fragment 단계 : 그림을 그리는 단계

브라우저 렌더링 시스템은 geometry 단계를 거친 후 fragment 단계를 거칩니다. 즉, 영역을 먼저 잡은 뒤에 그림을 그립니다. 앞으로 설명할 내용들은 geometry 또는 fragment 에 포함되므로 개념이 어느 단계에 속해지는지를 생각하면서 보면 좋습니다.

Box model

Box model 란 무엇일까요? 먼저 개념부터 살펴보겠습니다. Box model이란, 상자의 다른 부분 (여백, 테두리, 패딩 및 내용)이 함께 작동하여 페이지에서 볼 수있는 상자를 만드는 방법을 정의합니다. (MDN 참고)

'CSS layout box 라는 시스템에 들어가는 모든 element 들이 지켜야하는 Box 규격' 이라고 설명합니다. 즉, CSS layout box라는 규격에 들어가는 element 들은 다 Box model을 가지고 있습니다. 

(기존 설명은 CSS layout box 에 대한 설명이 없고 이에 따라 적절한 설명이 되지 않아 새로 수정하였습니다)

 

Box model은 다음 4가지 요소로 구성되어 있습니다.

  • Margin : Border 바깥 영역으로 무조건 투명하게 되어있음. 그러나 값 지정시 실제 공간 또한 확보함.
  • Border : 외곽선을 치는 경계면. 제한없이 크기를 가질 수 있으며 이를 통해 width에 값을 줘서 컨텐츠로도 사용할 수 있음. 또한 background 배경도 줄 수 있음.
  • Padding : Border로부터 Contents 박스를 띄워주는 역할을 하며 내부 Margin 이라고도 부름. Margin과 마찬가지로 투명함.
  • Contents : Box의 내용을 나타내는 영역으로 텍스트나 이미지 등을 배치하여 표현할 수 있음.

여기까지 Box model에 대한 기본적인 개념 및 요소들에 대해서 알아보았습니다.

 

Box-sizing

다음은 Box model의 사이즈 지정에 관한 Box-sizing이라는 개념에 대해서 알 차례인데요, Box-sizing의 개념은 다음과 같습니다.

 

Box-sizing :

  • width와 height를 계산할 때 적용되는 방법을 지정함
  • Border-box, Padding-box, Contents-box 중 어떤 것을 Box의 size 로 삼을 지 마음대로 설정할 수 있도록 해주는 것이 Box-sizing

Box-sizing에는 다음과 같이 3가지 속성이 있습니다.

  • Border-box : 모든 브라우저에서 사용할 수 있도록 구현되어 있음
  • Padding-box : Firefox
  • Contents-box : 모든 브라우저에서 사용할 수 있도록 구현되어 있음.

여기까지 읽었을 때 box sizing의 용도가 감이 잘 안올 수 있습니다. 이는 나중에 예제를 같이 살펴보면서 더 이해하도록 해보겠습니다.

 

Box model을 확립시키고 layout system 을 만든건 Microsoft 였습니다. Microsoft 에서 처음으로 Box sizing 개념을 도입했을 때 원래는 Border-box를 기준으로 width를 계산하였고 그래서 width:100px은 border-box가 100px이라는 의미였습니다. 그러나 CSS 2.1 때 Contents-box가 표준으로 지정되면서 현재 Box sizing의 기본값은 Contents-box로 되었습니다.

 

여기까지가 CSS 2.1 이후 하나도 변하지 않은 고전 Box model 에 대한 내용입니다.

고전 Box model은 CSS layout box를 만들어내기 때문에 자기만의 공간을 차지하고 CSS Normal Flow에서 배치를 결정하거나 위치를 계산할 때 영향을 줍니다. 다른 말로는 geometry 단계에서 영향을 미친다고 볼 수 있겠죠?

 

그러나 그 이후에 확장되어 있는 CSS Module 에서는 CSS layout box에는 영향을 주지 않지만 그림에는 더 많은 외곽선을 그릴 수 있는 스펙을 발표를 하였습니다. 개요 부분에서 언급한 fragment와 연관이 있는데, 이 후 스펙은 fragment 단계에서만 영향을 미치게 되는 것이죠.

 

Box-shadow

더 많은 외곽선을 그릴 수 있는 스펙 중 그 첫번 째가 바로 Box-shadow라는 스펙입니다.

 

Box-shadow : 해당 요소의 테두리를 감싼 그림자를 만들어주는 속성

 

Box-shadow는 그림자를 주기 위해 만든 것으로, 그림자 또한 일종의 상자이며 원래 상자로부터 얼만큼 떨어질 지 offset 값에 'left,top'을 주게 되어 있습니다.

 

box-shadow에는 다음 속성들을 순서대로 지정할 수 있습니다.

  • offset(left, top) : 그림자를 그릴 수 있는 위치를 확보함. 그림자의 기본 크기 값은 원래 상자와 같음.
  • blur : blur 값을 크게 줄수록 shadow가 흐릿해짐
  • spread : 얼만큼 퍼뜨릴 것인가. 크기를 얼마로 만들 것인가를 결정함.

이 속성들을 잘 이용하면 여러 형태를 만들 수 있는데, 예를 들어 blur를 제거하고 offset에 0을 주면 box model의 border와 완전히 똑같은 형태와 색깔을 확인할 수 있습니다.

 

offset: 0, blur: 0을 주었을 때 정확하게 그 상자위치에 spread 값 만큼 퍼져있는 크기의 border를 얻을 수 있습니다.

box-shadow는 border-box 바깥쪽에 그려지므로 box-shadow를 이용하면 border-box 바깥쪽에 외곽선을 하나 더 그릴 수 있습니다. 그러나 box-shadow는 layout 상자에 영향을 끼치지 않으므로 box-shadow를 늘림으로 인해 다른 박스가 밀려나거나 layout의 크기가 커지진 않습니다.

box-shadow는 geometry 단계가 아닌 fragment 단계(그림을 그릴 때만)에서만 영향을 미치기 때문이죠.

 

Box-shadow는 몇 겹이라도 더 만들 수 있고 무지개 형태로도 그릴 수 있습니다.

 

box-shadow를 써서 외곽선을 더 그릴 때는 바깥쪽의 'Margin'으로 영역을 확보해줄 필요가 있습니다. 그래야지만 box-shadow 로 그린 외곽선이 다른 박스에 겹치거나 덮어쓰는 현상을 방지할 수 있습니다.

 

Margin을 설정해줘야지만 다른 박스와 겹치는 현상을 미리 방지할 수 있습니다.

 

속성에 Inset 이라는 것을 주면 Border 안쪽에도 그릴 수 있는데 이것이 Box-shadow Inset 입니다.

Box-shadow Inset은 Padding 영역 일부에 그림을 그립니다.

 

이어서 외곽선을 그릴 수 있는 Outline을 소개하겠습니다. Outline은 Border-box 바깥쪽에 그려지며 앞선 box-shadow, box-shadow inset과 마찬가지로 geometry에 영향을 미치지 않습니다. Outline은 Outline은 Box-shadow와 정확히 똑같은 위치에 그려지지만 Box-shadow보다 위에 그려집니다.

 

outline의 파란색이 box-shadow의 검정색을 덮음

 

이제까지 배운 것을 Geometry, Fragment 개념을 가지고 살펴보면 Geometry에 영향을 미치는 것은 고전 box model이고 Fragment에 영향을 미치는 건 box-shadow, box-shadow inset, Outline 3가지 요소가 있습니다.

 

Box-shadow와 Box-shadow Inset은 border-radius의 영향을 받으므로 border가 곡선으로 그려지면 box-shadow 또한 곡선으로 그려집니다.

그러나 outline 은 border-radius 에 영향 받지 않고 box 그대로 그려집니다. 이를 응용한 기술이 'stitched' 라고 부르는데 잠시 후 살펴보겠습니다.

 

앞서 설명했던 box sizing에 대한 예제를 살펴보겠습니다.

CSS에서 Box-sizing의 기본 값은 Contents-box입니다.

width:100px,height:100px 씩 줬다는 것은 Contents-box의 크기를 잡아준 것입니다. 빨간 박스는 box sizing으로 Contents-box가 설정된 박스로 width, height 값을 제외한 나머지는 margin 이나 padding 값이 더해진 것이죠.

그러나 이와 다르게 width, height에 100px씩 준 값이 이 박스의 전체크기로 잡히길 원한다면 파란 박스와 같이 box-sizing 속성을 border-box로 고치면 됩니다.

 

Contents box를 바라보느냐, border box를 바라보느냐에 따라 똑같이 100, 100을 줬음에도 불구하고 나타나는 모양이 전혀 달라지는 것을 알 수 있죠.

(기본 값인) Contents-box는 Contents 영역의 크기를 100 으로 보존하고 싶을 때 사용하고 border-box는 값 100 안에 전체 크기를 담고 싶을 때 사용합니다. 각각 용도에 맞게 사용하면 됩니다.

 

두 박스 사이에 흰 줄이 생김

빨간박스와 파란박스 사이에 있는 흰줄은 inline 요소를 인식하여 생기는 것입니다. 즉 아래와 같이,

<div></div>

<div></div>

일 때 첫번 째줄의 div와 두번 째줄의 div 간에 '엔터'를 친 공백이 있기 때문이죠. 이를 방지하려면 첫번 째줄의 닫는 div 태그만 두번째 줄의 앞에 넣어주면 됩니다.

 

두 박스 사이에 흰 줄이 사라짐

<div>

</div><div></div>

와 같이 작성하면서 해결이 되었습니다.

 

참고로 background에 색깔을 지정하면 border-box가 있는 영역까지 색칠이 됩니다. border box를 제외하고 그림을 그리는게 아니라 해당 영역까지 포함해서 그림을 그리게 되는 것이죠. fragment 개념을 가져온다면 그림을 그려주는 fragment의 영역은 border-box 까지 인 것입니다. 다음 예제를 통해 border-box까지 그려지는 지 확인해보겠습니다.

background에서 색깔을 지정하고 border 속성을 dash로 설정함

dash를 줌으로써 background 색깔이 border-box까지 그려지는 것을 확인하였습니다.

모던 브라우저의 대부분은 border 색깔에 alpha를 줄 수 있어 밑에 부분이 비치는 것 또한 확인할 수 있고 border를 두껍게 줘서 액자를 만드는 등 border의 style 를 이용해서 다양한 기법들을 표현할 수 있습니다.

 

앞서 얘기한 것중 box-shadow는 border-box 바깥쪽에 그려지고 geometry에는 영향을 미치지 않는다고 얘기했습니다. box-shadow에 관한 예제를 살펴보겠습니다.

 

 

파란박스는 offset과 blur에는 0을 주고 spread와 색깔만 지정하였습니다. 이 때 파란박스는 10px의 border를 가진 box와 똑같아 지는 것을 의미합니다.

 

예제에서 빨간박스가 먼저 그려진 다음 파란박스가 그려지므로 노란 border 가 빨간 박스 위에 올라가는데 이 때 z-index의 우선순위가 어떻게 놓여지는지 볼 수 있습니다 (box-shadow > 빨간박스 )

(z-index : 요소들의 수직 위치를 결정하는 값, 숫자가 클 수록 위로 올라감)

 

많은 사람들이 position이 absolute 또는 fixed 만 z-index로 순서가 있다고 생각하지만 위의 예제는 각 박스들이 inline 요소임에도 불구하고 그려지는데 순서가 있는 것을 보여줍니다. 또 geometry에는 전혀 영향을 끼치지 않고 그림에만 영향을 끼치죠.

 

만약 빨간 박스에 relative를 주면 어떻게 될까요? 빨간박스에 position:releative를 주면 위에 있던 노란색 border가 사라지게 됩니다.

이는 relative 속성을 설정했으므로 relative로 노란색 border위에 다시 그려지기 때문입니다. relative는 normal flow가 그림을 다 그린 이후에 그 위에 떠서 상대위치를 계산해서 그려집니다.

 

**) 이 말인 즉슨 geometry 영역에서 영역을 잡을 때 각 요소에 그려지는 우선 순위 없이 영역이 잡혔다면, fragment 영역으로 넘어오면서 relative 속성으로 인해 그려지는 순서가 바뀌게 되었다 라는 뜻인가요?

아니면 모든 요소들이 static으로 먼저 그려진 다음 relative 로 준 요소들만 다시 static 요소들 위에 그려지도록 한다 라는 뜻인가요?

 

브라우저의 작동은 random하게 움직이지 않습니다. 브라우저의 작동은 Graphics engine과 Rendering engine을 배웠다면 쉽게 이해할 수 있습니다. 스펙에 다 설명이 되어있기 때문이죠.

 

Inset 속성을 주면 border 안쪽에, 즉 padding 과 같은 위치에 그려집니다.

 

bow-shadow의 대단한 점은 콤마(,)를 찍으면 몇개의 레이어라도 그림을 그릴 수 있다는 것입니다. 이렇게 하면 box-shadow로 border 바깥쪽에 그리는 동시에 inset으로 안쪽에도 그림을 그릴 수 있습니다. 총 border를 3개 그릴 수 있게 되는 거죠(바깥쪽, 안쪽, border). 다음 예제에 N개의 레이어가 표현된 박스가 있습니다.

 

 

여기서 주의할 점은 맨 처음의 box-shadow가 그 다음 box-shadow fragment 영역을 가리게 되므로 더 큰 값으로 줘야 그려진 것처럼 표현이 됩니다. 내부에 그려지는 box shadow inset을 봤을 때 green이 외곽에 10px로 그려져있고 yellow는 그보다 더 안쪽에 똑같은 10px 굵기로 나타나 있습니다. 이 때 yellow는 20px로 줘야지만 여러겹의 형태로 위와 같이 그려지게 됩니다.

이처럼 산수적인 계산을 하여 표현을 해야 하는데 box-shadow는 자동 확장이 되는 게 아니라 각각이 겹쳐지기 때문입니다.

 

그러나 주의해야 할 점이 또 하나 있습니다. box-shadow : 0 0 0 10px, 0 0 0 20px ... 이런식으로 속성을 기술했을 때 이후의 속성 값이 앞선 속성 값을 덮어쓰는게 아니라 선언의 반대 순서로 그려집니다. 스택처럼 쌓이기 때문에 제일 마지막에 선언한 것 부터 먼저 그려지는 것입니다. 만약 20px 이 먼저 나오고 10px 이 이후에 나오면 가려지게 됩니다. 이처럼 box-shadow를 쓸 때 주의할 점은 반대로 그려지기 때문에 밑 바닥부터 설계를 해나가야 됩니다.

 

box-shadow는 border-radius 를 따라오기 때문에 border-radius에 값을 줘서 변형이 되면 그 형태를 따라갑니다. 이런 특성을 이용해 별, 당구공, 빛나는 공 등 다양한 그림을 그릴 수 있게 됩니다. border gradation을 이용해 border에 그라데이션을 줘서 이러한 형태를 만들 수도 있습니다.

 

box-shadow에는 animation도 걸 수 있습니다.

 

 

from에서는 border가 0 0 0 인 상태이고 to에서 10px 나 20px로 증가하는 상태. animation은 border 설정을 @keyframes ani 에게 맡기고 animate를 동작시켜서 그림을 그립니다.

 

마지막으로 outline 예제를 살펴보겠습니다. outline은 앞서 얘기한대로 border-box에 영향을 받지 않는데 이 outline을 통해 'stitched' 라는 기술을 사용합니다.

예제에서 outline이 10px, box-shadow는 10px이라 서로 같은 영역을 먹고 있어서 겹치고 있으며 border-radius:15px을 준 상태입니다.

초록색 박스가 outline이고 outline은 border-box의 속성중 하나인 border-radius에 영향을 받지 않으므로 외곽이 직각인 박스로 그려집니다. box-shadow는 border-radius를 따라 외곽이 원형으로 그려지며 마지막으로 border는 border line에 점선으로 그려졌습니다. box-shadow가 outline 을 덮고 자기의 색깔로 채워줬기 때문에 예제 2와 같은 그림이 그려집니다. 이렇게 outline과 box-shadow, border를 응용해 위와 같이 그림을 그린 것을 stitched 라고 부르며 실무 CSS 상에서 직선 탭인데 내부에 곡선 탭을 줄 때 엄청 많이 쓰는 기술 중 하나입니다.

 

이제 Position 모델에 알아볼 차례입니다. Normal Flow를 배울 때 static, relative는 배웠으므로 Absolute, Fixed 에 대해서 알아보겠습니다.

 

CARET POSITION & OFFSET

Caret Position 의 뜻은 텍스트문서에서 text-cursor이 현재 놓여진 위치를 의미합니다.

Offset은 중의적인 뜻을 가진 단어로, CSS에선 left, top 과 같은 속성들을 그룹지어 표현한 것입니다. 어떤 요소가 original position에서 거리를 두고 싶다면 값을 설정하면 됩니다. left:10px 과 같이 주면 원래 위치에서 왼쪽으로 10px만큼 움직입니다.

 

offset 의 값이 실제로 어떻게 계산되어 나오는지에 대해서 알아보겠습니다. 우리는 보통 화면을 만들 때 tag의 포함관계를 만들고 CSS에 속성을 줘서 화면을 구성합니다. 다르게 표현하면 우리의 코드에 맞게 geometry를 확보하고 fragment 를 채우는 것이죠. 이 때 우리는 지시를 내릴 때 완벽하게 내리지 않고 'left에 그려, right에 그려, float로 그려' 처럼 추상적으로 지시를 내립니다.

그러나 추상적인 명령이라도 geometry 계산이 끝나고 나면 fixed number로 바뀌며 pixel 위치가 나오게 됩니다. 즉, 우리가 준 명령은 계산이 끝나고 결과가 나왔을 때 숫자로 바뀌어 있습니다. 이 숫자들을 전부 다 고유명사로 offset이라고 부르는 것입니다. offset 의 개념은 다음과 같이 정의할 수 있습니다.

 

offset : geometry 계산이 다 끝나서 fixed number 체계로 바뀌어 있을 때 숫자

 

이 때의 offset은 참고할 수 있지만 변경할 수 없는 읽기 전용 속성입니다. 왜냐하면 우리의 명령대로 geometry를 이미 다 계산한 결과이기 때문입니다. 변경할 수 없는 이유에 대해선 'frame' 과 재계산 로직을 설명할 때 다시 얘기하겠습니다.

Offset 개념과 더불어 HTML 렌더링 시스템의 동작방식에 대해서 알아보겠습니다. Html, CSS를 짜던간에 이러한 것들은 '화면이 특정 형태로 나오길 바라는' 우리의 희망사항이고 브라우저가 희망사항을 잘 받아들이냐 마느냐는 브라우저 마음입니다. 한 예로, 구형 ie6에는 float를 주면 무조건 padding:3 을 때리는 버그가 있었습니다. 의도와는 다른 형태로 padding에 3이란 값이 붙었지만 이처럼 우리의 희망을 코드로 표현했지만 실제 그림을 그리는 브라우저가 자신의 로직에 따라 '난 이렇게 그릴 껀데?' 라며 그리고 난 결과가 offset인 것입니다.

우리가 할 수 있는 건 희망사항을 적는 것 뿐이며 실제 그림은 브라우저가 우리가 준 요청대로 계산해서 그립니다. 이 때 그림을 그릴 때 사용하는 계산식은 브라우저 마음대로입니다. 우리가 이를 다 통제하고 싶다면 우리만의 수학 알고리즘을 만들고 시스템을 만들어 canvas 2d에 bitmap을 그릴 때 우리 마음대로 정확하게 그 위치에 그릴 수 있습니다. 그러나 HTML 렌더링 시스템을 쓰고 있으므로 이 때는 브라우저의 렌더링 시스템이 계산하는 것을 우리가 지시만 내릴 수 있을 뿐 실제 계산된 값은 모릅니다. 아는 방법은 오직 offset을 조사하는 것 뿐입니다.

여기서 문제가 발생하는데 브라우저는 효율적인 계산을 하기 위해서 계산을 몰아서 하려고 합니다. 즉 브라우저에게는 geometry 계산을 한번에 몰아서 해야지만 유리합니다. 예를 들어서 설명하겠습니다.

굉장히 복잡한 layout에서 어떤 box하나만 건드려도 전체 계산을 다 해야하는 경우가 생기게 됩니다. layout 안에 box 두개가 있을 때, 그 두개가 각각 움직일 때마다 전체 계산을 다시 하고 또 다시하게 되면 전체적인 계산 동작이 굉장히 많아지고 이를 그리는 렌더링 시스템 입장에서 버겁기 때문에 각각의 요소들을 움직이는 것만 따로 모아놨다가 한번에 다시 뭉쳐서 한꺼번에 계산을 하려고 하는 로직을 가지게 되었습니다.

이 때, 이렇게 한번에 묶어서 계산하는 단위를 'Frame' 이라고 부릅니다.

그림을 그릴 때 기존에 변경해야 하는 모든 요소들을 그냥 변경만 시켜놓고 재계산을 한번씩 때려주는 타이밍이 바로 'Frame' 입니다. 그래서 원래는 frame 단위로 한번만 계산을 합니다. 그리고 기존에 내가 변경시켜놨던 것들을 막 쌓아놓고 있다가 한번에 그릴 때 일어나는 일이 'Flush' 라고 합니다. 브라우저는 싸여있던 Queue를 한꺼번에 소진하고 flush 시키면서 전체 재계산을 한번만 하려고 하는 기본적인 구조로 되어있습니다.

 

그래서 어떤 element의 offset을 조사한다면 즉시 재계산을 해야합니다. 왜냐하면 누군가 offset을 요청했을 때 offset을 알려주기 위해선 Queue를 바로 지우고 화면에 아직 갱신이 되던 안됬던 간에 바로 재계산을 다시 해야하기 때문입니다.

그래서 그림을 그리는 로직 사이사이에 한 frame이 아닌데도 불구하고 offset을 요청하면 재계산을 마구마구 하게 됩니다. 그러므로 offset은 왠만하면 요청 안하는게 제일 상책입니다. offset을 요청하면 앞서 얘기했던 최적화 로직이 깨져버리기 때문이죠. 이러한 이유로 offset을 못 쓰게합니다.

 

offset을 마구 불러도 되는 경우는 계산이 다 끝난 element 인 것들, 즉 geometry에 변화가 없는 요소들 뿐입니다. 이들은 offset을 불러봤자 queue에 쌓여있는 것이 없기 때문에 재계산을 하지 않습니다.

Javascript 등에서 사용자가 offset과 관련해 대부분 하게 실수는 layout을 그리기 위해 offset을 받아왔는데, 이 받아온 offset에서 또 layout을 그리며 이러한 행위를 반복적으로 하는 경우입니다. 이러면 queue에 계속 쌓이고 계속 재계산하고 한 frame 그릴 때 전체 계산을 몇번 씩 때리게 되고 엄청나게 느려집니다. offset은 조회 전용이고 layout 계산이 없을 때는 많이 불러도 상관없지만 offset을 바탕으로 layout을 또 다시 계산하는 로직을 짤 때는 조심해야 됩니다.

 

offset을 계산하려면 실제 pixel 값을 계산해야 합니다.

 

Offset Parent

이제 offset parent에 대해서 얘기를 하겠습니다. offset parent의 개념은 한 마디로 어떤 요소의 기준점을 의미합니다.

offset을 계산하는 가장 기본적인 로직은 상대적인 위치로 모든 수치가 기술되어 있기 때문에 어디가 기준점인지 찾는게 가장 우선순위입니다. 예를 들어 left:100, right:100 이렇게 값을 준다면 '기준점으로부터' 라는 뜻이 됩니다. 이러한 기준점을 찾아야지 그림을 그릴 수 있고, 이 때 이 기준점을 Offest Parent 라고 부릅니다.

(내가 실제로 계산됬을 때는 어떤 애를 나의 Offset parent로 보고 그걸 기준으로 내가 그림을 그렸어)

이 때 offset parent는 DOM parent가 아닙니다. 그림을 그려주는 로직과 DOM의 data 적인 구조는 완전히 별개의 것들입니다.

static과 relative의 offset parent 는 DOM parent 일치하느냐하면 꼭 그렇지 만은 않습니다.

이전 강의에서 span안에 block 들어있는 예제에서 처럼 inline 안에 block이 들어있으면 bfc의 offset parent를 계산할 때 바깥쪽에 있는 애를 기준으로 계산하거나 inline 요소를 강제로 끊어내서 위치를 계산함. 이에 더해 relative를 주면 같이 딸려오기도 했습니다. => 정리 필요

 

position:absolute 모델에서는 offset parent를 계산하는 공식이 표준으로 나와있으며 DOM에 있는 부모는 무시합니다. 다음과 같이 offset parent를 계산합니다.

 

offset parent를 계산하는 기준

 

1. NULL

  • Root 요소이거나, Html, body 인 경우 offset parent는 null(즉 존재하지 않음)
  • position:fixed 마찬가지로 offset parent가 없음. 기준은 Chrome, 즉 browser의 bound box(테두리)를 기준으로 그림을 그림.
  • Out of DOM Tree : DOM 트리에서 빼내는 것. 예를 들어 appendChild 메소드로 child를 추가한 것을 remove해서 child를 빼버릴 때 경우 즉시 offset parent가 없어짐.

** 또 createElement로 element를 만들면 이 상태에서 offset parent는 존재하지 않고 append를 해서 DOM tree안에 들어와야지만 offset parent가 생겨납니다. 빈 element 를 만들기만 할 땐 offset parent는 없습니다.

 

2. Recursive search : offset parent를 찾는 방법으로 보통 어떤 element로 부터 기준을 찾기 위해선 재귀적으로 찾아 들어갑니다.

- Parent.position.fixed = null : 내 부모가 fixed 이면 검색이 바로 끝이나서 더 이상 찾지 않습니다. 나의 offset parent 도 null이 됩니다.

- Parent.position.!static = OK : 내 부모가 static이 아니면 OK입니다. OK라는 뜻은 부모의 위치를 계속 찾으러 위로 위로 다닌다는 뜻으로 static이 아닌 부모를 찾을 때까지 계속 부모 위로 올라갑니다.

만약 내가 position:absolute 인데 부모가 static이면 내 부모는 offset parent가 될 자격이 없는 것입니다. 그러면 부모는 최소한 static 은 아니여야 되며 이 때 남은 position은 absolute와 relative 뿐입니다. 그래서 absolute의 offset parent가 될 수 있는 남은 position은 absolute이거나 relative 뿐입니다.

 

position:absolute를 주면 position:absolute의 offset parent 가 될 수 있는 애는 오직 absolute와 relative 밖에 없습니다.

 

이 규칙에 따라 position:absolute일 때 내가 그림을 그리는 기준점 위치는 내 DOM상의 부모가 아니라 내 부모들 중 position:absolute인 부모이거나 relative인 부모가 나의 기준점이 되는 것이고 이들을 만날 때까지 찾아들어갑니다.

 

layout 을 구성하는 데 있어 중요한 스킬에 대한 설명을 하겠습니다.

absolute는 내가 위치를 다 지정해야 하지만 relative는 static으로 위치가 정해집니다. layout을 구상하다보면 static으로 컨텐츠의 흐름따라 자연스럽게 진행하는 도중 특정 부분에서 absolute를 쓰고 싶은 needs를 많이 겪게됩니다. 이 때 relative로 만들면 relative 안에 absolute를 막 집어 넣을 수 있고 내부의 absolute는 relative를 안 뚫고 나가게 되며 원하는 대로 구현할 수 있습니다. relative의 역할은 layout을 잡을 때 static안에 absolute를 넣기 위한 컨테이너로서 사용을 합니다. static으로 자연스럽게 그림을 그리고 있었는데 그 안에 absolute 관련된 것을 넣고 싶을 때 이 absolute의 컨테이너로서 relative를 지정합니다. 그러면 static을 쓰다가 자연스럽게 absolute 또한 사용할 수 있게 됩니다.

이것이 첫번 째로 익혀야 하는 layout 스킬입니다. 이렇게 함으로써 absolute에 left, top 을 줬을 때 relative 컨테이너 안에서 그려지게 할 수 있고 이것이 이 스펙의 교훈입니다.

 

Parent.Position.!Static = OK : position:absolute의 부모가 자격이 될 애는 relative이거나 absolute 여야 함.

 

TD, TH, TABLE = OK

마치 body에서 offset parent를 계산을 한 것처럼 TD 안에 absolute 요소를 집어넣으면 TD가 offset parent가 되는 것이란 의미일 것 같지만, 전혀 그렇게 동작하지 않습니다. 이 부분은 모든 브라우저에서 다 동작하지 않으며 스펙과 다른 부분입니다. offset parent가 되긴 하지만 계산을 해보면 제대로 그려지지 않습니다.

그래서 가장 일반적이고, 정의와 다르면서,거의 모든 브라우저에 적용되는 패치는 TD안에서 다시 div로 relative를 넣어야지만 제대로 동작합니다.

 

offset-parent를 알았다면 브라우저가 실제 계산한 다양한 offset 값들을 읽기 전용(쓰기 전용 X) 으로 참조 할 수 있습니다. 그 값이 Offset Value 입니다.

 

Offset Value

offsetLeft,offsetTop : offset parent로 부터 위치가 얼만큼 떨어져있느냐

offsetWidth, offsetHeight : 실제로 확보한 geometry 크기가 얼마인지 확인

 

offsetScrollTop, offsetScrollLeft : 브라우저가 실제로 그림을 그리고 나니 scroll bar를 만들어지게 되면서 scroll 영역이 생겨버린 것에 대해 scroll의 현재 위치를 알려줌

offsetScrollWidth, offsetScrollHeight : 브라우저가 실제로 그림을 그리고 나니 scroll bar를 만들어지게 되면서 scroll 영역이 생겨버린 것에 대해 scroll의 크기를 알려줌

 

다른 렌더링 시스템에서 scroll 기능은 따로 붙여줘야 지만 만들 수 있습니다. 그러나 html 의 렌더링 시스템은 scroll 기능을 기본으로 내장하고 있어서 overflow 속성만 주면 scroll box가 생깁니다. 이 scroll box는 원래 굉장히 크기가 큰데 좁은데 갖혀서 스크롤 바가 생긴 것입니다. 이러한 이유로 가장 안전한 속성이자 진짜 컨텐츠의 크기는 width, height 를 얻는게 아니라 ScrollWidth, ScrollHeight가 진짜 크기입니다.

 

정리하자면 다음과 같습니다.

  • offsetLeft,offsetTop, offsetWidth, offsetHeight 는 보이는 곳의 영역
  • offsetScrollTop, offsetScrollLeft, offsetScrollWidth, offsetScrollHeight 는 진짜 contents의 영역

 

그림에서 스크롤 영역 위에 점선영역은 offset parent로서 absolute 또는 relative 일 것입니다. 그리고 offsetTop, offsetLeft는 그려지는 영역에서 top, left가 얼만큼 벗어났는지를 보여주고 offsetWidth, offsetHeight는 그려지는 영역의 크기를 나타냅니다. 그리고 scrollbar의 현재 위치가 offsetScroll의 top, left가 됩니다.

scrollTop이 20이면 20 만큼 내려올 것입니다. 만약 scrollbar의 화살표 아래 방향을 눌러서 offsetHeight 만큼 이동하고 싶을 땐 scrollTop:OO 처럼 직접 값을 주는 것으론 속성값을 변경할 순 없습니다. offset 값은 browser가 그린 결과일 뿐이고 이 속성을 직접 수정할 수 없기 때문입니다. 대신 scrollTo 라는 메소드를 사용하여 인자로 y 값을 줘서 원하는 동작을 시킬 수 있습니다.

 

예제

파란박스의 offset-parent는 body 태그

예제의 노란박스는 margin:100px로 줘서 그만큼 밑으로 밀립니다. 이 때 빨간박스에는 absolute만 줬으며 노란박스를 offset parent로 삼은 것처럼 나와있습니다. 마치 static 요소가 자리를 잡을 때의 위치에 있는 것처럼 보이죠. 만약 'A'라는 텍스트 인라인 요소를 빨간박스 블록태그 위에 넣으면 빨간박스가 위치한 자리에 위치할 것입니다.

이어서 파란박스에 left를 줬더니 노란박스는 static 박스이므로 offset parent가 될 자격이 없습니다. 그래서 파란박스는 노란박스 밖으로 벗어나고 이 후 바깥에 있는 body 태그가 offset parent가 됩니다. 이 때 left:0 인데 이는 offset parent인 body를 기준으로 0을 준 것입니다. 이 때의 top은 값을 주지 않았는데, 예제에선 마치 top의 위치가 노란 박스에 static으로 그려진것과 같이 유지하고 있습니다. 많은 사람들이 헷갈려 하는 부분 중 하나가 left와 top을 안 줬을 때 기본값이 offset parent의 0,0 과 같을 것이라 예상하는 것입니다.

 

position:absolute의 기본값은 offset parent와 무관하게 DOM상의 있는 부모의 기본값을 갖고서 그림을 그립니다.

 

빨간 박스는 이와 같은 원리에서 그려진 것입니다. absolute로 줬지만 마치 static을 준 것처럼 그려졌으며 이게 absolute을 줬을 때의 기본 값입니다.

 

position:absolute의 기본값은 static의 위치와 똑같은 기본값을 갖는다.

 

이 때 left 또는 top에 값을 주게 되면 더 이상 static 부모를 바라보지 않고 offset parent를 찾아서 offset parent를 기준으로 계산을 합니다. 파란박스는 top 값을 주지 않았을 때 top을 계산하는 것은 offset parent가 아니라 나의 부모에 있는 top 값을 기준으로 계산을 합니다. 그런데 top 값을 주면 offset parent를 찾아서 그 offset parent를 기준으로 계산을 합니다.

 

top이나 left는 offset parent 를 기준으로 계산하는 속성이다.

 

CSS에 있는 속성은 모두 (숫자 대신 쓰고 있는) 계산 공식입니다.

 

만약 static에게 left, top을 주면 어떻게 될까요? static은 자기의 부모를 계산할 때 normal flow로 그림을 그립니다. 만약 left,top 값을 주면 그냥 무시합니다. static은 normal flow로 그림을 그리기 때문이죠. 각 포지션에 대하여 offset 기본값을 정하는 기준을 정리하면 다음과 같습니다.

  • static일 떄의 left, top : 값 무시
  • absolute일 때의 left, top : offset parent로부터의 거리
  • relative일 때의 left, top : normal flow로 그리고 난 이후의 거리

left, top은 중의적인 의미를 가지고 있으며 CSS는 이러한 중의적인 의미를 많이 가지고 있어 헷갈리는 부분이 많습니다.

 

CSS 속성을 배운다는 것은 마치 강사님이 left, top을 설명했을 때처럼 설명할 수 있고 정확하게 정의할 수 있도록 직접 찾아보고 숙련하는게 좋습니다. 쓸데없는 속성을 지울 수 있고 이렇게 작동할 것이라 예상하여 연습장에 그릴 수 있을 정도가 되야지만 숙달했다고 볼 수 있습니다.

 

Q) position:absolute를 줬는데 float도 주면 어떻게 될까? position이 이길까? float가 이길까?

A) float는 normal flow일 때만 성립한다. 새로운 bfc 영역을 만들어야하기 때문. 그래서 position:absolute로 주는 순간 float는 무의미해짐.

 

예제에서 inline-block을 줬으므로 브라우저의 크기를 늘이거나 줄이면 자연스럽게 위치를 바꿔주며 normal flow로 처리되는 것을 볼 수 있습니다.

 

inline-block : inline이지만 block처럼 box model을 소유할 수 있게 만든 것.

 

(** display model도 공부해야 됩니다. display model은 고전 display model과 modern display model이 있으며 flexbox, gridbox 또한 display 입니다. 최근에는 layout 이라는 속성도 새로 생겼습니다)

 

inline-block은 마치 글자를 넣었을 때와 같이 contents가 많으면 알아서 줄 바꿈이 되고 그림을 그려주는 normal flow의흐름을 가집니다. 이 때 각 점선 박스 안에 그래프나 다른 그림을 넣고 싶다면 어떻게 해야될까요? 다음과 같이 합니다.

우선 absolute 속성을 가진 파란 박스를 각 점선 박스 안에 요소로 DOM 트리 내에 여러개 배치한다면 그려졌을 때 모두 왼쪽 상단 밑에 위치하게 됩니다. 왜냐면 각 점선 박스들은 static이므로 파란박스에 left, top 을 주는 순간 offset parent 인 body를 기준으로 계산을 하기 때문이죠. 해법은 파란박스가 담긴 각각의 점선박스에 position:relative를 주는 것입니다. 각 그림이 담긴 div 태그에 position:relative 속성을 주면 그림이 각 위치에 알맞게 자리를 잡습니다.

 

static안에 absolute가 같이 공생하는 방법이 바로 relative 이다.

 

오늘은 box model, position:absolute, offset system 을 배웠습니다.

 

댓글