SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

XMLとXSLによるフォルダツリーの作成

XMLとXSLを使った高度なUIデザイン:パート1

XML構造

 ツリーに何を求めるかは、それこそ千差万別です。私がツリーに望むこととあなたが望むことの間には、きっと大きな違いがあるでしょう。ツリーエンティティは、ツリーごとに特異です。そうした特異なツリーエンティティを記述するのに、XMLは最適なインタフェースを備えています。どのフォーマットを選ぶかで、XSLスタイルシートとクライアント動作が大きく変わります。私が選んだのは、再帰的XSLスタイルシートに適したフォーマットです。これは、本稿で取り上げている深さ無限のフォルダツリーという条件をよく満たしています。

 私が作成したXMLドキュメントには、treeという名前のルート要素が含まれています。これはentity要素だけを含むことができます。これらのentity要素を、他のentity要素内のcontents要素の入れ子とすることで、ツリーの構造が暗黙裏に定義されます。次に示すのは、entity要素に属するすべての要素と属性のリストです。

名前種類内容
id属性個々のエンティティを指し示すのに用いられる一意の文字列または整数です。
description要素個々のエンティティの記述。ユーザーに表示されるテキストです。
onClick要素onClickイベントで実行されるクライアント側関数の名前です。
image要素エンティティが閉じたとき、または選択されなかったときに表示される画像です。
imageOpen要素エンティティが開いたときに表示される画像です。
contents要素エンティティ要素を含みます。あるエンティティに子があるかどうかを調べるのに使われます。

 実際のツリーでは、ツリー内部の各オブジェクトに関する具体的データを、エンティティ要素によって付加していきます。具体的データとは、たとえば、そのエンティティによって表されるデータベースレコードのIDかもしれません。次回以降では、各エンティティ固有のコンテキストを作成し、それをoncontextmenu要素として実装する(エンティティ要素に追加していく)方法を紹介します。

 本稿で使用するXMLは次のとおりです。ここでは、わかりやすいように静的XMLドキュメントとして作成していますが、動的XMLデータベースクエリとして実装することもできます。

<?xml version="1.0"?>
<tree>
  <entity id="e1">
    <description>Customers</description>
    <oncontextmenu></oncontextmenu>
    <image>images/book.gif</image>
    <imageOpen>images/bookOpen.gif</imageOpen>
    <contents>
      <entity id="e2">
        <description>Microsoft</description>
        <image>images/book.gif</image>
        <imageOpen>images/bookOpen.gif</imageOpen>
        <onClick>displayCustomer(12345)</onClick>
        <contents>
          <entity id="e3">
            <description>Orders</description>
            <image>images/book.gif</image>
            <imageOpen>images/bookOpen.gif</imageOpen>
            <onClick></onClick>
            <contents/>
          </entity>
        </contents>
      </entity>
      <entity id="e4">
        <description>IBM</description>
        <image>images/book.gif</image>
        <imageOpen>images/bookOpen.gif</imageOpen>
        <onClick>displayCustomer(12346)</onClick>
        <contents>
          <entity id="e5">
            <description>Orders</description>
            <image>images/book.gif</image>
            <imageOpen>images/bookOpen.gif</imageOpen>
            <onClick></onClick>
            <contents/>
          </entity>
        </contents>
      </entity>
      <entity id="e6">
        <description>Sun Microsystems</description>
        <image>images/book.gif</image>
        <imageOpen>images/bookOpen.gif</imageOpen>
        <onClick>displayCustomer(12347)</onClick>
        <contents>
          <entity id="e7">
            <description>Orders</description>
            <image>images/book.gif</image>
            <imageOpen>images/bookOpen.gif</imageOpen>
            <onClick></onClick>
            <contents>
              <entity id="e8">
                <description>#12345</description>
                <image>images/paper.gif</image>
                <imageOpen>images/paper.gif</imageOpen>
                <onClick></onClick>
                <contents/>
              </entity>
              <entity id="e9">
                <description>#12346</description>
                <image>images/paper.gif</image>
                <imageOpen>images/paper.gif</imageOpen>
                <onClick></onClick>
                <contents/>
              </entity>
            </contents>
          </entity>
        </contents>
      </entity>
      <entity id="e10">
        <description>Oracle</description>
        <image>images/book.gif</image>
        <imageOpen>images/bookOpen.gif</imageOpen>
        <onClick>displayCustomer(12348)</onClick>
        <contents>
          <entity id="e11">
            <description>Orders</description>
            <image>images/book.gif</image>
            <imageOpen>images/bookOpen.gif</imageOpen>
            <onClick></onClick>
            <contents/>
          </entity>
        </contents>
      </entity>
    </contents>
  </entity>
  <entity id="e12">
    <description>Reports</description>
    <oncontextmenu></oncontextmenu>
    <image>images/book.gif</image>
    <imageOpen>images/bookOpen.gif</imageOpen>
    <contents>
      <entity id="e13">
        <description>Income</description>
        <oncontextmenu></oncontextmenu>
        <image>images/paper.gif</image>
        <imageOpen>images/paper.gif</imageOpen>
        <contents>
        </contents>
      </entity>
      <entity id="e14">
        <description>Expenses</description>
        <oncontextmenu></oncontextmenu>
        <image>images/paper.gif</image>
        <imageOpen>images/paper.gif</imageOpen>
        <contents>
        </contents>
      </entity>
    </contents>
  </entity>
