jQuery Mobileのイベントを検証してみる

最近めっきりjquery mobileを追っかけていなかったのですが、jquery mobile1.1になってとても使えそうなイベントが追加されましたのでご紹介します。

不確かな情報で申し訳ないですが、確か以前まであったイベントですと

$(document).bind('イベント名',function(){
     //イベント発生時の処理
});

という書き方をしなければならなかったと思います。
これだと、特定のページを表示させた時に処理を行おうとする時、少し書き方がめんどくさいというか、書けたのかな?ごめんなさい。試したことないです。。。

で、今回追加された新しいイベントpageinit()がそんな悩みを解決してくれます。
このpageinit()はどうやって使うかというと

$('#hoge').live('pageinit',function(){
   //ページID:hogeが表示された時に処理を行う
})

こんな感じで書けますので、特定のページを表示した際に処理することが簡単になりました。
これは大きな進歩だと思います。
jquery mobileを使う際に一番懸念されることが、ページ遷移を繰り返すと構造がカオスになってしまう事だと思うのですが、今回のpageinit()をうまく使えば、割りと効率的にイベントを管理できるようになるんじゃないかと思います。

そんな訳で、どれだけイベントの管理が簡単になるかデモを作ってみました。
ちょっと複雑なものになってしまいましたが、簡単に説明すると

トップページ
 ├地図ページ//google mapを表示
 └リストページ//jsonを動的に読み込んでリストを生成

こんな感じの構成になっています。

DEMO

ちょっとgooglemapがうまく表示されなかったりしますがorz 
それなりに動いています。

コードの解説は次回詳しく書きたいと思いますが、pageinit()の部分だけ掲載すると

//#mapの時にgoogle mapを表示させる
$('#map').live('pageinit',function(){
   var latlng = new google.maps.LatLng(36.238874,137.968613);
   var options = {
      zoom: 15,
      center: latlng,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    };
   var map = new google.maps.Map(document.getElementById('map_canvas'), options);
   var pageHeight = $(document).height();
   $('#map_canvas').css('height',pageHeight);
});

var list = [
   { "title": "yahoo!","url": "http://yahoo.co.jp",},
   { "title": "google","url": "http://google.co.jp",},
   { "title": "jquery","url": "http://jquery.com/",	},
   { "title": "jquery mobile","url": "http://jquerymobile.com/",},	
];
//#listの時にjsonからリストを表示させる
$('#list').live('pageinit',function(){
   var jsonAray = list;
   var spotList = $( '#spotListTmpl').tmpl( jsonAray );
   $('#spotList').append( spotList );
   $('#spotList').listview('refresh');
});

こんな感じで、ページごとにイベントを分けて管理できるようになっています。
java scriptとhtmlを比較的分離できるようになりましたので、直感的でわかりやすですよね。
jsonの展開の関係でHTML内にもjsのコードが入ってしまいましたが・・・

<!-- #index インデックスページ -->
<div data-role="page" id="index">
   <div data-role="header">
      <h1>indexページ</h1>
   </div>
   <div data-role="content">
      <p>インデックスページです。</p>
      <ul data-role="listview" data-inset="true">
         <li><a href="#map">地図ページへ</a></li>     
         <li><a href="#list">リストページへ</a></li>  
      </ul>
   </div>
   <div data-role="footer">
      <h4>footer</h4>
   </div>
</div>
<!-- #index インデックスページ ここまで -->
  
<!-- #map 地図ページ -->
<div data-role="page" id="map">
   <div data-role="header">
      <h1>地図ページ</h1>
   </div>
   <div data-role="content">
      <div id="map_canvas" style="width: 100%;"></div>
   </div>
   <div data-role="footer">
      <h4>footer</h4>
   </div>
</div>
<!-- #map 地図ページ ここまで -->
  
