1.023world - ヤドカリパークとマリンアクアリウム -

海洋の仕組みと細菌・微生物から学ぶマリンアクアリウムサイト

1.023world Facebook

結果 Oh! Life (旧ブログ)

懲りずに書いてみたりする結果オーライな日記

そろそろ透過PNG?

IE 7.0

IE 7.0 がリリースされてから、もうかれこれ2年以上が経過し、一方、IE 6.0 のシェアはそろそろ20%を切ったあたりだそうです。・・・って、まだ全然多いんですけど(汗)

とは言え、IE 7.0 になってから、ひとつだけ助かったことがあります。それは、24ビット PNG のアルファチャンネルに対応してくれたことです。これでようやく透過 PNG が貼れるようになりました。

透過PNGのサンプル

と言いたいところですが、まだ IE 6.0 ユーザーが残ってます。
そんなモン早く窓から放り投げてください(笑)

ちなみに IE 6.0 では、こんな風に表示されちゃいます。

透過PNGをIE6.0で表示した場合

なので、もし現時点で透過 PNG をウェブで使おうと思ったら、AlphaImageLoader フィルター(IE独自拡張)を活用せざるを得ないのが現状です。
(IE6ユーザーを切り捨てるなら話は別ですが)
幸いこれを使えば、辛うじて IE 6.0 でも透過 PNG を表示させることができます。

と言ってもコレ、ちょい面倒くさいんですよね。。。

AlphaImageLoader をどう使うか

例えば、透過 PNG を CSS の background 属性に限定して記述するなら、通常の background 属性の記述に続いて、filter 属性への AlphaImageLoader の設定と、アンダースコアハック(underscore hack)による background の消去を行うことで適用できます。(アンダースコアハックはIE6のみに適用させる場合に用いるハック方法)
但し、縦横のサイズが PNG と同一のレイヤに対しては問題ありませんが、PNG よりも大きなレイヤに背景として配置するような場合には注意が必要です。(AlphaImageLoader フィルターの sizingMethod オプションを crop にするか scale にするか等)

しかし、AlphaImageLoader フィルターを、HTML 内の <img> タグで貼られた透過 PNG に適用させる場合は、単に <img> タグに対して AlphaImageLoader フィルターを適用させただけでは、src 属性で指定された元々の PNG が残ったままなので、IE6 では相変わらず非透過の PNG が表示されたままとなります。これは外部 CSS ファイルで記述した場合でも、 <img> タグへ style属性で記述した場合でも同様です。そのため <img> タグの src 属性に透明 GIF を割り当てる等の最終処理が必要になります。そしてこの場合は何らかの JavaScript での処理に頼ることになります。

また、W3C に準拠した Valid でクリーンなソースを壊したくない場合や、ブラウザの独自拡張になるべくなら頼りたくない場合など、おかしなハックやコメントの記述をためらうこともあるでしょう。そう言う理由からも、このようなややこしい処理は初めからすべて JavaScript に丸投げする方法を推奨します。そうすることで、必要悪は最小限に抑えられるでしょう(苦笑)

ちょっと検索してみたら、この機能を持った JavaScript モジュールがいくつか配布されているようでした。ただ、実際にコードを見てみると、必要のない機能が多かったり、無駄にファイルサイズが大きかったりするので、PNG だけの処理には持てあますかも知れません。たかだか IE6 のためだけに資源を無駄に使うこともありませんし。

PNG → AlphaImageLoader 自動転換 JavaScript コード

そこで、PNG だけを処理してくれる JavaScript を組んでみました。(昔組んだものを少しいじっただけですが)
例によって、ページのヘッダに事前に読み込んでおくだけで、あとは自動的に処理してくれるものです。

このコードは、外部 CSS ファイルや HTML 内の <style> タグによる CSS や <img> タグの PNG をスキャンして、自動的に AlphaImageLoader を適用します。但し、<img> タグへの置換用に、予め透明 GIF をサーバーのどこかにアップロードして、ie-png.js 内の gif 変数に絶対パスで設定しておいてください。

変換対象
外部 CSS ファイル
  • background:url(PNG-URL) [color repeat position]
  • background-image:url(PNG-URL)
  • PNG 画像パスは自動的に解析し割り当てるので相対パスでもOK
  • PNG 画像パスをクオート(シングル・ダブル)で囲んであってもOK
内部 <style> タグによる CSS
  • background:url(PNG-URL) [color repeat position]
  • background-image:url(PNG-URL)
  • PNG 画像パスは自動的に解析し割り当てるので相対パスでもOK
  • PNG 画像パスをクオート(シングル・ダブル)で囲んであってもOK
