IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

IE で一時的に要素のスタイルを変更する

いろいろためしたけど、以下が一番楽

// 変更
element.runtimeStyle.cssText = ' width: 100%; height: 100%';

// 戻す
element.runtimeStyle.cssText = '';

特に、戻すときに cssText 以外のプロパティを空にしても、おかしな挙動をするので cssText を使うといい。

しかも IE だけは、 CSSStyleSheet オブジェクトにも cssText が使える

// これも IE だけ
var sheet = document.createStyleSheet();

sheet.cssText = 'div { hoge: hoge; fuga: fuga } div div { piyo: piyo }';
sheet.cssText = '';

要素固有の ID を取得する

IE では

element.uniqueID

というのがある。
これは、要素に固有の番号を付けたものだ。
これが結構便利で、

var seen = {};
for (var i = 0; i < elemenets.length; i ++) {
  if (seen[elements[i].uniqueID])
    elements.splice(i, 1);
  else
    seen[elements[i].uniqueID] = true;
}

みたいな感じで、使うことができる。

他のブラウザ

他のブラウザにはこれがないので、結構不便。
で、 getter で出来るじゃんって思ってググってみたら。
Dean Edwards さんがもう作ってた。さすが
http://dean.edwards.name/moz-behaviors/src/

HTMLElement.prototype.__defineGetter__("uniqueID", function() {
 // a global counter is stored privately as a property of this getter function.
 // initialise the counter
 if (!arguments.callee.count) arguments.callee.count = 0;
 // create the id and increment the counter
 var $uniqueID = "moz_id" + arguments.callee.count++;
 // creating a unique id, creates a global reference
 window[$uniqueID] = this;
 // we don't want to increment next time, so redefine the getter
 this.__defineGetter__("uniqueID", function(){return $uniqueID});
 return $uniqueID;
});

ちょっとだけ自分用にカスタマイズ

(function() {
    var nextUniqueID = 1; 
    HTMLElement.prototype.__defineGetter__('uniqueID', function() {
        var uniqueID = 'id' + nextUniqueID++
        this.__defineGetter__("uniqueID", function(){return uniqueID});
        return uniqueID;
    });  
})();

id:javascripter さんの指摘で直しました><ありがとうございます><

これ便利だなー

(Dean Edwards)++

IE の Ajax (XMLHTTP) で、通信が行われたかキャッシュが使われたかを判定する方法

どうやら

以下の方法で判定することができるようです。

// リクエストオブジェクトを作る
var req = new ActiveXObject('Microsoft.XMLHTTP');

// フラグの準備
var sended = false;

// readystatechange イベントを待つ
req.onreadystatechange = function() {
    if (req.readyState == 4) {

        // キャッシュに存在しない場合
        if (sended) {
            alert('loaded');
        }

        // キャッシュに存在する場合
        else {
            alert('cached');
        }

    }
};

// 通信開始
req.open('GET', 'hoge');
req.send();

// フラグを立てる
sended = true;

解説

  • IE では、キャッシュにファイルが存在した場合に req.send 関数の中で req.onreadystatechange が呼ばれる
  • つまり、さっきの例の sended というフラグを立てる前に req.onreadystatechange が呼ばれる
  • これを利用すれば、キャッシュから読み込まれたか、通信が行われたかが分かる

ということです

どんなものがキャッシュに入っているか

これで試してみると、コンテンツ中で読み込まれたファイル (css とか js とか) は Ajax でアクセスしたときにキャッシュされているようです。
ということは、逆に言ってしまうと js とか css とかの生のテキストが欲しい場合は、非同期ではない Ajax でアクセスしてしまってもなんら問題ない訳ですね。(実際には同期して、キャッシュから読んでいるので)

さらに考える

あと、キャッシュされているなら以下の二つのコードは全く同じということですね

