agenda 2002-09(上旬) ヤッペとド・エスコバアルは如何に殴りあったか

MS独自拡張selectNodesメソッドについて

公開
2002年9月15日

selectNodesメソッドは、Nodeインターフェイスに実装されているMSの独自仕様。引数のXPath式で指定されたノード集合が返却される。

実際に非常に役立ったので、具体的な使い方をメモしておく。ちなみにMSXML3.0以前のことは知らない。

将来ノード集合の指定方法を拡張する予定があるのか、面倒なことにXPathを使用することを明言しなければならない。DocumentオブジェクトのsetPropertyメソッドで指定。


var xmlDoc = new ActiveXObject('Msxml2.DOMDocument.4.0');
xmlDoc.async = false;
xmlDoc.load('foo.xml');
xmlDoc.setProperty('SelectionLanguage', 'XPath');

XPathでは、ノードテストにQNameが使われる為、名前空間接頭辞がどの名前空間を指しているかを明示する必要も出てくる。明示には同じくsetPropertyメソッドを使う。

以下は、XHTMLの要素をhtmlという名前空間接頭辞で特定する場合。

xmlDoc.setProperty(
  'SelectionNamespaces',
  'xmlns:html="http://www.w3.org/1999/xhtml"'
);

well-formedなだけで名前空間もnullな謎XML文書を扱う場合には無関係。ノードテストで要素名を使った際、名前空間接頭辞が無い場合、それは名前空間がnullであることを意味するので、例えばxmlns="http://www.w3.org/1999/xhtml"等とデフォルトの名前空間を設定しても無意味。そんなことが出来てしまったら名前空間がnullな要素を特定不可能になってしまう為。

ちなみにasyncプロパティは、デフォルトのtrueのままだと文書実体のロード完了を待ってくれないらしい。一々falseに設定するのが面倒で仕方がないけれども、処理速度を優先したい時にデフォルトがfalseだと非効率なので合理的。

ともあれこれでselectNodesメソッドが使用可能になる。任意のノードのメソッドとして使用可能で、そのノードが、XPathにおけるコンテクストノードとなる。以下はXHTML文書にて、ルート要素(HTML要素/xmlDoc.documentElement)をコンテキストノードとして、body要素の子要素であり、かつ、class属性値が"footer"であるdiv要素全てをノードリストとして変数nlDivsに格納した例。


var nlDivs;
nlDivs = xmlDoc.documentElement.selectNodes(
	'child::html:body/child::html:div[@class="footer"]'
);

XPath式での指定に失敗した場合、空のノードリスト(length == 0)が返却される。

注意点

selectNodesメソッドは、常にノードリストを返却する。


var nAddress;
nAddress = nlDivs.item(0).selectNodes(
	'following-sibling::html:address[1]'
);

と、このように特定の一つのaddress要素を指定したつもりでも、やはりノードリスト型のオブジェクトが返却される。実際にこのaddress要素を取得するには、itemメソッドで、nAddress.item(0)とする。

指定するノードが一つであることが予め分かっているなら、selectSingleNodeメソッドを使った方が明らかにベター。この場合、式で複数のノードが選択されようとも、そのうちの最初のノードが返却される。

その他雑記

というわけで、Weblogの更新記事をカテゴリごとに分類したり色々するために、そりゃあもう恐ろしいほどの作業を全部MSXML君にやってもらうことにした。ついでに更新履歴も。時期によっては、ホームページの更新とログファイルの生成も行わせるので、下手をすると15以上の文書を生成させることに。もしselectNodesメソッドが使えなかったら、嫌になって途中で止めていたような気がする。

「ツリーを辿る」というまどろっこしいことをしないで済むことが多いので、簡単な変換ならXSLTを使うより楽。

XSLTにおけるNMTOKENS型属性の扱い

公開
2002年9月12日

カレントノードが、例えば次のようなh2要素であった場合を考える。

<h2 class="foo bar baz">heading text</h2>

このとき、class名である「foo」、「bar」、「baz」それぞれについて、これらをspan要素として生成したいとする。

まずこのように、名前付きテンプレート(forEachNMTOKENS)にclass属性値をパラメータとして渡しつつ呼び出す。名前付きテンプレート「forEachNMTOKENS」は後述。

<xsl:call-template name="forEachNMTOKENS">
 <xsl:with-param name="NMTOKENS" select="normalize-space(@class)" />
</xsl:call-template>

