근태관리 프로젝트 중 기획자가 원하는 스팩이 특이한 테이블 구조가 있었다.
일별 직원근태 현황을 표현하는 웹 페이지인데, 기획 옵션이 아래와 같다.
- 상단/좌측 고정 테이블
- 행 마다 마우스 호버시 전체적으로 border가 생겨야 된다.
- 행 전체 클릭요소가 들어가야 된다.
구조를 어떻게 짜느냐에 따라 정말 고민이 많은 UI가 될 예정이다.
생각보다 많은 시간을 소요가 되어, 이후 동일한 고민을 하는 사람들에게 조금이나마 도움이 될까하여 글을 작성해 본다.
우선 가장 중요한 것은 html 구조를 어떻게 만드냐에 따라 스크롤 컨트롤을 하는데 매우 중요하다.
틀고정의 경우 아래의 이미지와 같이 총 4개의 구역으로 나뉠 수 있다.
어떻게 어떻게 묶는 것이좋을까?
1. header와 footer(상하 분리)
2. th와 td(좌우 분리)
나는 1번을 택했다. 그 이유는 다음과 같다.
- 좌우 스크롤 시 노랑색, 녹색 영역 내 cell마다 구분선이 없기 때문에 부드럽게 스크롤이 된다.
- 좌우 스크롤 노출이 테이블 전체에 노출이 된다.
만약 '좌우분리'를 하는 경우 스크롤 영역이 녹색부터 노출이 된다(ie에서) 때문에 좌측의 리스트가 삐쳐나오는 현상이 발생할 수 있기 때문에 사전에 막고자 '상하분리'를 하였다.
결론적으로 내가 결정한 레이아웃은 아래와 같다.
퍼블리셔로 일하면서 새로생긴 속성 중 가장 좋은게 무엇이냐는 질문을 받은 적이 있다.
나는 망설임 없이 sticky 속성이라고 대답하였다.
다양한 속성들이 있지만, sitcky란 녀석은 참 모바일 버전과 다양한 화면에서 스크립트를 이용하지 않아도 부드럽게 div를 고정시킬 수 있도록 도와주는 녀석이다. 그래서 나는 참 이녀석을 좋아한다.
sticky에 대한 자세한 설명은 아래의 링크를 통해 확인 가능하다.
레진 기술 블로그 - CSS{position:sticky}
우선 position: sticky로 고정해야되는 부분을 지정하는 것이다.
기획자의 요청에 따라 '상단','좌측'을 고정시킨다.
물론 부모요소에는 position: relative를 이용하여 기준값을 잡아준다.
그럼 코드를 살펴보자,
<div class="fixTable_wrap" style="height: 500px;">
<!-- 헤더 영역 -->
<div class="fixTable_header">
<div class="th_row">
<!-- 헤더 / 좌 영역 -->
<div class="fixTable_th type_fix">
<p class="text">이름</p>
<p class="text">근무통계</p>
</div>
<!-- //헤더 / 좌 영역 -->
<!-- 헤더 / 우 영역 -->
<div class="fixTable_th type_move">
<div class="col">
<p class="day">1<em>(수)</em></p>
</div>
...
...
...
<div class="col">
<p class="day">31<em>(일)</em></p>
</div>
</div>
<!-- //헤더 / 우 영역 -->
</div>
</div>
<!-- //헤더 영역 -->
<!-- 바디 영역 -->
<div class="fixTable_body">
<div class="row">
<div class="fixTable_td type_fix ">
<div class="info_name">
<p class="main_text">홍길동 연구원</p>
<p class="sub_text">서비스개발 Unit > 개발1Cell</p>
</div>
<div class="info_date">
<p class="day"><em>0</em>/00일</p>
<p class="time"><em>00h 00m</em>/000h</p>
</div>
</div>
<div class="fixTable_td type_move">
<div class="col">
<p class="col_text red">00h</p>
<p class="col_text green">00h</p>
<p class="col_text blue">00h</p>
</div>
...
...
<div class="col">
<p class="col_text">00h</p>
</div>
</div>
</div>
</div>
<!-- //바디 영역 -->
</div>
위의 코드에서
기준이 되는 부모요소에는 아래와 같이 코드를 작성한다.
<div class="fixTable_wrap" style="position:relative;"></div>
그리고, position: sticky가 들어가는 곳은 다음과 같다.
<div class="fixTable_th type_fix"></div>
<div class="fixTable_td type_fix "></div>
이렇게 2곳을 position:sticky 지정을 해주면 된다!
스크롤 적용은 부모요소에 해버리면 된다!
<div class="fixTable_wrap" style="overflow:auto;"></div>
이와 같이 적용을 하면 틀고정이 되고 상/하/좌/우 스크롤이 되는 테이블을 볼 수 있다. 그럼 아래와 같이 노출이 된다.
그런데 이슈가 생겼다. 좌측의 틀이 사라지는 이슈이다.
영상을 한번 보자.
진짜이거 때문에 고민을 많이 했다. 왜 없어질까..?? 힌트는 상단에 회색 색상이 끊기는 지점부터 사라진다는 것이었다.
원인은 다음과 같았다.
부모 박스에 position:relative; width: 100%; 로 인해 너비값이 고정인 상태에서 position:sitiky; left: 0으로 위치를 잡는 경우 우측의 리스트가 부모의 너비(100%) 이상이 되는 경우 스크롤은 그대로 되면 부모의 위치 자체가 뒤로 밀리는 현상이다.
아래의 사진을 보면 더 이해하기 쉬울 것이다.
class=row의 길이는 부모요소에 종속이 되어 부모요소와 동일한 너비를 같는다. 그렇기 때문에 그 안에서 부모보다 넓은 영역이 scroll이 생기는 것이다.
결국, class=row의 너비보다 자식요소의 그 이상이되면 이러한 이슈가 생성이되는 것이었다.
다른 방법이 있으시다면 코멘트 달아주세요 ㅠㅠ !
결국 이부분은 javascript의 힘을 빌리기로 했다.
$(document).ready(function() {
var th_boxWidth = $(".fixTable_header .type_fix").outerWidth();
var td_boxWidth = $(".fixTable_body .type_move").outerWidth();
$('.fixTable_body .row').width(th_boxWidth + td_boxWidth + 'px');
$('.fixTable_header .th_row').width(th_boxWidth + td_boxWidth + 'px');
});
위의 방법은 row의 자식요소의 전체 너비를 구하여 그 값을 row에 넣는 것이다.
그럼 해결이 된다 !! 짠 !!!
position:stiky를 왜 사람들이 잘 안쓸까?(모바일 제외) 답은 아래를 보면 알 수 있다.
절망적이다.. 절망적이야 !!!!
IE !!!!!!!!!!!!!!!!!!!!!
여기서 많은 사람들이 말을한다.
"야 처음부터 javascipt로 하지 구지 왜 position:stikcy를 써서 고생이야?"
그럼 난 매번 이렇게 말을한다.
"나 웹퍼블리셔야!"
물론 웹퍼블리셔라고.. javasicript를 모르면 안된다. 아니 잘 알아야된다. 하지만, 두 가지 기능을 모두 안다면, 좀더 웹에 부담이 덜되는 방법을 쓰는게 좋은 개발자가 아닌가?
그래서 나는 이러한 방법을 택했다.
ie를 타겟팅하여 ie에서만 javascipt로 되도록 class로 제어
이러면, ie를 제외한 나머지 브라우저에서는 css로 최적화된 스크롤을 볼 수 있고 ! ie는.. 머.. 이미 생을 마감햇지만.. 사용하시는 분들을 위해 최소한으로 해주고 !! 그래서 나는 결국 자바스크립트를 사용하였다.
<script>
// ie 대응 스크롤
// ie 브라우저 타겟팅
var agent = navigator.userAgent.toLowerCase();
if( navigator.appName == 'Netscape' && navigator.userAgent.search('Trident') != -1 || (agent.indexOf("msie") != -1)) {
$('.fixTable_wrap').addClass('ie');
}
const container = document.querySelector('.fixTable_body'); //부모 박스
const fixBox = document.querySelector('.type_fix'); // 고정 박스
// 스크롤 이벤트 영역
container.addEventListener('scroll', function() {
console.log(container.scrollLeft)
var scrollnum = container.scrollLeft + 'px'; //스크롤 위치값 px값으로 가져오기
if(container.scrollLeft != 0 ){
$('.fixTable_body .type_fix').css({"transform": 'translateX' + '(' + scrollnum + ')'}); // 스크롤이 위치값이 0이 아닐때 transform 값 변경
$('.fixTable_th.type_move').css({"transform": 'translateX' + '(-' + scrollnum + ')'});
} else{
$('.fixTable_body .type_fix').css("transform", "translateX(0)");
$('.fixTable_th.type_move').css("transform", "translateX(0)");
}
})
</script>
transform을 이용하여 위치값을 변경해주었다. left값이 아닌 transform을 이용한 이유는?
모바일 메뉴에 기름칠 하기 ! (feat. animation 최적화)
이거 보면 좋아요 !
자 그럼 결과물은 github에 올려놨다. github
우선 크롬 브라우저 !!
그 다음은 ! IE
크.............. 완벽하다...라고 말하고싶습니다:)
구글링을 하여 틀고정 테이블의 샘플을 찾기란 어려웠다. 그이유는 ie대응이 안되는 것들이 있거나 한쪽만 고정하는 겨우의 테이블 샘플만 있었기 때문이다.
물론 나도 어느정도 javascript의 힘을 빌리긴 했지만 생각보다 부드러운 UI로 나와 매우 흡족하였다.
작업을 하며 이렇게 생각을 하며 작업하는게 역시 개발의 매력인가보다 ㅋㅋㅋㅋㅋ
이래서 이 짓을 그만 못하지 ㅋㅋㅋㅋ
으아 해당 파일 공유 해주신 것 클릭하니깐 깃허브 연결되는데 아이디 까먹어서 접속이 불가합니다~!! 해당 파일이 지금 너무 필요해서요. 알려주시면 너무 너무 감사드려요~! 아 진짜 막혀서 아 어떻게 해결해야하지 하고 고민하고 있었는데 여기 해결법이 딱!!!!! 있더라구요.