As Sloth As Possible

可能な限りナマケモノでありたい

Text::MicroTemplateで遊ぶ

こないだ言ってたPlackアプリのサンプルでは、出来るだけ普段使ってない構成にしようと目論んでて、そのために例えばテンプレートエンジンにはText::MicroTemplateを使ってみたりしている。

Text::MicroTemplateはシンプルで軽くてなかなか良かった。ちなみにこんな感じで使う。

use Text::MicroTemplate qw(:all);

my $html = render_mt('Hello, <?= $_[0] ?>', 'faultier')->as_string;

でもファイルから読みたいよねーそれ実装しなきゃなのかなーとか思ってたらちゃんとText::MicroTemplate::Fileってのがあって、こっちを使えばキャッシュも使ってくれるしラッパーの機能とかもあるしで素敵だ。

?# hello.mt
Hello, <?= $_[0] ?>
use Text::MicroTemplate::File;

my $mtf = Text::MicroTemplate::File->new(use_cache => 1);
my $html = $mtf->render_file('hello.mt', 'faultier');

で、ここまでちょこちょこ弄ってみて思ったけど、やっぱりviewの中で@_とかを触るのはなんか嫌だ。そりゃ、引数にHashRef渡してやるとかテンプレートの頭で変数に入れてやるとかすればいいんだろうけど、そういう生々しい情報はviewで扱いたくない。つまり、やりたいのはこういうこと。

?# hello.mt
Hello, <?= $name ?>
use Text::MicroTemplate::File;

my $mtf = Text::MicroTemplate::File->new(use_cache => 1);
my $html = $mtf->render_file('hello.mt', { name => 'faultier' });

で、どの辺をいじればいいんだろうと読んでたら、Text::MicroTemplateのbuildメソッドの中身はこんな風になっていた。

    my $expr = << "...";
package $_mt->{package_name};
sub {
    ${_mt_setter}local \$SIG{__WARN__} = sub { print STDERR \$_mt->_error(shift, 4, \$_from) };
    Text::MicroTemplate::encoded_string((
        $_code
    )->(\@_));
}
...

ふむふむ。この$_mt_setterってなんだろうなと見てったら、デフォルトでは空、つまり何もしないんだけど、ここに適当なコードを突っ込むとテンプレートを処理する前にやることを追加できるようだ。

use Text::MicroTemplate qw(:all);

$Text::MicroTemplate::_mt_setter = 'my $name = shift;';
my $html = render_mt('Hello, <?= $name ?>', 'faultier')->as_string;

おお、出来た。んでも実はText::MicroTemplate::Fileの方ではこれはできない。build_fileの中でbuildを呼ぶ前に

local $Text::MicroTemplate::_mt_setter = 'my $_mt = shift:';

ってやっちゃってるし、テンプレートに渡す引数がわかるのはrender_fileの段階だし、大体これ多分外から挙動を制御する用じゃないよなぁ。というわけで当初は自前でモジュール組んでたんだけど、良く考えたら事前に引数を名前付きで渡せないこと以外は概ねText::MicroTemplate::Fileで良いわけで、作ってるうちにどんどん似てきてしまったのでじゃあText::MicroTemplate::Fileを拡張すればいいじゃんってことになった。それでこんなものを作ってみた。

faultier's p5-text-microtemplate-file-bindvars at master - GitHub

use Text::MicroTemplate::File::BindVars;

my $mtf = Text::MicroTemplate::File::BindVars->new(use_cache => 1);
my $html = $mtf->render_file('hello.mt', { name => 'faultier' });

動いたー。一応Text::MicroTemlate::Fileと同等の内容のテスト通るし、ベンチも取ってみたらキャッシュ無しのときでほんのちょびっと速いくらい、キャッシュ有りだとちょっと残念なことに15%ほどもパフォーマンス落ちちゃうけどそれでもTTの4、5倍くらい速いしまぁいっかーなんて思ったりしました。どうせここ全然ボトルネックじゃないし。

とそんな感じで一通り書き終えてこの記事をまとめてるところでこんなものがあるのに気付いた。

Text::MicroTemplate::Extended - search.cpan.org

…これでいいじゃん。マクロとかブロックとかあるし高機能じゃん。最初に良く調べなさいよって話ですねほんと。CPANで関係ありそうなモジュール探すとか基本中の基本じゃん。残念すぎる。

