typeOf 'aki_mana'

December 2010


検索フレーズで意外と多かったのが、iUIのフォームボタンに関するもの。


ソースの addEventListenter('click', function(){ ~ }) を読めば判ることなのだけど、軽く説明。

iUIは、DOM3イベントで実装する工夫をしているのか、「バブリングを利用し、findParent() メソッドを使ってイベント発生要素から任意の要素を特定する。」

以前、書いたことだけど「マークアップ構造がライブラリによって固定され、デザイナの介入を認めないからこうした実装が可能」。

iUI のフォームでは、A要素が INPUT[type="submit"]と、INPUT[type="cansel"]の代わりをする。

* INPUT[type="submit"] → A[type="submit"]
* INPUT[type="cansel"] → A[type="cansel"]

これらのA要素を特定した後、link という変数に格納されてるんだけど、
form.submit() な感じでJavaScript で動的にフォーム内容を送信してるのも特徴。

少なくとも、Eventのバブリングやキャプチャリングがわからない人は、利用を諦めたほうがいい。


iUIって、単に「ソース読もうぜ?」と言う気にはなれない難解なライブラリです。
デザイナな人は、DIV.panel 部分だけが任意のデザインを許可されていることに注意すべき。それ以外は、ライブラリの機能を殺してしまう可能性もあるので、JSerとしての知識が無いなら、まず使いこなせないと思う。

iUI のコードを読んでて思ったのが、完全に自由なデザインをやめてハードコードすべきところを増やすのが、HTML5時代のライブラリかなと。


パンくずリスト - ウェブマスター ツール ヘルプ


microdata と RDFa の2つの技術によるパンくずリストの解説がある。
JavaScript で microdata のHTMLを生成してみた


CSS:
div[itemscope] { display:inline; }

HTML:
<div itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
    <a itemprop="url" href="(URL of ルート)">
        <span itemprop="title">(テキスト of ルート)</span>
    </a> > 
    <div itemprop="child" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
        <a itemprop="url" href="(URL of CHILD)">
            <span itemprop="title">(テキスト of CHILD)</span>
        </a> > 
        <div itemprop="child" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
            <a itemprop="url" href="(URL of GRAND-CHILD)">
                <span itemprop="title">(テキスト of GRAND-CHILD)</span>
            </a>
        </div>
    </div>
</div>

JavaScript:
// MarkupHelper
var E = function(tn){ return new MarkupHelper(tn); }

function MdBreadCrumb(){
    this.initialize.apply(this, arguments);
}
(function(__){
    __.crumbs = [];
    __.initialize = function(delimitter){
        this.delimitter = delimitter|| '';
    };
    __.addChild = function(href, text){
        
        isChild = this.crumbs.length >0? true: false;
        if(this.delimitter !== '' && isChild){
            this.crumbs[this.crumbs.length-1].child( this.delimitter );
        }
        this.crumbs.push( MDBC(href, text, isChild) )
    };
    __.getCrumbs = function(attr){
        attr = attr||{};
        while(this.crumbs.length > 1){
            var child = this.crumbs.pop();
            this.crumbs[this.crumbs.length - 1].child( child )
        }
        return this.crumbs[0].attr(attr).asString();
    };
    // microdata BreadCrumbs
    var MDBC = function(text, url, child){
        return E('div').attr({
            'itemprop' : (child ? 'child': null),
            'itemscope':'itemscope',
            'itemtype' :'http://data-vocabulary.org/Breadcrumb' 
        }).child([
            E('a').attr({ 'itemprop':'url', href:url }).child(
                E('span').attr({ 'itemprop':'title'}).child( text )
            )
        ]);
    }
})(MdBreadCrumb.prototype)

////////////////////////
// 実際に生成するのはここから
////////////////////////
var TP = [ // = TOPICPATH
    { text:'ルート', url:'http://domain/' },
    { text:'子', url:'http://domain/child/' },
    { text:'孫', url:'http://domain/child/grandchild/' }
];
var bc = new MdBreadCrumb(' > '); // デリミタを指定
for(var i=0,l=TP.length; i<l; i++){
    bc.addChild( TP[i].url, TP[i].text );
}
var htmBreadCrumbs = bc.getCrumbs({ id:'bread-crumbs' });