</tree>

 このXMLファイルの名前は「tree.xml」です。このファイルはダウンロードサンプルに含まれています。

XSLスタイルシート

 図1と図2は、どちらも、XMLドキュメントにXSLスタイルシートを適用して得られた結果でした。XMLドキュメントに適用される標準のXSLスタイルシートは次のとおりです。

<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl"
                language="JavaScript">

<xsl:template match="tree">
  <xsl:apply-templates select="entity"/>
</xsl:template>

<xsl:template match="entity">
  <div onclick="window.event.cancelBubble = true;clickOnEntity(this);"
       onselectstart="return false" ondragstart="return false">
  <xsl:attribute name="image"><xsl:value-of select="image"/>
  </xsl:attribute>
  <xsl:attribute name="imageOpen"><xsl:value-of select="imageOpen"/>
  </xsl:attribute>
  <xsl:attribute name="open">false</xsl:attribute>
  <xsl:attribute name="id">f<xsl:value-of select="@id"/>
  </xsl:attribute>
  <xsl:attribute name="open">false</xsl:attribute>
  <xsl:attribute name="STYLE">
    padding-left: 20px;
    cursor: hand;
    <xsl:if expr="depth(this) > 2">
      display: none;
    </xsl:if>
  </xsl:attribute>
    <table border="0" cellspacing="0" cellpadding="0">
      <tr>
        <td valign="middle">
          <img border="0" id="image">
            <xsl:attribute name="SRC">
              <xsl:value-of select="image"/>
            </xsl:attribute>
          </img>
        </td>
        <td valign="middle" nowrap="true">
        <xsl:attribute name="STYLE">
          padding-left: 7px;
          font-family: Verdana;
          font-size: 11px;
          font-color: black;
        </xsl:attribute>
        <xsl:value-of select="description"/></td>
      </tr>
    </table>
  <xsl:apply-templates select="contents/entity"/>
  </div>
</xsl:template>

</xsl:stylesheet>

 このXSLスタイルシートを使って作成したフォルダツリーを図3に示します。

図3 XSLによる変形
図3 XSLによる変形

クライアント側操作

 このフォルダツリーをそれらしく動作させるには、クライアント側で次の操作が必要です。

  1. 初期化
  2. 展開
  3. 折り畳み
  4. 完全展開(最大化)
  5. 完全折り畳み(最小化)

 この5つの操作を実行するコードを次に示しておきます。このコードは、ダウンロードサンプルの「tree.js」ファイルに含まれています。

function initialize() {
  var xmlDoc
  var xslDoc

  xmlDoc = new ActiveXObject('Microsoft.XMLDOM')
  xmlDoc.async = false;

  xslDoc = new ActiveXObject('Microsoft.XMLDOM')
  xslDoc.async = false;

  xmlDoc.load("tree/tree.xml")
  xslDoc.load("tree/tree.xsl")

  folderTree.innerHTML = xmlDoc.documentElement.transformNode(xslDoc)
}

function clickOnEntity(entity) {
  if(entity.open == "false") {
    expand(entity, true)
  }
  else {
    collapse(entity)
  }
  window.event.cancelBubble = true
}

function expand(entity) {
  var oImage

  oImage = entity.childNodes(0).all["image"]
  oImage.src = entity.imageOpen

  for(i=0; i < entity.childNodes.length; i++) {
    if(entity.childNodes(i).tagName == "DIV") {
      entity.childNodes(i).style.display = "block"
    }
  }
  entity.open = "true"
}

function collapse(entity) {
  var oImage
  var i

  oImage = entity.childNodes(0).all["image"]
  oImage.src = entity.image

  // collapse and hide children
  for(i=0; i < entity.childNodes.length; i++) {
      if(entity.childNodes(i).tagName == "DIV") {
        if(entity.id != "folderTree") 
entity.childNodes(i).style.display = "none"
        collapse(entity.childNodes(i))
      }
    }
  entity.open = "false"
}

function expandAll(entity) {
  var oImage
  var i

  expand(entity, false)

  // expand children
  for(i=0; i < entity.childNodes.length; i++) {
    if(entity.childNodes(i).tagName == "DIV") {
      expandAll(entity.childNodes(i))
    }
  }
}

Webベースのフォルダツリーの今後の展開

 Web技術の進歩にともない、Webアプリケーション内部にステート情報を維持できるようになって、しだいに多くの機能がサーバ側からクライアント側に移され、インタフェースは複雑になる一方です。しかし、その結果、サーバの負担が軽くなり、インタフェースの使い勝手とアプリケーションのパフォーマンスが向上するわけですから、喜ぶべきことでもあります。

 いずれ、本稿で作成したツリーをさらに発展させ、ツリー内部にあるオブジェクトの挿入・削除・改名・再表示を動的に行うAPIを作成して、紹介したいと考えています。

終わりに

 本稿の内容が、Webアプリケーションインタフェースの質的向上に役立てば幸いです。質問・コメント・提案があれば、筆者紹介にあるアドレスまで遠慮なくメールをお送りください。

 この原稿に目を通し、表現と内容の両方をチェックしてくれたLee McGrawに感謝します。

 次回は、カスタムコンテキストメニューの作成を取り上げます。

本連載のその他の記事

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Joe Slovinski(Joe Slovinski)

1993年以来、Webアプリケーションの開発に継続的に携わる。本稿で紹介したコードについてのお問い合わせはJoe Slovinskiまで。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/236 2006/04/11 19:05

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー