2009년 6월 10일 수요일

DIV 객체에서 valign='middle' 구현하기

Ajax 프로그래밍을 하다보면 DIV를 수시로 사용해야 하는데 DIV는 테이블과 다르게 valign="middle" 속성이 존재하지 않는다. 여러가지 방법을 고려하던 중 몇몇사이트에서 힌트가 될만한 내용을 얻게 되었다.

첫번째 방식의 경우

첫번째 방식은 다음과 같은 소스로 구성되어 있다.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-2">
<title>Universal vertical center solution</title>
<style>
.greenBorder {border: 1px solid green;}
</style>
</head>

<body>

<h1>Vertical Centering in CSS - Example</h1>

<div class="greenBorder" style="display: table; height: 400px; #position: relative; overflow: hidden;">
    <div style=" #position: absolute; #top: 50%;display: table-cell; vertical-align: middle;">
        <div class="greenBorder" style="#position: relative; #top: -50%; ">
            any text<br>
            any height<br>
            any content, for example generated from DB<br>
            everything is vertically centered
        </div>
    </div>
</div>

<p>Please view source for details or read the
<a href="../css-vertical-center-solution.html">CSS
vertical centering article</a>.
<p>See also the <a href="vertical-align-valid-solution-en.html">structural and
valid example</a>.
</body>
</html>

IE의 경우 Layer를 3중으로 감싸서 첫번째 Layer에서 전체적인 위치를 잡고 두번째 Layer에서는 첫번째 Layer기준으로 가운데(상대 좌표 기준 50%로 top 설정)하고 세번째 Layer에서 두번째 Layer기준으로 -50%로 top을 설정하는 방식이다. 이 방식의 경우 IE의 거의 모든 버전에서 동작하는 장점이 있지만 데이터가 많아지는 경우 스크롤바가 생기도록 할 수 없다.

  • 데이터가 많지 않은 경우
     
  • 데이터가 많은 경우

Firefox와 Opera의 경우 IE에서 사용하는 방식을 이용하는 것이 아니라 display 라는 CSS 속성을 table로 설정하는 방식으로 처리가 가능하다. 이 방식의 경우 가장 밖에 있는 Layer에 display: table를 설정하고 두번째 Layer에는 display: table-cell; vertical-align: middle;를 설정하면 되는데 데이터가 많은 경우 Layer이 커져서 Layout이 변경되는 문제가 있다.

  • 데이터가 많지 않은 경우

  • 데이터가 많은 경우

두번째 방식의 경우

두번째 방식은 좀더 복잡한데, 분리된 CSS와 JavaScript파일을 통합하면 다음과 같은 소스로 구성되어 있다.

<!-- Centered Layout Code by Christian Fecteau www.metaw3.ca 29/09/05 Do not remove this comment -->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Centered Layout</title>

<!-- Centered Layout Code Start -->
<style type="text/css" id="layout">
#outer { width: 50%; } /* customize width of the page: 700px, 90% etc. */
</style>
<script type="text/javascript">

/* Coded by Christian Fecteau 26/09/05 www.metaw3.ca */

var layout_ie5 = (window.ActiveXObject && document.getElementById);
var layout_ie5Mac = (!window.showModelessDialog && window.ActiveXObject && document.getElementById);

if (layout_ie5)
{
    var layout_old_onload = null;
    if (typeof window.onload == "function")
    {
        layout_old_onload = window.onload;
    }
    window.onload = layout_fix;

    var layout_old_onresize = null;
    if (typeof window.onresize == "function")
    {
        layout_old_onresize = window.onresize;
    }
    window.onresize = layout_fix;

    if (!layout_ie5Mac)
    {
        var layout_ow = null;
        var layout_ss = document.styleSheets('layout').rules;
        for (var i = 0; i < layout_ss.length; i++)
        {
            if (layout_ss[i].selectorText == '#outer')
            {
                layout_ow = layout_ss[i].style.width;
            }
        }
    }
}

function layout_fix()
{
    if (layout_old_onload)
    {
        layout_old_onload();
        layout_old_onload = null;
    }

    if (layout_old_onresize)
    {
        layout_old_onresize();
    }

    if (layout_ie5Mac)
    {
        document.body.style.height = '0px';
        document.body.style.width = '0px';
    }
    else
    {
        if (layout_ow) middle.style.width = layout_ow;
        outer.style.width = '100%';
    }

    outer.style.height = 'auto';
    outer.align = 'center';
    middle.align = 'left';
    inner.style.styleFloat = 'left';

    var layout_b = Number(document.body.clientHeight);
    var layout_c = Number(inner.offsetHeight);

    if (layout_b > layout_c)
    {
        outer.style.marginTop = (layout_b-layout_c)/2 + 'px';
    }
    else
    {
        outer.style.marginTop = '0px';
    }
}

</script>
<!-- Centered Layout Code End -->

<style type="text/css">

#container {
    color: black;
    background-color: silver;
}

p {
    font: 0.7em verdana, serif;
    text-align: justify;
    margin: 0;
}

body, html {
    height: 100%;
    margin: 0;
    padding: 0;
    border: 0;
}

#outer {
    display: table;
    height: 100%;
    margin: 0 auto;
}

#middle {
    display: table-row;
}

#inner {
    display: table-cell;
    vertical-align: middle;
}
</style>
</head>
<body>

<!-- Centered Layout Code Start -->
<div id="outer"><div id="middle"><div id="inner">
<!-- Centered Layout Code End -->

