Một vài thủ thuật làm việc với các dạng layout - Phần 3

Posted at June 19, 2020

Một vài thủ thuật làm việc với các dạng layout - Phần 3

Chào mọi người, tiếp tục với series's về các dạng layout hôm nay mình sẽ chia sẻ thêm một trick thú vị nữa về layout table.

Đợt này mình ôm 2 project nên hơi sấp mặt, cũng là mấy con admin nhưng design có vẻ hứng thú và bắt mắt hơn mấy cái dashboard trước đây. Trong bài hát"Đường đến ngày vinh quang" của cố nhạc sĩ Trần Lập có câu"chặng đường nào trải bước trên hoa hồng bàn chân cũng thấm đau vì những mũi gai"... đi liền với cái đẹp thường là sự phức tạp, tuy nhiên làm việc với design đẹp và những cái đẹp là điều làm mình hứng thú.

Đâu đó cũng 3 tuần rồi, đang vật lộn với việc estimate task bên dự án khác thì lại bị vỗ vai nhờ support. Lần này không phải em gái xinh xắn bàn bên mà là một em trai cao to da trắng tưởng tới rủ mình đi chơi nhưng lại nhờ fix giúp cái table. Mình không còn sợ chị designer buồn dẫn đến trầm cảm nữa mà mình sợ anh em Backend sấp mặt với đội QA vì dự án sắp release nên mình đã ra tay giúp đỡ.

Bây giờ mình sẽ tái hiện lại chiếc table mình làm hôm trước. Anh em review phát xem có gọn gàng không nhé.

1. Sync scroll for table has a space

Thông thường, với kiểu tablecó khoảng trống ngăn giữa các row mọi người sẽ nghĩ ngay đến việc dùng margin để tạo khoảng cách. Đúng như vậy nhưng nhiều khi cuộc sống đâu như những giấc mơ, việc coding để đẹp như design cũng không phải đơn giản.

Một vài thủ thuật làm việc với các dạng layout - Phần 3

Mô tả

- Table xuất hiện scroll khi có nhiều cột

- Cột đầu tiên được cố định khi scroll

- Row cuối cùng cách các row trên 1 khoảng và table có bo tròn để tăng tính thẩm mĩ.

Ý tưởng thực hiện

  • Giống nhưbài trước table lần này vẫn có bo tròn, có đường line đẹp đẹp.
  • Tạo 2 table giống nhau, cómargin để tạo khoảng cách
  • Viết 1 đoạnjs nhỏ để getwidth của từngcell và set vàotable bên dưới để đồng bộ size.
.wrap
 .container
   table
     thead
       tr
         th Cột title
         th Hích đờ
         th Hích đờ
         th Hích đờ
         th Hích đờ
         th Hích đờ
         th Hích đờ
         th Hích đờ
     tbody
       each row in [1, 2, 3, 4]
         tr
           td Row title
           td cell content
           td cell content
           td cell content
           td cell content
           td cell content
           td cell content
           td cell content
       tr.table-row-last
         td.text-nowrap Row title long text...
         td cell content
         td cell content
         td cell content
         td cell content
         td cell content
         td cell content
         td cell content

//- table (row cuối)
.wrap
 .container
   table.table-footer
     tbody
       tr
         td Sum sum sum
         td cell content
         td cell content
         td cell content
         td cell content
         td cell content
         td cell content
         td cell content
.wrap {
 flex: 1;
 width: 100%;
 margin: auto;
 margin-bottom: 5px;
 border: 1px solid #36304a;
 border-radius: 5px;
 overflow: hidden;
}

// Chỗ này để chứa table scroll
.container {
 display: block;
 width: 100%;
 overflow-x: auto;
}

table {
 width: 100%;
 min-width: 1000px;

 tr {
   &:not(:last-child) {
     td {
       border-bottom: 1px solid gray;
     }
   }

   &:nth-child(even) {
     td {
       background: #ffc7f8;
     }
   }
 }

 td {
   padding: 10px 20px;
   background: #ffedfd;
 }

 th {
   padding: 20px;
   color: white;
   background: #36304a;
   white-space: nowrap;
   text-align: center;
 }

 td, th {
   // Fixed cột đầu tiên của table
   &:first-child {
     position: sticky;
     left: 0;
     z-index: 9;
   }

   &:not(:last-child) {
     border-right: 1px dotted gray;
   }
 }
}

// customize scroll cho đẹp
::-webkit-scrollbar {
 width: 5px;
 height: 5px;
}

::-webkit-scrollbar-track {
 background: white;
}