各class属性値が、一つ以上の空白記号で区切られている場合考慮に入れ、最初にこのテンプレートを呼び出す際に、class属性値を正規化(normalize-space(@class))している点に注意。

<xsl:template name="forEachNMTOKENS">
 <xsl:param name="NMTOKENS">arbitrarySTRING</xsl:param>
 <xsl:variable name="dividedNMTOKENS" select="substring-after($NMTOKENS, ' ')" />

 <xsl:if test="$dividedNMTOKENS = ''">
  <!-- 属性値が一つしかない場合は空文字列が返却される -->
  <!-- ここに属性値($NMTOKENS)に関するリテラル結果要素等 -->
  <span><xsl:value-of select="$NMTOKENS" /></span>
 </xsl:if>

 <xsl:if test="$dividedNMTOKENS != ''">
  <!-- 複数存在した場合 -->
  <xsl:variable name="ATTRVALUE" select="substring-before($NMTOKENS, ' ')" />
  <!-- ここに属性値($ATTRVALUE)に関するリテラル結果要素等 -->
  <span><xsl:value-of select="$ATTRVALUE" /></span>
  
  <xsl:call-template name="forEachNMTOKENS">
   <xsl:with-param name="NMTOKENS" select="$dividedNMTOKENS" />
  </xsl:call-template>
 </xsl:if>
</xsl:template>

これで、「foo」、「bar」、「baz」それぞれを内容に持ったspan要素が3つ生成されることになる。

汎用性を持たせるなら、リテラル結果要素の部分をテンプレート呼び出しにしてしまえば良い(筈)。何のテンプレートを呼び出すかは、もう一つ別のパラメータを作り、それで判別。

不便?

XPathで、NMTOKENS型属性の値それぞれをノードとして指定できれば、このようなことをする必要は無いのだけど。というわけで、もしそのようなことが出来るなら、このテンプレートは無価値。

これは何?

例えばこのWeblogのXMLソースにて各記事に「topic="XSLT XPath"」 というような属性を設けておいて:

  • 特定のトピックに関する記事のアーカイブを生成するのに役立てる
  • そのリストへのアンカーを生成

こういうことしよういうのが狙い。前者はDOMで、後者はXSLTで。

css-discuss

公開
2002年9月12日

css-discuss (英語)はCSSのメーリングリスト。一日に50程の投稿があり、クロスブラウザマニアが沢山いる。

word-break問題を検索してみたけれど、やはりIE5 later以外は我慢しろとかスペースを入れろとかいう結論しか出ないようだった。

ちなみにword-breakプロパティは、MSの独自拡張。キーワード:break-allによるURLの折り返しが便利。

agendaの過去ログリストを自動生成(XSLT)

公開
2002年9月12日

長いです。

<ul>
<li>
 <a href="agenda.html" title="記事のURLが変ります。">
  <span class="desc">agenda</span>最新(更新版)
 </a>
</li>
<li>
 <a href="d20029f.html" title="記事のURLが変りません。">
  <span class="desc">agenda</span>最新(保存版)
 </a>
</li>
<li>
 <a href="d20028l.html">
  <span class="desc">agenda</span>2002-08(下旬)
 </a>
</li>
<li>
 <a href="d20028f.html">
  <span class="desc">agenda</span>2002-08(上旬)
 </a>
</li>
<li>
 <a href="d20027l.html">
  <span class="desc">agenda</span>2002-07(下旬)
 </a>
</li>
<li>
 <a href="d20027f.html">
  <span class="desc">agenda</span>2002-07(上旬)
 </a>
</li>
<li>
 <a href="d20026l.html">
  <span class="desc">agenda</span>2002-06(下旬)
 </a>
</li>
<li>
 <a href="d20026f.html">
  <span class="desc">agenda</span>2002-06(上旬)
 </a>
</li>
<li>
 <a href="d20025l.html">
  <span class="desc">agenda</span>2002-05(下旬)
 </a>
</li>
<li>
 <a href="d20025f.html">
  <span class="desc">agenda</span>2002-05(上旬)
 </a>
</li>
<li>
 <a href="d20024l.html">
  <span class="desc">agenda</span>2002-04(下旬)
 </a>
</li>
<li>
 <a href="d20024f.html">
  <span class="desc">agenda</span>2002-04(上旬)
 </a>
</li>
<li>
 <a href="d20023l.html">
  <span class="desc">agenda</span>2002-03(下旬)
 </a>
