「overflow: scroll」要素の内部の右側のmarginを空ける方法

CSS

CSSを使って横方向にコンテンツをスクロール表示する要素を作っているとき、要素の内側の右側にmarginもpaddingも利かなくて困っていることはないだろうか?

今回はそんな問題を解決する方法を紹介する。

また、正しくやっているはずなのにうまくいってないときに見落としがちなポイントも併せて解説する

スクロール要素の中で起きている問題

まずはscroll要素を確認してみよう。下のデモをよく見て、左右にスクロールしてほしい。

content-1

content-2

content-3

content-4

content-5

おわかりいただけただろうか?

content-5の右が詰まってる!
marginはどこにいったの?

もうちょっと例を確認しよう。

このようにcontentに影を付けてhoverエフェクトも付与したらこうなってしまう。

content-1

content-2

content-3

content-4

content-5

content-5の右側の影が切れてデザインが崩れちゃってる……

困った……原因はなんなのだろうか?

いちばん右側のmarginだけが効いてない理由

さて、なぜcontent-5の右のmarginが効かないのか?

理由は簡単であり、先ほどのふたつのデモの「demo」と「demo-content」の部分のCSSコードはこのようになっている。

CSSコード
/*DEMOのCSSコード*/
/*ここから*/
.mycus-demo .demo {
	display: flex;
	flex-wrap: nowrap;
	justify-content: unset;
	overflow-x: auto;
	overflow-y: hidden;
	background-color: #fff;
}

.mycus-demo .demo-content {
	min-width: 30%;
	margin: 10px 20px;
	background-color: #999;
	color: #fff;
	text-align: center;
}
/*ここまで*/

上のコードをよく読んでもらうと理解できると思うが、「demo」要素にはwidthを指定していない。「demo」からあふれてはみ出した「demo-content」要素が「demo」要素を右へ右へと押し広げていく表示形式となっているのだ。

つまり、「demo」の幅は設定されていないので、内部の長さは無限と同じ状態となっている。だが、どこかで終わらせないと無限にスクロールすることになってしまう。なので、最後に表示されるコンテンツの右端で要素の幅を切って終わらせるようになっている

こんなわけで右の間隔が無くなってしまっているのだ。

なぜ「demo」の幅を設定しないのか?ということを疑問に思った人がいるだろう。

これは、スクロール要素の内部に表示されるコンテンツの数がページによって増減しても影響を受けないというメリットがあるからだ。

例えば、Aというカテゴリーの記事ではスクロール要素に表示される「demo-content」の数が18個だとする。しかし、Bというカテゴリーの記事では「demo-content」は6個しか表示されない。

この場合、「demo」要素を入れ子にして、その要素の幅を「width: 300%;」としたらAではピッタリ表示される。しかし、Bでは空のスクロールが発生してしまう。これでは不格好だ。

なのでスクロール要素に幅を設定することは避けたほうがいいのだ。

なお、このことの説明についてはこちらでも行っている。

「demo」のwidthをJavaScriptで制御する方法もあるんじゃないの?と思う人もいるかもしれない。

だが、今日Googleが推し進めるAMPのことを考えると、JSを使用することは極力避けたいのでCSSだけを使うことにしている。

padding-rightは意味がない理由

じゃあ、「demo」要素に「padding-right」をあてればいいんじゃね?と考えた人もいるかもしれない。そして、実際にやってみて意味がないことに気が付くはずだ。

この現象をわかりやすくイラストにしたので見てほしい。

まず、これがスクロールバーが左端にあるとき

そして、これがスクロールバーを右端まで動かしきったとき

薄黄緑色のpadding-rightの位置が動いていないことがおわかりいただけただろうか?

先ほど、スクロール要素内部の幅は無限だと説明した。しかし、「demo」には幅がある。

「demo」には決められた幅がある、だが「demo」の内部には幅が無い……なぜなら「demo」の中身は「demo」に収まっていないから

このため、「demo」にはpaddingがかかるものの、その中身に影響を与えることは不可能なのだ。

こういったわけで、padding-rightは意味がないということになる。

最後のコンテンツの右側の間隔を空ける方法

それでは、この問題を解決する方法を紹介しよう。

まず、このデモを見て動かしてほしい。

content-1

content-2

content-3

content-4

content-5

content-5の右側にちゃんと空間がある!

このデモのCSSコードはこのようになっている。

CSSコード
/*最後のコンテンツの右側の間隔を空けるCSSコード*/
/*ここから*/
.mycus-demo .demo-3 .demo-content {
	position: relative;
	overflow: visible;
}

.mycus-demo .demo-3 .demo-content:last-child::after {
	position: absolute;
	content: '';
	top: 0;
	left: 100%;
	height: 1px;
	width: 20px;
	background-color: transparent;
}
/*ここまで*/

先ほどの「いちばん右側のmarginだけが効いてない理由」を読んでいてピンときた人もいると思われるが、「overflow: scroll;」はふつう最後のコンテンツの右端で終わる仕組みとなっている。