::-webkit-scrollbar-thumb {
 background: #36304a;
}
// Đoạn này dùng để get/set width cho table
var firstTableWidth = [];

// selector đến th của table trên và get width th
$('table th').each(function(i, item) {
 firstTableWidth.push($(this).width());
});

// set width vào từng td của table còn lại
$('.table-footer td').each(function(i, item) {
 $(this).width(firstTableWidth[i]);
});

// Đồng bộ scroll cho 2 table
$('.container').on('scroll', function() {
 $('.container:not(this)').scrollLeft($(this).scrollLeft());
});

Ngoài cách trên:

  • Để tạo phân cách: mình cũng nghĩ việc dùng:beforehoặc:afterset cùng màu background để tạo line phân cách. Tuy nhiên cách này sẽ không linh hoạt nếu bên số lượng row nhiều và theo design thì ở đây cóborder-radiuscho 2 table nên cách này là không dùng được.
  • Không cần đồng bộ scroll bằng JS: có thể dùng 1 wrapper bọc cả 2 table khi đó cũng scroll đồng bộ được, tuy nhiên mình cũng đã thử nhưng không giữ đượcborder-radiusnên dùng js có vẻ hợp lý.

2. Table responsive

Table có 2 dạng responsive đó là dạng có scroll và dạng từng list theo hàng. Tuy nhiên đối với table responsive có scroll ngang chỉ nên áp dụng với trường hợp table quá nhiều cột, dù ở PC / laptop không đủ kích thước mới nên xuất hiện. Đối với mobile, table chuyển thành từng hàng như design bên dưới (mình chưa biết thuật ngữ gọi là gì) sẽ phù hợp hơn vì nhìn rõ ràng, không phải scroll ngang gây cảm giác khó chịu.

Table scroll trên màn hình PC (trường hợp quá nhiều cột sẽ xuất hiện scroll)

Một vài thủ thuật làm việc với các dạng layout - Phần 3

Table responsive trên màn hình Mobile (chia theo từng hàng, không còn hiển thị theo từng cột nữa)

Một vài thủ thuật làm việc với các dạng layout - Phần 3

Ý tưởng thực hiện

  • - Dựng table cho PC như bình thường
  • - Responsive
  • - Ẩn row header của table
  • - Tạo label của header bằng cách dùng before hoặc after thông qua content
.wrap
 .container
   table
     thead
       tr
         th Ticket
         th Tracker
         th Status
         th Priority
         th Assignee
         th Target version
         th Due date
         th Estimated time
     tbody
       each item in [1, 2, 3, 4]
         tr
           //- data-label là attr dùng để hiển thị label khi về mobile
           td(data-label='Ticket') #0001
           td(data-label='Tracker') Task
           td(data-label='Status') Resolved
           td(data-label='Priority') Hight
           td(data-label='Assignee') Nguyen Huu Khuyen
           td(data-label='Target version') Sprint 3
           td(data-label='Due date') 06/21/2020
           td(data-label='Estimated time') 1616
//-- Những phần tương tự của table trên mình không lặp lại ở đây --

// Chỉ scroll table cho màn hình từ tablet
@media screen and (min-width: 640px) {
 td, th {
   &:first-child {
     position: sticky;
     left: 0;
     z-index: 9;
   }

   &:not(:last-child) {
     border-right: 1px dotted gray;
   }
 }
}

@media screen and (max-width: 640px) {
 table {
   min-width: auto;

   // Ẩn header table
   thead {
     position: absolute;
     width: 1px;
     height: 1px;
     overflow: hidden;
   }

   tr {
     display: block;

     // tạo đường line kết thúc mỗi khối data
     &:not(:last-child) {
       border-bottom: 2px solid #8e3c78;
     }

     &:nth-child(even) {
       td {
         background: #fff;
       }
     }

     td {
       display: block;
       font-size: 14px;
       text-align: right;
       border-bottom: 1px solid #ddd;

       &:before {
         // get value từ attribute 'data-label' để hiển thị
         content: attr(data-label);
         float: left;
         font-weight: bold;
         text-transform: uppercase;
       }

       &:last-child {
         border-bottom: 0;
       }
     }
   }
 }
}

3. Tổng kết

Trên đây là 2 dạng table tiếp theo sau một loạt bài về table mình vừa gặp, cũng được anh em trong dự án trầm trồ. Hi vọng nhữngtrickstrên sẽ giúp mọi người có thể tận dụng đượcctrl + cctrl + vtừ code của mình.

May mắn lớn nhất của cuộc đời, không phải nhặt được tiền, cũng không phải trúng số, mà là có người có thể dẫn bạn đi đến 1 nền tảng cao hơn.