</li>
<li>
 <a href="d20023f.html">
  <span class="desc">agenda</span>2002-03(上旬)
 </a>
</li>
<li>
 <a href="d20022l.html">
  <span class="desc">agenda</span>2002-02(下旬)
 </a>
</li>
<li>
 <a href="d20022f.html">
  <span class="desc">agenda</span>2002-02(上旬)
 </a>
</li>
<li>
 <a href="d20021l.html">
  <span class="desc">agenda</span>2002-01(下旬)
 </a>
</li>
<li>
 <a href="d20021f.html">
  <span class="desc">agenda</span>2002-01(上旬)
 </a>
</li>
</ul>

このリストは、最新版の年、月、上旬/下旬 が分かっていれば、自動で生成できるはず。例えば:

<blog-archive latest="2002-09-f" />

このような空要素を一つ書いて、後はXSLTに任せることができる。もちろんDOMでやった方が速いのだけれどもー。リハビリ。

マッチさせるテンプレートと変数の設定

まず、blog-archive要素にマッチさせるテンプレートと、年、月、上旬/下旬 にバインドさせるローカル変数を作る。テンプレートを作る際にはマッチする優先順位に注意。


<xsl:template match="blog-archive">
 <xsl:variable name="year" select="substring(@latest, 1, 4)" />
 <xsl:variable name="month">
  <xsl:choose>
  <xsl:when test="substring(@latest, 6, 1) = '0'">
   <xsl:value-of select="substring(@latest, 7, 1)" />
  </xsl:when>
  <xsl:otherwise>
   <xsl:value-of select="substring(@latest, 6, 2)" />
  </xsl:otherwise>
  </xsl:choose>
 </xsl:variable>
 <xsl:variable name="division" select="substring(@latest, 9, 1)" />
</xsl:template>

リテラル結果要素を書く

次に、テンプレート内にリテラル結果要素を書く。繰り返し生成する部分は、別の名前付きテンプレートを呼び出す形にする。名前付きテンプレートは後述。

<ul>
<li>
 <a href="agenda.html" title="記事のURLが変ります。">
  <span class="desc">agenda</span>最新(更新版)
 </a>
</li>
<li>
 <a href="d{$year}{$month}{$division}.html" title="記事のURLが変りません。">
  <span class="desc">agenda</span>最新(保存版)
 </a>
</li>
<xsl:call-template name="createBlogList">
 <xsl:with-param name="year" select="$year" />
 <xsl:with-param name="month" select="$month" />
 <xsl:with-param name="division" select="$division" />
</xsl:call-template>
</ul>

createBlogListという名前のテンプレートを呼んでいる。年、月、上旬or下旬をパラメータとして渡す。

リストを繰り返し生成する為のテンプレートを書く

for-eachなどは使わない。for-eachを「繰り返し」と考えている限りは、XSLTは異常に難しい(と思う)。for-eachが繰り返しならば、apply-templatesも繰り返し。リテラル結果要素を中に書くか、外に出すかの違いが主。他にも色々あるけれども、「繰り返し」とは全然関係ない。

<xsl:template name="createBlogList">
 <!-- paramの初期値。呼び出し元から渡されない場合の値を書く -->
 <xsl:param name="year">yyyy</xsl:param> <!-- 適当 -->
 <xsl:param name="month">m</xsl:param> <!-- 適当 -->
 <xsl:param name="division">d</xsl:param> <!-- f,l 以外 -->
 
 <!-- ひと月減らす。 -->
 <xsl:variable name="premonth" select="$month - 1" />

 <xsl:if test="$division = 'l'">
  <li>
   <a href="d{$year}{$month}f.html">
    <span class="desc">agenda</span>
    <xsl:value-of select="$year" />
    <xsl:text>-</xsl:text>
    <xsl:if test="$month - 10 &lt; 0">0</xsl:if>
    <xsl:value-of select="$month" />(上旬)
   </a>
  </li>
 </xsl:if>

 <xsl:if test="$month &gt; 1"><!-- 0月は無い為 -->
  <li>
   <a href="d{$year}{$premonth}l.html">
    <span class="desc">agenda</span>
    <xsl:value-of select="$year" />
    <xsl:text>-</xsl:text>
    <xsl:if test="$premonth - 10 &lt; 0">0</xsl:if>
    <xsl:value-of select="$premonth" />(下旬)
   </a>
  </li>
  <li>
   <a href="d{$year}{$premonth}f.html">
    <span class="desc">agenda</span>
    <xsl:value-of select="$year" />
    <xsl:text>-</xsl:text>
    <xsl:if test="$premonth - 10 &lt; 0">0</xsl:if>
    <xsl:value-of select="$premonth" />(上旬)
   </a>
  </li>
 </xsl:if>

 <!-- ひと月減らして再帰呼び出し。1月の場合は呼び出さない。 -->
 <xsl:if test="$premonth &gt; 1">
  <xsl:call-template name="createBlogList">
   <xsl:with-param name="year" select="$year" />
   <xsl:with-param name="month" select="$premonth" />
  </xsl:call-template>
 </xsl:if>

</xsl:template>

というわけでXSLTリハビリ終了。

今後

憧れのdive into mark (英語)に負けないようなWeblog管理を目指したい。アメリカではBlog管理ツールが沢山あるようだけれど、ローカルで処理してスタティックな文書をputするしかないJCOMユーザーな私は、ツールに頼らず自分でやるしかない。

具体的にはカテゴリ別に分けたWeblogのアンカーリストと、各記事の双方向リンクを実現したい。XSLTだけでは無理っぽいので、全Weblog文書のDOMツリーを弄ってシンプルな(エディタで直接編集可能な)XMLを生成した後、XSLTで構造化された公開用のXHTMLに変換するとか。

カテゴリをどうやって識別するかが大問題。NMTOKENS型の属性で各記事とカテゴリを関連付けるか、属性ではなく複数の要素でやるか。どちらにしろ手作業が辛い。

その他愚痴

雑誌を立ち読みして驚いた。

雑誌の入門記事などには必ずと言っていい程 <xsl:value-of select="." /> が出てくる。何故省略しなければならないのか。カレントノードが要素ノードで、子がテキストノードしか無いならば、child::text()の方が処理が速い。不許可。self::node()のように「軸 + ノードテスト」の形で一貫させた方が遥かに理解は容易になるはずなのに、何も考えずに省略形を濫用するのも不許可。value-of要素のselect属性にノードが指定された時の、String関数の挙動についての説明もなし。動けばいいんですかそうですか。

よく分からない内は省略しない方が良いに決まっているのだけれど、読者を馬鹿にした著者はそう思わないらしい。XSLTを「簡単そうで難しいもの」にしているのはあなた方だ。

愚痴は続く

さらに悪質だと思っているのがこれ。<xsl:apply-templates />。だから何故入門の癖にselect属性を省略するのか。これでは、カレントノードリストがどう変更されたのか分からない。カレントノードリストを把握できずにXSLTなんか書ける訳が無いだろうに。少なくとも何の応用も効かない筈。select="child::node()" は知っていても書くべきだ。

<xsl:value-of select="." />と<xsl:apply-templates />を使った省略だらけの例を示して、簡単な「XSLTの概要」とか称しているものもあるが、その「例」で「実験」してみたところで、それで得られた経験は何一つ応用の効かないものだ。

Bookmark : dive into mark

公開
2002年9月9日
dive into mark (英語)
Weblog。CSS関連、Accessibility関連の記事は読み応えがある。dive into mark/categories (英語)はカテゴリ別アーカイブ。関連情報が丁寧に纏められていて、HyperTextの特徴が活かされている。
  • 「良いリンクが沢山あるサイトは良いサイト」を実感
  • bloggerの真髄を見た気がした

ショックです。

スタイルシート切替を提供している英語圏サイト

公開
2002年9月8日

そろそろ英語圏のCSS採用サイトを探してみようかと。とっつきやすそうなので「スタイルシート切替」採用サイトから探してみた。

Home pageでやっているサイトは結構少ないみたい。専用のページを設けて、細かく設定できるようにしてあるサイトが多い。

そしてウェブリング発見。よく見てないけど一応リンクしておくとしよう。

このウェブリングはナビゲーションが分かりづらいけれども「< ? skinned! # >」これがそうらしい。で、#がリストになっている、と。……小さすぎ。もしリウマチを患っていたらクリックできないよ。「ダイレクトリンク」でもしますか。

FAQ:ウェブページのリンクが機能しないときには

公開
2002年9月7日

IE/Win、Mozilla/Win使用時、アンカーをクリックしても何も起らない時の対処法を。

  1. とりあえずクリックしてください
  2. Tabキーを押してフォーカスを移してください
  3. Enterキーを押します

これが一般常識だったら「対処」なんかしなくて済むのに。

ブックマーク

公開
2002年9月7日

文字砂漠つながり。特に意味なし。

でも訳の分からないフレームの使い方をしているのは勿体無い。検索で見つけたフレームの断片文書から、「ディレクトリを掘って」見つけたのだけれども、まあ、普通の人は「Back」ボタンだ。

それから、妙な造語というのは自分の中だけで完結していなければならないと思う。アカウント名に留めるとか。そうでなければ言語学的センスをふり絞るべきであって、自虐的な、オヤジギャグ的な使い方には嫌悪感を覚える。どんどん醜くなる言葉。

日付が変ってしまったか。

ブックマーク不要時代

話は変ってブックマークそのものについて。

例えば文字実体参照についてはHTML4 で使える文字実体参照をブックマークに入れておくと便利といえば便利だけれども、Googleで検索すればこの文書は最上位に表示される。自分のブックマークフォルダ内を探すのと、Googleで検索するのではどちらが速いか。こういう場合、ちょっと気の効いたブラウザを使えば、Googleで検索したほうが速い。

fallacy

公開
2002年9月6日

この「人間同士は気持ちを互いにわかりあえる」という文を逆から言ってみると、これがいかに恐ろしい思想であるかが見えてくる。「人間同士は気持ちを互いにわかりあえる」ということは、逆に言えば、「気持ちをわからない相手は人間ではない」という恐ろしい発想になる。だからシミンは、どうしても気持ちがわからない相手、例えば、ヤクザ、浮浪者、特定の宗教団体構成員、などを、人間扱いしない。

思いやりのある人たちへ より

「人間同士は気持ちを互いにわかりあえる」という文は、文であって命題ではないのだから、この文から「気持ちをわからない相手は人間ではない」なんてものは導出され得ない。そういう「発想」が出てくる可能性はあるが、決して必然ではない。そしてこういった誤謬を積み重ねて珍妙な結論を導き出している。

ちなみに「あらゆる人間は、お互いの気持ちを分かりあう」ならば「気持ちが分からない対象は人間ではない」といえる。しかし、「あらゆる人間は、お互いの気持ちを分かりあう」と主張するのは気違いだ。滅多にいない。むしろ見たことも聞いたこともない。

シミンは多数だが、幼児的であり、一方的である。シミンを言葉で説得するのは困難である。シミンに対抗していくには、シミン以外の者が存在をアピールしながら、シミンたちの意識を成長させていくしかない。

隠れている非シミンのみなさん、シミンたちの横暴がこれ以上広まらないよう、少しずつ活動を開始しよう。少数意見を遠慮なく言える社会を求めて。対話のある、豊かな社会を目指して。

思いやりのある人たちへ より

シミンを言葉で説得するのは困難である、などと、対話を拒否するような主張をする人間が、対話のある、豊かな社会を目指していると放言する。この主張は、その「シミン」とやらへの差別意識を助長するものである。人間を都合良く分類して自らのエリート意識を確認する作業はあまりに憂鬱なので、存在をアピールなんて御免だ。

人様を分類するときには覚悟を決めろ、という話。

IE6/Win DOM不具合 - input要素のname属性生成

公開
2002年9月6日

IE6/Winにて、type="radio"なinput要素を生成してみたのだけど、以下の3通りの方法では、input要素を追加した際name属性が消えてしまった。

  • HTMLInputElement.name = 'foo';
  • HTMLInputElement.setAttribute('name', 'foo');
  • HTMLInputElement.setAttributeNode(ATTRIBUTE_NODE);

属性によっては、input要素に追加するタイミングで解決できることもあるのだけど、name属性はどうやっても駄目だった。

luneスタイル実験中

公開
2002年9月4日

白が眩しい閲覧者用のCSSということで、暗い感じにしてみた。

制作する前にfine arts方面を見て回り、妙に鬱な気分になって帰ってきた。……雰囲気まで暗くする必要はなかったわけで。まあ、色んなサイトが消えて鬱なので、今月はこのままで行こう。鬱といえば、一冊だけ残っていたJOJOは28卷だったし。謎。

それにしても、Win/IE6で表示した際、大きな背景画像があるとピカピカと光るのは何なのだろう。癲癇(てんかん)を起されて訴えられたらどうするのだ。いやこちらにそのような表現をする意図は無いのだから、そうなったならMSが訴えられるべきである。