var req = new ActiveXObject('Microsoft.XMLHTTP');
// 非同期通信でも
req.open('GET', 'hoge');
req.send();
var text = req.responseText;
var req = new ActiveXObject('Microsoft.XMLHTTP');
// 同期通信でも
req.open('GET', 'hoge', false);
req.send();
var text = req.responseText;

さらに考える

ということは、以下のようにすればキャッシュにある場合だけ使うということができるということですね!
これは使えるかも!

var req = new ActiveXObject('Microsoft.XMLHTTP');

// 非同期通信するが、 onreadystatechange は使わない。
req.open('GET', 'hoge');
req.send();

var text = null;
try {
  text = req.responseText
}
catch(e) {
  // キャッシュにない場合はここにくる
  req.abort();
}

まとめ

IE 限定ですが、キャッシュから取得されたか通信が行われたかを判定できることが分かりました。

IE 限定 offsetParent を実験

以下の log 関数には全部 true が渡される

http://amachang.art-code.org/ierendering/014.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<title>IE Rendering Test</title>
<script type="text/javascript">
document.attachEvent('onreadystatechange', function() {
    var $ = function(id) { return document.getElementById(id) };
    var log = function(message) {
        $('log').appendChild(document.createTextNode(message));
        $('log').appendChild(document.createElement('br'));
    }

    if (document.readyState == 'complete') {
        // 基本的には、直近の layout を持った祖先要素が offsetParent になる
        log($('e000') == $('e001').offsetParent && $('e000').currentStyle.hasLayout);
        log($('e000') == $('e002').offsetParent && $('e000').currentStyle.hasLayout);
        log($('e000') == $('e003').offsetParent && $('e000').currentStyle.hasLayout);
        log($('e003') == $('e004').offsetParent && $('e003').currentStyle.hasLayout);

        // tagName のない無名の layout を持った要素が $('e005') と $('e004') の間に生成されている
        log($('e005').offsetParent.tagName == '');
        log($('e005').offsetParent.currentStyle.hasLayout);
        log($('e005').offsetParent.offsetParent == $('e004'));

        // td を作ると無名の要素はできない
        log($('e007').offsetParent == $('e006') && $('e006').currentStyle.hasLayout);
        log($('e008').offsetParent == $('e007') && $('e007').currentStyle.hasLayout);

        // tr 要素は layout を持つのに td 要素の offsetParent は table 要素になる
        log($('e010').offsetParent == $('e009') && $('e009').currentStyle.hasLayout);
        log($('e011').offsetParent == $('e009') && $('e009').currentStyle.hasLayout);
        log($('e010').currentStyle.hasLayout);
        log($('e012').offsetParent == $('e011') && $('e011').currentStyle.hasLayout);
    }
});
</script>
<style type="text/css">
.LAYOUT { zoom: 1 }
.RELATIVE { position: relative }
</style>
</head>
<body id="e000">
    <div id="e001">
        <div id="e002">
            <div class="LAYOUT" id="e003">
                <table id="e004">
                    <div id="e005">HOGE</div>
                </table>
                <table id="e006">
                    <td id="e007">
                        <div id="e008">HOGE</div>
                    </td>
                </table>
                <table id="e009">
                    <tr id="e010">
                        <td id="e011">
                            <div id="e012">HOGE</div>
                        </td>
                    </tr>
                </table>
            </div>
        </div>
    </div>
    <div id="log"></div>
</body>
</html>

table が evil なだけなのかどうなのか。そこが問題だ

IE の innerHTML や appendChild で要素が挿入された瞬間を取得する方法

要素が挿入された瞬間を取得する

今までは出来ないと思っていたのですが、今日いろいろ試していて出来る方法が分かりました。
ですので、ちょっと紹介したいと思います。今のところアイデアなので、実用性は?です。


方法

HTML に以下の style 要素を挿入することで実現することができます。

<style type="text/css">
* {
    display: expression(function() {
        if (!this.__mark) {
            this.__mark = true;
            alert('inserted node: ' + this.tagName);
        }   
        return ''; 
    }.apply(this));
}
</style>

実際に例を見てみましょう

http://amachang.art-code.org/ieexpression/000.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<title>IE Rendering Test</title>
<style type="text/css">
* {
    display: expression(function() {
        if (!this.__mark) {
            this.__mark = true;
            alert('inserted node: ' + this.tagName);
        }   
        return ''; 
    }.apply(this));
}
</style>
</head>
<body>
    <input type="button" value="appendChild" onclick="document.body.appendChild(document.createElement('div'))" />
    <input type="button" value="innerHTML" onclick="document.body.innerHTML += 'aaa<br />'" />
</body>
</html>

解説

  • 要素が新しく挿入される
  • レンダリングエンジンは CSS の値を新しい要素に割り当てようとする
  • その際に expression がプロパティ値に設定れていると、その JavaScript 式をその場で実行しようとする。
    • そのときのスコープは onclick 属性などと同じ
  • expression に JavaScript の関数を指定しておいて、 this (その要素)を apply して
  • 関数の中で、一度見た要素に印を付ける
  • 印の付いてない要素は、新しく作られた要素!

という仕組みです。

まとめ

CSS expression 面白いです。
もちろんまともな CSS パーサーだとエラーになります。
なので、気になる人は条件コメントを使うなどしたほうがいいかもしれません。

めも

このメモについて

  • id:amachang の私的な学習目的
  • IE6 の標準モードに関するバグ

いろいろとポイント

  • layout
    • IE6 の視覚整形モデルの中で要素は、大きく分けて layout を持つ要素と、 layout を持たない要素に分けることができる。
    • layout を持つ要素とは、自分のサイズや位置に責任を持つ要素である。
    • layout を持つ要素は、四角形の領域を作る。
    • layout を持たない要素は、直近の layout を持つ要素のサイズおよび位置と、そこの間にあるすべての要素の margin, padding, border によってその開始点、折り返し点が決まる。
    • layout を持たない要素は、四角形の領域を作らない。上下左右の間隔を保持しているだけに過ぎない。
    • layout を持たない要素間の上下マージン(相殺されるマージン)は二つの要素間の共有のもので、ある要素特有の管理下にはない。(つまり、要素のレンダリングのバグを直したいときにマージンは影響しないことが多い)
    • 要素に layout を与える場合は style に zoom: 1 を指定すればいい

(border-left または border-right) と padding-bottom の問題

以下の HTML によって発生する

http://amachang.art-code.org/ierendering/000.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<title>IE Rendering Test</title>
</head>
<body>

    <!-- border-left と padding-bottom を持つ layout を持たないブロックレベル要素 -->
    <div style="border-left: 1em solid blue; padding-bottom: 1px;">

        <div>ほげほげほげほ</div>
        <div>ほげほげほげほ</div>
        <div>ほげほげほげほ</div>

        <!-- この要素の最後に layout を持たないブロックレベル要素 -->
        <div>layout を持たないブロックレベル要素</div>

    </div>

    <!-- この要素が 2em (ボーダーの幅 x 2)だけずらされる -->
    <div>ずれてますか?</div>

</body>
</html>
ずれないパターン 1

http://amachang.art-code.org/ierendering/001.html

<body>

    <div style="border-left: 1em solid blue; padding-bottom: 1px;">

        <div>ほげほげほげほ</div>
        <div>ほげほげほげほ</div>
        <div>ほげほげほげほ</div>

        <!-- この最後の要素をインライン要素にする -->
        <span>layout を持たないブロックレベル要素</span>

    </div>

    <!-- ずれない -->
    <div>ずれてますか?</div>

</body>
ずれないパターン 2

http://amachang.art-code.org/ierendering/002.html

<body>

    <div style="border-left: 1em solid blue; padding-bottom: 1px;">

        <div>ほげほげほげほ</div>
        <div>ほげほげほげほ</div>
        <div>ほげほげほげほ</div>

        <!-- この最後のノードをテキストノードにする -->
        テキストノード

    </div>

    <!-- ずれない -->
    <div>ずれてますか?</div>

</body>
ずれる幅が違うパターン 1

http://amachang.art-code.org/ierendering/003.html

<body>

    <div style="border-left: 1em solid blue; padding-bottom: 1px;">

        <div>ほげほげほげほ</div>
        <div>ほげほげほげほ</div>
        <div>ほげほげほげほ</div>

        <!-- border-bottom を付ける -->
        <div style="border-bottom: 1px solid blue;">layout を持たないブロックレベル要素</div>
    </div>

    <!-- 2em ではなくて 1em しかずれない -->
    <div>ずれてますか?</div>

</body>
ずれる幅が違うパターン 2

http://amachang.art-code.org/ierendering/004.html

<body>

    <div style="border-left: 1em solid blue; padding-bottom: 1px;">

        <div>ほげほげほげほ</div>
        <div>ほげほげほげほ</div>
        <div>ほげほげほげほ</div>

        <!-- padding-bottom を付ける -->
        <div style="padding-bottom: 1px;">layout を持たないブロックレベル要素</div>
    </div>

    <!-- 2em ではなくて 1em しかずれない -->
    <div>ずれてますか?</div>

</body>
ずれないパターン 3

http://amachang.art-code.org/ierendering/005.html

<body>

    <!-- border-bottom を追加 -->
    <div style="border-left: 1em solid blue; border-bottom: 1px solid blue; padding-bottom: 1px;">

        <div>ほげほげほげほ</div>
        <div>ほげほげほげほ</div>
        <div>ほげほげほげほ</div>

        <div>layout を持たないブロックレベル要素</div>
    </div>

    <!-- ずれない -->
    <div>ずれてますか?</div>

</body>
ずれないパターン 4

http://amachang.art-code.org/ierendering/006.html

<body>

    <!-- layout を与える -->
    <div style="zoom: 1; border-left: 1em solid blue; padding-bottom: 1px;">

        <div>ほげほげほげほ</div>
        <div>ほげほげほげほ</div>
        <div>ほげほげほげほ</div>

        <div>layout を持たないブロックレベル要素</div>
    </div>

    <!-- ずれない -->
    <div>ずれてますか?</div>

</body>
ずれないパターン 5

http://amachang.art-code.org/ierendering/007.html

<body>

    <div style="border-left: 1em solid blue; padding-bottom: 1px;">

        <div>ほげほげほげほ</div>
        <div>ほげほげほげほ</div>
        <div>ほげほげほげほ</div>

        <!-- layout を与える -->
        <div style="zoom: 1">layout を持つブロックレベル要素</div>
    </div>

    <!-- ずれない --> 
    <div>ずれてますか?</div>

</body>
ずれる幅が違うパターン 3

http://amachang.art-code.org/ierendering/008.html

<body>

    <div style="border-left: 1em solid blue; padding-bottom: 1px;">
        <!-- からっぽ -->
    </div>

    <!-- 1em ずれる -->
    <div>ずれてますか?</div>

</body>
現象
  • 要素の下辺( margin を考えない)が padding で、最後の子要素が non-layout ブロックレベル要素のときに発生する
  • 最後の子要素がインライン要素の場合に発生しない
  • 最後の子要素に対して borderpadding を指定した場合にずれる幅が変わる(半分になる)

はみ出した箇所が表示されない問題

http://cssbug.at.infoseek.co.jp/detail/winie/b025.html
http://cssbug.at.infoseek.co.jp/detail/winie/b041.html
http://cssbug.at.infoseek.co.jp/detail/winie/b046.html

実際の例と回避

http://amachang.art-code.org/ierendering/009.html

