R Markdownで任意の領域を表示/非表示ボタンの実装

こういう機能が欲しかったので作ってみた。

## R Markdownが読み込んでいるもの

R Markdownの基本outputであるhtml_documentには、Bootstrapというデザインワークフレームが組み込まれています。その関係で、jQueryも合わせて読み込まれています。したがって、意図的に除去しない限りはこれら2つは標準で利用可能となっています。

これら2つが使えるということは、Webにおけるいろいろな要素を簡単に組み込むことができます。そこで今回は、Webでもよく見かける「あるエリアの表示/非表示をボタンで切り替える」というのを実装します。

## 目標

- ボタンをクリックすると、狙ったエリアが表示/非表示される
- 表示/非表示の状態にあわせてボタンのテキストも変化
- 表示/非表示エリアがページ内に複数あっても対応可能
- 汎用性が高い(他のRmdでもコピペですぐ使える)コード
- 極力Rmdファイル内にhtmlタグを使用しない

なんかハードル高いですが、やるからには…

## 実装

できました。サンプルとして、以下の内容を含むRmdファイルを準備すればいけます:


    ## エクササイズ
    
    ### 問題1
    irisの先頭から3行分を出力せよ。
    
    <button type="button" class="btn btn-primary ansBtn"></button>
    
    ### 問題1の解答例 {.ans}
    
    ````{r}
    head(iris, 3)
    ```
    
    ### 問題2
    irisの末尾から3行分を出力せよ。
    
    <button type="button" class="btn btn-primary ansBtn"></button>
    
    ### 問題2の解答例 {.ans}
    
    ```{r}
    tail(iris, 3)
    ```
    
    ## 仕込んだjsコード
    
    ```{js}
    $(function(){
      $(".ans").hide();
      $(".ansBtn").text("答えを表示");
      $(".ansBtn").click(function(){
        var index = $(".ansBtn").index(this);
        if($(".ans").eq(index).is(":visible")){
          $(".ans").eq(index).hide();
          $(".ansBtn").eq(index).text("答えを表示");
        }else{
          $(".ans").eq(index).show();
          $(".ansBtn").eq(index).text("答えを隠す");
        };
      });
    });
    ```

以下のような感じになります:

![a1_2452.jpg](https://qiita-image-store.s3.amazonaws.com/0/35118/cca1bd30-b9b8-48ca-37c3-9a43f8b23753.jpeg)

![a2_2323432.jpg](https://qiita-image-store.s3.amazonaws.com/0/35118/bf974268-9e45-e1ca-f1fc-bc37c1559680.jpeg)


検証してもらえるとわかるのですが、先ほどの条件は全てクリアしています。

## 実装手順

以下の手順でやればOKです。

### 切り替えるターゲット用のclass属性とボタンのclass属性を決める

まず、この2つを決定します。今回の実装方法では、表示/非表示を切り替えるエリアには統一したclass属性を付与させる必要があります。今回はいわゆるエクササイズの解答エリアを表示/非表示にしたかったという設定なので、`.ans`という属性にしてみました。

また、ボタンにも統一したclass属性を付与させる必要があります。今回は解答エリアのボタンということで、`.ansBtn`としました。

### 切り替えるターゲットの見出しにクラスを付与

これはPandocの機能を活用しています。PandocはR Markdownでmdから各種出力形式へとドキュメントを変換するソフトウェアです。rmarkdownはこれを使ってドキュメントを生成しています。

さて、Pandoc拡張Markdownには、heading(#とか##とか)の行末に`{.ans}`のように`{}`をつけると、それがそのブロックを覆うdivエリアにclass属性を付与します。詳しくは以下の記事などを眺めてください:

- [R Markdownで使えるMarkdown記法 - Qiita](http://qiita.com/kazutan/items/1758627d9ed9d3db3bd0)
- [RStudioの&quot;knit HTML&quot;でPandocに送っている内容 - Qiita](http://qiita.com/kazutan/items/eb15a42607f87f57b525)
- [R MarkdownでBootstrapのグリッドシステムを利用する - Qiita](http://qiita.com/kazutan/items/6301087a41a18c96faf2)

というわけで、今回の例の設定では「解答エリアをターゲットにしたい」わけなので、以下のようにしました:

```
### 問題1の解答例 {.ans}
(以下ターゲットの内容)
```

こうすることでターゲットとして設定することができます。

### ボタン用htmlタグを準備

表示/非表示を切り替えるためのボタンを設置します。R MarkdownはBootstrapが組み込まれているので、ボタンも簡単に実装できます:

```
<button type="button" class="btn btn-primary ansBtn"></button>
```

これを、隠すエリアの直前に差しこめばOKです。なお`class`属性の`btn btn-primary`についてはBootstrapでのボタン実装に関するところなので、詳しく知りたい方はBootstrap関連のドキュメントを検索してください。

### jsコードを準備

以上でドキュメント側の準備ができたので、jQueryのスクリプトをドキュメントの最後に記述します。通常なら`<script>`タグを使って記述するところですが、せっかくのRmdファイルなのでjsチャンクでやりました。

このjQueryのコードについての解説は省きます。なにせjQueryというかJavaScript自体もまともに書けない人間なので…。

## 留意点

いくつかあります。

### ボタンと切り替えのターゲットは必ず1対1にする

今回の実装では、「上から何番目のボタンが押されたか」という番号を元に、「表示/非表示を切り替える対象のindex」を指定しています。なので、**ボタン設置し忘れ**や**入れるはずの領域に該当クラスが付与されてない**場合はずれてしまいます。自分の能力不足で、現状このあたりまででした。。

### jsコードの仕込み方

実際にやるなら、jsの部分は見えないようにしたほうがいいと思います。

一番いいのは、その該当箇所を別ファイル(html)で切り出し、その中で`<script>`タグで挟み込んだものを準備し、それをRmdのyaml front matterでhtmlファイルをincludesで指定する方法です。この方法なら、他のドキュメントへの使い回しも簡単ですし、コストがぐっと下がります。

このあたりについては、R Markdownの公式サイトを検索してください。

## 雑感

自分の中で模索してたエクササイズ用のドキュメント作りが、これである程度落ち着きそうです。あと個人的に、もうちょっとR Markdownの潜在能力を引き出せたらなぁと思ってます。ぼちぼちやっていきます。

**Enjoy!**