<!-- your page starts here -->
<div id="container">
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
<!-- your page ends here -->

<!-- Centered Layout Code Start -->
</div></div></div>
<!-- Centered Layout Code End -->

</body>
</html>

IE의 경우 windows의 onresize 이벤트를 받아서 위치를 계산해 준다.

  • 데이터가 많지 않은 경우
  • 데이터가 많은 경우

Firefox와 Opera의 경우 처음 방법과 비슷하게 display: table를 사용하는데 두번째 Layer에 display:table-row라는 속성을 지정한 부분만 차이가 있다.

  • 데이터가 많지 않은 경우

  • 데이터가 많은 경우

이 방식의 문제점은 IE의 경우 onresize라는 event를 이용하는데 이 event가 DIV객체에서는 발생하지 않는다는 점이다.

웹스퀘어를 위한 개선책

이 두가지 방식 모두 2% 부족하므로 웹스퀘어를 위한 새로운 해결 방법을 고민하게 되었다. 먼저 웹스퀘어의 경우 별도의 Layout엔진을 사용하므로 하나의 HTML소스에서 여러 브라우저를 모두 지원하지 않아도 된다. 그래서 IE를 지원하기 위한 소스와 Firefox, Opera를 지원하기 위한 소스로 분리하였다.

IE의 경우

IE의 경우 첫번째 방식이 좋은 해결책이지만 데이터의 양이 많아지게 되면 스크롤바를 이용해서 데이터를 처리할 수 없다. 만일 첫번째 Layer의 overflow를 auto로 하게 되면 다음과 같은 일이 발생한다.

데이터가 많지 않은 경우 정상적으로 동작한다.

데이터의 양이 많아 져서 세번째 Layer의 높이가 첫번째 Layer의 50% 이상인 경우 스크롤바가 생기게 된다!!!

그래서 두번째 Layer의 margin-top을 첫번째 Layer와 두번째 Layer의 높이의 절반으로 설정하는 두번째 방식과 expression이라는 IE전용 CSS함수를 이용해서 다음과 같이 수정하였다.

<div id="layer1" style="background-color:#9999FF;">
    <div id="layer2" style="background-color:#99FF99; margin-top:expression( ( eval(this.offsetParent.offsetHeight) -  eval(this.offsetHeight) )/2 );">
        ajax 테스트<br>
        valign=middle<br>
        데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>
    </div>
</div>

expression은 계산 결과를 CSS에 적용할 수 있도록 지원하는 함수이다. 화면은 아래 처럼 나타나는데 데이터 양에 상관없이 정상적으로 동작하는 것을 확인할 수 있다.



전체 소스는 다음과 같다.

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=EUC-KR">
<title>DIV valign=middle</title>
<style>
#layer1 {position: absolute; top:100px; left:100px; height: 400px; width:700px; overflow: auto;}
</style>
<script>
function short() {
    layer2.innerHTML = "ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플";
}

function middle() {
    layer2.innerHTML = "ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>";
}

function long() {
    layer2.innerHTML = "ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>";
}

</script>

</head>

<body>
모든 경우 정상적으로 동작하지만 expression의 overhead가 얼마나 될지 모른다.<br>

<a href="javascript:short()">짧은글</a>&nbsp;&nbsp;&nbsp;
<a href="javascript:middle()">중간글</a>&nbsp;&nbsp;&nbsp;
<a href="javascript:long()">긴글</a>&nbsp;&nbsp;&nbsp;

<div id="layer1" style="background-color:#9999FF;">
    <div id="layer2" style="background-color:#99FF99; margin-top:expression( ( eval(this.offsetParent.offsetHeight) -  eval(this.offsetHeight) )/2 );">
        ajax 테스트<br>
        valign=middle<br>
        데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>
    </div>
</div>
</html>

Firefox와 Opera의 경우

Firefox와 Opera의 경우에는 display:table 속성이 있는 Layer를 다시 display 속성이 없는 Layer로 감싸는 방식으로 해결할 수있다.



 



 

전체 소스는 다음과 같다.

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=EUC-KR">
<title>DIV valign=middle</title>
<style>
#layer1 {position: absolute; top:100px; left:100px; height: 400px; width:700px; overflow: auto;}
#layer2 {display: table; width:100%; height:100% }
#layer3 {display: table-cell; vertical-align: middle;}

</style>
<script>
function short() {
    layer3.innerHTML = "ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플";
}

function middle() {
    layer3.innerHTML = "ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>";
}

function long() {
    layer3.innerHTML = "ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>ajax 테스트<br>valign=middle<br>데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>";
}

</script>

</head>

<body>
ff.html의 문제를 해결하기 위해서 display 속성을 별도로 지정하지 안은 layer로 한번 더 감싸는 형태로 구성되어있다.<br>
<a href="javascript:short()">짧은글</a>&nbsp;&nbsp;&nbsp;
<a href="javascript:middle()">중간글</a>&nbsp;&nbsp;&nbsp;
<a href="javascript:long()">긴글</a>&nbsp;&nbsp;&nbsp;

<div id="layer1" style="background-color:#9999FF;">
    <div id="layer2" style="background-color:#FF9999;">
        <div id="layer3" style="background-color:#99FF99;">
            ajax 테스트<br>
            valign=middle<br>
            데이터의 길이에 상관 없이 가운데 정렬하기 위한 샘플<br>
        </div>
    </div>
</div>
</html>

출처 : 생각노트

댓글 없음:

댓글 쓰기