<body>

    <div style="zoom:1; border: 1px solid blue; margin: 2em;">
        あああ
        <div style="border: 1px solid red">端っこ見えてます?</div>
        あああ
        <div style="margin: 0 -1em; border: 1px solid red">端っこ見えてます?</div>
        あああ
        <div style="zoom: 1; margin: 0 -1em; border: 1px solid red">端っこ見えてます?</div>
        あああ
        <div style="position: relative; margin: 0 -1em; border: 1px solid red">端っこ見えてます?</div>
        あああ
        <div style="zoom: 1; position: relative; margin: 0 -1em; border: 1px solid red">端っこ見えてます?</div>
        あああ
    </div>

    <div style="zoom:1; border: 1px solid blue; margin: 2em;">
        あああ
        <div style="margin: -2em 0 0; border: 1px solid red">端っこ見えてます?</div>
    </div>

    <div style="zoom:1; border: 1px solid blue; margin: 2em;">
        あああ
        <div style="zoom: 1; margin: -2em 0 0; border: 1px solid red">端っこ見えてます?</div>
    </div>

    <div style="zoom:1; border: 1px solid blue; margin: 2em;">
        あああ
        <div style="position: relative; margin: -2em 0 0; border: 1px solid red">端っこ見えてます?</div>
    </div>

    <div style="zoom:1; border: 1px solid blue; margin: 2em;">
        あああ
        <div style="zoom: 1; position: relative; margin: -2em 0 0; border: 1px solid red">端っこ見えてます?</div>
    </div>

    <div style="border: 1px solid blue; margin: 2em;">
        あああ
        <div style="border: 1px solid red">端っこ見えてます?</div>
        あああ
        <div style="margin: 0 -1em; border: 1px solid red">端っこ見えてます?</div>
        あああ
        <div style="zoom: 1; margin: 0 -1em; border: 1px solid red">端っこ見えてます?</div>
        あああ
        <div style="position: relative; margin: 0 -1em; border: 1px solid red">端っこ見えてます?</div>
        あああ
        <div style="zoom: 1; position: relative; margin: 0 -1em; border: 1px solid red">端っこ見えてます?</div>
        あああ
    </div>

    <div style="border: 1px solid blue; margin: 2em;">
        あああ
        <div style="margin: -2em 0 0; border: 1px solid red">端っこ見えてます?</div>
    </div>

    <div style="border: 1px solid blue; margin: 2em;">
        あああ
        <div style="zoom: 1; margin: -2em 0 0; border: 1px solid red">端っこ見えてます?</div>
    </div>

    <div style="border: 1px solid blue; margin: 2em;">
        あああ
        <div style="position: relative; margin: -2em 0 0; border: 1px solid red">端っこ見えてます?</div>
    </div>

    <div style="border: 1px solid blue; margin: 2em;">
        あああ
        <div style="zoom: 1; position: relative; margin: -2em 0 0; border: 1px solid red">端っこ見えてます?</div>
    </div>

</body>

floatclearbackground の問題

layout を持たない親要素の中で floatclear すると背景が margin 領域まで広がる。そして、自分の兄要素の layout を持たないものが消える(消えると言っても実際に消えるわけではなく、「再描画」が伝搬しなくなる。スクロールによって出現する)
この消え方を Peek-a-boo というらしい
http://amachang.art-code.org/ierendering/010.html