せっかくなので取ったベンチ結果。

Benchmark: timing 10000 iterations of T::MT::E, T::MT::F, T::MT::F::BV, TT...
  T::MT::E: 16 wallclock secs (16.01 usr +  0.61 sys = 16.62 CPU) @ 601.68/s (n=10000)
  T::MT::F: 17 wallclock secs (16.00 usr +  0.39 sys = 16.39 CPU) @ 610.13/s (n=10000)
T::MT::F::BV: 16 wallclock secs (15.85 usr +  0.39 sys = 16.24 CPU) @ 615.76/s (n=10000)
        TT: 46 wallclock secs (45.60 usr +  0.55 sys = 46.15 CPU) @ 216.68/s (n=10000)
              Rate           TT     T::MT::E     T::MT::F T::MT::F::BV
TT           217/s           --         -64%         -64%         -65%
T::MT::E     602/s         178%           --          -1%          -2%
T::MT::F     610/s         182%           1%           --          -1%
T::MT::F::BV 616/s         184%           2%           1%           --
Benchmark: timing 10000 iterations of T::MT::E, T::MT::F, T::MT::F::BV, TT...
  T::MT::E:  1 wallclock secs ( 1.04 usr +  0.06 sys =  1.10 CPU) @ 9090.91/s (n=10000)
  T::MT::F:  1 wallclock secs ( 0.75 usr +  0.05 sys =  0.80 CPU) @ 12500.00/s (n=10000)
T::MT::F::BV:  1 wallclock secs ( 1.10 usr +  0.06 sys =  1.16 CPU) @ 8620.69/s (n=10000)
        TT:  5 wallclock secs ( 4.92 usr +  0.00 sys =  4.92 CPU) @ 2032.52/s (n=10000)
                Rate           TT T::MT::F::BV     T::MT::E     T::MT::F
TT            2033/s           --         -76%         -78%         -84%
T::MT::F::BV  8621/s         324%           --          -5%         -31%
T::MT::E      9091/s         347%           5%           --         -27%
T::MT::F     12500/s         515%          45%          37%           --

できること少ないのに負けてるじゃん…。ちなみに、T::MT::EとT::MT::F::BVは全く同じテンプレートがレンダリングできたので同じの使ってる。キャッシュの仕方が悪いんだろうなー。でもT::MT::Eの方はオブジェクト生成時に引数指定しなきゃだから、複数ファイルをレンダリングさせたらちょっと変わったりするかしら。あとはまぁ高度なことをやらせればその分速度落ちるけど、そもそもT::MT:FとT::MT::F::BVには出来ないことなのでそこで差がついたとしてもT::MT::Eを選ぶなぁ。しかしそれにしてもTT遅いな。残念な子ですわね。

追記

やってみたけど大してかわんない。そもそも何万何十万て回数やるうちの数回オブジェクト生成が増えたところで差が付くわけもないだろっつー。あまりに意味ないのでもうちょい見直そう。

さらに追記

ちょっと試行錯誤してみたけど上手くいかなかった。思い付きで作ったものじゃ勝てないかー。ほとんど何もやってないんだけどな…。で、単純に1ファイルだけのテンプレートでキャッシュ有りだと当然のことながらText::MicroTemplate::Fileのがずっと速い(余計なことしてないから)。十万回やったら3秒くらい差がついた。T::MT::EとT::MT::F::BVの差はまぁ数%ってところ。十万回やって0.3秒違うだけだしTTとの40秒の差とくらべたらなんてことないかな。

ラッパー有りだと細かい処理の差よりそっちのが重いのでT::MT::FとT::MT::F::BVの差が3割弱から1割強まで縮まる。悪くはない。是非T::MT::Eのテンプレート継承機能との差を試したかったんだけどなんか上手く動かせなくて悩み中。TTは十万回やると1分くらい帰ってこなくなるのでもうなんかいいやって感じになってきた。いや、うちのサービスのほとんどがTT使ってるんだからキャッシュの仕組みとか工夫してさえいれば大した問題じゃない(だってDBとかのがずっと遅いんだもの)のはわかってるんだけど、こんだけ速度に差が付いてて、素のPerlで書けて、HTMLのエスケープもデフォルトでやってくれて、とか考えたらこっち使いたくなるねぇ。

faultierfaulist  at 22:26  | コメント( 2 )  | トラックバック( 0 )  | この記事をクリップ! このエントリをつぶやく