<img> タグの PNG
  • 予め透明 GIF をサーバーにアップロードして、JavaScript コードにその透明 GIF 画像パス(絶対パス推奨)を設定してください
    var gif = 'アップロードした透明GIFの絶対パス';
  • PNG 画像パスは自動的に解析し割り当てるので相対パスでもOK

テスト結果:サンプルページ * IE 6.0 で見ないと効果が判りません
ダウンロード:ie-png.js

ひとりごと

確か、IE 6.0 だけじゃなくて、IE 5.5 とかにも AlphaImageLoader は使えたと思ったのですが、今手元の IETester で試してみると IE 5.5 では AlphaImageLoader による透過 PNG が表示されません(汗)
一応、コード自体は IE 5.5 でも走るように書いたつもりですが、もし動作確認がとれた方はご一報いただけると嬉しいです。

また、インライン CSS 分の画像パスや絶対パスによる記述分は良いとして、外部 CSS ファイル内の相対パスによる画像パスについて、なんかもっとうまく処理する方法はないかしら。例えば、外部 CSS ファイルを HTML とは別の階層から呼び出していた場合に、相対パスだと AlphaImageLoader に渡す画像パスがずれてしまうので、今は地道に生成するようにしてあります。

その他、background 関係以外の画像適用 CSS プロパティ(list-style-image とか)までは面倒なので実装を省略しました(汗)

以下、一応 ie-png.js のコードです。

var gif = 'null.gif';

var env = new Object;
env.ua = navigator.userAgent.toLowerCase();
env.win = (env.ua.indexOf('windows') != -1)? 1: 0;
env.ie = (!window.opera && env.ua.indexOf('msie') != -1)?
           Number(env.ua.charAt(env.ua.indexOf('msie ') + 5)): 0;

var url = new Object;
url.host = 'http://' + window.location.host;
url.path = window.location.pathname.replace(/^(.*?)/[^/]*$/,'$1') + '/';

function setOnload(func){
  (window.addEventListener)? window.addEventListener('load',func,false):
  (window.attachEvent)? window.attachEvent('onload',func):
  window.onload = func;
}

var checkPNG = {
  CSS: function() {   // background, background-image propaty only.
    if(!env.win || env.ie > 6 || env.ie < 5) return false;
    var path = new Array();
    var num = 0;
    var node = document.getElementsByTagName('head')[0].childNodes;
    for(var i=0;i<node.length;i++){
      if(node[i].nodeName.match(/^style$/i))
        path[num] = url.host + url.path;
      else if(node[i].nodeName.match(/^link$/i) &&
          node[i].getAttribute('rel') &&
          node[i].getAttribute('rel').match(/stylesheet/i)){
        var href = node[i].getAttribute('href');
        if(href.match(/^//)) path[num] = url.host + href;
        else if(!href.match(/^https?:///))
               path[num] = url.host + url.path +
                 href.replace(/^(([^/]*/)*).*?$/,'$1');
        else path[num] = href;
      }
      if(path[num]) num++;
    }
    for(var i=0;i<document.styleSheets.length;i++){
      var rule = document.styleSheets[i].rules;
      for(var j=0;j<rule.length;j++) {
        var png = checkStyle(rule[j],'background');
        if(!png) png = checkStyle(rule[j],'backgroundImage');
        if(!png) continue;
        png = (png.match(/^//))? url.host + png:
          (!png.match(/^https?:///i))? path[i] + png: png;
        rule[j].style['filter'] =
          'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=' +
          png + ',sizingMethod=crop)';
      }
    }
    function checkStyle(obj,propaty){
      var value = obj.style[propaty];
      if(!value || !value.match(/.png/i)) return false;
      var png = value.replace(/^.*?url(["']*([^"']+.png)["']*).*$/i,'$1');
      obj.style[propaty] = '';
      return (!png.match(/^[^"']+.png$/i))? '': png;
    }
  },
  HTML: function(){   // '<img>' elements only.
    if(!env.win || env.ie > 6 || env.ie < 5) return false;
    var img = document.getElementsByTagName('img');
    for(var i=0;i<img.length;i++){
      var src = img[i].getAttribute('src');
      if(!src.match(/.png/i)) continue;
      if(!src.match(/^https?:///i)) src = url + src;
      img[i].setAttribute('src',gif);
      img[i].style.filter =
        'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=' +
        src + ',sizingMethod=scale)';
    }
  }
}

function onloads(){
  checkPNG.HTML();
}

checkPNG.CSS();

setOnload(onloads);

こちらのエントリーもどうぞ♪