<!-- #list リストページ -->
<div data-role="page" id="list">
   <div data-role="header">
      <h1>リストページ</h1>
   </div>
   <div data-role="content">
      <ul data-role="listview" data-inset="true" id="spotList">
      
      </ul>
      <script id="spotListTmpl" type="text/x-jquery-tmpl">
      <li><a href="${url}">${title}</a></li>
      </script>
   </div>
   <div data-role="footer">
      <h4>footer</h4>
   </div>
</div> 
<!-- #list リストページ ここまで --> 

[js]callback関数を使ってみた

僕は、元々マークアップから入った人間なので、あまりプログラム的な部分は強くないのですが、必要に迫られてjava script(特にjquery)を触るようになってきました。
初歩的な事しかできないのが現状なのですが、勉強のために、僕がつまづいた事をご紹介したいと思います。

callback関数
Aの処理を終わったあとにそのAに対してBの処理を実行させる事・・・・でいいのかな。僕の認識はそんな感じですが、違っていたらご指摘ください。

例えば、外部のRSS情報を読み込ませて、生成したhml要素に対して「日付で検索して特定の日付以前のものに色をつける」とか。
(あまり実務的な例ではありませんが)

java scriptは基本的に上から一行づつ実行していきますので、単純に考えると

$(document).ready(function(){
  $.jGFeed('http://www.matsuaz.com/4_1/rss.xml'+'?'+(new Date()).getTime(),
  function(feeds){
    if(!feeds){
   return false;
		}
   for(var i=0; i<feeds.entries.length; i++){
    var entry = feeds.entries&#91;i&#93;;
    var date = new Date(entry.publishedDate);
    var pday =date.getDate();//日
    var pmonth = date.getMonth() + 1;//月
    var pyear = date.getFullYear();//年
    var fullDate = pyear + "/" +pmonth + "/" + pday;		
    var feedsItem = &#91;{
     title:entry.title,
     link:entry.link,
     postDate: fullDate,
    }&#93;;
    template = Handlebars.compile( $('#template').html());
    $('#feed').append(template(feedsItem))
		
   }
    }, 10);
		
		//RSSの読み込み後に続けて記述する
   var date = new Date('2012/8/10 23:59:59');
   $('#feed li span').each(function(){
    var entryDate = new Date($(this).html());
    if(date > entryDate){
     $(this).css('color','red');
    }
   });	 
});

と書いてしまいがちです。実際に実行してみると、RSSは読み込まれていますが、上記赤字の部分の処理(2012/8/10以前のエントリーの日付を赤くする)が実行されていないはずです。

DEMO(失敗例)

これは、色を変えるという処理が実行される時に、「RSSの読み込み→HTMLの生成」が完了していないからです。実在しないオブジェクトに対して処理を行う事はできませんので、当然、色を変える事は不可能です。

それではどうすればいいか。

$(document).ready(function(){
  $.jGFeed('http://www.matsuaz.com/4_1/rss.xml'+'?'+(new Date()).getTime(),
  function(feeds){
    if(!feeds){
   return false;
		}
   for(var i=0; i<feeds.entries.length; i++){
    var entry = feeds.entries&#91;i&#93;;
    var date = new Date(entry.publishedDate);
    var pday =date.getDate();//日
    var pmonth = date.getMonth() + 1;//月
    var pyear = date.getFullYear();//年
    var fullDate = pyear + "/" +pmonth + "/" + pday;		
    var feedsItem = &#91;{
     title:entry.title,
     link:entry.link,
     postDate: fullDate,
    }&#93;;
    template = Handlebars.compile( $('#template').html());
						
    //callbakc関数をhtml要素の生成後に実行する
    if($('#feed').append(template(feedsItem))){
     myCallback();
    };
		
   }
    }, 10);
		
  //callback関数として定義
  function myCallback() {
   var date = new Date('2012/8/10 23:59:59');
   $('#feed li span').each(function(){
    var entryDate = new Date($(this).html());
    if(date > entryDate){
     $(this).css('color','red');
    }
   });
  }		
 
});

色を変える処理の部分をcallback関数にして、「RSSの読み込み→HTMLの生成」が完了した段階で読みこまれるようにしました。これで意図通りの結果が得られます。

