月別アーカイブ: 2012年7月

Safari、Chromeで動くJavaScriptがFirefoxで動かない→宣言部、ヘッダ部を疑いましょう

JavaScriptが満載のWEBページを制作していて、ページの一部の機能がSafari、Chromeでチェックした際には動作するのに、Firefoxでは全く動作しないという状況に遭遇しました。
具体的には、ナビゲーションボタンのマウスホバー時の挙動として、透明度の指定をjQueryで行った箇所と、画像のLightBox的挙動のためjQuery.lightboxを使った箇所です。

マウスホバーの部分については、ホバー状態が終了する際に、FirebugのコンソールにremoveAttribute is not a functionというエラーが表示されます。lightboxのエラーは、画像が拡大表示された後ページに戻る事ができず背景が暗いままという不具合で、こちらは特にコンソールに表示はありません。

これはjQueryの動作に問題があるのだろうとあたりをつけ、様々な手段を使ってチェックを行いました。

ホバー部分のコードについては、以下のようなものでした。

<script>
$(function(){
$(".hover").hover(
function(){
$(this).fadeTo(0,0.5);
},function(){
$(this).fadeTo(0,1.0);
});
</script>

hoverメソッドは引数に関数を二つ取り、それぞれホバー時と終了時の処理を書く事ができます。処理の部分で、fadeToではなくcssメソッドを使ってopacityを指定するも動作せず。しかしながら、cssメソッドでwidthなどの他の値を指定する場合は動作します。つまり透明度関係の指定が上手く行っていないということが予想できます。
そこでブラウザのopacityの実装の違いではないかと考え、Firefoxについてのリファレンスを漁ったり、懐かしの-moz-opacityを引っ張り出してきたりと、色々と試みるも挙動が変わる事はありません。
次にプラグイン同士の相性を疑い、抜き差し抜き差しするも効果なし。jQueryのファイル名やヴァージョンの変更を行うも駄目。この時点で既に大量の時間を原因の究明に費やしています。

戯れに同じコードを別の製作中のページに貼付けてみると、ちゃんと動作しました。そのページはXHTMLではないHTML5のページだったので、jQueryやlightboxプラグインのドキュメントをあたり必須要件がHTML5なのかチェック、しかしながら特に指定はありません。

結局不具合の原因が何だったのかというと、

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja" dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Style-Type" content="text/css;" />
<meta http-equiv="Content-Style-Type" content="text/javascript;" />

こんなに単純な事でした。本来Content-Script-Typeでtext/javascriptを指定しなければならないところを、上のcssの指定をコピーしてそのまま、Content-Style-Typeとしていたわけですね。
何故HTML5で無事に動いたのかというと、これらの部分が省略可だからでした。

つまりトラブルを一般化すると、SafariやChromeで動くJavaScriptがFirefoxで動かない場合、間違ったXHTMLの記述に対する、ブラウザの解釈の違いが原因である場合があるということです。
一字一句間違えてはならないDOCTYPE宣言についても、思わぬエラーの原因になりそうなものです。実際標準モードと互換モードでcssの解釈が異なるので、意図せず互換モードが呼び出され、jQueryでの表示不具合に直結している可能性があるのではないでしょうか。

毎度の事ですが、ニッチなエラーに足を取られ易い私の失敗談が、誰かの役に立てば幸いです。


console.logでJavaScriptの変数の中身を見る

JavaScriptのコーディング中、オブジェクトや変数の中身を確認したくなることは多々あります。PHPの場合であればprint_rやvar_dumpで間に合うのですが、JavaScriptの場合にはconsole APIに対応したブラウザで対話式にチェックを行うのが便利なようです。

具体的には、FirefoxにFirebugを搭載した状態もしくはSafari、Chromeなどのwebkitブラウザで開発メニューを有効化した状態にて、webコンソールあるいはwebインスペクタのコンソールの入力エリアに命令を投げ与えます。

console APIのコンソール

console APIのコンソール

変数やオブジェクトの中身を参照したいなら、console.log(対象);という命令を与えエンターを押すと、コンソール画面に表示してくれます。特に便利なのは、引数にjQueryのオブジェクトを与えても表示してくれるという点です。さらにwebkitのブラウザは至れり尽くせりで、jQueryのセレクタでヒットした要素をちゃんと入れ子状にまとめて表示してくれるので、この方法を知っているか知らないかで、jQueryでの開発効率は違ってくるはずです。

コンソールに投げられる命令は結構ありますが、オブジェクトのプロパティをリストアップしてくれるconsole.dir()なども便利そうです。
また、コード中の任意の場所で呼び出して、ブラウザのコンソールに任意の時点での変数の状態を吐き出させることもできるので、通常の標準出力のような使い方も可能です。

なんというか素晴らしい時代ですね。

(追記:IEではconsole.logの仕様に落とし穴があるようなので、こちらのエントリも参考に)


clearfixのささやかな覚え書き

複数カラムのブログテンプレートなどを作成するとき、一般的にはCSSのfloatプロパティを使ってレイアウトを行います。たとえば2カラムで左にサイドメニュー、右に記事本文というレイアウトは、以下のようなhtml&CSSになるでしょう。

HTML部
<div class="wrapper">
<div class="sidebar">
//サイドメニューエリア本文
</div>
<div class="articlearea">
//記事本文
</div>
</div>
 
CSS
.sidebar{
float : left;
}
.articlearea{
float : right;
}

カラムを包括する親ボックスの大きさが指定されており、各カラムが親ボックスの高さの値を超えないときには、特にレイアウト上の問題は起こりません。しかしながら、ブログのようにカラムの本文が可変長で親ボックスの高さを超えるケースが考えられるときには、カラムが親ボックスをはみ出て表示されてしまい、レイアウトが崩壊してしまいます。

はみ出たカラム

これは、ボックス内でfloat指定されている要素の大きさが親ボックスの大きさを決める際に参照されないという特性によるものです。したがって最も仕様に適った解決は、floatさせた要素の後にclearプロパティでfloat解除した要素を設けることです。空のdiv要素やvisibilityプロパティをhiddenにしたhr要素、br要素がよく使われます。

HTML部
<div class="wrapper">
<div class="sidebar">
//サイドメニューエリア本文
</div>
<div class="articlearea">
//記事本文
</div>
 
//空divの場合
<div style="clear : both"></div>
 
//非表示にしたhrまたはbr
<hr class="clearfix" />
<br class="clearfix" />
</div>
 
CSS
.sidebar{
float : left;
}
.articlearea{
float : right;
}
.clearfix{
visibility : hidden;
clear : both;
}

この方法はシンプルですが、ページのセマンティック(意味)としてはデザイン修正の為だけの無意味な要素をhtmlに加える事になるので好ましくありません。
そこで、無駄な要素を加えることなくレイアウト修正を行う、名前もそのまま”clearfix”というテクニックが開発されました。

HTML部
<div class="wrapper">
<div class="sidebar">
//サイドメニューエリア本文
</div>
<div class="articlearea">
//記事本文
</div>
</div>
 
CSS
//clearfixの追加部分
.wrapper:after{
content : "";
display : block;
clear : both;
}
//IE6,7用
.wrapper{
zoom : 1;
}
 
.sidebar{
float : left;
}
.articlearea{
float : right;
}

CSSのafter疑似要素を使って、floatを解除する要素を無理矢理埋め込むという形です。この方法ならば、検索エンジンやアプリケーションがページのhtmlのみを取得する場合に、解釈の混乱をひきおこす無駄な要素が発生しません。IE用の追加部分については、独自仕様のhasLayoutというプロパティがtrueであれば子要素のfloatを親ボックスのサイズ計算に含めるという特性を利用しています。無害なレイアウト指定(zoom:1;やwidth:100%;など)であれば何でも良いのですが、ソースの意図を明確にする為にzoom:1;の指定をしている場合が多いようです。

ここまでがclearfixの説明です。先に述べた通り、floatを解除する要素を最後に加えるという解決方法ですね。

それに対して、親ボックスのoverflowプロパティを明示的にhiddenと指定することで、親ボックスのサイズ計算方法を変えるというテクニックもあります。overflowプロパティとは、ボックス内の要素がボックスのサイズを超えてしまった際の表示方法についての指定で、何も指定しない状態はvisible、つまりはみ出た部分も表示する指定になります。これをhiddenと指定した場合、はみ出す部分のみ非表示となると同時に、ボックスの高さの計算式にfloatさせた要素も含まれるようになります。

HTML部
<div class="wrapper">
<div class="sidebar">
//サイドメニューエリア本文
</div>
<div class="articlearea">
//記事本文
</div>
</div>
 
CSS
.wrapper{
overflow : hidden;
//IE6,7用
zoom : 1;
}
.sidebar{
float : left;
}
.articlearea{
float : right;
}

場当たり的な対応ではありますが、セマンティックを汚染せずさらに簡単なので、こちらをバカの一つ覚えのように使ってしまって良いかもしれません。