2016.2.10
0からはじめるPHP#79【親要素を基準にしたfixed】
前回スクロールスパイの話をしましたが、CSSだけでなんとか実装することができました。
そもそも何でユーザビリティに問題があるのかっていうと、Flexを用いると・・・・・・まぁ要するに
フレームになるのと同義なので、該当するFlexアイテムの上でスクロールしないとスクロールできないという問題がありましてね。画像用意するのはめんどくさいので口頭で理解して下さいお願いします。←
同様の実装は、日記ログの目次部分にあたりますね。目次の上でスクロールしても本文はスクロールしませんよね。
ただ、twitterみたいなやつだと、マウスカーソルがどこにあってもスクロールしてくれるのが一番使い勝手が良いのは分かりきったことなので、これをどう実装するか、という問題がありました。
一つはjavascriptでスクロールスパイを実装する方法だったのですが、これは要するにスクロールが発生したらpositionをfixedにして画面に固定する、というやり方です。
しかし
Flexアイテムにfixedは使えないというCSS上の制約により、この方法は使えなくなりました。マージンが消失してしまうんですね。
ただ、このサイトのトップページのようにサイドバーが長いワケではないので、最初からfixedで固定しても良いわけですね。これだとjavascriptを使わなくても良いです。
しかし、やはりfixedはflexと相性が最悪なので、画面幅に応じた左右のマージンというのが消失してしまうわけですね。でも、画面左端にべったりというのはダサいですし、やはり中央寄せがしたいところです。
そこで
position:fixedで、左端を親要素基準にするというページを見つけましたので、これを参考にcssを組み立ててみます。
<body>
<div id="parent">
コンテンツ
<div id="child">左メニュー</p>
</div>
</body>
まず、htmlのコツとしては左メニューを子要素にすることですね。兄弟じゃないんですねこれが。見た目上は、何らかのコンテナに入ってそうに見えちゃいますけど、そうすると上手くいかないです。
#parent{
}
#child{
width:313px;
position:fixed;
left:auto;
margin:0 0 0 -313px;
padding:0;
}
実際使ってるコードがこんなんです。これで#parentを基準にしてfixedさせることができます。あとは#parentの方をいい感じに弄れば良いと思います。
ちなみに実際は私は#parentの方をwidth:58%にして、あとはleftとかちょいちょいいじって、positionをabsoluteにして・・・・・・ということをやってます。この辺の調整にはだいぶ苦労してますが、まぁ現状満足です。
ついでに、脱flexということで、上のメニューバーもfixedで固定させました。ぶっちゃけflex使うよりラクでした。差分はbodyにpaddingでもさせとけば良いです。
さらっと書いてますが、これで半日使ってます。
だからCSSは融通効かなくて嫌いなんですよ
・0からはじめるPHP#80【Bootstrapに手を加える-多重モーダル-】
夢の二本立てです。
2本ともPHPは全く関係ないんですけどね。
今回はBootstrapのお話です。
Bootstrapの基本機能の一つに
Modalという機能があります。まぁ要するにポップアップです。
こいつのお陰で、ページ遷移なしに、ちょっとしたアラートを表示させることができます。この前見せた「会話」のダイアログも、このモーダルを用いて実装しています。とても便利です。
ただ、このモーダルの挙動を観察してみると、以下のような流れになってます。
1.スクロールバーを消す(これをしないと背景がスクロールする)
2.スクロールバーを消した場合、その分のマージンを入れて誤魔化す
3.モーダルを出す
4.モーダルを消す
5.スクロールバーを戻し、マージンを消す
通常の使用であれば問題ないのですが、fixedを用いてる場合、画面幅が変化することにより
モーダルを開くと画面がズレるんですよ。これはウザい。
.modal-open {
overflow-y: scroll;/*autoでも可?*/
}
これを解決するには、modal-openクラスのoverflowプロパティを上書きしてしまいましょう。すると、上の過程の(1)がなくなります。
ただ、そうすると(2)の過程が要らなくなるので、Bootstrap本体の方に手を加えて消しちゃいましょう。
Modal.prototype.show = function (_relatedTarget) {
...
this.checkScrollbar()
//this.setScrollbar()//これをコメントアウト
this.$body.addClass('modal-open')
...
}
/*もしくは・・・・・・*/
Modal.prototype.setScrollbar = function () {
/*
これ全部コメントアウト
var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
this.originalBodyPad = document.body.style.paddingRight || ''
if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
*/
}
(2)の過程を担っている部分をコメントアウトしてしまいましょう。
そもそも関数を呼びださなければ良いので、showメソッドの中にある関数呼び出しをコメントアウトするだけで良いと思います。
さて、これで画面のズレは解決しましたが、スクロールバーを出現させたままにしたことで
背景のスクロールが有効になってしまうという問題が新たに浮上します。ユーザビリティガタ落ちです。
ということで
モーダルを開いている状態では背景を固定するCSSを設定するという方針で行きましょう。
Modal.prototype.show = function (_relatedTarget) {
var pointY = $(window).scrollTop();
$("body").css({
"position": "fixed",
"width": "100%",
"top": -pointY
});
~略~
Modal.prototype.hide = function (e) {
~略~
$("body").css({
"position": "relative",
"width": "",
"top": ""
});
}
Bootstrap自身もjQueryで書かれてますので、そのまま書き加えてしまいましょう。
showメソッドが呼び出された時、bodyにcssを設定し、hideが呼び出された時にそれを解除すれば良いです。具体的な解説はめんどくさいのでしません。
普通はこれで望む動作が得られますが、実はまだ問題があります。
何が問題かというと
2重にモーダルを呼び出した時が問題になります。
モーダルを二重に呼び出した時、一方のモーダルを解除するためにhideが呼ばれるのですが、まだモーダルが残ってるのにbodyの固定を解除してしまうのが問題になります。
つまり、モーダルがまだ残ってるかどうかの処理が必要になりますね。
if(!$("#モーダルのID.in")[0]){
$("body").css({
"position": "relative",
"width": "",
"top": ""
});
}else{
$("#モーダルのID.in").css({
"overflow-y":"auto"
});
$("body").css({
"overflow-y":"scroll"
});
}
コードを読んでも、どこでスクロールバーを戻してるのかがいまいちはっきり分かんなかったので、メソッドの最後にこいつをぶっ込んで再設定させればいいです。(笑)
親となるモーダルが生きてるかどうかの分岐を最後に差し込みます。モーダルが開いてる間はinというクラスがモーダルにくっつくので、それを利用しています。
ちなみに、この例では二重に開く可能性があるモーダルが1通りしかないのでこのような実装になってますが、複数通り考えられる場合はテキトーに調整すればたぶん動くはずです。Chromeでしか確認してませんけど、現状これでなんとか動いてくれます。
モーダルの二重呼び出しなんてケースを解説してるサイトなんかあんまないので、この辺は完全に自分の考えで実装してます。初心者のゴリ押し戦法なので使う方はあんまり信用しないでくださいね(笑)
これでとりあえず表示上の問題は全てクリアしたことになります。
まぁ、強いてあげるとすれば、モーダル自体がスクロールバーを持つようになると、スクロールバーが二重に表示されてダサいんですが、片方を消すと画面がズレるので、背景がスクロールしなければいいじゃんという結論に達しました。
もうこんなの気にしてるとBootstrap使わずに自分で実装しろって話になってとてもめんどくさいので諦めます。しんどいです。
この2つの問題を解決するのに丸一日費やしてしまいました。全然おもしろくないです。やっぱデザインって嫌いです。(笑)
・0からはじめるPHP#81【Bootstrapに手を加える-多重モーダル(追記)-】
上の記事をアップロードしてから
新たな問題を見つけてしまったので、改善策を。
どういう問題かというと、スクロールした状態でモーダルを表示させると、モーダルを解除した時に
スクロール位置が最上部に戻ってしまうという致命的な欠陥を抱えていました。
つまり、モーダルを閉じた時、スクロール位置を本来の位置に戻す作業がまた新たに必要になってくるわけですね。
var Modal = function (element, options) {
this.options = options
this.$body = $(document.body)
this.$element = $(element)
this.$dialog = this.$element.find('.modal-dialog')
this.$backdrop = null
this.isShown = null
this.originalBodyPad = null
this.scrollbarWidth = 0
this.ignoreBackdropClick = false
//self
this.pointY_self =null;//これを追加
まず、オブジェクトなのかメソッドなのかよくわかんない変数があるんで、そこにクラス変数のようなものが定義されてます。これにスクロール位置を格納する変数を定義しておきます。
ここに置くことで、showメソッドとhideメソッドで共通した値を持たせることができます。変数が被ることがないように、_selfという語尾をつけてますがなくても良いかも知れません。念には念をね。
$(\body').css({
.........
"top": ""
});
$(window).scrollTop(this.pointY_self);//これを追加
再設定してる部分にこれを追加すればいいです。すると閉じた時に元に戻ります。
あ、当然showメソッドの該当部分の変数の名前を変えるのを忘れないようにしてくださいね。
こういう細かいとこもちゃんと実装しなくてはいけないので、完成が更に遠くなっていきます・・・・・・あはは・・・・・・。