これでできる! クロスブラウザJavaScript入門

第9回 実践DOMスクリプティング#2:DOMとHTML

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

cloneNodeによる要素の組み立て

そこでオススメなのが,cloneNodeで要素を複製する方法です。まず,次のような⁠できあがった⁠HTMLを用意します。

テンプレート用HTML

<ul id="twitter-search-t2" style="display:none;">
 <li>
   <a class="usr">
     <img width="48" height="48"><br>
     <span>boosim</span>
   </a>
   <p class="entry"></p>
   <div class="time"><a></a></div>
 </li>
</ul>

このHTMLをベースに,cloneNodeで複製した要素を基点にgetElementsByTagNameで要素を選択し,各属性とテキストを設定します。

cloneNode方式の組み立て

var _t=document.getElementById('twitter-search-t2');
var tmpl = _t.getElementsByTagName('li')[0];
function TwitterCallback2(data){
  var div = document.getElementById('twitter-search-r2');
  /* 前回の結果がある場合に削除 */
  while (div.firstChild) {
    div.removeChild(div.firstChild);
  }
  var results = data.results;
  var ul = document.createElement('ul');
  ul.className = 'twl';
  for (var i = 0, len = results.length;i < len; i++){
    var usr = results[i];
        var user = usr.from_user;
    /* 要素を作る */
    var li = tmpl.cloneNode(true);
    var link = li.getElementsByTagName('a')[0];
    var icon = link.getElementsByTagName('img')[0];
    var name = link.getElementsByTagName('span')[0];
    var entry = li.getElementsByTagName('p')[0];
    var time = li.getElementsByTagName('div')[0];
    var timelink = time.getElementsByTagName('a')[0];
    /* CSS用にclassを設定 */
    li.className = ((i+1)%2) ? 'odd' : 'even';
    /* リンクや画像などの属性を設定 */
    link.href = 'http://twitter.com/' + user;
    var src = usr.profile_image_url;
    if (src.indexOf('http') === 0) {
      icon.src = src;
    }
    icon.width = 48;
    icon.height = 48;
    timelink.href = 'http://twitter.com/' +
                user +'/status/' + usr.id;
    var d = new Date(usr.created_at);
    var date = d.getFullYear() + '/' + (d.getMonth()+1) +
           '/' + d.getDate() + ' ' + d.getHours() + ':' +
           ('0'+d.getMinutes()).slice(-2);
    /* テキストノードの挿入 */
    entry.appendChild(document.createTextNode(usr.text));
    timelink.appendChild(document.createTextNode(date));
    name.appendChild(document.createTextNode(user));
    /* 要素の組み立て */
    ul.appendChild(li);
  }
  /* 画面に反映 */
  div.appendChild(ul);
}
var b2 =document.getElementById('twitter-search-b2');
b2.onclick=function(){
  var script = document.createElement('script');
  script.src = 'http://search.twitter.com/search.json'+
    '?callback=TwitterCallback2&lang=ja&q=JavaScript';
  document.body.appendChild(script);
}

ご覧の通り,こちらの方法ではappenChildの順番で要素を組み立てていた部分をきれいに省くことができます。また,ウェブデザイナーから受け取ったHTMLをほぼそのまま使え,style="display:none;"の指定を削除すればJavaScript不要で見た目の調整ができる点もポイントです。

またパフォーマンスの点でも,IEではcloneNode版のほうが速いくらいです。IE以外ではややcloneNode版のほうが劣るものの,一回あたりの差は1msにも満たいない程度と十分な速さです。

なお,img要素のsrc属性に付いてはスキーマ次第でスクリプトが実行できてしまうケースがあるので,httpで始まっているかチェックしています。同様にhref属性なども注意が必要です。

また,JavaScriptというテーマから外れる部分ではありますが,今回のように見栄えをそれなりにすると当然CSSのクロスブラウザ化が問題となります。今回は角丸をborder-radiusをサポートしている場合だけと割り切っているので,CSSハック的なものはIE6でmin-heightを使う方法のみを使用するだけで済んでいます。

IE6でmin-heightを使う方法とは,次のように全ブラウザにmin-heightを指定し,IE6だけにheightを指定しています。IE6はmin-heightが無視されheightが指定されるので,60pxに収まらないときは要素をはみ出してしまうように思えますが,IE6は要素内がはみ出した時のデフォルトの挙動が「要素を広げる」であるため,min-heightが指定されているかのように内容に応じて高さが変わります。

min-heightの代替ハック

li p.entry{
  min-height:60px;
}
/*スターハック IE6(とIE7の互換モードなど)のみに適用*/
* html li p.entry{
  height:60px;
}

なお,IE6だけにスタイルを当てるCSSハックはいくつかありますが,スターハックはCSSの文法には違反しないためバリデータでエラーがでないという点がポイントです。

まとめ

今回はDOM操作の実例に触れてみましたが,実は今回の範囲ではブラウザごとの分岐処理などを一切使用していない点も重要なポイントです。DOMを使った要素の組み立ては基本に忠実な扱い方を押さえるだけで,ライブラリなどを使うまでもなくクロスブラウザ化することが可能です。

次回はJavaScriptから操作するCSSを中心に取り上げる予定です。

著者プロフィール

太田昌吾(おおたしょうご,ハンドルネーム:os0x)

1983年生まれ。JavaScriptをメインに,HTML/CSSにFlashなどのクライアントサイドを得意とするウェブエンジニア。2009年12月より、Google Chrome ExtensionsのAPI Expertとして活動を開始。

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