ソースを並べて表示する@はてなブログ
この記事の余談。
同じことをやるふたつの方法の比較なので、ソースを並べて表示したかった。
ソース
HTML
<button id="toggle-h-v">ソースを横に並べる</button> <!-- button for toggle --> <div class="source-area-container"> <div class="source-area"> <!-- source-left --> <h5>Google Map</h5> >|php| ... ||< </div> <!-- source-left --> <div class="source-area"> <!-- source-right --> <h5>OpenStreet Map (OpenLayers API)</h5> >|php| ... ||< </div> <!-- source-right --> </div> <!-- .source-area-container --> <style id="source-area-style"></style> <!-- style calculated by javascript -->
ソースコードはスーパーpre記法(シンタックスハイライト)。
それぞれにタイトルを付けて、それも含めて横並びにしたいので、div.source-area でくくる。
ふたつの div.source-area を更に div.source-area-container でくくる。
後は、切り替えをするボタンと、動的に値を変えるスタイルを書き込むための style タグを記述。
javascript
window.addEventListener("DOMContentLoaded", function() { var btn = document.getElementById("toggle-h-v"); btn.onclick = function() { var e = document.body.querySelector(".source-area-container"); e.classList.toggle("source-area-container-h"); // クラスの切り替え var sty = document.getElementById("source-area-style"); // 計算した値を使うクラスを書き込む /* Element.classList.toggle() は boolean を返す(true:クラスを追加した) */ if (e.classList.contains("source-area-container-h")) { sty.innerHTML = [ ".source-area-container-h { ", " width: " + window.innerWidth + "px; ", " left: -" + e.getBoundingClientRect().left + "px; ", "} ", ".source-area-container-h pre.code { ", " max-height: " + (window.innerHeight - 24) + "px; ", "} ", ].join("\n"); btn.original_title = btn.innerHTML; btn.innerHTML = "縦に戻す"; } else { sty.innerHTML = ""; btn.innerHTML = btn.original_title; } }; });
切り替えのボタンでは、ふたつのソースのエリアをくるんでる div.source-area-container に、横並びのためのスタイルを書いたクラス .source-area-container-h をつけたり、外したりする。
追加するクラスは、ふたつの要素を横並びにするために、幅を 50% より小さくして display: inline-block
を指定するのが主体。
記事のエリアをはみ出してウィンドウの幅いっぱいに div.source-area-container のサイズを広げたいのだけれど、値を計算する必要がある(calc()
じゃ無理だよね?)ので、javascript で値を求めて、それを style 要素に書き出している。
対象は、.source-area-container-h の左端と幅、それと内側にあるソースコードの pre 要素の高さ。
これ以外のスタイルは、静的に書ける。
本当は Template String を使いたいところだけど、ここは互換性を気にしておく。
CSS
.source-area-container-h { position: relative; z-index: 999; background-color: white; border: 2px inset silver; } .source-area-container-h .source-area { width: 48%; display: inline-block; vertical-align: top; } .source-area-container-h .source-area:first-child { margin-right: 1em; } .source-area-container-h .source-area pre.code { overflow-y: auto; font-size: 12px; } .source-area-container .source-area h5 { margin-top: 1em; } #toggle-h-v { font-size: 90%; }
主体は、.source-area-container-h がついている四つのルール。
- .source-area-container-h …… ふたつのソースを抱える領域
- 幅を広げるための相対位置
- サイドバーを隠すために、上に持ってきて背景色をつける
- 枠などをつけてみる
- .source-area-container-h .source-area
- 並べるために幅をそろえて、inline-block にする
- 縦位置は、上端でそろえる
- .source-area-container-h .source-area:first-child
- ふたつのソースコードの隙間
- .source-area-container-h .source-area pre.code
- 文字を少し小さくして、
- 縦方向のスクロールバー(高さは、javascript で求める)
後は、使ってるスタイルの h5 の margin-top が空き過ぎてたのと、ボタンの文字が小さかったのを調整。
実は、手こずった
はてな記法を使っているのに、それぞれのソースについている見出しに、なぜ見出し記法 ***
を使わずに、h5 を直接 書いているか、という話。
最初はこんな感じで、見出し記法を使ってた。
** まずはソース <div class="source-area-container"> <div class="source-area"> <!-- source-left --> *** Google Map >|php| ... ||< </div> <!-- source-left --> <div class="source-area"> <!-- source-right --> *** OpenStreet Map (OpenLayers API) >|php| ... ||< </div> <!-- source-right --> </div> <!-- .source-area-container -->
軽くプロトタイプを書いてローカルで動作の検証をしてから記事を書き始めたのに、なんかうまく動かなくて、変だなー、変だなー、って。
はてな記法が展開された HTML がこちら(p タグなどを消して、インデントつけたりしてる)。
<div class="section"> <h4>まずはソース</h4> <div class="source-area-container"> <div class="source-area"> <!-- source-left --> | <div class="section"> | <h5>Google Map</h5> | <pre class="code lang-php" data-lang="php" data-unlink=""> | ... | </pre> | </div> | <div class="source-area"> <!-- source-right --> | | | | <!-- ※ EMPTY ! --> | | | </div> <!-- source-right --> | <div class="section"> | <h5>OpenStreet Map (OpenLayers API)</h5> | <pre class="code lang-php" data-lang="php" data-unlink=""> | ... | </pre> | </div> </div> <!-- source-left --> </div> </div>
見出し記法は、見出しのタグ h* をつけるだけではなく、適当な範囲を div.section でくくる、などしてくれている。
そのせいで、兄弟になるはずの div.source-area が入れ子になり、右側に来るはずの div はソースを書いた pre が子要素になっていないという。
そりゃあ、動かないわけだ (´・ω・`)
もうひとつの仕込み
を同じことをやっている部分の順番を合わせて、並べたソースを同時スクロールというのも考えたのだけれど、微妙に合わない。
なので、コメントに入れた ■([A])
という部分をクリックすると、もう片方のソースで該当箇所が見えるようにスクロールするようにしてみた。
- ソース領域の該当するコメントを探して、目印をつけたタグでくくる
- イベントは Bubbling するので、ソース領域
pre.code
の onclick のイベントハンドラで拾う - もう片方のソースから同じ記号を持っているところを探して、見えてなかったらそこまでスクロール
- なんかやってますよ的なアピールとして、コメントの色を変えてみる
ある要素が見えるようにするには、Element.scrollIntoView という便利なメソッドがあるのだけれど、スクロールできる領域が入れ子になってると、外側の body までもスクロールしてくれるので、微妙に見た目が悪いので、自前で scrollTop を指定してるとかなんとかあるけれど、offsetParent とかあって微妙に面倒だった。