天気予報jQueryプラグインをもっと手軽に使えるブログパーツ(API)に拡張しました

2015年7月24日に公開しました天気予報のjQueryプラグインを、タグ貼り付けだけで使えるブログパーツに拡張しました。
デザインなどのカスタマイズは従来通りに可能であり、また読み込む外部スクリプトがhttpsのため、https化したサイトにもご利用いただけます。
もちろんご利用は無料です。
(本プログラムは、Livedoor Weather HacksとGoogle Feed APIを利用していますので、これらの利用規約をご確認の上でご利用ください。)

便宜上「ブログパーツ」と紹介しておりますが、実際はHTMLタグを貼り付けるだけで利用できるクライアントサイドプログラムです。
ホームページのサイドやトップページに、天気予報を手軽に掲載したい場合に最適です。

ブログパーツとしての利用方法

まずはこちらの天気予報ブログパーツのページをご覧ください。

  • 天気予報ブログパーツの設定ページを開きます。
  • 左上が基本設定です。
    「エリア選択」で天気予報を表示させる地域を選んでください。
    ここではほかに、「縦横」欄でレイアウト、「対象」で天気予報を表示する期間、「説明あり」で説明文の有無、をそれぞれ選択できます。
  • 右上はデザインです。
    それぞれ項目名を参考に、実際に色やスタイルを選択したりスライダーを移動してデザインを変更してみましょう。
    上手くデザインができない場合は、ページを再読み込みすることで設定をリセットできます。

    また、右下はプレビューのサイズ・背景色を設定できます。設置を予定する場所を再現する目的で利用できます。この設定はデザインとしては出力されません。
  • 下部の「ブログパーツのコードを取得」をクリックして、コードを記載した小窓を表示し、コードをコピーしてください。
  • 天気予報を表示したい箇所にコードを貼り付けてください。
    尚、貼り付ける際はHTMLとして貼り付ける必要があります。

今回ご紹介している天気予報ブログパーツは、デザインをカスタマイズ可能です。デザインはCSSコード化され、ページを読み込み時に動的に設置しています。
また、共通のスタイルについても、ページ読み込み時に設置しています。
ここでは、平文のスタイルと、外部スタイルシートファイルをJavascriptで動的に設定する方法について解説します。

外部スタイルシートファイルをJavascriptで動的に読み込む

内容は非常にシンプルです。
まず、古いIE系用に、document.createStyleSheetが使えれば利用します。
それ以外は、直接linkタグをheadタグ内に設置します。

jQuery版

var path = "test.css";
if(document.createStyleSheet) {
    document.createStyleSheet(path);
} else {
    $("head").append("<link href=\"" + path + "\" rel=\"stylesheet\" type=\"text/css\" />");
} 

非jQuery版

var path = "test.css";
if(document.createStyleSheet) {
    document.createStyleSheet(path);
} else {
    var link = document.createElement("link");
    link.href = path;
    link.rel = "stylesheet";
    link.type = "text/css";
    document.getElementsByTagName("head").item(0).appendChild(link);
} 

平文のスタイルをJavascriptで動的に設定する

その1 styleタグを設置

まずは準備として、スタイルを設置するためのstyleタグを設置します。

jQuery版

$("head").append("<style type=\"text/css\" title=\"myid\"></style>"); 

非jQuery版

var style = document.createElement("style");
style.setAttribute("title", "myid");
style.type = "text/css";
document.getElementsByTagName("head").item(0).appendChild(style); 

この中で、titleにmyid(任意で設定してください)を設置しているのは、この後でCSSStyleSheetオブジェクトを取得するためです。
尚、設置済みのstyleを使用することも可能ですし、予めstyleタグを設置しておいても構いません。

その2 CSSStyleSheetオブジェクトを取得する

次に、document.styleSheetsをループして、titleがmyidに一致するものを探します。

var sheet;
for(var i = document.styleSheets.length - 1; i >= 0; i--) {
    if(document.styleSheets.item(i).title == "myid") {
        sheet = document.styleSheets.item(i);
        break;
    }
} 

「判別用のmyidを設定したstyleタグを設置し、後からループでmyidがあるstyleを再取得する」というとても遠回りな処理ですが、これは取得できる要素が下記のように異なるため、必要な手順です。

オブジェクトの取得方法オブジェクトの種類
document.getElementsByTagName("style").item(?)
または
$("style").eq(?)など 
htmlStyleElement
document.styleSheets.item(?)CSSStyleSheet

つまり、styleタグとして取得するDOMノードとは別に、CSSStyleSheetオブジェクトというものが存在しており、動的にCSSを設定する場合は後者が必要です。
ここではtitle属性を利用してhtmlStyleElementとCSSStyleSheetが一致するかを判別していますが、別の属性値を使ったり、あるいはもっとスマートなやり方があるかもしれません。ご存知の方はご教授願います。
尚、
var sheet = document.styleSheets.item(document.styleSheets.length - 1);
として「最後のCSSStyleSheetオブジェクトを取得する」のはお勧めしません。なぜなら、TwitterやFacebookのAPIや他のブログパーツなども動的にCSSを生成するため、まったく予期しないstyleを取得する可能性があるからです。

その3 スタイルを適用する

まずは、sheetにselectorとrulesを適用する関数を用意します。

var style_apply = function(selector, rules, sheet) {
    if(typeof(sheet.addRule) == "function") {
        var rules_arr = [];
        var reg = new RegExp("([^\r\n:;]+):([^\r\n;]+?);", "g");
        var matched;
        while( (matched = reg.exec(rules) ) != null) {
            rules_arr.push(matched[1].replace(/^ +| +$/g, "") + ":" + matched[2].replace(/^ +| +$/g, "") );
        }

        for(var i = 0; i < rules_arr.length; i++) {
            if(!rules_arr[i]) {
                continue;
            }

            try {
                sheet.addRule(selector, rules_arr[i], 0);
            } catch(e) {
                //error処理
            }
        }

    } else if(typeof(sheet.insertRule) == "function") {
        try {
            sheet.insertRule(selector + " { " + rules + " }", 0);
        } catch(e) {
            //error処理
        }
    }
}; 

処理は、旧IE系用のsheet.addRuleの利用可否で分岐しています。
sheet.addRuleの場合は、複数のルールを分離して1つずつ適用する必要があるため、正規ひょゆげんでさらに分離しています。
一方、sheet.insertRuleが使える場合は、selectorとrulesは接着してから引数に設定しています。
いずれも、念のためにtry cache(e)を利用して、エラーで処理が停止しないようにしています。
尚、ベンダープレフィックスがある場合、例えば「-webkit」付のルールをFirefoxに適用するとエラーとなります。厳密にこのエラーを回避したい場合は、rulesの解析と例外処理を追加しましょう。

後は、平文のスタイルを(セレクター) { (ルール) }のブロックごとに正規表現で分解し、上で用意したstyle_apply関数を順次適用します。

var reg = new RegExp("([^{}]+){((?:[^{}]+{[^{}]+})+|[^{}]+);?}", "g");
var matched;
while( (matched = reg.exec(conf.style) ) != null) {
    style_apply(matched[1], matched[2], sheet);
} 

平文のスタイルをセレクターとルールに分解し、モダンブラウザではstyle_apply関数内で再度接着してからinsertRuleしていますが、これは旧IE系でルールをさらに分解する必要があること、また正規表現によって「ついで」にセレクターとルールに分解することが可能なことから、採用している手順です。
セレクターとルールに分解しなければ、旧IE系での処理が増えますし、分解しなくても分解と同じ正規表現によるループ処理をする、という理由です。

2015/10/9