DEMO(callback関数を利用)

ちなみに、バッドノウハウとして、色を変える処理を$(window).loadイベントで実行させることも可能ですが(というかそうしてました)。これは処理のタイミングを遅らせるための苦肉の策として考えたモノです。

$(document).ready(function(){
  $.jGFeed('http://www.matsuaz.com/4_1/rss.xml'+'?'+(new Date()).getTime(),
  function(feeds){
    if(!feeds){
   return false;
		}
   for(var i=0; i<feeds.entries.length; i++){
    var entry = feeds.entries&#91;i&#93;;
    var date = new Date(entry.publishedDate);
    var pday =date.getDate();//日
    var pmonth = date.getMonth() + 1;//月
    var pyear = date.getFullYear();//年
    var fullDate = pyear + "/" +pmonth + "/" + pday;		
    var feedsItem = &#91;{
     title:entry.title,
     link:entry.link,
     postDate: fullDate,
    }&#93;;
    template = Handlebars.compile( $('#template').html());							
   }
    }, 10);
});
//window loadイベントでページが完全に表示されてから実行する
$(window).load(function() {
  function myCallback() {
   var date = new Date('2012/8/10 23:59:59');
   $('#feed li span').each(function(){
    var entryDate = new Date($(this).html());
    if(date > entryDate){
     $(this).css('color','red');
    }
   });
  }		
});

DEMO(window loadイベント)

ちなみに$(document).readyはDOMが構築されたら実行されるのに対して、
$(window).loadはページが完全に表示されたら実行しますので、上記のような使い方の場合は、ほぼほぼ意図通りに実行されますが、処理内容によっては、うまく動かない場合もありますので、やはりcallback関数を利用して実行した方が無難ですね。

[追記]
1.DEMO(window loadイベント)のソースが間違っていましたので修正しました。
2.コールバックのデモですが、HTMLの生成部分自体がjGFeedというRSSを展開するコードのコールバックになっているので、文字の色を変えるコードはコールバックのコーバックという扱いになるのでは。とのご指摘を頂きました。調べましたところ、ご指摘の通りで、下記のようにわざわざコールバック関数にしなくても、意図した動きを再現できました。

$(document).ready(function(){
  $.jGFeed('http://www.matsuaz.com/4_1/rss.xml'+'?'+(new Date()).getTime(),
  function(feeds){
    if(!feeds){
   return false;
		}
   for(var i=0; i<feeds.entries.length; i++){
    var entry = feeds.entries&#91;i&#93;;
    var date = new Date(entry.publishedDate);
    var pday =date.getDate();//日
    var pmonth = date.getMonth() + 1;//月
    var pyear = date.getFullYear();//年
    var fullDate = pyear + "/" +pmonth + "/" + pday;		
    var feedsItem = &#91;{
     title:entry.title,
     link:entry.link,
     postDate: fullDate,
    }&#93;;
    template = Handlebars.compile( $('#template').html());
						
    //html要素の生成
    $('#feed').append(template(feedsItem))
   //文字の色を変える処理
   var date = new Date('2012/8/10 23:59:59');
   $('#feed li span').each(function(){
    var entryDate = new Date($(this).html());
    if(date > entryDate){
     $(this).css('color','red');
    }
   });
   }
    }, 10);
});

DEMO(jGFeedのコールバックとして処理する)

window.matchMediaを使ってjava scriptでメディアクエリを行なってみる

あまりモバイル系には強くないので、あれなんですけど・・・
ワンソースでPC、モバイルと対応させる場合、css3のメディアクエリを利用するのが一般的です。

<link rel="stylesheet" media="screen and (max-width: 600px)" href="small.css" />

こんな感じで、デバイスの幅に応じてcssファイルを読み替えたりするんですが、同じ事をjava scriptのwindow.matchMediaで実現できるようなので、試してみました。

今回やってみたことは、ウィンドウの幅に応じて、背景の色とテキストを変えるようにしてみました。
firefoxやchormeでウインドウ幅を適当に縮めてもらって再読み込みをすると背景の色が変わるはずです。

