透過オーバーレイと、固定された背景

- 「半透明のオーバーレイを画面全体に表示する」
- 「オーバーレイを表示したタイミングで背景をスクロール位置で固定」
- 「オーバーレイを閉じた時、スクロールしていた位置を保持」

といった要件を満たす画面を作るにはどうすればいいのか？

実際に使った方法を記録しておきます。

# サンプルコード

まずはサンプルコードを見てみましょう。
全体を載せているので、少し長いです。

```html:fixed.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<link rel="stylesheet" href="fixed.css">
	<title>背景固定のオーバーレイ</title>
</head>
<body>
	<main id="jsi-main" class="main">
		<a href="javascript:void(0);" id="jsi-open-overlay" class="open-overlay">オーバーレイを表示</a>
		<div class="content-wrapper">
			<div class="content1">コンテンツ1</div>
			<div class="content2">コンテンツ2</div>
			<div class="content3">コンテンツ3</div>
		</div>
		<div id="jsi-overlay" class="overlay">
			<a href="javascript:void(0);" id="jsi-close-overlay" class="close-overlay">オーバーレイを閉じる</a>
		</div>
	</main>
	<script src="fixed.js"></script>	
</body>
</html>
```

```css:fixed.css
body {
	width: 100%;
	height: 100%;
	margin: 0;
	padding: 0;
}
.main {
	width: 100%;
	height: 100%;
}
.content-wrapper {
	width: 100%;
	height: auto;
}
.content1,
.content2,
.content3 {
	width: 100%;
	height: 500px;
}
.content1 {
	background: #FF0000;
	margin-top: 50px;
}
.content2 {
	background: #00FF00;
}
.content3 {
	background: #0000FF;
}
.open-overlay {
	position: fixed;
	top: 0;
	left: 0;
	display: block;
	background: #FFFFFF;
	width: 100%;
	height: 50px;
}
.overlay {
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	background-color: #000000;
	display: none;
	opacity: 0.5;
}
.close-overlay {
	position: absolute;
	top: 0;
	right: 0;
	color: #FFFFFF;
	display: inline-block;
}
```

```javascript:fixed.js
/* スクロール位置を保持しつつ、オーバーレイを表示する */

var scrollPosition,
	main = document.getElementById('jsi-main'),
	overlay = document.getElementById('jsi-overlay'),
	overlayOpener = document.getElementById('jsi-open-overlay'),
	overlayCloser = document.getElementById('jsi-close-overlay');

// オーバーレイの開閉処理
overlayOpener.addEventListener('click', function(){
	// スクロール位置保持
	scrollPosition = document.documentElement.scrollTop || document.body.scrollTop;
	overlay.style.display = 'block';
	main.style.position = 'fixed';
	main.style.top = '-' + scrollPosition + 'px';
});
overlayCloser.addEventListener('click', function(){
	overlay.style.display = 'none';
	main.style.position = 'static';
	main.style.top = 0;
	window.scrollTo(0, scrollPosition);
});
```

# 実装の手順・ポイント等

## 挙動とポイント
スクロールに追従するヘッダー内にある「オーバーレイを表示」をクリックすると、
ページ全体に覆いかぶさるようなオーバーレイが表示されます。
オーバーレイの右上にある「オーバーレイを閉じる」をクリックすると、オーバーレイが閉じます。
今回、ポイントとなるのは「__スクロール位置を記憶しつつ、オーバーレイの開閉を行える__」というところです。

## スクロール位置の記憶
```javascript:fixed.js
// オーバーレイの開閉処理
overlayOpener.addEventListener('click', function(){
	// スクロール位置保持
	scrollPosition = document.documentElement.scrollTop || document.body.scrollTop;
	overlay.style.display = 'block';
	main.style.position = 'fixed';
	main.style.top = '-' + scrollPosition + 'px';
});
overlayCloser.addEventListener('click', function(){
	overlay.style.display = 'none';
	main.style.position = 'static';
	main.style.top = 0;
	// 保持したスクロール位置まで戻す
	window.scrollTo(0, scrollPosition);
});
```

コードの一部抜粋です。
2行目：オーバーレイを表示時のスクロール位置を変数に保持する。
11行目：オーバーレイを閉じた後、保持しておいたスクロール位置まで画面をスクロールさせる。
という処理を行うことで、スクロール位置の保持を行っています。

## オーバーレイを被せている要素を固定する
スクロール位置を保持する以外にもポイントがあります。

半透明のオーバーレイを表示している間、背後の要素をposition: fixedで固定しておかないと
背後の要素をスクロール出来てしまうということです。
オーバーレイを表示している間は、背後の要素をスクロールする必要はないので対応策を打っておく方が良いでしょう。

```javascript:fixed.js
// オーバーレイの開閉処理
overlayOpener.addEventListener('click', function(){
	scrollPosition = document.documentElement.scrollTop || document.body.scrollTop;
	overlay.style.display = 'block';

	// 背後要素の固定
	main.style.position = 'fixed';
	main.style.top = '-' + scrollPosition + 'px';
});
overlayCloser.addEventListener('click', function(){
	overlay.style.display = 'none';

	// 背後要素の固定解除
	main.style.position = 'static';
	main.style.top = 0;
	window.scrollTo(0, scrollPosition);
});
```

- オーバーレイを表示する時に、topの値を指定しつつ背景を固定
- オーバーレイを閉じる時、topの値をリセットしつつ背景の固定を解除

topの値を指定しているのは理由があります。
topの初期値は0なので、topの値を指定せずにposition: fixedをあててしまうと固定した背景が上に戻ってしまうためです。

# まとめ

（個人的にはあまり好きではないんですが）実装する機会が多いオーバーレイ表示と、透過であえて見せている背景の固定について簡単に説明してみました。

- __より効率の良い実装方法__
- __上のコードの問題点__

等ありましたら、コメントで教えて頂けると嬉しいです！