IEのImage.src.width(height)の値はどこから来るのか?

theater.js作成開始当初から、不思議に思っていることがあります。IEのImageオブジェクトの振る舞いについてです。

theater.jsは、画像を画像ボックスに合わせて縮小して表示するため、画像の幅・高さをImage.src.width(height)で取得しています。これを元に表示サイズを算出し、IMGタグに幅・高さを設定して画像を表示します。

サンプルHTMLは以下です。

<html>
<head>
<title></title>
<script language="javascript">
<!--
//画像オブジェクト配列
var images = [new Image(), new Image()];
//画像先読み
images[0].src = "./sample9.gif";  //最初の画像
images[1].src = "./sample10.gif"; //次の画像

//ページ読み込み完了時実行関数
function init(){
  changeImage(document.getElementById("img"), 0);
}

//画像変更
function changeImage(imgElem, index){
  //画像そのものの幅・高さのポップアップ
  alert("images[" + index + "].width : " + images[index].width);
  alert("images[" + index + "].height : " + images[index].height);
  //IMGタグの幅・高さのポップアップ
  alert("imgElem.width : " + imgElem.width);
  alert("imgElem.height : " + imgElem.height );
  //IMGタグのサイズ(表示サイズ)を画像サイズに合わせる
  imgElem.height = images[index].height;
  imgElem.width = images[index].width;
  //画像表示
  imgElem.src = images[index].src;
}
//-->
</script>
</head>
<body bgcolor="blue" onload="init();/*読み込み時に最初の画像を設定*/">
<img id="img" onclick="changeImage(this, 1);/*クリック時に次の画像を設定*/"/>
</body>
</html>
サンプル画像 sample9.gif sample10.gif

上のHTMLをIEで開くと、最初の画像が表示されます。画像をクリックすると、次の画像が表示されます。ここでは、取得した画像の幅・高さを、IMGタグにそのまま設定しています。

ここまでは問題ありません。問題が発生するのは、画像の名前(アドレス)を変えずに幅・高さを変えた場合です。ペイント等でsample10.gifの幅・高さを変更してください。(色を塗ると後で分かりやすいです) その後、IEでHTMLを開きなおし、画像をクリックしてください。すると、画像は変わっているのに幅、高さが変わっていません。リロードしても、キャッシュをクリアしても、ブラウザの落とし上げをしてもダメです・・・。

これは、ポップアップを見ると分かるのですが、Image.src.width(height)が前の画像の情報を保持しているために、そのサイズで表示し続けてしまうからです。なぜ保持しているのかは分かりません。また、どこに保持しているのかも分かりません。

当事象は、IE以外のブラウザでは確認できていません。どうも、IEがプログラムで情報を保持しているような気がするのですが・・・。今のところの対策としては、画像の名前・アドレスを変えるくらいしか思いつきません。申し訳ないです・・・。

この件について何かご存知の方がいらっしゃいましたら、コメントいただけるとありがたいです。(Questionを出すことも検討しています)

○コメントにて↓より簡単な対処法を教えて頂きました。最後の追記を参照してください。○

2006/12/12 追記。

↑とか言っておきながらすっかり忘れてたら、他の方がはてなで質問してくれました^^;

http://q.hatena.ne.jp/1165179638

で、改めて考えてみたら、対策がみつかったので回答しました。

回答に書いたとおりですが、いまだ原因は不明、しかし以下で対応できそうです。

  • 一度、style visibility:hidden; position:absolute;なimg要素のsrcプロパティにImageオブジェクトをセットする。(width、heightは無指定)
  • その後、上記img要素のoffsetHeight、offsetWidthを使用する。

これを共通関数化してみました。

【参考】Imageオブジェクト 幅・高さ 取得関数
function getImageDimensions(image){
  var wk = document.createElement('img');
  wk.style.position = 'absolute';
  wk.style.visibility = 'hidden';
  wk.style.top = '0px';
  wk.style.left = '0px';
  document.body.appendChild(wk);
  wk.src = image.src;
  var width = wk.offsetWidth;
  var height = wk.offsetHeight;
  wk.parentNode.removeChild(wk);
  return {width:width, height:height};
}