「Plackで一発ネタは俺に任せろ!」バリバリ「やめて!」

タイトルからして既に残念な感じが漂ってるけど、前回の記事で言った通りPlackでアプリを作ってみようと試行錯誤中。ちなみにこないだサーバをApacheからnginxに変えたついでにHTTP::EngineのアプリもFastCGIで動かすようにしたので、PlackのアプリもFastCGIで動かしてみた。設定はこんな感じ。

# app.psgi
use NetaKit::TweetProxy::App;
use Plack::Builder;
builder {
    #mount '/baritter' => builder {
        NetaKit::TweetProxy::App->new->to_app;
    #};
};
#!/usr/bin/env perl
# bin/fcgi.pl
use strict;
use warnings;
use File::Spec;
use FindBin qw($Bin);
use Plack::Server::FCGI;
use lib File::Spec->catfile( $Bin, qw(.. lib) );
my $app = eval { require File::Spec->catfile( $Bin, qw(.. app.psgi) ) };
my $server = Plack::Server::FCGI->new(
    nproc       => 4,
    listen      => '/tmp/plack_netakit.sock',
    pidfile     => '/tmp/plack_netakit.pid',
    detach      => 1,
);
$server->run($app);
# nginx.conf
server {
    listen       80;
    server_name  localhost;
    location / {
        root   /var/www/faultier.jp/htdocs;
        index  index.html index.htm;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   share/nginx/html;
    }
    location /baritter {
        set $script "";
        set $path_info $uri;
        fastcgi_pass unix:/tmp/plack_netakit.sock;
        include fastcgi_params;
    }
}

PlackのがH::EよりもRackと似たような感じに書けて楽だった。DLSとかURLMapとかもあるしね。あと、Rackで言うところのconfig.ruにあたるのがapp.psgiなんだけど、せっかくこれ書いたのにfcgi.plで同じのまた書くのやだなぁと思ってなんとなくrequireしてみたら普通に行けた。素敵。使い方これでいいのか疑問だけど。mount使おうとして上手くいかなくてコメントアウトした形跡があるのはまぁ御愛嬌。

baritter

いい加減彼女が404ばかりで飽きてきたので今回は真面目になんかしら遊べるものを作ってみることにした。

バリバリ 嘘じゃないもん

