Webサイトにつけるウィジェットをiframeで作る基本的な手順

ブログパーツなどでよく使われる、HTMLタグをコピペするだけでサードパーティーのコンテンツを表示させることができるウィジェット。
TwitterやFacebook、はてなブログ等いろいろなサービスがウィジェットを提供していたり、表示をサポートしていたりしますね。

先日GitHubのコントリビューションチャートを任意のページに貼り付けられるウィジェット[Yourcontributions](http://ikuwow.github.io/yourcontributions/)を作ったので、これを例にしてiframeを使ったウィジェットのベーシックな作り方をメモ。

> GitHubのcontributionを見られるウィジェットを作った : Query OK.
> http://queryok.ikuwow.com/entry/github-widget-released/

こんなウィジェットです。

<img width="802" alt="Screen Shot 2016-02-01 at 1.05.15 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/28666/580019f1-1eb6-0028-ff2b-30c40af6efbe.png">

> Yourcontributions: GitHub Contribution widget
> http://ikuwow.github.io/yourcontributions/

Qiitaのwidgetもあり同じ仕組みで作っているため、こちらを参考にしてもいいかもしれません。

> Qiita Widget : Qiitaの投稿を表示できるブログパーツ - Qiita
> http://qiita.com/suin/items/f44b9ee388668fd277a6

## 生HTMLでwidgetを作る

まずは表示させたいウィジェットの中身を作ります。外部APIから動的に表示させる部分もいったんは仮の値を入れて作っておきます。今回は横幅を固定して作ったと仮定します。

リクエスト数を増やすとウィジェットを貼り付けたサイトのパフォーマンスを落とすので、CSSやJavaScriptも`<style>`タグや`<script>`タグで入れ込みます。

こんな感じで作りました。

```html

<div class="cbox">
  <div class="ctitle">
    <img src="https://avatars.githubusercontent.com/ikuwow?s=40">ikuwow's Contributions
  </div>
  <div class="cbody">
    <img src="http://ghchart.rshah.org/ikuwow" alt="ikuwow's Github chart"><a href="https://github.com/ikuwow" target="_blank">See on GitHub</a>
  </div>
</div>

<style>
* {font: 13px / 1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";}
.cbox {max-width: 728px;border: 1px solid #d8d8d8;border-radius: 3px; background: #fff; overflow: hidden; }
.ctitle { background-color: #f5f5f5; border-bottom: 1px solid #d8d8d8; padding: 6px 10px 8px; font-size: 14px; line-height: 17px; font-weight: bold; }
.ctitle img { height: 20px; width: 20px; margin-top 2px; margin-right: 4px; }
.cbody { padding: 4px; overflow: hidden; }
.cbody img { float: right; margin-right: 4px; }
.cbody a { color: #4078c0; text-decoration: none; font-size: 11px; float: right; padding: 4px 10px 4px;}
</style>

```

## ユーザーが貼り付けるタグを決める

ユーザーから見るとコピペするだけでウィジェットができるHTMLタグを作ります。このタグを貼り付けた場所にウィジェットが出現します。
ウィジェット内で動的に表示させる部分はタグのどこかにキーとなるパラメータを持たせておきましょう。

こんな感じです。

```html
<a href="http://github.com/[:username]" class="gh-contributions" data-gh-username="[:username]" target="_blank">[:username]'s contributions</a>
<script src="http://ikuwow.github.io/yourcontributions/widget.min.js"></script>
```

ポイントとしては以下の様な感じでしょうか。

* `<script>`タグでJavaScriptを一つ読み込む（widget.jsとか）
* タグを入れた場所にウィジェットをレンダリングできるような一意なクラス名をつけたタグを入れる
* 万が一JavaScriptが動作しなくても情報そのものへ到達できない状態を避ける
  * リンク入れたり

## widget.jsでiframeを作る

ここまで来たら、タグから読み込むwidget.jsでウィジェットを生成する処理を書きます。
ウィジェットを入れる対象のページのCSSやJavaScriptの影響を受けないように、ウィジェットは必ずiframeの中に入れます。

必要となるパラメーターを取得したり、iframeそのものの属性を指定したりする程度です。

```js
(function(){

'use strict';

// 目印のaタグからパラメータとってきたら消す
var atag = document.getElementsByClassName('gh-contributions');
var username = atag[0].dataset.ghUsername;
atag[0].style.display = 'none';

var iframe = document.createElement('iframe');
iframe.scrolling = 'no';
iframe.frameBorder = 0;
iframe.marginWidth = 0;
iframe.marginHeight = 0;
iframe.width = '100%';
iframe.height = '180px';
iframe.width = '100%';
iframe.height = '100%';
iframe.id = 'gh-contributions-widget';

// atagの隣にiframeを挿入
atag[0].parentNode.insertBefore(iframe,atag[0]);

// widgetの中身（ここに後で中身を書いていきます）
var widget = 'test';

// iframeにウィジェットの内容
var doc = iframe.contentWindow.document;
doc.open();
doc.write(widget);
doc.close();

})();
```

## 中に表示させたいものを入れる

ここから先ほど生HTMLで作成したウィジェットを`widget`の中に入れていきます。
地道な作業で骨が折れます。

```js
// こんな感じでゴリゴリ書く
var widget = '<div class="cbox">';
widget += '<div class="ctitle">';
widget += '<img src="https://avatars.githubusercontent.com/'+username+'?s=40"/>'+username+'\'s Contributions';
widget += '</div>';
widget += '<div class="cbody">';
widget += '<img src="http://ghchart.rshah.org/'+username+'" alt="'+username+'\'s Github chart" />';
widget += '<a href="https://github.com/'+username+'" target="_blank">See on GitHub</a>';
widget += '</div>';
widget += '</div>';
```

JavaScriptにはヒアドキュメントがないので、以下の様な裏技的な構文で擬似ヒアドキュメントをするのが楽です。

```js
widget += (function(){/*
 // ここに好き勝手書く
<style>
* {
    font: 13px / 1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
// ...省略
*/}).toString().replace(/(\n)/g, '').split('/*')[1].split('*/')[0];
```

基本的な手順はここまでで、あとは任意のページに貼り付けて確認して直してを繰り返して完成！

## オマケ

### タグ出力インターフェースを作る

ウィジェット内に動的なコンテンツが有る場合がほとんどなので、キーとなるパラメータをタグに含める必要があります。「このdata-github-usernameにユーザー名をコピペして・・・」などと説明を書いてやってもらうのはお互い面倒なので、タグを出力するインターフェースを作っておくと良いです。

### JavaScriptをminifyする

毎回JavaScriptのリクエストが発生し、それからウィジェットを書き出してレンダリングします。基本的にページのファーストビューが表示されてからウィジェットが表示されるまでは時間がかかるので、この時間を短くするためにパフォーマンスには気を使いましょう。

それからJavaScriptを配信するWebサーバーでgzip圧縮などの設定もしておくとよいです。
余談ですが、FacebookのSDKはgzip圧縮がされていなかったりして、Google Speed Insightsで見た時にめっちゃそのサイトのパフォーマンスが低く見えてしまいます。

マイナーですがDNSプリフェッチなんかも効果ありますね。

## 終わりに

ウィジェットづくり楽しいですね。

今回はウィジェットのサイズを固定にしましたが、中のコンテンツのサイズに応じてiframeのサイズを変えたりするとちょっと面倒くささが増します。

Qiita WidgetやTwitterやInstagramの埋め込みも参考になるので見てみると良いかと思います。

かなりざっくり書いたたためちょくちょく間違っているかもしれません。もし見つけたら指摘していただけると幸いです。