요구 사항
- 테이블에서 같은 카테고리의 셀은 병합하여 표시한다.

rowspan
- 일반적으로 테이블 셀을 수직으로 병합하기 위해
rowspan 속성을 사용할 수 있다.
rowspan 속성은 HTML 테이블에서 셀(td 또는 th)이 수직으로 몇 개의 행(row)을 병합할 것인지를 지정하는 속성이다.
- 기본적으로
rowspan 속성은 한 개의 셀을 여러 행에 걸쳐 병합할 때 사용되며, 병합된 셀은 하나의 큰 셀처럼 보이게 된다.
기본 문법
<td rowspan="n">내용</td>
- 여기서
n은 셀이 병합할 행의 수를 나타낸다.
- 예를 들어
rowspan="2"는 셀이 두 개의 행에 걸쳐 병합됨을 의미한다.
방법 1. class 비교
- 주어진 클래스 이름을 가진 HTML 테이블 셀(
td) 요소들을 찾아, 동일한 텍스트 콘텐츠를 가진 셀들을 수직으로 병합
const genRowspan = (className: string) => {
const elements = Array.from(document.querySelectorAll(`.${className}`)) as HTMLTableCellElement[];
let lastCategory = "";
let rowspan = 1;
let firstElement: HTMLTableCellElement | null = null;
elements.forEach((element) => {
const text = element.textContent;
if (text === lastCategory) {
rowspan++;
element.style.display = "none";
if (firstElement) {
firstElement.setAttribute("rowspan", rowspan.toString());
}
} else {
lastCategory = text || "";
rowspan = 1;
firstElement = element;
element.removeAttribute("rowspan");
element.style.display = "";
}
});
};
- 전체 코드
useEffect 훅을 사용하여 컴포넌트가 마운트된 후 genRowspan 함수를 호출
"use client";
import { useEffect, useRef } from "react";
import "../styles/table.scss";
export default function Home() {
const data = [
{ category: "Fruit", item: "Apple", price: "$1" },
{ category: "Fruit", item: "Banana", price: "$1" },
{ category: "Vegetable", item: "Carrot", price: "$2" },
{ category: "Vegetable", item: "Broccoli", price: "$2" },
{ category: "Vegetable", item: "Spinach", price: "$2" },
];
useEffect(() => {
genRowspan("category");
}, []);
const genRowspan = (className: string) => {
const elements = Array.from(
document.querySelectorAll(`.${className}`)
) as HTMLTableCellElement[];
let lastCategory = "";
let rowspan = 1;
let firstElement: HTMLTableCellElement | null = null;
elements.forEach((element) => {
const text = element.textContent;
if (text === lastCategory) {
rowspan++;
element.style.display = "none";
if (firstElement) {
firstElement.setAttribute("rowspan", rowspan.toString());
}
} else {
lastCategory = text || "";
rowspan = 1;
firstElement = element;
element.removeAttribute("rowspan");
element.style.display = "";
}
});
};
return (
<div className="container mx-auto py-20 flex flex-col gap-12">
<div className="horizontal-headings">
<table>
<thead>
<tr>
<th>Category</th>
<th>Item</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{data.map((row, index) => (
<tr key={index}>
<td className="category">{row.category}</td>
<td>{row.item}</td>
<td>{row.price}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
방법 2. attribute 비교
- HTML에서 동일한 클래스를 여러 용도로 사용할 수 있지만, 이는 클래스 이름의 중복 문제를 야기할 수 있다.
- 속성을 사용하면 특정 용도에 맞는 고유한 식별자를 제공할 수 있고, JavaScript와 HTML 간의 데이터 전달이 더 명확해진다.
- 속성 값을 비교하여 셀을 합치도록 함수 수정
const genRowspan = (attribute: string) => {
const elements = Array.from(document.querySelectorAll(`[${attribute}]`)) as HTMLTableCellElement[];
let lastCategory = "";
let rowspan = 1;
let firstElement: HTMLTableCellElement | null = null;
elements.forEach((element) => {
const text = element.getAttribute(attribute);
if (text === lastCategory) {
rowspan++;
element.style.display = "none";
if (firstElement) {
firstElement.setAttribute("rowspan", rowspan.toString());
}
} else {
lastCategory = text || "";
rowspan = 1;
firstElement = element;
element.removeAttribute("rowspan");
element.style.display = "";
}
});
};
"use client";
import { useEffect, useRef } from "react";
import "../styles/table.scss";
export default function Home() {
const data = [
{ category: "Fruit", item: "Apple", price: "$1" },
{ category: "Fruit", item: "Banana", price: "$1" },
{ category: "Vegetable", item: "Carrot", price: "$2" },
{ category: "Vegetable", item: "Broccoli", price: "$2" },
{ category: "Vegetable", item: "Spinach", price: "$2" },
];
useEffect(() => {
genRowspan("data-category");
}, []);
const genRowspan = (attribute: string) => {
const elements = Array.from(
document.querySelectorAll(`[${attribute}]`)
) as HTMLTableCellElement[];
let lastCategory = "";
let rowspan = 1;
let firstElement: HTMLTableCellElement | null = null;
elements.forEach((element) => {
const text = element.getAttribute(attribute);
if (text === lastCategory) {
rowspan++;
element.style.display = "none";
if (firstElement) {
firstElement.setAttribute("rowspan", rowspan.toString());
}
} else {
lastCategory = text || "";
rowspan = 1;
firstElement = element;
element.removeAttribute("rowspan");
element.style.display = "";
}
});
};
return (
<div className="container mx-auto py-20 flex flex-col gap-12">
<div className="horizontal-headings">
<table>
<thead>
<tr>
<th>Category</th>
<th>Item</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{data.map((row, index) => (
<tr key={index}>
<td data-category={row.category}>{row.category}</td>
<td>{row.item}</td>
<td>{row.price}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}