<body>

    <div style="margin: 1em; background: gray;">
        <div>あああ</div>
        <div style="position: relative">いいい</div>
        <div style="zoom: 1">ううう</div>
        <div style="zoom: 1; position: relative">えええ</div>
        <div style="float: right">おおお</div>
        <div style="clear: right">かかか</div>
        <div>あああ</div>
        <div style="position: relative">いいい</div>
        <div style="zoom: 1">ううう</div>
        <div style="zoom: 1; position: relative">えええ</div>
        <div style="float: right">おおお</div>
        <div style="clear: right">かかか</div>
        <div>あああ</div>
        <div style="position: relative">いいい</div>
        <div style="zoom: 1">ううう</div>
        <div style="zoom: 1; position: relative">えええ</div>
    </div>

    <div style="margin: 1em; zoom: 1; background: gray;">
        <div>あああ</div>
        <div style="position: relative">いいい</div>
        <div style="zoom: 1">ううう</div>
        <div style="zoom: 1; position: relative">えええ</div>
        <div style="float: right">おおお</div>
        <div style="clear: right">かかか</div>
        <div>あああ</div>
        <div style="position: relative">いいい</div>
        <div style="zoom: 1">ううう</div>
        <div style="zoom: 1; position: relative">えええ</div>
        <div style="float: right">おおお</div>
        <div style="clear: right">かかか</div>
        <div>あああ</div>
        <div style="position: relative">いいい</div>
        <div style="zoom: 1">ううう</div>
        <div style="zoom: 1; position: relative">えええ</div>
    </div>

    <div style="margin: 2em; background: gray;">
        <div>あああ</div>
        <div style="position: relative">いいい</div>
        <div style="zoom: 1">ううう</div>
        <div style="zoom: 1; position: relative">えええ</div>
        <div style="float: right">おおお</div>
        <div style="clear: right"></div>
    </div>

    <div style="zoom:1; margin: 2em; background: gray;">
        <div>あああ</div>
        <div style="position: relative">いいい</div>
        <div style="zoom: 1">ううう</div>
        <div style="zoom: 1; position: relative">えええ</div>
        <div style="float: right">おおお</div>
        <div style="clear: right"></div>
    </div>

    <div style="margin: 2em; background: gray;">
        <div>あああ</div>
        <div style="position: relative">いいい</div>
        <div style="zoom: 1">ううう</div>
        <div style="zoom: 1; position: relative">えええ</div>
        <div style="float: right">おおお</div>
        <div style="clear: right"></div>
    </div>

</body>
基本的には以下の有名なバグも仕組みは同じ

http://www.positioniseverything.net/explorer/peekaboo.html
http://amachang.art-code.org/ierendering/011.html

<body>
    <div style="background: gray">
        <div style="height: 20em; float: left">
            <br />
            <span>&nbsp;Float&nbsp;
                <br /><br />
                <a href="#">&nbsp;test link&nbsp;</a>
            </span>
        </div>
        This is bare text. <a href="#">Test link</a> 
        <div style="border: 3px solid #f00; background: #dde;">This is text inside a div. <a href="#">Test link</a></div>
        This is bare text. <a href="#">Test link</a>
        <div style="border: 3px solid #0c0; background: #dde;">This is text inside a div. <a href="#">Test link</a></div>
        This is bare text. <a href="#">Test link</a>
        <div style="border: 3px solid #00f; background: #dde;">This is text inside a div.  <a href="#">Test link</a></div>
        This is bare text. <a href="#">Test link</a>
        <div style="clear: left">Clearing div</div>
        <div style="border: 3px solid #00f; background: #dde;">This div is after the cleared div. (purple box) If cleared div 
        does not touch float, bug is not triggered. <a href="#">Test link</a></div>
    </div>
</body>
float よりも clear が原因

float というよりもボックス内で clearance マージンが設定されたかどうかで発生するかが決まる。
たとえば、コンテンツを文字で満たして float 要素を囲み、 clearance マージンの設定が必要のない状態にするとこの問題は発生しない。( clear を指定した要素の margin-top を clearance よりも大きくしてもこの問題は発生しない)
あと、 clearance マージンより下方向にコンテンツがない場合は、 Peek-a-boo は起きない。
重要なことは、 clearance マージンと non-layout なブロックの背景。

float の 3px

このバグは。。。 3px くらい空いてほうがいいでしょ的な気分で付けたとしか思えない。。。
(margin-left or margin-right): -3px くらいしか解決策はないのかな?

(続くかも)