WinIEでabbr要素を何とかする

WinIEがabbr要素を理解しないことへの対応として、The Web Kanzakiで用いられいてたスクリプトを少し変更したものを使ってました(参照: 見よう見まねJavascript)。それで今まで動作上特に問題もなく、またinnerHTMLと正規表現を使ってソースを書き替えるという手法はスクリプトの見た目もすっきりしていて分かりやすいのですが、別の手段として、正規のDOM的手法を用いてなんとかならないかと考えまして、試してみることにしました。DOMの勉強も兼ねて。

やり方としては、abbr要素ノードからその子テキストノードとtitle属性を取り出して、それを新しく作ったspan要素に適用し、span要素を文書に追加する、という感じで行けるかなと思っていたのですが、実際にやってみるとabbr要素の子ノードが取り出せなくて躓きました。色々と試してみる中で、確認のため、

<abbr title="world wide web">WWW</abbr>

というabbr要素ノードnに対し、n.outerHTMLをとってみたところ、このような結果に。

<ABBR title="world wide web">

あれ、要素の中身と終了タグはどこに? その行方を探すべくまた色々と試したところ、どうやら理屈が分かりました。

例えば以下のようなp要素:

<p>本日は<abbr title="world wide web">WWW</abbr>日和なり。</p>

について、その全ての子ノードを取り、各子ノードのnodeType, nodeName, nodeValueを確認してみたところ、WinIEでは以下のような結果となります。

順序nodeTypenodeNamenodeValue
03 (text)#text本日は
11 (element)ABBRnull
23 (text)#textWWW
31 (element)/ABBRnull
43 (text)#text日和なり。

つまりabbr要素の開始タグ部分は空要素のように扱われており、要素の子ノードになると思っていた"WWW"は空要素ノードのnextSiblingノード、さらにその弟ノードになってる終了タグ部分は"/ABBR"という不思議なタグ名を持つ空要素になっていると……。なるほど、道理でabbr要素に対してスタイルを適用できないわけだと納得。ちなみにabbrタグに限らず、WinIEが認識しない(HTMLの要素として存在しない)タグがソースにあった場合は、このような扱われ方になるようです。

一応、MozillaとOpera 7での場合も。こちらは流石に期待通りの結果となってます。

順序nodeTypenodeNamenodeValue
03 (text)#text本日は
11 (element)ABBRnull
23 (text)#text日和なり。

ともあれ、動作が多少変であってもabbr要素の中身部分をDOM的手段で(childでなくnextSiblingとして)取り出せることが分かったので、それを用いて新たにspan要素を作り、文書へ追加することは何とかできました。

が、ふと「スクリプトで新たにabbr要素を作って文書に追加したら、それはどのように扱われるのだろう」という考えがよぎりましたので試してみたところ、そうして作られたabbr要素はtitle属性がツールチップとして表示されるし、CSSによるスタイル適用もできるではありませんか。

そういうことであるのなら、何もspan要素なんかの助けを借りるまでもなく、もともとの妙な形で扱われているabbr要素をちゃんとしたabbr要素として生成しなおせばよいわけです。最終的には以下のようなスクリプトとなりました。

/*  abbr要素を再生成する(for WinIE) */
function regenerateAbbr() {
    var abbrs = document.getElementsByTagName('abbr');
    for (var i = 0; i < abbrs.length; i++) {
        var oldAbbr = abbrs.item(i);
        var newAbbr = document.createElement('abbr');
        newAbbr.title = oldAbbr.title;
        oldAbbr.parentNode.insertBefore(newAbbr, oldAbbr);
        while (oldAbbr.nextSibling.nodeName != '/ABBR') {
            newAbbr.appendChild(oldAbbr.nextSibling);
        }
        oldAbbr.parentNode.removeChild(oldAbbr.nextSibling);
        oldAbbr.parentNode.removeChild(oldAbbr);
        /*alert(newAbbr.parentNode.innerHTML);*//*確認用*/
    }
}

と、手間かけて作ってみたこのスクリプトなのですが、実は効率の点ではinnerHTMLを書き換える方法に劣ります。残念。しかしながらWinIEにもabbr要素をabbr要素として認識させることができる、という点に意義があるような気がするので、当サイトではしばらく前からこちらを使用しています。

(2003年5月15日)

北村曉 kits@akatsukinishisu.net