IT戦記

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

body の overflow: hidden が変

以下のようなコードがあって

...
<html>
...
 <body>
  <div></div>
 </body>
</html>

body が overflow hidden で幅固定で div が横に伸びていったら div は body を「はみ出ない」と思うんだ。
でも、ブラウザによってははみ出てしまう。

Opera 9
はみ出る
Safari 3
はみ出る
Firefox 2
はみ出ない
IE 6
はみ出ない
IE 7
はみ出ない

ただのバグなのかな?でも、 SafariOpera が同じ挙動なのでどうなんだろう?

この変は見てみたけど

http://www.w3.org/TR/CSS21/visufx.html#overflow
「はみ出ない」のが正しい気がする。

style.cssText の使い処に関する考察

cssText って知ってますか?

cssText っていいうのは CSSRule オブジェクトとか CSSStyleDeclaration が持っている情報を css ファイルに書く形式の文字列にしてくれる機能です。
乱暴に言ってしまえば、 css 系のオブジェクトが持っている innerHTML みたいなものです。

具体例を見てみましょう。

こんな感じの body があるときに

<body style="margin: 1em; padding: 1em; border: 1em">....</body>

こんな感じで使えます。

alert(document.body.style.cssText); // 'margin: 1em; padding: 1em; border: 1em'

