소프트웨어/JavaScript • Dhtml

테이블 틀고정 스크롤

falconer 2007. 4. 13. 07:51
링크 #1. 테스트 페이지 입니다.
테이블 소스는 엑셀에서 txt로 저장한걸 php에서 작업해서 테이블로 만든겁니다.
텍스트 뿐이고 td는 white-space:nowarp를 줘서 줄바꿈이 없게 했습니다.

업로드한 js 파일 보시면 대충이나마 주석 달았습니다.


조금 자세한 설명...

스크롤을 하려면 몇가지 방법이 있습니다.

div 하나에 원본 테이블과, 고정행(첫줄)과 고정열이 있다면
div의 크기는 고정이고 테이블의 크기는 그것보다 더 큽니다.
고로 스크롤이 발생하는데, 이때 스크롤되어 화면(div 시야 내)에서 사라지는 고정행과 고정열의 top과 left를 scrollTop과 scrollLeft 로 조절합니다.
처음에는 이렇게 했지만, 이 방식은 흔들림이 심하더군요.
(스크롤을 밑으로 하면 고정행이 위아래로 흔들리고, 옆으로 하면 고정열이 좌우로 흔들립니다.)


그리고 완전히 삽질한 방식이 있는데...
원본 테이블만으로 작업을 합니다.
고정행의 td(혹은 tr)과 고정열의 td에 전부 class를 주고
(어차피 테이블로 만드는건 php가...)
스크롤이 될 때마다 해당 클래스의 속성을 조절합니다.

document.styleSheets[0].cssRules[0].style.margin = xxx...

이런식으로 접근해서 일단 되기는 되는데 IE였나 FF였나 둘 중 하나는 조금 어긋났었고, 게다가 각 td별로 적용되다보니 스크롤 자체가 상당히 느려졌습니다.



지금 쓴 방식은

전체 div : #sortTable {position:relative}

이 안에

테이터 div : #dataDiv {position:relative ; width:700px ; height:500px ; overflow:scroll}
고정행/열 div : #keyDiv,#rowDiv,#colDiv {position:absolute ; top:0px ; overflow:hidden}

의 4개 div가 있는 상태.

dataDiv 안에는 원본 데이터 테이블이 있고, keyDiv/rowDiv/colDiv 에는 각각의 고정부분이 있습니다.

고정된 dataDiv 안에 그보다 더 큰 크기의 dataTable가 있기 때문에 스크롤이 발생하되, 이 스크롤은 dataDiv의 스크롤이므로 다른 고정부분 div/table에는 영향을 끼치지 않습니다.
그러므로 아무리 스크롤을 해도 그보다 위에 있는(코드상 뒤에 있으므로 z-index가 높은) 고정 부분 테이블은 움직이지 않습니다.

onscroll로 상하로 스크롤시 고정열(세로축)도 상하로 움직이면 되고, 좌우로 스크롤시 고정행(가로축)도 좌우로 움직이면 됩니다.

* 고정부분의 div들이 스크롤을 가리는 것을 막기 위해 각 div의 크기를 dataDiv의 clientWidth/Height로 적용합니다.
* 그리고 테이블의 경우 너비를 지정하지 않으면 모체의 크기로 전체가 잡혀버리므로 역시 원본 테이블의 너비를 가져와서 적용
* 제목 셀(thead)이 짧아도 밑의 내용 셀이 길기 때문에, 각 제목 셀의 너비를 원본 테이블 각 셀의 너비로 적용

* td에 nowrap를 줘서 모두 한줄로 적용되고 텍스트 뿐인 테이블이기 때문에 height는 손 대지 않아도 됩니다.




스크롤 자체는 위와 같은 방식입니다.

다만 고정행/열을 만드는 것도 문제인데...

db에서 뽑아오거나 해서 php에서 처리하는 경우

<div id="sortTable">

    <div id="dataDiv">
        <table id="dataTable">
            <?php 전체 자료 테이블 생성 ?>
        </table>
    </div>

    <div id="rowDiv">
        <table id="rowTable">
            <?php 생성 : 고정시킬 행 ?>
        </table>
    </div>

    <div id="colDiv">
        <table id="colTable">
            <?php 생성 : 고정시킬 열 ?>
        </table>
    </div>

    <div id="keyDiv">
        <table id="keyTable">
            <?php 생성 : 완전 고정시킬 왼쪽 상단 부분 ?>
        </table>
    </div>

</div>

이런식으로 생성하고 나서 각각의 너비/높이를 맞춰주면 됩니다.