DEMO

$(document).ready(function(){
if (window.matchMedia("(min-width: 980px)").matches) {
          $('body').css('background','#fab174');
          $('p').html('横幅が980px以上です。')
} else if(window.matchMedia("(min-width: 480px)").matches) {
          $('body').css('background','#fc9dd9');
          $('p').html('横幅が480px以上です。')
}else{
          $('body').css('background','#9da3fc');
          $('p').html('横幅が479px以下です。')
}
});

こんな感じで、3つのウインドウ幅に対してそれぞれの処理を入れています。

window.matchMedia("(min-width: 980px)").matches

iPadでデモを見たところ、対応しているようでした。
残念ながら、僕の携帯(Android 2.1)に載っているブラウザは対応しておりませんで、対応ブラウザは下記のようです。

Firefox 6 以降
Google Chrome 9 以降
Safari 5.1+
モバイル版 Firefox
Android 版の Google Chrome ベータ
iOS 版の Safari 5

Android系がちょっと弱いので、微妙なんですが、java scriptでメディアクエリを管理できるようになると、例えば、デバイスに応じてjava scriptのライブラリを読み替えたりもできるようになります。
モバイルデバイスだけ、jquery mobileを読み込ませたりできるかもしれないですね。
既存のモバイル系のライブラリはpc環境ではどうしてもデザインの表現性に欠けてしまったりと、扱いにくかったりしますので、こういったアプローチは試してみてもいいかもしれませんね。

channel amplifizr(チャンネル アンプリファイザー)が面白い件

岡山で活躍されているクリエイターさん(?)@maeponさんと@ohtsuki2843のUSTREAM番組です。
ちなみに、このお二人はCSS NITE岡山などを主催されている有名な方で、今日配信された長谷川さんのAutomagicにもご出演されてました。
一回目の放送を偶然ツイッターか何かで見つけて、なんとなくアフター配信まで聞いてしまいました。

USTのページには「岡山を拠点にWeb制作に携わっている中年親父2人が、Web全般に関する話題を適当に見繕って適当に盛り上がる、適当な番組です。」なんて書いてありますが、かなりWEBに関する深いところを突いていてとても面白かったです。なんか、会社でエンジニア同士がだべってる感じ?大変失礼な表現です。
というか岡山熱いですね。ちょっとうらやましい。

本日、そんなchannel amplifizrの2回目の放送があるようなので、シェアします。

配信は23:00~なので、ちょっと遅めですが、良かったらご覧ください。
僕も、家でブログのメンテナンスをしながら聞く予定。それまでに仕事をorz

channel amplifizr(チャンネル アンプリファイザー)

web制作の効率を高めてくれると思う5つのツール

web制作を行なっている人の多くが納期やら急な対応やらに追われてギリギリ状態であると思います。
(それが楽しくてやってる部分も多いんですが)
「クオリティを保ったまま、楽をしたい」というのは誰もが思う事じゃないでしょうか。
僕も日々制作をする中で、できるだけ楽をしたいと思っています。
今日は、実際に制作を行なう中で使っているツールをご紹介します。

1.リネームツール

もはや手放せません。特定の命名規則に基づいてファイル名を変換してくれるツール。
会社ではwinなので「Flexible Renamer」を使っています。
Flexible Renamerは、とても高性能で「文字列+連番」とか「大文字→小文字」といった変換や「頭から◯文字を削除」といった事もできるので、提供画像が「画像1.JPG」みたいにどうしようもない場合でも一発で変換できてしまいます。

インターフェースが少しマニアックなのが玉に瑕ですけど・・・慣れれば平気です。

2.クリップボード拡張ツール

コーディングをしている時に、よく使う文字列や使いまわす文字列などをクリップボードに入れておきコマンド一つで呼び出せるようになります。これも鉄板です。
win環境では「clibor」を使っています。
cliborは、過去24個の履歴と「定型文」として事前に登録しておいた文字列を呼び出せます。

