Greasemonkeyによるアプリケーション開発

第2回 Greasemonkeyでカレンダアプリを作ってみる:まずはシンプルに。

この記事を読むのに必要な時間:およそ 10.5 分

実行時の月のカレンダにする

実行時の月のカレンダにするには,実行時の月を取得する必要があります。これには組み込みオブジェクトのDateを使います。new Date()を実行することでその時点の日時情報を持つDateオブジェクトが返ります。Dateオブジェクトは内部で保持している日時情報を取得,設定するメソッドを持っています。そこで,このメソッド群を活用して,実行時の月を取得し,日にちを一日ずらしながらカレンダを表すHTMLを生成する,というアプローチをとることとします。

前ページでで示したスクリプト図3ではHTML文字列をインラインで用意してそれをそのままdiv要素内のHTMLとして設定することでページ内にカレンダを挿入していました。このようにHTMLを生成するのにできあがりのHTMLの文字列を生成していく方法をとることもできますが,DOMオブジェクトを生成してツリーを構成していく方法もあります。ここでは後者のDOMツリーを構成していく方法をとることとします。

一般に,DOMツリーを構成する場合,document.createElement()やElement#appendChild()(Greasemonkeyでは正確にはElementそものではなくXPCNativeWrapperでラップされたオブジェクト)を利用することになりますが,そのまま使うとプログラムが長くなって読みにくくなったり,階層構造が分かりにくくなったりしがちです。そこで私はこの手のプログラムを書くときは短くプログラムが書けるようにするためのユーティリティ関数を自作して対処しています。図5が私が自作したユーティリティ関数の定義です。

図5 ユーティリティ関数の定義

/**
 * document.createElement() +アルファな関数
 * attrsに指定した属性を設定し,stylesに指定したCSSプロパティを設定する
 */
function $tag(tagName, attrs, styles){
  var tag = document.createElement(tagName);
  if(attrs){
    for(a in attrs){
      if(attrs.hasOwnProperty(a)){
        tag[a] = attrs[a];
      }
    }
  }
  if(styles){
    for(a in styles){
      if(styles.hasOwnProperty(a)){
        tag.style[a] = styles[a];
      }
    }
  }
  return tag;
}

  
/**
 * $tagのショートカット定義
 * $div()でdiv要素を生成できるようにする。
 * 引数は$tagの第2引数,第3引数をそのまま第1引数,第2引数として利用できる。
 */
"div p span a img table tr th td form label input textarea".split(" ").forEach(function(tagName){
    var func = function(attrs, styles){
      return $tag(tagName, attrs, styles);
    };
    eval("$" + tagName + "= func;" );
  });


/**
 * document.createTextNode()のエイリアス
 */
function $text(text){
  return document.createTextNode(text);
}

/**
 * Element#appendChild() +アルファな関数
 * 第1引数の要素の末尾要素として第2引数以降で指定する要素を追加する
 * Element.prototypeの関数として定義するほうがスマートになりそうだが,
 * GreasemonkeyではElement要素を直接扱えないのでこの定義方法をとった。
 */
function $add(parent, children){
  if(arguments.length < 2) return "";
  for(var i=1, child; child=arguments[i];i++){
    if(typeof child != "object"){
      child = $text(child+"");
    }
    parent.appendChild(child);
  } 
  return parent;
}

これらのユーティリティ関数を使ったところで実行速度が上がる訳ではありません。むしろ多少下がるでしょう。しかし込み入ったツリーを作るプログラムでは見た目上分かりやすくなる方が多少の速度低下よりもスクリプトのメンテナンス性が高くなると考え,このような関数を使うことにしています。

使い方の具体例を図6に示します。

図6 ユーティリティ関数の利用例

表現したいHTML:
<form action="http://hoge.foo.com">
  <label>label<input type="checkbox" /></label>
  <input type="submit" value="send" />
</form>
document.appencChild/document.craeteElementを使った場合:
var form = document.createElement("form");
form.action = "http://hoge.foo.com";
var label = document.createElement("label");
label.textContent = "label";
var checkbox = document.craeteElement("input");
checkbox.type = "checkbox";
var submit = document.createElement("input");
submit.type = "submit";
submit.value = "send";
form.appendChild(label);
label.appendChild(checkbox);
form.appendChild(submit);
document.body.appendChild(form);
$add, $tagの派生関数群を使った場合
$add(document.body,
     $add($form({action:"http://hoge.foo.com"}),
          $add($label(), 
               "label",
               $input({type:"checkbox"})),
          $input({type:"submit", value:"send"})));

いかがでしょう。階層構造も分かりやすいし,各タグ要素の属性値も読みやすいと思います。階層構造が分かりやすいのはインデントのおかげですが,インデントをサポートする機能のあるエディタであれば,おおむね上記のようなインデントになるでしょう。前者のコードも文法を無視してインデントすれば階層構造の理解に役立てることはできるでしょうが,そのようなインデントを自らつけるのは大変だと思います。

著者プロフィール

gotin(ゴチン:GOrdon TImothy Nathanson)

会社員です。肩書きは特にありません。Greasemonkeyは会社の後輩が使っていてそれ面白いじゃんって思って使い始めました。自宅サーバで作ったRuby on Railsな自作ブログツールで作った自作ブログにGreasemonkeyのユーザスクリプトを載せたりもしていたんですが,誰にも読んでもらえなくて寂しかったのです。で,作ったままほったらかしにしていたはてなダイアリーに載せたら自作ブログよりかはありがたいことに多少読んでもらえるようになり,その後いろいろあって,そして今ここにいます。

URLhttp://d.hatena.ne.jp/gotin/