참고//

링크 2번은...

php가 아닌 js로 정렬하게 만들어봤습니다.
데이터를 php로 가공해서 js의 이중배열로 만들고, 이걸 정렬 시킵니다.
그리고 그 결과를 바탕으로 html 코드를 만들고 innerHTML 로 집어넣기.

아래는 정렬 함수입니다.
test[0] = ["이완용","xx","123","Z",0];
test[1] = ["김구","백범","-23","Kim",1];
test[2] = ["최익현","면암","23","Chol",2];
test[3] = ["여운형","몽양","-93","Yeo",3];

sortKey = 2;
sortMode = 1;
// 1은 정렬, -1은 역순정렬
column = test[0].length;

test.sort(sortFunc);

function sortFunc(a,b) {
    var aS = a[sortKey].toString(10).toUpperCase();
    var bS = b[sortKey].toString(10).toUpperCase();
    if(aS == bS) return a[column-1]-b[column-1];

    while(aS || bS) {
        var aS = aS.replace(/^((\-?\d*)[^\d]*)/,"");
        var aT = RegExp.$1;
        var aN = RegExp.$2;

        var bS = bS.replace(/^((\-?\d*)[^\d]*)/,"");
        var bT = RegExp.$1;
        var bN = RegExp.$2;
    
        if(aN && bN && aN != bN) {
            return (Number(aN)-Number(bN))*sortMode;
        }
        else if(aT != bT) {
            var sort2 = new Array(aT,bT);
            sort2.sort();
            return ((sort2[0] == aT)?-1:1)*sortMode;
        }
    }
}

정렬하는 배열의 각 2차배열의 sortKey 인덱스 기준 정렬...

숫자와 문자를 구분하여 정렬하되, 숫자가 가장 앞에 나올 경우 음수까지 비교.
php 에서 이 함수를 적용하려 했는데 찾아보니 배열함수 natsort와 문자열 비교 함수 strnatcmp가 있더군요.
(그런데 이건 음수 구분을 안하네요.)

그리고 각 내부 배열의 마지막 값은 데이터가 아니라 정렬 순서를 저장하게 됩니다.
그래서 다른 인덱스로 정렬을 할 때 같은 값이 되면 이전 정렬 순서를 기준으로 정렬합니다.

링크 #1. 테스트 페이지 입니다.
테이블 소스는 엑셀에서 txt로 저장한걸 php에서 작업해서 테이블로 만든겁니다.
텍스트 뿐이고 td는 white-space:nowarp를 줘서 줄바꿈이 없게 했습니다.

업로드한 js 파일 보시면 대충이나마 주석 달았습니다.


조금 자세한 설명...

스크롤을 하려면 몇가지 방법이 있습니다.

div 하나에 원본 테이블과, 고정행(첫줄)과 고정열이 있다면
div의 크기는 고정이고 테이블의 크기는 그것보다 더 큽니다.
고로 스크롤이 발생하는데, 이때 스크롤되어 화면(div 시야 내)에서 사라지는 고정행과 고정열의 top과 left를 scrollTop과 scrollLeft 로 조절합니다.
처음에는 이렇게 했지만, 이 방식은 흔들림이 심하더군요.
(스크롤을 밑으로 하면 고정행이 위아래로 흔들리고, 옆으로 하면 고정열이 좌우로 흔들립니다.)


그리고 완전히 삽질한 방식이 있는데...
원본 테이블만으로 작업을 합니다.
고정행의 td(혹은 tr)과 고정열의 td에 전부 class를 주고
(어차피 테이블로 만드는건 php가...)
스크롤이 될 때마다 해당 클래스의 속성을 조절합니다.

document.styleSheets[0].cssRules[0].style.margin = xxx...

이런식으로 접근해서 일단 되기는 되는데 IE였나 FF였나 둘 중 하나는 조금 어긋났었고, 게다가 각 td별로 적용되다보니 스크롤 자체가 상당히 느려졌습니다.



지금 쓴 방식은

전체 div : #sortTable {position:relative}

이 안에

테이터 div : #dataDiv {position:relative ; width:700px ; height:500px ; overflow:scroll}
고정행/열 div : #keyDiv,#rowDiv,#colDiv {position:absolute ; top:0px ; overflow:hidden}

의 4개 div가 있는 상태.

dataDiv 안에는 원본 데이터 테이블이 있고, keyDiv/rowDiv/colDiv 에는 각각의 고정부분이 있습니다.