MdBreadCrumbクラスは、MarkupHelperクラスを薄くラップする形で作成。
prototypeプロパティを定義するクロージャ内で有効なプライベートメソッド MDBC()で microdata のパンくずリストの1つを生成する。

複数のパンくずリストを書く必要があるとして、終盤のfor(){} でデータをMdBreadCrumbに与えるだけで、構築できるのが特徴。


JavaScriptも、一般的な管理システムを作成するのに十分な性能を持っている。
唯一の課題が、「第三者コードによる意図的なオブジェクト汚染を困難にすること」。

JavaScriptシステムの開発について述べてみると
* JSONで気軽に windowオブジェクトを構築するのが今のwebデザインの開発手法。
* だけど、オブジェクトツリーの構造が解析されてコードを読まれると、乗っ取りを企てるのは容易。
* オリジナルドメインポリシーがあるからこそ、簡単に乗っ取られることは無いが、極端な話、ブックマークレットを使ってフォームからPOSTされるデータにちょっかいを出すってことも可能。たいていはPOST操作が伴う場合、ログイン中で個人を特定できる条件も揃っているので、実践する馬鹿は居ないと思うが。
* サーバー側スクリプトは絶対にサニタイズを怠ってはならない。

Livedoor ニュースから辿った、この記事タイトルが気になったので読んでみた。
徳大寺有恒氏「女にモテる車を作れば若者の車離れは止まる」

>> 草食系などと揶揄されるイマドキの若い男たちも、女の子にモテそうなクルマなら
考えが古いと思う。草食系な若い男って、「何人にもプライベートタイムを蝕まれたくない」という前提条件があると思う。たとえ身内でも「ウザイ」と思ってる部分があってそれを口には出さないけど自覚はしてるはず。ましてや、他人である「女の子」なんかは自我の領域に近づけようとしないはず。だから、草食と呼ばれていると思うんですよね。

氏の年齢から推察するに、酒、タバコ、女といった肉食系ともいえる昭和初期の男性ですよね。このページにある「秘策」てのは「ジェネレーションギャップ」しか感じられない。「女にモテる=セックス」という発想だと思うけど、これは、草食系の発想じゃないでしょ。説得力が無くて失笑せざるをえない。






Fireworks MX がそれまでのツールだったのだけど、CS5 (Fireworks 単体)を買った。

このソフト、パッケージ印刷まで考えない画像加工では最強なんじゃないかと思ってます。

パッケージ印刷、ポスター印刷を手がける場合は、フォトショorイラレが必要らしいですけど。




さて、久しぶりにいろいろググってたところ、懐かしい記事に。
今更ながらですが、prototype の実装方法でapplyを使う方法に。

JavaScriptで継承やるときにprototype書きまくるのめんどい人は

というエントリ。
去年見たよな(継承を調べてるときDanさんの万能継承関数のページからたどった記憶がある)。
そのときは深く考えてなかったけど。再度考察


function Class(){}
(function(){
this.member = hoge
}).apply(Class.prototype)




ちなみに私の書き方:
ダブルアンダースコアを使って prototype オブジェクトを表現してます。

function Class(){}
(function(__){
__.member = hoge
})(Class.prototype)

どっちがいいかは別として、共にプライベートメソッドを実装しやすかったり。コードを末尾から探すとき終端を確認しやすかったり。

覚えておくといいかも。

追記)
JavaScriptの引数が全てリファレンスということに着目すれば、後者の書き方も思いつくって事なんだけど、(function{}) という無名関数をオブジェクト化した表記にapply() を加えられるという事、失念してたなぁ。と。



MarkupHelper を作ってから、JavaScriptでのマークアップ(ハードコード)が多くなった気がする。

jQueryにDOM構築系のメソッドって無かったよな?と思ったので、jQueryポケットリファレンスを読み返してみたのだけど、それっぽい記述が無い。

$(対象要素).append( DOMオブジェクト/HTMLソース ) がイメージしたものに近い。

