CSSでテーブルの一部を固定してスクロールさせる方法(IEはjQueryで対応)

HTMLで組んだテーブルを使って表示したいことがある時に、長くなりすぎてスペースに入り切らなかったり、見た目がよろしくない感じになることがあります。
そういう場合に「Excelのウィンドウ枠の固定」のように、一部を固定してスクロールさせる方法をご紹介します。

IE非対応でも問題がない場合は、CSSプロパティ「position: sticky;」を使用すれば一発で実装可能です。

  1. stickyで固定してスクロール
  2. stickyはIE非対応
  3. IEに対応させるためjQueryで固定

stickyで固定してスクロール

HTML

<div class="table-outer">
	<table>
		<tr class="sticky">
			<th>見出し</th>
			<th>見出し</th>
			<th>見出し</th>
		</tr>
		<tr>
			<td>テキスト</td>
			<td>テキスト</td>
			<td>テキスト</td>
		</tr>
		<tr>
			<td>テキスト</td>
			<td>テキスト</td>
			<td>テキスト</td>
		</tr>
		<tr>
			<td>テキスト</td>
			<td>テキスト</td>
			<td>テキスト</td>
		</tr>
		<tr>
			<td>テキスト</td>
			<td>テキスト</td>
			<td>テキスト</td>
		</tr>
		<tr>
			<td>テキスト</td>
			<td>テキスト</td>
			<td>テキスト</td>
		</tr>
	</table>
</div>

CSS

body {
	margin: 0;
}
.table-outer {
	height: 300px;
	overflow-y: scroll;
}
table {
	width: 100%;
	border-spacing: 0;
	border-collapse: collapse;
}
th, td {
	padding: 20px;
	text-align: center;
	vertical-align: middle;
	border: 1px solid #ccc;
}
th {
	color: #fff;
	background: #333;
}
td {
	height: 150px;
}
.sticky th {
	position: sticky;
	top: 0;
}
.sticky th:before {
	position: absolute;
	top: -1px;
	left: -1px;
	width: 100%;
	height: 100%;
	content: "";
	border: 1px solid #ccc;
}

簡単な解説

「position: sticky;」は「指定された場所までいくと固定」される仕様です。上部で固定したいので、「top: 0;」を指定しています。
スクロールされている要素が見えてしまうのを防ぐため、「:before」で固定した要素に枠線を設定しています。

stickyはIE非対応

position: sticky;」は今の所IE非対応です。

stickyfillというjsを使えばIEに対応させることが可能なのですが、こちら現時点でtableには効きません。

  • works with table cells removed for consistency until Firefox makes a native implementation
  • https://github.com/wilddeer/stickyfill

    「IEではthが固定されない」というだけで表示には問題がないため、割り切って使うのもありだと思いますがIEに対応したい場合の記述もご紹介します。

    IEに対応させるためjQueryで固定

    HTML

    <div class="table-outer">
    	<table>
    		<tr class="fixed">
    			<th>見出し</th>
    			<th>見出し</th>
    			<th>見出し</th>
    		</tr>
    		<tr>
    			<td>テキスト</td>
    			<td>テキスト</td>
    			<td>テキスト</td>
    		</tr>
    		<tr>
    			<td>テキスト</td>
    			<td>テキスト</td>
    			<td>テキスト</td>
    		</tr>
    		<tr>
    			<td>テキスト</td>
    			<td>テキスト</td>
    			<td>テキスト</td>
    		</tr>
    		<tr>
    			<td>テキスト</td>
    			<td>テキスト</td>
    			<td>テキスト</td>
    		</tr>
    		<tr>
    			<td>テキスト</td>
    			<td>テキスト</td>
    			<td>テキスト</td>
    		</tr>
    	</table>
    </div>

    CSS

    body {
    	margin: 0;
    }
    .table-outer {
    	height: 300px;
    	overflow-y: scroll;
    }
    table {
    	width: 100%;
    	border-spacing: 0;
    	border-collapse: collapse;
    }
    th, td {
    	box-sizing: border-box;
    	padding: 20px;
    	text-align: center;
    	vertical-align: middle;
    	border: 1px solid #ccc;
    }
    th {
    	color: #fff;
    	background: #333;
    }
    td {
    	height: 150px;
    }
    .fixed {
    	position: fixed;
    	top: 0;
    }

    jQuery

    $(function() {
    	var tdWidth = $('.fixed + tr td').width();
    	$('.fixed th').width(tdWidth);
    	var fixedHeight = $('.fixed').height();
    	$('table').css('margin-top',fixedHeight);
    });

    簡単な解説

    IEではstickyが使えないため、fixedを使用します。
    fixedを指定するとwidthが要素内に記述されている文字列の長さの幅になるため、次の行のtdのwidthを取得して指定します。 このままでは固定した行の下に次の行が潜ってしまうため、固定した行の高さを取得しtableの上marginに指定します。

    fixedは位置指定をブラウザからの位置で指定するため、tableでの使い勝手はあまりよくありません。
    はやいところstickyがIEで標準対応されるか、stickyfillがtableでも使えるようになってほしいですね。

    書いた人:やまライダー(嫁)

    Web屋さんで8時間フルタイム勤務をしている、いわゆるワーキングマザー。
    夫婦共働きで、ムスメ氏を育てています。
    主に資産運用にかかわるお金の話、ムスメ氏を育てていく上での子育ての話、そしてたまにお仕事関連の記事を書いていく予定。
    プロフィールを見る