고정된 dataDiv 안에 그보다 더 큰 크기의 dataTable가 있기 때문에 스크롤이 발생하되, 이 스크롤은 dataDiv의 스크롤이므로 다른 고정부분 div/table에는 영향을 끼치지 않습니다.
그러므로 아무리 스크롤을 해도 그보다 위에 있는(코드상 뒤에 있으므로 z-index가 높은) 고정 부분 테이블은 움직이지 않습니다.

onscroll로 상하로 스크롤시 고정열(세로축)도 상하로 움직이면 되고, 좌우로 스크롤시 고정행(가로축)도 좌우로 움직이면 됩니다.

* 고정부분의 div들이 스크롤을 가리는 것을 막기 위해 각 div의 크기를 dataDiv의 clientWidth/Height로 적용합니다.
* 그리고 테이블의 경우 너비를 지정하지 않으면 모체의 크기로 전체가 잡혀버리므로 역시 원본 테이블의 너비를 가져와서 적용
* 제목 셀(thead)이 짧아도 밑의 내용 셀이 길기 때문에, 각 제목 셀의 너비를 원본 테이블 각 셀의 너비로 적용

* td에 nowrap를 줘서 모두 한줄로 적용되고 텍스트 뿐인 테이블이기 때문에 height는 손 대지 않아도 됩니다.




스크롤 자체는 위와 같은 방식입니다.

다만 고정행/열을 만드는 것도 문제인데...

db에서 뽑아오거나 해서 php에서 처리하는 경우

<div id="sortTable">

    <div id="dataDiv">
        <table id="dataTable">
            <?php 전체 자료 테이블 생성 ?>
        </table>
    </div>

    <div id="rowDiv">
        <table id="rowTable">
            <?php 생성 : 고정시킬 행 ?>
        </table>
    </div>

    <div id="colDiv">
        <table id="colTable">
            <?php 생성 : 고정시킬 열 ?>
        </table>
    </div>

    <div id="keyDiv">
        <table id="keyTable">
            <?php 생성 : 완전 고정시킬 왼쪽 상단 부분 ?>
        </table>
    </div>

</div>

이런식으로 생성하고 나서 각각의 너비/높이를 맞춰주면 됩니다.




참고//

링크 2번은...

php가 아닌 js로 정렬하게 만들어봤습니다.
데이터를 php로 가공해서 js의 이중배열로 만들고, 이걸 정렬 시킵니다.
그리고 그 결과를 바탕으로 html 코드를 만들고 innerHTML 로 집어넣기.

아래는 정렬 함수입니다.
test[0] = ["이완용","xx","123","Z",0];
test[1] = ["김구","백범","-23","Kim",1];
test[2] = ["최익현","면암","23","Chol",2];
test[3] = ["여운형","몽양","-93","Yeo",3];

sortKey = 2;
sortMode = 1;
// 1은 정렬, -1은 역순정렬
column = test[0].length;

test.sort(sortFunc);

function sortFunc(a,b) {
    var aS = a[sortKey].toString(10).toUpperCase();
    var bS = b[sortKey].toString(10).toUpperCase();
    if(aS == bS) return a[column-1]-b[column-1];

    while(aS || bS) {
        var aS = aS.replace(/^((\-?\d*)[^\d]*)/,"");
        var aT = RegExp.$1;
        var aN = RegExp.$2;

        var bS = bS.replace(/^((\-?\d*)[^\d]*)/,"");
        var bT = RegExp.$1;
        var bN = RegExp.$2;
    
        if(aN && bN && aN != bN) {
            return (Number(aN)-Number(bN))*sortMode;
        }
        else if(aT != bT) {
            var sort2 = new Array(aT,bT);
            sort2.sort();
            return ((sort2[0] == aT)?-1:1)*sortMode;
        }
    }
}

정렬하는 배열의 각 2차배열의 sortKey 인덱스 기준 정렬...

숫자와 문자를 구분하여 정렬하되, 숫자가 가장 앞에 나올 경우 음수까지 비교.
php 에서 이 함수를 적용하려 했는데 찾아보니 배열함수 natsort와 문자열 비교 함수 strnatcmp가 있더군요.
(그런데 이건 음수 구분을 안하네요.)

그리고 각 내부 배열의 마지막 값은 데이터가 아니라 정렬 순서를 저장하게 됩니다.
그래서 다른 인덱스로 정렬을 할 때 같은 값이 되면 이전 정렬 순서를 기준으로 정렬합니다.


출처 : iamSeeker