けど、 DOMオブジェクト部分にHTMLを文字列で書くってのが納得できないんですよね。Ajaxで読んだHTMLソースを流し込めるのは判るのだけど。

$(対象要素).append( '<div id="hoge"></div>' );

たとえば、DIV#wrapper に次のHTMLを構築するときどうします?

//  <div id="overlay"></div>
//  <div id="lightbox">
//      <div id="outerImageContainer">
//          <div id="imageContainer">
//              <img id="lightboxImage">
//              <div style="" id="hoverNav">
//                  <a href="#" id="prevLink"></a>
//                  <a href="#" id="nextLink"></a>
//              </div>
//              <div id="loading">
//                  <a href="#" id="loadingLink">
//                      <img src="images/loading.gif">
//                  </a>
//              </div>
//          </div>
//      </div>
//      <div id="imageDataContainer">
//          <div id="imageData">
//              <div id="imageDetails">
//                  <span id="caption"></span>
//                  <span id="numberDisplay"></span>
//              </div>
//              <div id="bottomNav">
//                  <a href="#" id="bottomNavClose">
//                      <img src="images/close.gif">
//                  </a>
//              </div>
//          </div>
//      </div>
//  </div>

まぁ、Lightbox2 のマークアップ構造なんだけど。

MarkupHelper を使うDOM構築関数を定義して記述すると、 コード内でHMLエスケープを要する <, > が皆無に。

var E = function(tn, attr, child){
    var t=new MarkupHelper(tn);
        if(attr) t.attr( attr );
        if(child) t.child( child );
    t.dom = t.asElement;
    t.htm = t.asString;
    return t;
};

var lightBoxTemplate = (jQuery.map([
    E('div', {id:'overlay'}),
    E('div', {id:'lightbox'},[
        E('div', {id:'outerImageContainer'},
            E('div', {id:'imageContainer'}, [
                E('img', {id:'lightboxImage', style:''}),
                E('div', {id:'hoverNav', style:''}, [
                    E('a',{id:'prevLink', href:'#'}),
                    E('a',{id:'nextLink', href:'#'})
                ]),
                E('div',{id:'loading'},
                    E('a',{id:'loadingLink', href:'#'},
                        E('img', {src:'images/loading.gif'})
                    )
                )
            ])
        ),
        E('div', {id:'imageDataContainer'}, [
            E('div', {id:'imageData'},
                E('div', {id:'imageDetails'}, [
                    E('span', {id:'caption'}),
                    E('span', {id:'numberDisplay'})
                ])
            ),
            E('div', {id:'bottomNav'},
                E('a',{id:'bottomNavClose', href:'#'},
                    E('img', {src:'images/close.gif'})
                )
            )
        ])
    ])
], function(tag, idx){
    return tag.htm();
})).join('');


HTML+CSS しか書かなかった従来の方法に比べて、終了タグが不要になる分、精神的に落ち着く。


興味深い記事を読んだ。

未成熟だからおこったコンテンツ流通経路の二つの問題

「コンテンツの自由配信をしたければ、自前でサーバーを構えろよ。」
「クラウドは、契約内容如何によっては、コンテンツ配信というサービスの邪魔でしかない。」

てこと。

必然的に表現の自由に基づく配信を行うには、金がかかるってこと。

クラウドってのは、情報を他者に預けることでコストを抑えるクライアント・サーバーシステムなのだけど、「預けた情報の扱い」に関する契約内容の確認は重要ってこと。

「Appleの当事者間で解決しろ。アップロード者本人からの要請にしか対応しない。」ていう現状。これに倣う業者が増えそうなんですがw


iUI はBODY直下にタイトル&ツールバー領域とコンテンツ領域とを配備する



<body orient="landscape"> // portrait(縦置きの表示)
    <div class="toolbar">
    (略)
    </div>
    <ul>
    (略) UnorderedList は リスト系コンテンツになる。
    </ul>
    <div class="panel" selected="true"> // 表示中
    (略) div.panel は1ページを表現するコンテナになる。
    </div>
    <form class="panel">
    (略)
        <a type="submit"></a> // form のボタンもA要素で実現。
        <a type="cansel"></a>
    </form>
    <form class="dialog">
    (略)
        <a type="submit"></a>
        <a type="cansel"></a>
    </form>