3.zen-cording

言わずと知れたコーディング補助機能です。それほどゴリゴリとHTMLを書かないのですが、

<ul class="hoge">
     <li class="list01"><a href="#"></a></li>
     <li class="list02"><a href="#"></a></li>
     <li class="list03"><a href="#"></a></li>
     <li class="list04"><a href="#"></a></li>
     <li class="list05"><a href="#"></a></li>
     <li class="list06"><a href="#"></a></li>
     <li class="list07"><a href="#"></a></li>
     <li class="list08"><a href="#"></a></li>
     <li class="list09"><a href="#"></a></li>
</ul>

こんなのを書く時にコピペを多用して書いてもいいんですが、zen-cordingなら
ul.hoge>li.list0$*9>a
とたった一行でかけてしまいます。常にzen-cordingで書くという訳ではありませんが、
上記のようなルーチン的な構造の場合は使うようにしています。

4.css spriteジェネレータ

初めて使ったときはまさに青天の霹靂でした。
web制作をする際には、できるだけspriteにして画像をまとめるようにしています。
以前は、デザインから切り出し→パーツをphotoshopでsprite画像にまとめる→cssを全て自分で書く
という完全手作業で行なっていたんですが(慣れてたので割と早く作れたと思っていますが・・・)
ジェネレータを使えば切り出し以後の処理を自動で行なってくれます。
僕は、Stitches An HTML5 sprite sheet generatorというサービスを使っていますが、生成されたsprite画像とcssをダウンロードして画像は、状況に応じてjpgに変換、cssはクラス名のみ手作業で書き換えて使っています。
正確には全自動ではないのですが、かなり作業効率が上がりましたし、この作業を行なうときの「さてやるぞ」という気合いも不要でsprite画像の処理ができるようになったのはとても大きいです!

5.SNSをオフにする精神力

実はこれが一番大切なのかもしれません。そして、残念ながら僕は持ち合わせておりませんorz

これが全てではありませんが、なるべく効率的に作業を行って、限られた時間を有効に使いたいものですね。皆さんが使ってるツールなどあれば、ぜひ教えてください!

font-sizeのあれこれ

cssのかなりマニアックな話題です。

A List Apart: Articles: Learning to Love the Boring Bits of CSS というブログに書かれていたのですが、font-sizeを指定する場合、「px」で指定するとIEでフォントサイズが固定されてしまうという弊害があるので、「%」や「em」で指定するのが一般的です。

ちなみにemとは親要素のfont-sizeを1としたサイズです。
例えば、該当するブロックの親要素が14pxの場合 1em=14pxとなります。

個人的には、yui-fontというyahoo!が提供しているフォントサイズを特定の「%」で指定することで対応したpx単位のサイズを表現できるというライブラリを使っています。

「em」で指定する場合は、親要素のfont-sizeを62.5%と指定する事でデフォルトのフォントサイズを10px相当にすることができるので、14pxにしたければ フォントサイズを1.4と指定してあげればよい事になります。

html {
     font-size:62.6%;
}
p {
     font-size:1.4em;
}

ここからが、問題なのですが、フォントサイズが入れ子になっていて、一部だけ大きく表示したい場合

例えば、

<p>「ではみなさんは、そういうふうに川だと云われたり、
<span>乳の流れたあと</span>だと云われたりしていた
このぼんやりと白いものがほんとうは何かご承知ですか。」
先生は、黒板に吊した大きな黒い星座の図の、上から下へ
白くけぶった銀河帯のようなところを指しながら、
みんなに問をかけました。</p>
p {
     font-size:1.4em;
}
p span {
     font-size:1.8em;
}

spanの部分だけ18pxにしたいのですが、実際には
1.4 × 1.8 = 2.52 25px相当になってしまいます。
これは、emが親要素のフォントサイズを基準にしているためで、spanのフォントサイズは1.4em=14pxなので
14pxの1.8倍という数値になってしまうからです。

困ります。