ということは、最後のコンテンツだけ横のmargin分の長さをもった疑似要素を付け足してコンテンツの右側にはみ出すように配置すると、疑似要素のぶんだけスクロール要素内が押し広げられて、右のmarginが存在しているように見せることができるわけだ。

CSSコード解説
  • まず「demo-content(あなたがスクロール要素内に表示したいコンテンツ)」に「position: relative;」と「overflow: visible;」を設定する。
    コンテンツ自体に相対要素を設定することで、疑似要素の位置を定めることができる。
    また、overflowをvisibleに設定することも重要だが、これは後で詳しく説明する。
  • 次に、疑似クラス「:last-child」でスクロール要素内の最後のコンテンツだけを選び、「::after」を後ろに繋げて疑似要素を作る。
  • 疑似要素は「demo-content」を基準にした絶対位置に設定するので「position: absolute;」と入力する。
    そして、「content: ”;」で空のコンテンツを作成する。
  • 疑似要素の位置を決める必要がある。
    top: 0;」とすることで「demo-content」の最上部に固定、「left: 100%;」とすることで疑似要素を「demo-content」の右端まで持っていてはみ出させることができる。
    「left: 100%;」とすることで、疑似要素の位置が左から100%離れて始まる、つまり右端の外に位置することができる。
  • 疑似要素に高さがないと疑似要素が存在できないので「height: 1px;」に設定。1pxだけあれば充分である。
    そして、現在「demo-content」同士の横のmarginは20pxにしているので、疑似要素の幅を「width: 20px;」に設定する。
  • 最後に疑似要素を確実に透明にしておきたいので、背景を「background-color: transparent;」に指定して完了。これは念のためであるのだが。

だいたいはこのような説明になる。

「:疑似クラス」と「::疑似要素」は繋げて書くことができる

コロンがひとつだけが前、コロンがふたつが後ろと覚えよう

以上のようにすることで、右側の間隔を空けることができる。

うまくいっていないときに見直す箇所

さて、上記のようにやっているのにうまくいかない人はいるだろうか?

そのときは、スタイルシートのCSSをよくみてみよう。そして以下を確認してみよう。

overflow: hidden;」がスクロール内のコンテンツにかかってない?
そのせいで「overflow: visible;」が無効化されてない?

CSSの基本として、子孫結合には強さが存在する

より具体的に、そしてより上の親がセレクターに存在しているとそちらのほうが強くなるので上書きできないのだ。

こちらのデモを見てほしい。content-5の右側に空間が存在していないだろう。

content-1

content-2

content-3

content-4

content-5

そして、このデモのCSSコードはこうなっている。

CSSコード
/*visibleが効いていない例*/
/*ここから*/
.main .mycus-demo .demo-4 .demo-content {
	overflow: hidden;
}

.mycus-demo .demo-4 .demo-content {
	position: relative;
	overflow: visible;
}

.mycus-demo .demo-4 .demo-content:last-child::after {
	position: absolute;
	content: '';
	top: 0;
	left: 100%;
	height: 1px;
	width: 20px;
	background-color: transparent;
}
/*ここまで*/

このデモでは、CSSでより親のクラスである「.main」が前に付いて「.demo-content」に「overflow: hidden;」を指定してしまっている。

そしてoverflowプロパティのhiddenは、その要素からはみ出した部分を非表示にする役割を持っている。

このため、「overflow: visible;」が無効化されたので疑似要素も非表示化されてしまい、最後のコンテンツの右側に間隔が存在しなくなっているのだ。

これを解決するには、「.main」より上の親、例えば「.content」を使って「.content .mycus-demo .demo-4 .demo-content」と書いてやればいい

もしくは、「.mycus-demo」と「.demo-content」の間にあるセレクターを全部選んでもっと具体的に命令する方法でもよい

このふたつのうちのどちらかの方法で、「overflow: hidden;」を消すことができる。

もしWrodPressのテーマをカスタマイズしている最中に、うまくいっていない場合はこのことをよく覚えておいてほしい。

コンテンツの外観を整えるために「overflow: hidden;」とあらかじめ設定されているかもしれないからだ。

間隔を空ける方法のまとめ

「:last-child::after」で最後のコンテンツを選んで、右側にmarginぶんの長さの透明な疑似要素を作って配置しよう

もしうまくいってなかったら、より強い子孫結合で「overflow: hidden;」が設定されていないか確認してみよう

このふたつに注意してスクロール要素内の右の間隔を空けてみてほしい。


スクロール要素の右側の空間がどうしてもうまくいかないのでその原因を調べていたところ、様々なことがわかったので忘備録的に書き留めておくことにした。

特に「overflow: hidden;」が原因となって疑似要素が無効化されることになかなか気が付けなかったので。

この記事がカスタマイズの助けになり、また悩みを解決していると嬉しい。