tacamy--blog

JavaScriptを勉強中の人のブログです。

楽天カンタン送料表示(Chrome拡張)を公開したよ!

今週の月曜に突如Chrome拡張機能に興味をもって、火曜に勉強を始めて、木曜の今日Chromeウェブストアに公開しました!(∩´∀`)∩ワーイ

f:id:tacamy:20140123143708p:plain

ウェブストアに出てる!!!感慨深い!!!Developer感アル。

どういう機能かというと、楽天の商品ページにいくと、画面左下に送料をすぐに見られるためのボタンが表示されるというものです。

楽天の送料ページは、http://www.rakuten.co.jp/shopName/info2.htmlで見られるのですが、いろいろ面倒です。

  • 送料を見るために、商品ページから画面遷移が必要
  • 送料以外の情報も一緒に書かれている
  • 長すぎてどこを見ればいいのかぱっと見で分からない
  • ショップによって送料ページへのリンクの位置がバラバラ

これらのことを解決したくて、送料の部分のHTMLだけをスクレイピングして取得してきて、商品ページ内で送料をすぐに見られる機能がほしかったのでつくりました。

オススメの勉強方法

Chrome拡張機能についての勉強は実質2時間くらいしかしてないけど、すごくお手軽につくれてしまった。

事前知識ゼロからだったので、まずはドットインストールで勉強しました。1時間くらい。

これだけでかなり概要がわかって、私が作りたいものはカンタンそうだったので、勉強とかいいからとりあえずすぐにつくろうと思って、ほかにはとくに勉強してないです。

公式サイトには、マニフェストの書き方とか、パーミッションの与え方とか、APIの使い方とかぜんぶここに書いてあるのですが、英語なので困ったら見る程度しか使ってない・・。

技術的なこと

ソースコードGitHub上で公開してます。

jQuery使ってるし、とくに大したことは何もしてないのでアレですけど、次のようなことをしています。

パーミッションの設定

楽天の商品ページのURLはhttp://item.rakuten.co.jp/*なのですが、送料ページはhttp://www.rakuten.co.jp/*ドメインが異なるので、manifestにpermissionsの指定が必要でした。

"permissions": [
  "http://www.rakuten.co.jp/*"
],

スクレイピング

送料部分のHTMLにクラス名なりID名がついてればカンタンだったのですが、案の定テーブルレイアウトでそんなものなかったので、下記のような感じで、見出しにあたる部分の文字列を拾ってきて、そこから次のhrまでの部分をとってくるみたいなことしてます・・。

var $data = $(data)
  .find('font:contains("配送について")')
  .parent()
  .nextUntil('hr');

ショップによって、なぜか微妙にこのへんが違う場合があって、たまにこれで取得できない場合もあります。その場合は、ボタン自体出ないようにしました。

われながらめちゃ便利だと思うので、ぜひ使ってみてください!

手っ取り早く Mac でローカルサーバーを立てる方法

ローカルサーバーを立てると、同じネットワークにつながってる別の端末から簡単にアクセスできるようになるから、スマホの表示確認とかに便利。でもいちいち httpd.conf とかいじるのはダルい。このやり方なら一瞬でできるからちょう楽ちん。

ターミナルでサーバー起動

サーバーのルートにしたいディレクトリまで cd コマンドで移動したら、下記コマンドを実行。

$ python -m SimpleHTTPServer 8888

最後の数字はポート番号で、他に使われていなさそうな番号に適当に変えれば、複数のサーバーを同時に起動できる。

ブラウザで表示確認

localhost で確認

ブラウザのアドレスバーに http://localhost:8888/{ファイル名} と入力して表示確認。

表示できてるなら、ローカルサーバーを立てるのが成功してる。表示できなかったら、ターミナルを見て、さっきのサーバー起動時になんかエラー出てないか確認してググろう。

IP アドレスで確認

localhost だと他の端末からアクセスできないので、IP アドレスを使ってアクセスします。

  1. ターミナルで ifconfig と打つか、ネットワーク環境設定の画面を見て、自分の IP アドレスを調べる。
  2. ブラウザのアドレスバーに http://{IP アドレス}:8888/{ファイル名} と入力して表示確認。

これで見れてれば、同じネットワークにいる別の端末から、IP アドレス経由でアクセスできます。

CTO に教えてもろた。

input type=”email” のバリデーションチェックを止めたい

input type=”email” とすると、対応ブラウザの場合、ブラウザ側でバリデーションチェックをして、Email の形式じゃなかった場合はエラーメッセージを表示してくれる。

でもこれがジャマになって止めたいときもある。その場合は form タグに novalidate 属性をつければ抑止できるらしい。

<form>
<input type="email">
</form>

これを、

<form novalidate>
<input type="email">
</form>

こうするだけ。

Chrome のみで確認。

thx @yomotsu 先生

ssh の公開鍵認証のいろは

CTO が分かりやすく教えてくれたので、メモ。

公開鍵の基本

公開鍵とは、github や beanstalk、Heroku などの git を使うサービスを利用するときに必要な、~/.ssh 以下につくられるファイル。

公開鍵と秘密鍵

ファイル名の最後に .pub がついている方が公開鍵、ついていない方が秘密鍵。

公開鍵とは、例えるなら家の鍵穴みたいなもので、誰でも見ることができる。 秘密鍵とは、例えるなら家の鍵で、自分しか持ってないもの。 公開鍵(鍵穴)と秘密鍵(鍵)が一致することで認証される仕組み。 だから、秘密鍵は絶対に公開してはいけない。

rsa と dsa

rsa と dsa の違いは、暗号化方式の違い。

利用するサービスの公開キー登録方法の説明を読んでみて、書いてある方を使えばOK。

公開鍵の使い回し

サービス間での使い回し

サービスごとに別々に鍵を用意して分けてもいいけれど、使いまわすこともできる。

そのサービスで登録しているメールアドレスとは違うメールアドレスが記述されている鍵でも関係なく使い回しができる。サービスの説明ページで、ssh-keygen コマンドで鍵を作成するように書いてあったとしても、すでに別のサービスで作成済みであれば新たに作成する必要はない。

マシン間での使い回し

普通、鍵はマシン固有のものなので、複数のマシンを使うなら、それぞれで鍵をつくらないといけない。 その場合はマシンを買い換えたときも、鍵を新たに作りなおす必要がある。

複数のマシンで鍵を使い回す場合は、~/.ssh 以下のファイルをごっそり別マシンにコピーして、鍵照合すれば使える(?)。

感想

鍵穴と鍵の説明わかりやすくて、CTO さすが CTO。もうこれで怖くない!

gem update --system したら gem update bundler もね!

Sass やら Compass やらの普及で、フロントエンドな人も Ruby や gem に触れることが増えたけど、こないだ調子こいて gem をアップデートしたら、rails server でエラーが出て、rails で作られたシステムが起動できなくなってしまった・・。

エラーの内容はこんな感じだけど、bundler のバージョンはちょっと違ったかも(メモしてなかった)。

/usr/local/rvm/gems/ruby-1.9.3-p385@global/gems/bundler-1.1.5/lib/bundler/rubygems_integration.rb:187:in `stub_source_index170': uninitialized constant Gem::SourceIndex (NameError)

ぜんぜん分からなくて泣いていたら、ほ神ら先生が助けてくれたので、忘れないようにメモします。

やったこと

gem のバージョン確認

$ gem -v
2.0.3

gem のバージョンは、2.0.3 で新しい。むしろエラー出てない人より新しい。これのせい?

bundler もアップデート

$ gem update bundler

いろいろインストールされました。

bundler のバージョン確認

$ bundle -v
Bundler version 1.3.4

新しくなった。そして rails server できるようになりました!

解説

gem の bundler が何かを、Node.js に例えて分かりやすく教えてもらえました。

  1. Ruby の gem は、パッケージ管理ツールで、Node.js だと npm みたいなもの。
  2. npm の場合はプロジェクトごとに管理できるし、-g でグローバルにもできる。
  3. でも gem はグローバルしかできない(Node.js で言う -g だけ)。
  4. プロジェクトごとの管理は、bundler が行う。
  5. gem と bundler の 2 つを合わせて、Node.js の npm みたいに使える。

分かりやすい・・!ほかちゃんは説明の天才か。

まとめ

gem update --system

したら、

gem update bundler

もした方が安全。特に gem がメジャーアップデートとかしてたら、やった方がいいかも。

JavaScript で日付のフォーマットを整形する

Movable Type で日付のフォーマットをいじりたいときには、% を使って簡単に設定できます。 例えば、「2013年03月14日 (月)」と表示したいときは、次のように指定します。

<$MTDate format="%Y年%m月%d日 (%a)"$>

詳しくは、日付に関するテンプレートタグのモディファイアリファレンス を参照。

その感覚で、JavaScript でも同じようなことできると思っていたら、そういうものは無いらしい。 なので、自分でフォーマットを整形したいときは、次のように指定します。

// 今日の日付で Date オブジェクトを作成
var now = new Date();

// 「年」「月」「日」「曜日」を Date オブジェクトから取り出してそれぞれに代入
var y = now.getFullYear();
var m = now.getMonth() + 1;
var d = now.getDate();
var w = now.getDay();

// 曜日の表記を文字列の配列で指定
var wNames = ['日', '月', '火', '水', '木', '金', '土'];

// 「月」と「日」で1桁だったときに頭に 0 をつける
if (m < 10) {
  m = '0' + m;
}
if (d < 10) {
  d = '0' + d;
}

// フォーマットを整形してコンソールに出力
console.log(y + '年' + m + '月' + d + '日 (' + wNames[w] + ')');

コードの解説

「年」「月」「日」「曜日」を Date オブジェクトから取り出してそれぞれに代入

var y = now.getFullYear();
var m = now.getMonth() + 1;
var d = now.getDate();
var w = now.getDay();

date オブジェクトには、getFullYear() getMonth() getDate() getDay() というメソッドが用意されているので、取り出したらそれぞれ別の変数に格納しています。

なぜか getMonth() だけは、実際の月から -1 された数字が入るので、 +1 しています。

date オブジェクトのメソッドの詳細は Dateオブジェクト / JavaScriptリファレンス を参照しました。

曜日の表記を文字列の配列で指定

var wNames = ['日', '月', '火', '水', '木', '金', '土'];

曜日を取り出す getDay() では、0 から 6 の数字が取得できます。 0 が日曜日、1 が月曜・・・6 が土曜日という対応になっているので、曜日をどのように表記するかかを文字列の配列で持っておきます。

「月」と「日」で1桁だったときに頭に 0 をつける

if (m < 10) {
  m = '0' + m;
}
if (d < 10) {
  d = '0' + d;
}

「3月」を「03月」と 0 詰めして表記したい場合は、自分で処理しなければなりません。 入っている値が 1 〜 9 の場合は、頭に '0' という文字を追加することで、自動的に型変換されて 3 という数値が '03' という文字列になります。

フォーマットを整形してコンソールに出力

console.log(y + '年' + m + '月' + d + '日 (' + wNames[w] + ')');

あとは、それぞれ処理済みの変数を、好きなように整形して表示させるだけです。

関数化する

同じサイトの中では、日付の表記は普通は統一させると思います。でも、日付を扱う場所で都度上記の処理を行うのは面倒なので、フォーマットを整形する部分を関数化して、どこからでも呼び出せるようにしておくと便利です。

// Date オブジェクトを作成
var now = new Date();

// dateFormat 関数の実行
console.log(dateFormat(now));

// dateFormat 関数の定義
function dateFormat(date) {
  var y = date.getFullYear();
  var m = date.getMonth() + 1;
  var d = date.getDate();
  var w = date.getDay();
  var wNames = ['日', '月', '火', '水', '木', '金', '土'];

  if (m < 10) {
    m = '0' + m;
  }
  if (d < 10) {
    d = '0' + d;
  }

  // フォーマット整形済みの文字列を戻り値にする
  return y + '年' + m + '月' + d + '日 (' + wNames[w] + ')';
}

date オブジェクトを引数で渡して、整形済みの文字列を戻り値で受け取る関数にしてみました。

ゼロ埋めの方法を改善(2013-03-21 追記)

コメント欄で、ゼロパディング(ゼロ埋め)は .slice() メソッドを使えばできると教えてもらったので、dateFormat 関数を修正してみました。

// Date オブジェクトを作成
var now = new Date();

// dateFormat 関数の実行
console.log(dateFormat(now));

// dateFormat 関数の定義
function dateFormat(date) {
  var y = date.getFullYear();
  var m = date.getMonth() + 1;
  var d = date.getDate();
  var w = date.getDay();
  var wNames = ['日', '月', '火', '水', '木', '金', '土'];

  m = ('0' + m).slice(-2);
  d = ('0' + d).slice(-2);

  // フォーマット整形済みの文字列を戻り値にする
  return y + '年' + m + '月' + d + '日 (' + wNames[w] + ')';
}

if が取れてスッキリしました。うれしい。GAVAScript さん、吉田さん、どうもありがとうございます。

ライブラリを使う(2013-03-21 追記)

自分で書かずにライブラリを使うのが便利らしいです。教えてくれてあざます!

参考サイト Thx ♡

jQuery の on() と off() を理解する

初心者向けの本とかだと、イベントを jQuery オブジェクトの後に直接指定する、 $('.foo').click(); のような書き方で説明されている場合が多いけど、少し複雑なことをしようとするとそれだと困ることが出てきます。そんなときに便利なのが on() を使ったイベント設定です。

on() ひとつで bind() live() delegate() を表せる

jQuery 1.7 で、bind() live() delegate() がすべて on() に統合されたそうです。 つまり on() の書き方によって、3 パターンの使い方ができるということです。

.foo という要素をクリックしたら何かするという例で、イベントの設定と削除の方法をそれぞれ書いてみます。

bind()

$('.foo').bind('click', function(){...});$('.foo').click(function(){...}); と同じ意味で、ごく普通の基本的なイベント設定方法です。 その要素に対してイベントが発生したら何かするという感じ。

$('.foo').bind('click', function(){...});
$('.foo').unbind('click');

これを on() で書くとこうなります。

$('.foo').on('click', function(){...});
$('.foo').off('click');

一緒だったらわざわざ on() なんて使わずに click() でいいじゃんと思うかもしれませんが、このあとの話で on() にするメリットが出てきます。

delegate()

delegate() では、イベントを設定するのは親の要素に対して行います。delegate() の 1 番目の引数で、実際にクリックする要素 .foo を指定しますが、イベントが張り付いているのは親の .parent です。そして、 .parent の中にある .foo だけがクリックイベントの発生する対象となります。このように、親要素に対してイベントを設定することを、イベントデリゲートと言います。

最初に HTML が描画された後に、JavaScript によって要素を追加した場合、bind() でイベントを設定している場合は、後から追加された要素にはそのイベントが効きません。でも、delegate() の場合は、後から追加した要素にもちゃんとイベントを適用できます。

$('.parent').delegate('.foo', 'click', function(){...});
$('.parent').undelegate('.foo', 'click');

これを on() で書くとこうなります。

$('.parent').on('click', '.foo', function(){...});
$('.parent').off('click', '.foo');

live()

live() も delegate() と同じように、JavaScript を使って後から追加した要素に対してもイベントを設定できます。delegate() との違いは、イベントを設定する対象が document になります。でも、書き方は次のように .foo に対してイベントを設定しているように書きます。でも実際にイベントが設定されているのは document です。分かりにくい・・!

$('.foo').live('click', function(){...});
$('.foo').die('click');

これを on() で書くとこうなります。

$(document).on('click', '.foo', function(){...});
$(document).off('click', '.foo');

on() の書き方の方が、実際の挙動と見た目が一致していて分かりやすいです。あと、die() って名前、live() と対になってるのが分かりにくい気がする。英語できないからでしょうか・・。

ひとつの要素に複数のイベントを設定する方法

同じ処理をする複数のイベントを設定

on() のひとつ目の引数を、スペース区切りで複数指定することで、どのイベントが発生しても同じ処理をさせることができます。

$('.foo').on('click blur', function(){...});

別々の処理をする複数のイベントを設定

on() の中身を連想配列にすることで、イベントごとに異なる処理を記述できます。

$('.foo').on({
  'mouseenter': function(){...},
  'mouseleave': function(){...}
});

イベントデリゲートで複数イベントを設定する

イベントの連想配列の後に、カンマで区切ってイベント対象の要素を指定します。

$('.parent').on({
  'mouseenter': function(){...},
  'mouseleave': function(){...}
}, '.foo');

live() は廃止

live() と die() は jQuery 1.9 で廃止されました。 delegate() は今のところ廃止の予定はありませんが、on() が推奨なので、今から on() で書いておいたほうがよさそうです。

参考サイト & 本 Thx♡