HTMLテーブルの<th>や<td>にCSSでwidthを指定しても、意図した幅にならないことがあります。
これはテーブル特有の幅計算アルゴリズムが原因です。
この記事では、幅が効かない原因を7パターンに分類し、それぞれの対処法をコード付きで解説します。
- table-layout: auto と fixed の幅計算の違い
- widthが効かない7つの原因と対処法
- colgroup/col による列幅指定
- 長いテキストやURLが列幅を押し広げる問題の解決
- border-collapse と幅計算の関係
- レスポンシブテーブルでの幅制御
前提知識:テーブルの幅計算アルゴリズム
テーブルの列幅は通常のブロック要素とは異なるアルゴリズムで決定されます。まずこの仕組みを理解することが、問題解決の第一歩です。
table-layout: auto(デフォルト)
デフォルトのautoでは、ブラウザがすべてのセルの内容を読み取ってから列幅を決定します。
table {
table-layout: auto; /* デフォルト値 */
width: 100%;
}
この場合、セルの内容が指定した幅より大きければ、widthは無視されます。ブラウザは「すべての列が見える状態」を優先するため、指定した幅は「希望値」として扱われます。
table-layout: fixed
fixedでは、テーブルの1行目の幅指定だけを見て列幅を決定します。
table {
table-layout: fixed;
width: 100%; /* 必須:tableの幅も指定する */
}
内容に関係なく指定した幅が適用されるため、widthを確実に効かせたい場合はこちらを使います。
| 比較項目 | auto(デフォルト) | fixed |
|---|---|---|
| 列幅の決定基準 | 全セルの内容を考慮 | 1行目の幅指定のみ |
| widthの扱い | 希望値(内容が優先) | 確定値(内容を無視) |
| レンダリング速度 | 遅い(全行を解析) | 速い(1行目だけ解析) |
| はみ出し | 列が広がって対応 | overflow で制御が必要 |
| 使い分け | 内容量が不定のとき | 列幅を厳密に制御したいとき |
table-layout: fixedを使う場合、table自体にもwidth(100%や固定値)を指定してください。width: autoのままだとfixedが正しく機能しません。
table-layout プロパティの詳細な使い分けは「【CSS】table-layout: fixedとautoの違いと使いどころ」で解説しています。
原因①:table-layoutがautoのまま
最も多い原因です。デフォルトのautoではセルの内容量に応じて列幅が自動調整されるため、width指定が無視されます。
問題のコード
/* widthが効かない */
table {
width: 100%;
/* table-layout: auto; がデフォルト */
}
th:first-child {
width: 200px; /* 内容が多いと無視される */
}
対処法
/* widthが効く */
table {
width: 100%;
table-layout: fixed;
}
th:first-child {
width: 200px; /* 確実に200pxになる */
}
原因②:長いテキストやURLがセルを押し広げる
table-layout: fixedにしても、セル内のテキストがはみ出して見える場合があります。列幅自体は指定通りですが、内容がオーバーフローしています。
対処法A:折り返しを許可する
table {
table-layout: fixed;
width: 100%;
}
td {
word-break: break-all; /* 任意の位置で改行 */
}
対処法B:はみ出しを隠す(省略表示)
table {
table-layout: fixed;
width: 100%;
}
td {
overflow: hidden;
text-overflow: ellipsis; /* 「...」で省略 */
white-space: nowrap;
}
| プロパティ | 効果 | 適した場面 |
|---|---|---|
word-break: break-all |
任意の文字位置で改行 | 長いURLやコードを表示するとき |
overflow-wrap: break-word |
単語の途中で改行(単語単位を優先) | 英文テキストを自然に折り返したいとき |
overflow: hidden + text-overflow: ellipsis |
はみ出しを省略記号で表示 | 一覧表で見た目を揃えたいとき |
text-overflow の詳細は「【CSS】text-overflow: ellipsisが効かない原因と解決方法」で解説しています。
word-break と overflow-wrap の違い
/* word-break: break-all は文字単位で強制改行 */
td.url-cell {
word-break: break-all;
}
/* overflow-wrap: break-word は単語の切れ目を優先し、
収まらない場合だけ単語途中で改行 */
td.text-cell {
overflow-wrap: break-word;
}
日本語テキストはデフォルトで文字単位の折り返しが有効なので、問題になるのは主に英語の長い単語やURLです。
原因③:white-space: nowrap が設定されている
セルにwhite-space: nowrapが指定されていると、テキストの折り返しが禁止されます。内容が一行に収まらない場合、セルが指定幅を超えて広がります。
/* 問題:nowrapで幅が効かなくなる */
td {
white-space: nowrap;
width: 150px; /* 内容が150pxを超えると無視される */
}
対処法
/* nowrapを解除する */
td {
white-space: normal; /* デフォルト値に戻す */
width: 150px;
}
/* nowrapを維持しつつ幅を制限したい場合 */
td {
white-space: nowrap;
width: 150px;
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
}
原因④:colgroup / col の幅指定が競合している
HTMLの<colgroup>/<col>要素で列幅を指定している場合、CSSのwidthと競合することがあります。
<table>
<colgroup>
<col style="width: 100px;"> <!-- この指定が優先される場合がある -->
<col style="width: 200px;">
<col>
</colgroup>
<thead>
<tr>
<th>名前</th>
<th>メール</th>
<th>備考</th>
</tr>
</thead>
<!-- ... -->
</table>
対処法
<col>のstyle属性とCSSの指定を統一します。片方に寄せるのが安全です。
/* CSSで統一する場合(推奨) */
table {
table-layout: fixed;
width: 100%;
}
col:nth-child(1) { width: 100px; }
col:nth-child(2) { width: 200px; }
/* 3列目は残りの幅が自動的に割り当てられる */
ポイント:table-layout: fixedでは、<col>の幅 → 1行目の<th>/<td>の幅の順で参照されます。<col>に幅が指定されていればそちらが優先されます。
原因⑤:セルの結合(colspan/rowspan)の影響
colspanで結合したセルがある行では、列幅の計算が変わります。
<table>
<tr>
<th colspan="2">名前(姓・名)</th> <!-- 2列分を結合 -->
<th>メール</th>
</tr>
<tr>
<td style="width: 80px;">山田</td>
<td style="width: 80px;">太郎</td>
<td>taro@example.com</td>
</tr>
</table>
table-layout: fixedは1行目の幅指定を基準にするため、1行目がcolspanで結合されていると個別の列幅が正しく設定されません。
対処法
<colgroup>で列幅を明示するか、結合のない行を1行目にします。
<table>
<colgroup>
<col style="width: 80px;">
<col style="width: 80px;">
<col>
</colgroup>
<tr>
<th colspan="2">名前(姓・名)</th>
<th>メール</th>
</tr>
<!-- ... -->
</table>
セル結合の詳しい書き方は「【HTML】テーブルのセルを結合する方法」をご覧ください。
原因⑥:border-collapse と padding/border の影響
テーブルのデフォルトのbox-sizingはborder-boxです。しかしborder-collapseの設定によってボーダーの計算方法が変わり、意図した幅にならないことがあります。
| プロパティ | 効果 | 幅への影響 |
|---|---|---|
border-collapse: separate(デフォルト) |
各セルが独立したボーダーを持つ | border-spacingの分だけ余分に幅を消費 |
border-collapse: collapse |
隣接セルのボーダーを共有 | ボーダー幅が半分ずつ分配される |
/* border-spacing が幅に影響している場合 */
table {
border-collapse: separate;
border-spacing: 10px; /* セル間に10pxの隙間 × 列数分 */
}
/* 対処法:collapse にして余分な隙間を排除 */
table {
border-collapse: collapse;
/* border-spacing は無効になる */
}
計算例:4列のテーブルでborder-spacing: 10pxの場合、セル間の隙間だけで10px × 5 = 50px(列数+1箇所)が余分に消費されます。これが列幅のズレの原因になります。
原因⑦:width / min-width / max-width の使い分けミス
テーブルセルに対するwidth、min-width、max-widthの挙動は、通常のブロック要素とは異なります。
| プロパティ | table-layout: auto | table-layout: fixed |
|---|---|---|
width |
希望値(内容が優先) | 確定値 |
min-width |
尊重される | widthが優先(効きにくい) |
max-width |
効かないことが多い | 効かないことが多い |
/* auto の場合:min-width で最低幅を保証 */
table {
table-layout: auto;
width: 100%;
}
td:first-child {
min-width: 120px; /* 内容が少なくても120px以上を保証 */
}
/* fixed の場合:width で確定幅を指定 */
table {
table-layout: fixed;
width: 100%;
}
td:first-child {
width: 120px; /* 正確に120pxになる */
}
max-widthは基本的に効きません。これはCSSの仕様上、テーブルのレイアウトアルゴリズムがmax-widthを考慮しないためです。幅の上限を設けたい場合はtable-layout: fixed+widthで制御してください。
実践パターン:よくあるレイアウトの実装
パターン①:1列目を固定幅、残りを均等割り
table {
table-layout: fixed;
width: 100%;
}
th:first-child,
td:first-child {
width: 200px;
}
/* 残りの列は自動的に均等割りされる */
パターン②:列ごとに割合を指定
table {
table-layout: fixed;
width: 100%;
}
<table>
<colgroup>
<col style="width: 20%;">
<col style="width: 50%;">
<col style="width: 30%;">
</colgroup>
<!-- ... -->
</table>
パターン③:レスポンシブ対応(スマホで横スクロール)
/* テーブルをラッパーで囲む */
.table-wrapper {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.table-wrapper table {
min-width: 600px; /* 最低幅を保証 */
width: 100%;
table-layout: fixed;
}
<div class="table-wrapper">
<table>
<!-- ... -->
</table>
</div>
overflowは<table>に直接指定しても効きません。必ず外側の<div>に指定してください。
テーブルの横スクロールについて詳しくは「【CSS】テーブルを横スクロールさせる方法」、スマホ対応については「【CSS】スマホのみテーブルを横スクロール可能にする方法」をご覧ください。
トラブルシューティング早見表
widthが効かない場合に、原因を素早く特定するためのフローチャートです。
| 症状 | 最も可能性が高い原因 | 対処法 |
|---|---|---|
| 列幅がすべて均等にならない | table-layout: auto のまま | table-layout: fixed + width: 100% |
| 指定した幅より広くなる | セルの内容が長い | word-break: break-all または overflow: hidden |
| 指定した幅より広くなる(nowrap) | white-space: nowrap |
white-space: normal に変更 |
| 列幅がわずかにズレる | border-spacing の影響 | border-collapse: collapse |
| colspanのある行で幅がおかしい | fixed が1行目を基準にする | <colgroup>で列幅を定義 |
| max-widthが効かない | テーブルの仕様上の制限 | table-layout: fixed + width で代替 |
| スマホで列が潰れる | テーブル幅がビューポートを超過 | ラッパーdivにoverflow-x: auto |
| pxと%が混在して計算がおかしい | 単位の混在 | 一つの単位(%推奨)に統一 |
よくある質問(FAQ)
まとめ
テーブルのwidthが効かない原因と対処法を整理します。
| 原因 | 対処法 | キーとなるプロパティ |
|---|---|---|
| table-layout: autoのまま | fixedに変更 + テーブル幅を指定 | table-layout: fixed |
| 長いテキスト/URLがはみ出す | 折り返しまたは省略表示 | word-break / overflow |
| white-space: nowrap | 解除またはoverflow制御 | white-space: normal |
| colgroup/colの競合 | CSSに統一 | col:nth-child() |
| colspan が1行目にある | colgroupで列幅を定義 | <colgroup> |
| border-spacing の影響 | collapseに変更 | border-collapse: collapse |
| width/min-width/max-widthの誤用 | layoutモードに合った指定 | auto→min-width / fixed→width |
最も確実な方法はtable-layout: fixedとwidth: 100%をテーブルに指定し、各列の幅を<colgroup>または1行目の<th>で定義することです。