そこで登場するのが「rem」という単位です。これはcss3から登場した単位になりますので、一部のブラウザでは対応していないのですが、「root」+「em」という意味でroot要素つまりhtml要素のフォントサイズを継承してemと同じ計算ができる単位になります。

先ほどの例でいうとspan要素を1.8remと指定してあげれば、
root要素(10px)×1.8=18pxというフォントサイズになる訳です。

html {
     font-size:62.5%;
}
p {
     font-size:1.4rem;
}
p span {
     font-size:1.8rem;
}

DEMO

あまり華やかなコトではないので、スルーしがちですが、きちんと理解しておきたい部分ですね。

EC-CUBEで会員登録させない方法

もう扱うこともないかもしれないですが、備忘録として・・・

EC-CUBEは、このブログでも何度か取り上げている純国産のオープンソースECサイト作成CMSです。
賛否はあるにせよ、自由にカスタマイズできて、無料で使えるので、お使いの方も多いのではないかと思います。
で、そんなEC-CUBEですが、デフォルトでは、PCで表示すると会員登録画面が必ず表示されてしまいます。また、モバイルページに至っては、会員登録をしないと購入できないという仕様になっています。

利用用途によっては、会員登録させたくないという場合もありますので、その方法をご紹介します。

ちなみに今回のバージョンは2.11.4です。

PCの購入ページ

data/class/pages/shopping/LC_Page_Shopping.php

これのファイルは、会員登録しない場合の画面を制御するファイルです。
この中盤あたりに、
actionという関数が用意されています。詳しくわかりませんが、どうやらここで
購入ボタンを押したあとの挙動を判定しているようです。

        // お客様情報入力ページの表示
        case 'nonmember':
            $this->tpl_mainpage = $nonmember_mainpage;
            $this->tpl_title = $nonmember_title;
            $this->lfInitParam($objFormParam);

nonmemberという状態の時に個人情報を入力するページを表示させているようですので、
デフォルト状態でも同じ挙動にさせるために
このdefault:をごっそり

         $this->tpl_mainpage = $nonmember_mainpage;
         $this->tpl_title = $nonmember_title;
         $this->lfInitParam($objFormParam);

と書換えます。

あとは、テンプレート側で、会員登録やログインなどの表示を消してあげます。
(これが地味にめんどくさいです)

モバイルページ

data/Smarty/templates/moblime/shopping/index.tpl
デフォルトでは、会員登録や、ログインの記述がありますので、そこをごっそり消して、

購入手続き

<form name="member_form" id="member_form" method="post" action="./index.php">
<input type="hidden" name="mode" value="nonmember" />
<input type="hidden" name="<!--{$smarty.const.TRANSACTION_ID_NAME}-->" value="<!--{$transactionid}-->" />
<div align="center"><input type="submit" value="購入手続きへ" name="buystep" id="buystep"></div><br>
</form>

このようにします。

次に、
data/Smarty/templates/moblime/shopping/nomember_input.tpl
というファイルを新規で作成します。
これは、先にPC版で修正したファイルと同じようなものですが、購入者の情報などを入力させるためのコードが必要になります。
PC版か、スマフォの同名のファイルをコピーして、不要な部分を削除すればよいかと思います。

以上です。
こういったカスタマイズが自由にできるのは魅力ですが、あまり深入りしてしまうと、バージョンアップした時に動かなくなったり・・・と心配もあります。
・・・いづれにしても上記の方法で実装可能でした。やるやらないは、個人のご判断で。

■参考
KH-WEBLOG 携帯サイトでも会員登録しないで購入できるようにする:EC-CUBE2.11.1

MAMPでMySQLが起動しない

MAMPをインストールして、ローカル環境でwpとかテストしようと思っているのですが、インストールはできたけど、MySQLが起動しなくなってしまったので、対処法のメモ。

MAMPを起動させた状態で、MacのTerminal.appで “killall -9 mysqld” を実行するだけで使えるようになりました。

via:NATO BLOG MAMPでMySQLが起動しなくなった場合