簡単でしょう?(ボブ略

設定も出来ますよ

こんな感じ

// さっきの続き
document.body.style.cssText = 'width: 100px'; 
alert(document.body.style.margin);  // ''
alert(document.body.style.padding); // ''
alert(document.body.style.border);  // ''
alert(document.body.style.width);   // '100px'

簡単でしょう?

クロスブラウザ

Safari 3, Opera 9, Firefox 2, IE 6, IE 7 で動いたよ!

パフォーマンスは?

めっちゃ適当に計ってみたら、普通に style.width = '100px'; みたいに一個一個入れてくより、目安 2 〜 3 割遅いことが分かった。

で、

あんまり使う必要なくね?
うんうん。そう思う

でも

さっき便利な使い方を 3 つ思いついたので紹介します。

デバッグ時に値を見るとき

これは普通に便利。

いちち style.xxxx を書くのがめんどくさいとき

うん。あるある。

スタイルをキャッシュしておいて戻したりしたいとき(本題)

たとえば、キャッシュしておいて

var style = element.style;
var cache = style.cssText;

いろいろいじっても、元に戻せる機能を付けたいとか。

style.cssText = cache; // これだけで元の style に戻せる。

便利!
ちなみに
実はこれよりもっと効率的な方法があるのですが、その方法は IE では動きませんでした。その方法は以下。

// キャッシュするとき
var style = element.style;
var cache = {};
for (var i = 0, l = style.length/*←IEではこれがない*/; i < l; i ++) {
    var n = style[i]; // ←IEではこれもない
    cache[n] = style[n];
}

// 戻すとき
for (var i = 0, l = style.length; i < l; i ++) {
    var n = style[i];
    if (!(n in cache)) { // キャッシュにないプロパティは削除
        style[n] = '';
    }
}
for (var n in cache) style[n] = cache[n]; // キャッシュされてるものを戻す

長ったらしいけど、これが一番効率的だと思います。

もちろん、全プロパティを走査すれば簡単にキャッシュできるのですが、それだと激重です。

まとめ

style.cssText ってべんりー!

Opera の CSSStyleSheet.insertRule はちょっとバグってる

以下のコード

var element = document.createElement('style');
element.appendChild(document.createTextNode(''));
document.getElementsByTagName('head')[0].appendChild(element);
var sheet = element.sheet;

// 以下のように CSSStyleSheet の先頭に二つのルールを挿入していく
sheet.insertRule('html body { background: gray }', 0); // 0 番目にこのルールを挿入
sheet.insertRule('html body { background: red }', 0);  // 0 番目にこのルールを挿入

このようにするとルールの順番は html body { background: gray } のほうが下にくるので、
カスケーディング順位(CSS のルールの優先順位)は html body{ background: red } より html body{ background: gray } のほうが上位になるはずである。
ということはこれを実行すると、body 要素の背景は gray になるはずである。

しかし Opera では

背景は red になってしまう。
これは、 OperainsertRule の第二引数に関わらず、ルールをルールリストの最後に挿入してしまうためである。

ブックマークレット化したので、実際にやってみてください。(IE は全くの独自仕様なのでこのブックマークレットはうごきません。あしからず)

javascript:var element=document.createElement('style');element.appendChild(document.createTextNode(''));document.getElementsByTagName('head')[0].appendChild(element);var sheet = element.sheet;sheet.insertRule('html body { background: gray }', 0);sheet.insertRule('html body { background: red }', 0);void(0);

まとめ

現状 insertRule を使う場合は、以下のようにルールをルールリストの最後に追加しなければブラウザ間の整合性が保てなくなってしまう。

sheet.insertRule('html body { background: black }', sheet.cssRules.length);

まあ、 IE 独自の addRule も最後にしか挿入できないし、どっちにしろそうなんだけどね(ぇ

Safari で CSSStyleSheet オブジェクトを生成する方法

今まで Safari では出来ないと思っていました

var element = document.createElement('style');
document.getElementsByTagName('head')[0].appendChild(element);
var sheet = element.sheet;
alert(sheet); // null

こんな感じで null になってしまうのです。。。
しかし!

さっき出来る方法が分かりました!

var element = document.createElement('style');

element.appendChild(document.createTextNode('')); // この行を追加

document.getElementsByTagName('head')[0].appendChild(element);
var sheet = element.sheet;

alert(sheet); // [object CSSStyleSheet]

このように TextNode を追加することで sheet オブジェクトが取得できるようになりました。

あー。どうして今まで試さなかったんだろう!

でも、分かってスッキリ!

document.createStyleSheet で動的に CSS を生成

今まで

var element = document.createElement('style');
document.getElementsByTagName('head')[0].appendChild(element);
if (ie) {
    var sheet = element.styleSheet;
}
else {
    var sheet = element.sheet;
}

みたいにやっていた。
ところが

dojo のソースを眺めていたら

IE には createStyleSheet という関数があるみたい。

createStyleSheet を使うとこうなる。

if (ie) {
    var sheet = document.createStyleSheet();
}
else {
    var element = document.createElement('style');
    document.getElementsByTagName('head')[0].appendChild(element);
    var sheet = element.sheet;
}

あまり代わり映えはしないけど。無駄な要素を挿入しなくてもいいので、こっちのほうが軽いかな。
IE は多数派なので、直しておこうっと。

Safari3 では :checked :disabled :enabled が使えるっぽい

でも

<label> を横に並べて、 :checked + label とかやっても label は変わらなかった。まだバグがあるようだ。

:lang(en) とかも

使えるみたいだけどバグだらけで使いものになりませんでした。バージョンアップに期待。

getComputedStyle について調べてたら深みにハマったのでメモ

getComputedStyle とは!?

ある要素にどんなスタイルが当たっているかを計算してくれる。便利な関数。
使いかたはめっちゃ簡単!

var style = getComputedStyle(element, '');

alert(style.fontSize); // 14px
alert(style.color);    // rgb(0, 0, 0)

ちなみに第二引数は疑似要素の style を取りたい場合に使います。通常は空文字列でいい。

でも、 getComputedStyle はこのままでは IE, Safari では動かない。

Safari では

window(グローバル領域) に getComputedStyle は定義されてなくて、 document.defaultView だけに getComputedStyle が定義されている。
ちなみに、 Firefox, Opera では document.defaultView == window なので、以下のようにすることで Firefox, Opera, Safari で使うことができる。

var style = document.defaultView.getComputedStyle(element, '')

IE では

そもそも getComputedStyle という関数自体が存在しない
その代わり、 IE ではすべての HTMLElement が currentStyle というプロパティを持っていて、これが getComputedStyle で取得できるオブジェクトとほぼ同じ役割をする。
ということで、さっきの例は以下のようにすると IE にも対応できる。

var style = element.currentStyle || document.defaultView.getComputedStyle(element, '') 

という訳で↑これを使えば、クロスブラウザで、しかもワンライナーで getComputedStyle が使える!!テラ便利!!

と、ここで、終わっておけば良かったのですが、、、気になって、、

getComytedStyle について詳しく調べてみた。

getComputedStyle は以下のように ViewCSS というインタフェースに定義されている。

// Introduced in DOM Level 2:
interface ViewCSS : views::AbstractView {
  CSSStyleDeclaration getComputedStyle(in Element elt, 
                                       in DOMString pseudoElt);
};
http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-OverrideAndComputed

そして、 Window Object 草案では Window オブジェクトは ViewCSS を実装しなければならない。

In implementations that support Document Object Model CSS from DOM Level 2 Views [DOM2Views], every Window object MUST also implement the ViewCSS interface.

http://www.w3.org/TR/Window/#window

さらに、 ViewCSS は AbstractView を継承している。

A base interface that all views shall derive from.

http://www.w3.org/TR/DOM-Level-2-Views/views.html#Views-AbstractView

AbstractView はすべての view の基底インタフェースなので、 ViewCSS は view というものということになる。

That is, a view is some alternate representation of, or a presentation of, and associated with, a source document.

http://www.w3.org/TR/DOM-Level-2-Views/views.html

view とは元 document の表現のこと。つまり、 Window Object は document を ViewCSS で表現したものってこと(だと思う)。
document はデフォルトの view を示すために DocumentView というインタフェースを実装してもいいよ。とも書いてある

A Document may implement a DocumentView that has a default view attribute associated with it.

http://www.w3.org/TR/DOM-Level-2-Views/views.html

で、 DocumentView には defaultView というプロパティが定義されている。

It provides an attribute to retrieve the default view of a document.

// Introduced in DOM Level 2:
interface DocumentView {
  readonly attribute AbstractView     defaultView;
};
http://www.w3.org/TR/DOM-Level-2-Views/views.html#Views-DocumentView
つまり

Safaridocument のデフォルトの view (defaultView) は ViewCSS であるが、 window は view ではないというスタンスで実装している。
IE は、 view という概念自体が存在しない。
Firefox, Operadocument のデフォルトの view (defaultView) は ViewCSS であり window である。
ということらしい。実にややこしい

getComputedStyle で取れるオブジェクトは何か

再度ここを見てみると

// Introduced in DOM Level 2:
interface ViewCSS : views::AbstractView {
  CSSStyleDeclaration getComputedStyle(in Element elt, 
                                       in DOMString pseudoElt);
};
http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-OverrideAndComputed

CSSStyleDeclaration インタフェースを実装したオブジェクトということが分かる。
ちなみに、 CSSStyleDeclaratioin は、 element.style とかやって取れるオブジェクトが実装してるインタフェースと同じである。
さらに IE, Opera, Safari, Firefox では、このオブジェクトは CSS2Properties というインタフェースも実装している(はず)。
CSS2Properties のインタフェースは以下のようになっている

// Introduced in DOM Level 2:
interface CSS2Properties {
           attribute DOMString        azimuth;
           attribute DOMString        background;
           attribute DOMString        backgroundAttachment;
:
:
           attribute DOMString        width;
           attribute DOMString        wordSpacing;
           attribute DOMString        zIndex;
};
http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSS2Properties

なるほど、つまり、 element.style とやったときのように簡単に使えるってことですね。

getComputedStyle で取れるオブジェクトはダイナミックか

getComputedStyle で取れるオブジェクトはその時の値を示す静的なハッシュか、それとも元の要素の状態が変わると一緒に変化してくれる動的なオブジェクトなのかが気になった。
いろいろ試してみると元の要素の状態が変わると、一緒に変化してくれることが分かった。

つまり、

<body><p id="target">hoge</p></body>

という html があったとき

var element = document.getElementById('target');
var style = element.currentStyle || document.defaultView.getComputedStyle(element, '');

alert(style.color); // rgb(0, 0, 0)

// ここで <body> の color を変える
document.body.style.color = 'red';

// ちゃんと <p> に継承される
alert(style.color); // rgb(255, 0, 0)

という感じになる。
getComputedStyle で取れるオブジェクトはダイナミックぅ!

まとめ

これであなたも ComputedStyle マスター!