新 Aki SI&Eサイト構築と、納得できないCSS解釈

作り直してるよという話はしていたが、ついに全面刷新となったAki SI&Eの作成に着手した。

昨日1日でlightboxプログラムを作り、さらにメニューボタンアニメーションを作った。

Lightboxアプリケーションは作った時点で1kBを切っており、相当軽量な内容になっていた。
JQueryなどは使っていない。

(function() {
  if (! document.addEventListener ) { return false; }
  
  var wrapper = document.getElementById("WrapWindow") /* ModalWindow */
  var fadingTimer = false /* IntervalTimer */
  var alpha = 0.0 /* ModalWindows's alpha number */

  /* draw content */
  var displayContent = function() {
  }
  
  /* fading out (interval callback) */
  var fadeout = function() {
    if (alpha < 0.8) {
      alpha = alpha + 0.05
      wrapper.style.backgroundColor = "rgba(0,0,0," + alpha + ")"
    } else {
      clearInterval(fadingTimer)
      fadingTimer = false
      displayContent()
    }
  }
  
  /* set this for event callback */
  var setLightboxTrigger = function(e) {
    wrapper.style.visibility = "visible"
    fadingTimer = setInterval(fadeout, 30)
  }

  /* Return from lightbox */
  wrapper.addEventListener("click", function(e) {
    if (fadingTimer) {
      clearInterval(fadingTimer)
    }
    wrapper.style.backgroundColor = "transparent"
    wrapper.style.visibility = "hidden"
    alpha = 0.0
  }, false)

  
  document.getElementById("SideNotes").addEventListener("click", setLightboxTrigger, false)


  
})()

基本的な作りはごく単純だ。
htmlbodyheight: 100%;を持っていて、

<section id="WrapWindow">
</section>

#WrapWindow {
  visibility: hidden;
  z-index: 10000;
  background-color: #000; /* for RGBa Unsupported browseres */
  background-color: rgba(0,0,0,0);
  min-height: 100%;
  min-width: 100%;
  position: fixed;
  top: 0px;
  bottom: 0px;
}

となっている。これにより見えていないし触れることもできないが見えている画面より手前に全体を覆うブロックがあり、これがvisibleになることで全体を覆って操作不能にする。

プログラム全体を関数で書こうことで、全体でアクセスできるプロパティを外側に伝播しないように閉じ込めることができる。「クロージャを作ることで変数をローカル化し直ちに呼び出す」というテクニックは、オライリーのJavaScriptで紹介されている。

グローバルな値としてfadingTimeralphaが定義されている。
fadingTimersetInterval()オブジェクトを格納するためのものだ。これをどこかにとっておかないとclearInterval()することができずに困ってしまう。

イベントが発生した時にfadingTimerが真か偽か、によって判断を変えることができる。
fadingTimerが真の状態で呼ばれる可能性があるのは、#WrapWindowに対するクリックイベントだけだ。
なぜならば、fadingTimerがセットされる前に透明なだけでvisibilityはvisibleになっているので、クリックイベントは必ず#WrapWindowが取る。もちろん、なんらかのブラウザの不備でクリックされる可能性はないではないが。
では、「これから真になる、まだ偽の状態で発生するか」というと、真になる関数が走った時点で、JavaScriptはシングルスレッドなので、真になるまで他のイベントを発生させても実行されないので関係ない。
操作に対するフリーズタイムを短くするためにsetTimeout()を使うこともあるくらいだ。

文字列(evalされる)の登録でなくコールバック関数の登録をするためには、intervalで呼ばれるコールバックで繰り返し使われる値を覚えておくことができないので、これはコールバック関数に対してグローバルでなくてはいけない。
そのための値がalphaだ。

1.0になれば終了なのでその時点でclearIntervalだが、まだインターバルタイマーが動作している状態で#WrapWindowがクリックされる可能性はある。これが真の場合だ。

では、その場合どうするかというと、インターバルタイマーの状態に関わらずオーバーレイを消す。そのため、インターバルタイマーが働いていればclearIntervalしてしまう。

なお、最後に追加しているイベントは本番用ではなくテスト用だ。

CSS

今回の目玉のひとつが、main, article, section, header, footer, asideといった「HTML5への対応」だ。
私のサイトにある大量のdivを少しでも減らしたいからなのだが、HTML5への対応は様々なメリットがある。
一方でレガシー環境を切り捨てることになりそうだったので慎重に対応してきたが、NN 4.6相当でも動作しそうな見通しがたったので採用となった。

それはともかく、納得できない振る舞いに随分悩まされた。

        <div id="SideContent">
      	<section>
             <nav id="Toc" class="toc marginbox_main contentbox">
               <h1>目次</h1>
               <ul>
                 <li>ページナビゲーション</li>
               </ul>
             </nav>
        ...
        </section>
        </div>

div, section, nav、あるいはそれらが内包する全てのテキストに対してfont-sizeの相対的変更(80%など)をすると、ボックスが上下とも縮まり、左カラムと位置が揃わなくなる。
インスペクタで見ると、table-cell要素の#SideContentの位置は正しいが、sectionのマージン上辺と#SideContentの上辺の間に隙間ができる。

また、中に入っているボックスの上マージンを削ると、ボックスの高さがそれだけ減ってその分上に隙間が空く。
配置は関係ないらしく、またこれはpaddingが設定されている場合のみ発生する。
paddingを設定したボックスに内包されるブロック要素のmarginをいじるのは、高さを意識する必要がある場合は厳禁ということか。

        <section id="MainArticle">
          <article class="marginbox_main contentbox">
            <h1>記事のタイトル=章</h1>
                ...
          </article>

このh1のマージンを設定するとその分articleが下がる。
h1より上にボックスを置くとボックスは伸びるが、その分右カラムが下げられてしまう。

恐らくは「h1を特別扱いしている」のだろうと思う。
だが、このためにh1を修飾することが非常に難しい。

結局position: relativeにしてずらした。上以外のmarginについてはこうした問題はない。