引数はImageオブジェクト、返却値は{width:(幅), height:(高さ)}形式のカスタムオブジェクトです。注意として、document.body.appendChildする都合上、ページonload後でないと使用できません。

・・・ブラウザ判定はパス^^; 必要であれば各自でお願いします。。。

【例】
<html>
<head>
<title></title>
<script language="javascript">
<!--
//画像オブジェクト配列
var images = [];
var index = 0;
function addImage(src){
  var image = new Image();
  image.src = src;
  images.push(image);
}
//画像先読み
addImage("./sample9.gif");  //最初の画像
addImage("./sample10.gif");  //次の画像
addImage("./sample9.gif");  //次の画像

//画像変更
function changeImage(imgElem){
  if(index >= images.length) return;
  var str = "";
  str += "Imageオブジェクトのwidth, height<br/>";
  str += "images[" + index + "].width : " + images[index].width  + "<br/>";
  str += "images[" + index + "].height : " + images[index].height + "<br/>";
  var dimentions = getImageDimensions(images[index]);
  var width = dimentions.width;
  var height = dimentions.height;
  str += "IMGタグに設定した後のoffsetWidth, offsetHeight<br/>";
  str += "width : " + width + "<br/>";
  str += "height : " + height + "<br/>";
  //IMGタグのサイズ(表示サイズ)を画像サイズにあわせる
  imgElem.height = height;
  imgElem.width = width;
  //画像表示
  imgElem.src = images[index].src;
  document.getElementById("test").innerHTML = str;
  index++;
}
function getImageDimensions(image){
  var wk = document.createElement('img');
  wk.style.position = 'absolute';
  wk.style.visibility = 'hidden';
  wk.style.top = '0px';
  wk.style.left = '0px';
  document.body.appendChild(wk);
  wk.src = image.src;
  var width = wk.offsetWidth;
  var height = wk.offsetHeight;
  wk.parentNode.removeChild(wk);
  return {width:width, height:height};
}
//-->
</script>
</head>
<body bgcolor="blue" onload="changeImage(document.getElementById('img'));">
<div id="test" style="color:white;"></div>
<img id="img" onclick="changeImage(this);"/>
</body>
</html>

2011/05/02 追記。

白兎仙人さん、コメント頂いていたのに気がつかなくてすみません。。。

srcにイメージのURLを代入する直前に、srcに空文字('')を代入すると正しい画像サイズが取得できるようになりました(image.src='';)。

私のほうでも試してみました(Xp+IE8)。確かにそれでうまくいきそうですね^^
てか、まだコレ直ってないんですねorz メインPCがXpなのでまだIE8でしか試してませんが。IE9ではいい加減直っていてくれ><;

<html>
<head>
<title></title>
<script language="javascript">
<!--
//画像オブジェクト配列
var images = [new Image(), new Image()];
//画像先読み
images[0].src = "";  //★空文字を代入★
images[0].src = "./sample9.gif";  //最初の画像
images[1].src = "";  //★空文字を代入★
images[1].src = "./sample10.gif"; //次の画像

//ページ読み込み完了時実行関数
function init(){
  changeImage(document.getElementById("img"), 0);
}

//画像変更
function changeImage(imgElem, index){
  //画像そのものの幅・高さのポップアップ
  alert("images[" + index + "].width : " + images[index].width);
  alert("images[" + index + "].height : " + images[index].height);
  //IMGタグの幅・高さのポップアップ
  alert("imgElem.width : " + imgElem.width);
  alert("imgElem.height : " + imgElem.height );
  //IMGタグのサイズ(表示サイズ)を画像サイズに合わせる
  imgElem.height = images[index].height;
  imgElem.width = images[index].width;
  //画像表示
  imgElem.src = images[index].src;
}
//-->
</script>
</head>
<body bgcolor="blue" onload="init();/*読み込み時に最初の画像を設定*/">
<img id="img" onclick="changeImage(this, 1);/*クリック時に次の画像を設定*/"/>
</body>
</html>
サンプル画像 sample9.gif sample10.gif