</body>

selected="true" になってるのが、表示中/スライドアニメーション中の属性。selected="progress" はAJAXのコンプリートを待っている最中の属性

簡単だけど、CSSの 2つめの 区切り線(/*****/)までの説明が付くと思う。


JavaScriptは、DOM操作可能になってから、ページを表示するので、最初に次のイベント箇所を。

addEventListener('load', function(){} );

ここで、最終的には iui.showPage(); され、ページが表示される。


クリックしたときにその要素が AもしくはDIVなら共通化されたイベント処理が行われる。

addEventListener('click', function(){} );

この中で、findParent(event.target, 'a') とか、findParent(event.target, 'div') に着目。

コードのとおり解釈すると、

次のようなHTMLの場合、クリックすると、labelとか、spanが event.target になるけど、findParent() を使って、A[href='#_pageId'] まで直近の指定要素を探してる。


var link = findParent(event.target, 'a');
となってるので、link は項目名をクリックしようが、補足をクリックしようが、A[href='#_pageId']のHTMLElementオブジェクトになる。


    <div class="row">
        <a href="#_pageId">
            <label>項目名</label>
            <span>補足</span>
        </a>
    </div>



イベントを発火する全ての要素に対してアタッチする旧来の方法と異なり、「基本的にwindowオブジェクト1つにアタッチし、イベント発火したときに要素をたどる方法」になっている。

これ、マークアップ構造にルールを持たせることで可能なイベント処理。
しかも、たくさんの要素にイベントをアタッチするのではなく、windowオブジェクト1つにアタッチするので、軽量。多分IEでattachEventを使ってもメモリリークが起こりにくい実装なんじゃないかな。window.unload でガベージとして処理されるはずなので。

// IEに対応する場合
ev  = ev || window.event;
var src = ev.target || ev.srcElement;
var link = findParent( src )


今回は基本と言うことで HTMLのレイアウトを書いたけど、当然、CSSも絡んでくるし、待機状態でのクリックイベントの実装方法まで述べたつもり。もう「三位一体」という表現以外に思いつかないわけでw

はっきり言うと、HTMLを書くのも、CSSを書くのも動的処理が確実といったレベルなんです。で、例示したHTMLでも div[selected='true'] に関しては、ユーザが自由にレイアウトできるのかなと。


JavaScriptでXPathを使おうと思って、有名なJavaScript-XPathにたどり着いたのだけど、Typoがあるっぽい。

JavaScript-XPath 0.1.12

line 41

            :
            var configName = configStringSplited[0];
            var configValue = configStringSplited[1];
            if (configValue == undefined) {
                configValue == true; // ← ここ。コードの前後関係から単に代入となりそう。
            }
            else if (configValue == 'false' || /^-?\d+$/.test(configValue)) {
                configValue = eval(configValue);
            }
            config[configName] = configValue;
            :


てことで、Latest Compressed Version は使わないほうがよさげ。

追記)2012-02-15
改めてコードを読みなおして気づいた
ここのtypo は、SCRIPT要素のsrc属性にオプションを記述する場合に影響するものなので、
scriptaculous.js なんかで使われてた様に、こんな風に使うケースを想定しているっぽい。

28行から32行ね。


<script src="javascript-xpath.js?useInnerText=false&exportInstaller"></script>

29行で scriptElms[scriptElms.length - 1] としてるから、HTML中の最後にに記述されたSCRIPT要素を検査する。対象スクリプトは読み込みの最中である javascript-xpath.js となる。

このSCRIPT要素の記述だと、useInnerText をデフォルトとは異なる false にし、exportInstaller のフラグをtrue にしたい。exportInstaller のフラグが立たないバグということになる。

ただまぁ、typoではあるけど、「あまり使わない書き方だよね」ということ(使わないほうがよさげ。に打ち消し線を追記した理由)。
勿論、サーバーから出力するHTMLに埋め込むときには非常に便利だと思う。