まぁアプリ名とかから推測つきそうなもんですが、ようはtwitterのポストを拾ってきてアレげなフォーマットにはめ込んで表示するだけの簡単なお仕事をするサービスです。サンプルなので単語を抽出するとかそういう凝ったことは決してやりません!(キリッ。上のスクショはそもそも元発言(もしくは発言者の脳味噌)が残念なのであんまり変わらないけど、津田さんとか池田信夫さんとかにこのフィルタをかますと脱力感が半端ないので3秒くらいは楽しめた。

ちなみに成果物はこんなだけど、内部ではPlackとText::MicroTemplateとData::ModelとCoroを使ってるとかいう、構成だけ見ると面白いサンプルになってるんだぜ。無駄遣いにも程があるぜ。出来たらソースも公開する予定。

faultierfaulist  at 04:03  | コメント( 5 )  | トラックバック( 0 )  | この記事をクリップ! このエントリをつぶやく

nginxとUnicornでRackアプリを動かす

どうやらUnicornというのが良いらしいという噂を聞きつけたので、どんなもんじゃろと試してみることにした。

Route 477 - 大規模Railsサイトのための新しいHTTPサーバ、Unicorn

Unicornてのは何者なのかと言うと、Rack及びRailsに対応したRubyのWebアプリ用のHTTPサーバ。詳しくは上の記事を読んで下さい。githubでも使ってるそうだ。あと、名前が格好良い(あんまり関係ない)。

まずはunicornの設定

と言っても、gem install unicornしてconfig.ruがあるディレクトリでunicornコマンドを叩けば、thinとかと同じようにサーバが起動する。rackup互換のオプションも付いてるので特に悩むこともないと思う。あとは普通にApacheとかでプロキシの設定してやるなりなんなりすればすぐ使える。

それだけだと大して面白くないので、折角だから上の記事に書いてあるように、nginxでソケットでっていう設定をしてみる。先にUnicornの方の設定。ちなみにサンプルに使ったのは例によって彼女が404のやつ。今現在はApache2+Passengerで動いている

# unicron.conf
worker_processes  4
working_directory '/var/www/rackapp'
listen '/tmp/rackapp.sock', :backlog => 1
listen 4423, :tcp_nopush => true
timeout 10
pid '/tmp/rackapp.pid'
preload_app  true
stderr_path '/var/log/rackapp.log'

before_fork do |server, worker|
  old_pid = "#{server.config[:pid]}.oldbin"
  if old_pid != server.pid
    begin
      sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
      Process.kill(sig, File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
    end
  end

  sleep 1
end

after_fork do |server, worker|
  addr = "127.0.0.1:#{4423 + worker.nr}"
  server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => 1)
end

中身はほとんどUnicornの設定例からコピペ。ちなみに、nginxを推奨してるだけあってnginxの設定に似せたDSLになってるけど、実態は見ての通りRuby。その気になれば動的に設定をごにょごにょみたいなこともできる。ログはThinみたいによしなにやってはくれないけど、その辺は設定でLoggerを渡すなりRackのミドルウェアを使ってなんとかするなりすればOK。

んでこれを適当なファイル名で保存したら、次のようにunicornコマンドを叩く。

$ unicorn -D -c /var/www/rackapp/unicorn.conf /var/www/rackapp/config.ru

-Dオプションでデーモン起動、-cオプションは設定ファイルの指定、最後の引数はrackupファイルのフルパス。ちなみにこれを/var/www/rackappじゃないところで実行してみたんだけど、最初は「working_directoryとか指定してるから、そこにあるconfig.ruを勝手に読んでくれたりしないかな」とか思ってたんだけどやっぱりダメで、その後相対パスで指定してみたらrequireでコケて「そんなファイルねーよ、寝言は寝て言え」と怒り出したので、絶対パスにしたら行けた。

この時点でhttp://localhost:4423/がちゃんと見られることと、unicorn.sockが出来てることを確認。よし次はnginx。

nginxのプロキシ設定

こんな感じ。

# nginx.unicorn.conf
upstream rackapp {
    server unix:/tmp/rackapp.sock;
}

server {
    listen       80;
    server_name  localhost;

    location / {
        proxy_pass  http://rackapp;
    }
}

これを大本のnginx.confのhttpのブロックのどこかでincludeしてやる。一応言っとくとこれ以外にも普通のnginxの細かい設定はしてるけど、上の例では省略してます。

ちなみに、ソケットでやるように設定してるからこうなってるけど、TCPで良ければproxy_passのところをhttp://localhost:4423とかにしてやっても普通に動く。

とりあえずこれで完成。あとはnginxを起動してやれば、ちゃんと見られる。素敵。

インターフェースが統一されてるって素敵よね

いやぁ、やっぱRackでインターフェースが統一されてるって、良いですよね。アプリ側あんまり弄らなくてもさくっと移行できるわけですし(っていう話をこないだ書いた)。あと、ついでに同じサーバで動いてたHTTP::Engineのアプリも、apache+modperlからnginx+fcgiに移行してみたけど、こっちもちょこっと設定変えたくらいでアプリ側は全然弄ってない。良いなー。こうして新しい環境が出てきたりしたときにはやっぱりRackやなんやらが真価を発揮する。折角なので次はPlackでH::Eのときと同じことをやってみようと検討中。

んで、結局Unicornは良いの?イイの?

さぁ…ベンチ取ってないのでなんとも。取っても「彼女が404」じゃあんまり参考にならないんだよなー。ただ、アプリをロードし終わってからforkするとかnginx側に気付かせない用に再起動できるとかその辺だけでも十分魅力的ではある。あと、再起動は早かった。流石。

faultierfaulist  at 02:10  | コメント( 0 )  | トラックバック( 1 )  | この記事をクリップ! このエントリをつぶやく

MacRubyさんがいけず

表題通り。MacRubyとMacFUSEでファイルシステムを作ってみようとしたんだけど、なんかいまいち上手く行かない。

まずHotCocoaがちゃんと機能してない。hotcocoaコマンドでアプリの雛形を作ってくれて、macrakeすればビルドが走って.appの形にパッケージされるんだけど、できたアプリを起動しても起動するだけで落ちる。これだけだとあまりにも分からなすぎるので、アプリの形にしないでスクリプトのまま起動してみる。

$ hotcocoa sample
$ cd sample
$ macruby -d lib/application.rb
core:in `dump': nil is not a symbol (TypeError)
	from /Library/Frameworks/MacRuby.framework/Versions/0.5/usr/lib/ruby/1.9.0/hotcocoa/mappings/menu.rb:12:in `submenu:'
	from core:in `menu:'
	from /Users/taro/Projects/sample/lib/menu.rb:3:in `application_menu'
	from /Library/Frameworks/MacRuby.framework/Versions/0.5/usr/lib/ruby/1.9.0/hotcocoa/mappings/application.rb:17:in `load_application_menu'
	from /Library/Frameworks/MacRuby.framework/Versions/0.5/usr/lib/ruby/1.9.0/hotcocoa/mappings/application.rb:8:in `handle_block:'
	from core:in `application:'
	from lib/application.rb:in `start'
	from lib/application.rb:1:in `
'

一応hotcocoaのソースを読んでったんだけど、どうやらメニューバーを生成してるところでコケてるっぽい。そしたらってんでlib/manu.rbに書いてあるHotCocoa#application_menuの中身を丸ごとコメントアウトしたところ、一応動いた。

まぁ作ろうとしてるのはFUSEのファイルシステムをマウントするアプリなので、別にメニューも要らないしViewも出さないしいっか、と思って、気持ち悪いけどそれは特に原因追わずそのままにして、前回のコードをアプリケーションとして起動できるように直してやる。んで再びmacrubyで起動させてみると、どうやらちゃんとマウントできるようだ。よしよし、と気を良くしてmacrakeを走らせたらNamakeFS.appはできたものの、こいつをダブルクリックしても起動して直後に落ちる。スクリプトのままだと行けるんだけどアプリにするとダメ。くそっ。

そもそもHotCocoaでアプリを作るのが今回の主目的じゃないので、もういいや調べるのは別な機会に、と放置することにして、今度は素直にデーモンとして動くように作り直す。これは概ね上手くいって、namakefsコマンドを叩けばNamakeFSが/Volumesの下にマウントされて、FinderからNamakeFSをアンマウントしてやればnamakefsのプロセス自体も終了してくれるようにできた。よしよし。

じゃあ次は早速AtomPubで取得した情報を出してみよう、と思ってatomutilをインストールしてみたが、どうにもこれが動かない。そもそもatomutilは1.9系でちゃんと動くのかって言われると正規表現回りでコケたりして結構微妙なんだけど、まぁ中身読んだのでごまかす手段は知っている。でも、MacRubyから使おうとしたらどうもその問題の箇所にまで到達していない。しゃいせ。

落ち着け、こういうときは慌てず騒がず冷静に、「おかしも(おさない、かけない、しゃべらない、もどらない)」の精神が大切だ、と自分に言い聞かせて、とりあえず素のnet/httpとrexml/documentでサービス文書を取得してみる。そしたら何てことだ、もうその時点でダメだよ。REXML::Document.new(res.body)とかしてみたら、そこでもうコケる。ちょ。ドキュメントオブジェクトすら作れん。ここでatomutil版の方のスクリプトを動かしてみたら、出てるエラー一緒でやんの。ちなみにCRubyの1.9.1ではもちろん何の問題もなく動くコード。ちゃんと期待通りの動作をするのは確認済み。ふぁっきん。

ええい、もうあれだ、REXMLが使えないとかちょっと正直どうかと思うが、MacRubyにはまだあれがある!そうさ、NSXMLDocument(※Cocoaのクラス)!察しの良い方はお気付きかと思いますが、わざわざMacRubyで書いてるのにも関わらずMacFUSEやらNSXMLDocumentやらを使ってるせいで、段々中身はRubyっぽい文法のObjective-Cのコードと化して来ている。もうなんかそろそろ本末転倒な感は否めないけども、とりあえずnet/httpでWSSE認証かましてGETしてきたatomのデータをNSXMLDocumentに食わせればなんとか必要な情報は取れるところまでできた。よし、これをさっきのコマンドに組み込めば…!

segmentation fault …だと?!

なんだか良くわかりません。動いてるコード + 動いてるコード = セグフォ。もう疲れたよパトラッシュ…。

結論から言うとMacRubyはぶっちゃけバグとか地雷とかが多すぎて涙が出てくるので、まだこれでどうこうしようとかは無理です。流石、RubyKaigiのセッションで開発者自ら”Crazy"と言わしめただけあるぜ、MacRuby0.5。これみたいにすごいナチュラルにObjCとRubyの融合ができちゃうっていう「可能性」には期待しているのだけども、まだ早いみたいね…。

さてどうしよう。MacRubyに深入りするのはそれはそれで楽しそうではあるんだけど、そんなことしてる内にFUSEのこと忘れちゃいそうだしなぁ。とりあえずObjCで書くかなぁ。

faultierfaulist  at 02:56  | コメント( 0 )  | トラックバック( 0 )  | この記事をクリップ! このエントリをつぶやく

Re: JS習作:フォームにフォーカスしたらデフォルトテキストを消す

hamashun先生がJSの勉強を初めたみたいなので、早速第一週目のコードを勝手に添削してみた。

細かいこと

先に細かいことを何点か。

  • input要素のvalueはそのままずばりvalueで取れる。元のコードだと searchInpt.attr('value') ってしてるけど、これは searchInput.value でOK。
  • valueのセットも同様にそれ用のメソッドがある。 searchInpt.attr('value', 'hoge') だったら searchInpt.val('hoge') でOK。jQueryの場合、ここは value() とか value = じゃなくて val() らしいので注意。
  • searchInptのfocusとかblurとかに無名関数渡してるけど、あの中での this は searchInpt のことなので、全部 this で置き換えられる。
  • searchInpt.focus(function(){}).blur(function(){}) って繋げて書いてるけど、これ分けた方が良くない?中身の処理が長いので、なんかどのオブジェクトに対しての操作なのかわかりづらそう。JSあんま書かないのでよく知らないけど、こういう文化なのかな?

あと別にこれは正直このくらいの量のコード片だとどうでもいいっちゃいいんだけど、searchInptとかdefaultValとかって変数名が気持ち悪い。InputとInptとか一文字しか違わないんだし、素直に書いちゃって欲しい。略すんならむしろ長過ぎるのでアグレッシブに3文字以下まで縮めて欲しい。あんまりお勧めしないけど。

プログラミングしてると、「名前の付け方」ってのは意外かもしれないけどものすごーく重要(きちんと名前を付けるってのは「それが本来どう振る舞うべきものなのか」をちゃんと意識するってこと)なので、ちゃんと気を使って欲しいかも。

汎用性の話

「スクリプトを適用する要素」を指定する方法(コード1行目)を、ある程度汎用的にしたい。

今はidを直接指定しているので、そこを「jsファイルを読み込ませたら全てよしなにやってくれる」的にしたい。

「inputのtypeがtextで、かつデフォルトでvalueの中身に何か入ってる場合」とかでif文かなー。 JS側でclassをキメて、それをHTMLに書くのはちょっともにょるしなあ。

JS側でclassをキメて、それをHTMLに書けばいいと思います。えええーって顔されそう。もにょってる理由は「HTMLによりマークアップをする、とはつまり論理的な意味付けをすることであり(中略)だから動作や見栄えに関することをHTMLの外部の制約で決められたくないのだ!」ってことなんじゃないかと推測するけど、「検索フォームという機能を持った要素である」ってのは論理的な意味付けだよね。見た目や動作ではなく。「検索フォームという機能を持った要素である」という意味付けをするってことと、どういう大きさでどういう動きをするとかってHTMLに書くってことは違うから、別にマークアップとしてはアリじゃないかと思う。

JS側で頑張るアプローチで解決しようとすると、泥臭くて複雑になるか、このくらいの機能だと割に合わない重めな仕組みになるかするので、inputに特定のidやclassを付けておくのは割と妥当な気がする。強いて言うなら、元のコードはinputが一個だけしか想定してないけど、XPathとかで特定のクラスのinputを配列で取ってきてループで回して、みたいにして全部の入力フォームに適用できるようにしとくと良いかも。

あと汎用性の話で言うと、inputの指定のとこより、JSで要素に文字色をベタに指定してるとこを変えた方がいいと思います!デザインによっては文字色変えたくなるだろうし。

faultierfaulist  at 12:35  | コメント( 0 )  | トラックバック( 0 )  | この記事をクリップ! このエントリをつぶやく
livedoor プロフィール
記事検索
月別
カテゴリ別
あわせて読みたい
あわせて読みたい
なかのひと
  • livedoor Readerに登録
  • RSS
  • livedoor Blog(ブログ)