CSS Position Schemes- Float
이번 주제는 CSS Position Schemes의 두 번째 방식인 float이다. float가 생성될 때 발생하는 4가지 일을 보면서 float가 box요소의 위치를 계산하는 방식을 이해해 보겠다. 원래 float가 overflow 속성을 가진 요소와 함께 그려질 때의 특이 사항도 다루어야 하지만 사정상(너무 길다…) 해당 내용은 다음에 다루도록 하겠다. 이번에 다루는 내용들은 코드스피츠 76 CSS Rendering의 강의를 정리한 것이다.
Float가 생성되면 발생하는 일
어떤 element가 float 요소가 되면 다음의 4가지 일이 발생한다.
new bfc: 해당 float 요소부터 새로운 bfc가 생성된다.
float over normal flow: float로 인해 새로 생성된 bfc에 normal flow로 다른 block 요소가 그려질 수 있다. 이 경우 normal flow로 그려진 block 요소 위에 float 요소가 겹쳐서 그림이 그려진다.
text, inline guard: float로 인해 새로 생성된 bfc 이후에 생성된 inline 요소의 경우 float 요소와 겹쳐서 그려지지 않고 float 요소가 차지하는 영역만큼 밀려서 그려진다.
line box: float는 새로운 bfc를 생성하지만 bfc 방식으로 위치가 결정되지 않는다. line box 방식으로 위치가 계산된다.
이제부터는 float로 인해 발생하는 각각의 일들을 살펴보도록 하겠다.
New BFC and Float Over Normal Flow
See the Pen bfc+float by psy082 (@psy082) on CodePen.
위의 예제를 보자. red box이후에 그려지는 green box는 float 요소가 되었다. 따라서 앞의 red box와 동일한 bfc에서 그려지는 게 아니라 앞의 bfc와는 다른 새로운 bfc가 시작된다. 따라서 float 요소는 앞에 그려진 red box와는 겹치지 않지만(bfc가 다르기 때문에) 새로 생성된 bfc에 포함된 sky box와 겹쳐서 그려진다. float의 의미대로 float 요소인 green box가 sky box위에 떠 있는 것이다.
※ 좀 더 생각해볼 점: 따라서 float 요소를 다단으로 여러개 만들면, 그 때마다 새롭게 bfc가 생성되기 때문에 브라우저의 계산량이 늘어난다.
그렇다면, float로 인해 새롭게 생성된 bfc 영역의 크기는 얼마만큼일까?(normal flow에서 bfc의 크기는 부모요소의 너비와 block요소들의 높이 값들의 합이었다.) 바로 float 영역 전체와 inline 영역 전체가 차지하는 공간 끝까지이다.
Text, Inline Guard
See the Pen bfc+float ex2 by psy082 (@psy082) on CodePen.
위의 예제를 보자. HELLO는 green box가 float가 아니었더라면 green box 다음 줄에 위치할 것이다. 그러나 green box가 float가 되면서 새로운 bfc가 시작되었고, HELLO의 등장으로 새로운 ifc가 다시 시작되지만 green box가 float이기 때문에 green box가 차지하는 만큼 HELLO가 밀려서 그려진다. 재밌는 점은 뒤에 그려진 sky box의 경우, float가 block요소의 경우에는 guard로 작동하지 않기 때문에 겹쳐서 그려진다. 그러면서 동시에 sky box 안의 WORLD라는 text는 inline 요소이기 때문에 inline guard에 걸려서 또 float요소가 차지하는 만큼 밀려서 그려진다. 정리하자면 float요소는 float요소 내부의 inline요소가 아니라면 inline요소와 겹쳐서 그려지지 않는다. 즉, float는 inline 요소에 대하여 guard로 작동한다. 따라서 WOW를 제외한 다른 문자들은 green box와 겹치지 않게 그려진 것이다.
Line Box
float 요소의 위치는 line box라는 개념을 사용하여 그려진다. 아래 예제를 보면서 float가 어떻게 그려지는 지 살펴보겠다.
See the Pen float Line Box by psy082 (@psy082) on CodePen.
혹시 위의 코드를 읽는 것만으로도 그림이 어떻게 그려질 지 예상할 수 있다면, 이 글을 읽지 않아도 된다.(그런 분은 애초에 이 글을 읽으려고 하지 않으시겠지…) 하나씩 하나씩 단계별로 그림이 어떻게 그려지는 지 살펴보자. 아래의 모션은 위의 float 요소들이 단계별로 어떻게 그려지는 지를 보여주는 모션이다. 기본적으로 float 요소들은 line box 내부를 순서대로 차지하면서 그려진다.
line box는 bfc 전체 영역을 차지한 상태이다. 1 번째 green box는 float: left이기 때문에 line box 제일 왼쪽에 붙어서 위치한다. 즉, 현재의 line box의 왼쪽 200px을 차지한다. green box가 왼쪽 200px을 차지함에 따라서 line box의 너비가 300px로 줄어 들었다.(높이는 상관이 없다.)
2 번째 yellow box는 float: right이기 때문에 너비 300px의 line box 오른쪽 50px을 차지한다. line box의 너비가 250px로 줄어들었다.
3 번째 yellow box가 float: right이기 때문에 line box 오른쪽 50px을 차지한다. line box의 너비가 200px로 줄어들었다.
4 번째 green box가 float: left이기 때문에 line box 왼쪽 150px을 차지한다. line box의 너비가 50px로 줄어들었다.
5 번째 yellow box가 float: right이기 때문에 line box 오른쪽 150px을 차지해야 겠지만 line box의 너비는 50px밖에 되지 않는다. 그려야 하는 float 요소가 line box보다 큰 경우 line box의 영역이 변한다. line box의 영역을 바꿀 때는 line box의 top을 변화시키는데, 현재까지 그려진 float 요소들 중에서 height 값이 제일 작은 요소의 height 값이 새로운 line box의 top값이 된다. 따라서 현재까지 그려진 float 요소들 중에서 height 값이 제일 작은 4 번째 green box의 50px이 바뀔 line box의 top 값이 된다. 새롭게 구성된 line box는 이전 line box에서 50px 아래로 내려와 너비 200px이 되었다. 5 번째 yellow box는 새롭게 구성된 line box의 오른쪽 150px을 차지한다. line box의 너비가 50px이 되었다.
6 번째 green box가 float: left이기 때문에 line box 왼쪽 150px을 차지해야 겠지만 5 번째 경우와 동일하게 너비가 50px밖에 되지 않으므로 line box를 새로 구성한다. 새로 line box를 구성하기 위해 height 값이 가장 작은 요소인 3 번째 yellow box의 height가 선택되었다. 그러나 새롭게 구성된 line box의 너비도 50px밖에 되지 않는다.(5 번째 yellow box가 3 번째 yellow box보다 더 아래까지 그려졌기 때문이다.) 따라서 line box를 또 새롭게 구성한다. 다음으로 height 값이 작은 5 번째 yellow box의 height값을 기준으로 구성한 line box의 너비는 250px이다. line box내에 6 번째 green box를 그릴 수 있으므로 왼쪽 150px을 차지하도록 그린다. line box의 너비가 100px이 되었다.
7 번째 green box가 float: left이기 때문에 line box 왼쪽 150px을 차지해야 하지만, line box가 100px이므로 이번에도 line box를 새롭게 구성해야 한다. 2 번째 yellow box의 높이가 제일 낮으므로(1 번째 green box도 높이가 2 번째 box와 동일하지만 line box는 기본적으로 float: left요소와 float: right 사이에 위치하도록 구성되기 때문에 1 번째 green box가 선택되지 않고 2 번째 yellow box가 선택된다.) 2 번째 yellow box 아래에 구성되는 line box의 너비가 150px이므로 7 번째 green box를 그릴 수 있다. 7 번째 green box를 그리고 line box의 너비가 0px이 되었다.
float 요소들이 그려지는 과정을 보면 알 수 있는 특징이 있다.
line box가 계속해서 float: left와 float: right 사이에 구성된다.(빈 공간은 left와 right 사이에만 유효하다.)
따라서 float: right는 float: left 보다 왼쪽에 그리지 않고, float: left는 float: right보다 오른쪽에 그리지 않는다.(물론 다른 높이에서 float: right 보다 float: left가 더 오른쪽에 그려지도록 할 수는 있다. 이 경우는 같은 높이의 경우를 말한 것이다.)
※ 마지막으로 오해하면 안되는 점
float 요소가 생성되면 새로운 bfc가 생성된다고 했다. 그렇다면 앞의 예제에서는 7개의 float요소들이 있기 때문에, 7개의 bfc가 생성되었다고 봐야 할까? 그렇지 않다. float가 그려지는 line box가 유지되는 한 같은 bfc에 속하게 된다. line box를 해제하기 위해서는 별도의 다른 방법들을 사용해야 한다.(이번 글에서는 다루지 않는다.) 따라서 위의 예제에서 아직 다루지 않은 부분이 있는데, 마지막에 추가된 ABC 문자열을 가지고 있는 red block 요소이다. 해당 red block은 normal flow bfc이므로 새롭게 생성된 bfc의 시작지점에 위치한다. 문자열 ABC는 float요소와 겹쳐서 그려질 수 없기 때문에 3 번째 yellow box와 4 번째 green box 사이에 위치하게 된다. 그렇다면 만약에 문자열 ABC가 아니라 red block안에 들어갈 수 없을 만큼 긴 문자열, 예를 들면, ABCDEFG인 경우에는 어떻게 그려질까? 문자열은 아래로 내려가면서 float: left와 float: right 사이의 marginal box를 찾아 내려가면서 자신이 그려질 수 있는 marginal box를 찾는다. 이번 예제의 경우에는 ABCDEFG 문자열이 5 번째 yellow box 아래, 6 번째 green box 오른쪽에 그려진다.