Closure Compiler Service で [Simple] にして [Compile] すれば圧縮されたコードを取得できるので、Typoを修正して使ってみることに。


iUI の欠点は、独自のマークアップルールがあるため、
静的ページを作るのが面倒なんですよね。

で、作ってみた。

iFORMS : JavaScript Form Generator for iUI

管理画面でSEOが関係ないとか、スマートフォンだけで表示するとか、
条件が揃うなら、別にサーバーで整形する必要は無い。
てことで、JavaScriptでiui用の管理画面をするテスト


Firefox, Safari for Mobile でしか確認してません。

あと、リンク先でHTMLソースを見れば判ると思いますが、
まともなHTML文書ではありません。

可視コンテンツ書いてないしw



iui.js から抽出したスライド効果のロジック


とりあえず、次のようなURLで (pagename) 部分が変わると処理するようにした。

http://domain/hostpage.html#page=(pagename)

前回のエントリでも書いたのだけど、

「複数ペインをもつレイアウトのページで
特定のペインを対象にした効果を出したい」

てのを実現したつもり。

addEventListener() でイベントを登録してるので、JScript では動きません。プロダクトじゃないので、JScriptをターゲットにする必要はないかなと。...chakra では動くと思います。

iui から独自の実装に変更したのだけど、この実装だと addEventListener("click", ... ) で実装する方法と、ブラウザのbackボタンで戻る際のアニメーション効果が表れないこと。
iOSっぽくないかも。

なんていえばいいのか、「HTML+CSS+JavaScript」が三位一体になった仕様だと思う。

従来型のオープンソースのJavaScriptライブラリと違って「単に使うだけ」という目的では、意図したインターフェースを構築するのに物足りないんですよね。たとえば、iPadのように大きな画面を2つのペインに分割したいと時、そのままでは使えない。

で、改めてコードリーディングを開始したわけですが、

HTMLは、ツールバー領域とコンテンツ領域だけで構成し、フッターの存在は認めない仕様。BODY直下に、ツールバー用のマークアップとコンテンツ用のマークアップとがあり、コンテンツ領域では、よく知られるUL要素以外に、FORM要素も使っていたり。DIV.panel で表現することもある。
こんな風に、マークアップ自体は複雑だけどシンプルなルールで纏められていた。

けど、厄介に感じたのが独自の属性値とか、角丸表現に FIELDSET要素を使うこととか。普通に考えて FORM>FIELDSET以外は思いつかないのだけど、DIV > FIELDSET で角丸を表現してる。HTML5では認められてるっぽい(フローコンテンツが期待される場所で利用可能)。深く読むまでは思いつかないような実装が多いです。

BODY要素に独自のorient属性を加えていたり、
コンテンツ領域の要素には、スライド中&表示中は[selected="true"]が求められたりと、「独自の属性&属性値」がちりばめられている。
JavaScriptのコード内でバッファリングしないのか?ビジュアルツリーに配備したHTMLElement要素に独自の属性を加える形だと、ドキュメントベースのDBっぽい扱いになるのか?と考えたりもした。

表示の基本はCSSで定められてるけど、動的変化はJavaScriptで独自の属性をチェックしたり書き換えしたり。慣れていない手法が盛りだくさん。

クリックイベントは windowオブジェクトにアタッチして、イベント伝播を活用してイベント発生要素を元に切り分け処理してる。この方法、幾つもの対象要素全てにイベントを付与する入門レベルの実装方法よりも軽量なのだけど、コードリーディングしてない人には絶対に使いこなせないと感じた。


ちなみに、 stable latest である今でも、サードパーティのコードがサンプルに追加されていて、拡張例として参照できるけど、今のうちにリーディングしておかないと、0.40-dev2 では、スライドエフェクトの前後とか、ダイアログを呼び出したとき、キャンセルしたときなど、独自のイベント発火タイミングが加わってる。


最後に、iui=簡単と言ってる人は多いのですが、「iuiをクロスデバイス化してみ」というのが本稿の主旨です。

このページのトップヘ