XHTML/XHTML5の最近のブログ記事

昨日作った

M.C.P.C.: ケータイ3キャリア別URLに振り分けるPerlのCGI

ですが、よくかんがえると今月末(2012年3月31日)でdocomoのムーバ(PDC)が停波になるのです。そうすると、別にCHTMLで作る必要はなくね? XHTMLで作ればよくね? となります。厳密にいえば、900i以前のモデル(東芝とかの4ケタ型番のやつとか)はXHTMLはだめらしいのですけれども、実質試験機みたいなものだったので(すこぶるでかい)、コンテンツが見れなくてもしゃーなしだと思います。

というわけで、3キャリア対応でXHTMLで表示URLを振り分けるPerl CGIを作りました。ポイントは、docomoのXHTMLの古い仕様で、外部CSSファイルは使えないという制約があるのですが、PerlのHTML::MobileJpCSSでdocomo端末の時だけインラインCSSに展開してしまう、ていうところです。これにより3社対応ページが共通のHTMLとCSSで作ることができるようになりました。

Filename: index.cgi

#!/usr/bin/perl
 
use strict;
use warnings;
use utf8;
use CGI;
use Encode;
use Encode::JP::Mobile qw(:props);
use File::Spec;
use FindBin::Real;
use HTML::Template;
use HTTP::MobileAgent;
use HTTP::MobileAgent::Plugin::Charset;
use HTML::MobileJpCSS;
 
my $basedir = FindBin::Real::Bin();
my $q = CGI->new();
my $agent = HTTP::MobileAgent->new;
my $inliner = HTML::MobileJpCSS->new( base_dir => "$basedir");
 
my $emoticons = {
 'E63E' => 'sun',
 'E63F' => 'cloud',
 'E640' => 'rain',
 'E641' => 'snow',
 'E642' => 'thunder',
 'E643' => 'typhoon',
 'E644' => 'mist',
 'E645' => 'sprinkle',
 'E646' => 'aries',
 'E647' => 'taurus',
 'E648' => 'gemini',
 'E649' => 'cancer',
 'E64A' => 'leo',
 'E64B' => 'virgo',
 'E64C' => 'libra',
 'E64D' => 'scorpius',
 'E64E' => 'sagittarius',
 'E64F' => 'capricornus',
 
 'E650' => 'aquarius',
 'E651' => 'pisces',
 'E652' => 'sports',
 'E653' => 'baseball',
 'E654' => 'golf',
 'E655' => 'tennis',
 'E656' => 'soccer',
 'E657' => 'ski',
 'E658' => 'basketball',
 'E659' => 'motorsports',
 'E65A' => 'pocketbell',
 'E65B' => 'train',
 'E65C' => 'subway',
 'E65D' => 'bullettrain',
 'E65E' => 'car',
 'E65F' => 'rvcar',
 
 'E660' => 'bus',
 'E661' => 'ship',
 'E662' => 'airplane',
 'E663' => 'house',
 'E664' => 'building',
 'E665' => 'postoffice',
 'E666' => 'hospital',
 'E667' => 'bank',
 'E668' => 'atm',
 'E669' => 'hotel',
 'E66A' => '24hours',
 'E66B' => 'gasstation',
 'E66C' => 'parking',
 'E66D' => 'signaler',
 'E66E' => 'toilet',
 'E66F' => 'restaurant',
 
 'E670' => 'cafe',
 'E671' => 'bar',
 'E672' => 'beer',
 'E673' => 'fastfood',
 'E674' => 'boutique',
 'E675' => 'hairsalon',
 'E676' => 'karaoke',
 'E677' => 'movie',
 'E678' => 'upwardright',
 'E679' => 'carouselpony',
 'E67A' => 'music',
 'E67B' => 'art',
 'E67C' => 'drama',
 'E67D' => 'event',
 'E67E' => 'ticket',
 'E67F' => 'smoking',
 
 'E680' => 'nosmoking',
 'E681' => 'camera',
 'E682' => 'bag',
 'E683' => 'book',
 'E684' => 'ribbon',
 'E685' => 'present',
 'E686' => 'birthday',
 'E687' => 'telephone',
 'E688' => 'mobilephone',
 'E689' => 'memo',
 'E68A' => 'tv',
 'E68B' => 'game',
 'E68C' => 'cd',
 'E68D' => 'heart',
 'E68E' => 'spade',
 'E68F' => 'diamond',
 
 'E690' => 'club',
 'E691' => 'eye',
 'E692' => 'ear',
 'E693' => 'rock',
 'E694' => 'scissors',
 'E695' => 'paper',
 'E696' => 'downwardright',
 'E697' => 'upwardleft',
 'E698' => 'foot',
 'E699' => 'shoe',
 'E69A' => 'eyeglass',
 'E69B' => 'wheelchair',
 'E69C' => 'newmoon',
 'E69D' => 'moon1',
 'E69E' => 'moon2',
 'E69F' => 'moon3',
 
 'E6A0' => 'fullmoon',
 'E6A1' => 'dog',
 'E6A2' => 'cat',
 'E6A3' => 'yacht',
 'E6A4' => 'xmas',
 'E6A5' => 'downwardleft',
 
 'E6AC' => 'slate',
 'E6AD' => 'pouch',
 'E6AE' => 'pen',
 
 'E6B1' => 'shadow',
 'E6B2' => 'chair',
 'E6B3' => 'night',
 
 'E6B7' => 'soon',
 'E6B8' => 'on',
 'E6B9' => 'end',
 'E6BA' => 'clock',
 
 'E6CE' => 'phoneto',
 'E6CF' => 'mailto',
 
 'E6D0' => 'faxto',
 'E6D1' => 'info01',
 'E6D2' => 'info02',
 'E6D3' => 'mail',
 'E6D4' => 'by-d',
 'E6D5' => 'd-point',
 'E6D6' => 'yen',
 'E6D7' => 'free',
 'E6D8' => 'id',
 'E6D9' => 'key',
 'E6DA' => 'enter',
 'E6DB' => 'clear',
 'E6DC' => 'search',
 'E6DD' => 'new',
 'E6DE' => 'flag',
 'E6DF' => 'freedial',
 
 'E6E0' => 'sharp',
 'E6E1' => 'mobaq',
 'E6E2' => 'one',
 'E6E3' => 'two',
 'E6E4' => 'three',
 'E6E5' => 'four',
 'E6E6' => 'five',
 'E6E7' => 'six',
 'E6E8' => 'seven',
 'E6E9' => 'eight',
 'E6EA' => 'nine',
 'E6EB' => 'zero',
 'E6EC' => 'heart01',
 'E6ED' => 'heart02',
 'E6EE' => 'heart03',
 'E6EF' => 'heart04',
 
 'E6F0' => 'happy01',
 'E6F1' => 'angry',
 'E6F2' => 'despair',
 'E6F3' => 'sad',
 'E6F4' => 'wobbly',
 'E6F5' => 'up',
 'E6F6' => 'note',
 'E6F7' => 'spa',
 'E6F8' => 'cute',
 'E6F9' => 'kissmark',
 'E6FA' => 'shine',
 'E6FB' => 'flair',
 'E6FC' => 'annoy',
 'E6FD' => 'punch',
 'E6FE' => 'bomb',
 'E6FF' => 'notes',
 
 'E700' => 'down',
 'E701' => 'sleepy',
 'E702' => 'sign01',
 'E703' => 'sign02',
 'E704' => 'sign03',
 'E705' => 'impact',
 'E706' => 'sweat01',
 'E707' => 'sweat02',
 'E708' => 'dash',
 'E709' => 'sign04',
 'E70A' => 'sign05',
 'E70B' => 'ok',
 'E70C' => 'appli01',
 'E70D' => 'appli02',
 'E70E' => 't-shirt',
 'E70F' => 'moneybag',
 
 'E710' => 'rouge',
 'E711' => 'denim',
 'E712' => 'snowboard',
 'E713' => 'bell',
 'E714' => 'door',
 'E715' => 'dollar',
 'E716' => 'pc',
 'E717' => 'loveletter',
 'E718' => 'wrench',
 'E719' => 'pencil',
 'E71A' => 'crown',
 'E71B' => 'ring',
 'E71C' => 'sandclock',
 'E71D' => 'bicycle',
 'E71E' => 'japanesetea',
 'E71F' => 'watch',
 
 'E720' => 'think',
 'E721' => 'confident',
 'E722' => 'coldsweats01',
 'E723' => 'coldsweats02',
 'E724' => 'pout',
 'E725' => 'gawk',
 'E726' => 'lovely',
 'E727' => 'good',
 'E728' => 'bleah',
 'E729' => 'wink',
 'E72A' => 'happy02',
 'E72B' => 'bearing',
 'E72C' => 'catface',
 'E72D' => 'crying',
 'E72E' => 'weep',
 'E72F' => 'ng',
 
 'E730' => 'clip',
 'E731' => 'copyright',
 'E732' => 'tm',
 'E733' => 'run',
 'E734' => 'secret',
 'E735' => 'recycle',
 'E736' => 'r-mark',
 'E737' => 'danger',
 'E738' => 'ban',
 'E739' => 'empty',
 'E73A' => 'pass',
 'E73B' => 'full',
 'E73C' => 'leftright',
 'E73D' => 'updown',
 'E73E' => 'school',
 'E73F' => 'wave',
 
 'E740' => 'fuji',
 'E741' => 'clover',
 'E742' => 'cherry',
 'E743' => 'tulip',
 'E744' => 'banana',
 'E745' => 'apple',
 'E746' => 'bud',
 'E747' => 'maple',
 'E748' => 'cherryblossom',
 'E749' => 'riceball',
 'E74A' => 'cake',
 'E74B' => 'bottle',
 'E74C' => 'noodle',
 'E74D' => 'bread',
 'E74E' => 'snail',
 'E74F' => 'chick',
 
 'E750' => 'penguin',
 'E751' => 'fish',
 'E752' => 'delicious',
 'E753' => 'smile',
 'E754' => 'horse',
 'E755' => 'pig',
 'E756' => 'wine',
 'E757' => 'shock',
};
 
# テンプレートファイル読み込み
my $template = sub {
  open my $fh, '<', shift or die $!;
  local $/ = undef; my $s = <$fh>;
  close $fh;
  return $s;
}->('index.tmpl');
$template = decode_utf8( $template );
$template =~s|\\x{(.+?)}|chr(hex($1))|eg; # 絵文字記述をUnicode文字列に
 
# キャリア別処理
my $params;  # TemplateにUserAgent(キャリア名)をまとめて渡す
my $carrier; # Templateに渡すキャリア名
if      ( $agent->is_docomo   ) { $carrier  = 'docomo';
} elsif ( $agent->is_ezweb    ) { $carrier  = 'AU';
} elsif ( $agent->is_vodafone ) { $carrier  = 'SoftBank';
} else {
  $carrier  = 'パソコン/スマートフォン';
  $params->{pc} = $carrier;
  $template =~ s{(\p{InMobileJPPictograms})}{
    my $char = Encode::JP::Mobile::Character->from_unicode(ord $1);
    sprintf '<img src="/emoticons/%s.gif" />', $emoticons->{$char->unicode_hex};
  }ge;
}
my $encoding = $agent->encoding; # Encode用のキャリアごとのエンコーディング
my $charset = $encoding =~ /sjis/ ? 'Shift_JIS' : 'UTF-8'; # HTML用のcharset
my $content = $encoding =~ /x-utf8-docomo/ ? 'application/xhtml+xml' : 'text/html';
 
$params->{ carrier} = $carrier; # <tmpl_var name="carrier">でdocomoとか表示できる
$params->{$carrier} = $carrier; # <tmpl_if name="docomo">でdocomo用処理振り分けできる
$params->{ charset} = $charset; # <meta~charset=<tmpl_var name="charset">" />
$params->{ content} = $content; # <meta~content="<tmpl_var name="content">" />
 
# テンプレート処理
my $t = HTML::Template->new(
  scalarref => \$template,
  die_on_bad_params => 0
);
$t->param( $params );
my $html = $t->output;
$html = $inliner->apply( $html ); # CSSをキャリアごとに適用するよう書き換え
 
# 出力
print $q->header( -type => $content, -charset => $charset );
print encode( $encoding, $html );
 
exit;
 
__END__

Filename: index.tmpl

<?xml version="1.0" encoding="<tmpl_var name="charset">"?>
<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
  <head>
    <meta http-equiv="Content-Type" content="<tmpl_var name="content"
      >; charset=<tmpl_var name="charset">" />
    <title>エロいリンク</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes," />
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <div id="hd">
      <h1>エロいページ</h1>
      <h2>ケータイ版リンク集</h2>
    </div>
    <hr />
    <div>
      <dl>
        <dt>
          <span style="color: #0077ff;">\x{e6e2}</span
          ><a accesskey="1"
            href="<tmpl_if name="pc"
            >http://www.example.com/<tmpl_else><tmpl_if name="docomo"
            >http://www.example.com/i/</tmpl_if><tmpl_if name="au"
            >http://www.example.com/e/</tmpl_if><tmpl_if name="softbank"
            >http://www.example.com/j/</tmpl_if></tmpl_if
            >">お世話になったVHS</a>\x{E6DD}</dt>
        <dd>
          ツメオリのVHS情報がご覧になれます。
        </dd>
      </dl>
    </div>
    <hr />
    <div id="ft">
      <span>&copy;DTPWiki 2012</span>
    </div>
  </body>
</html>

Filename: styles.css

#ft {
  text-align: right;
  font-size: x-small;
  color: red;
}


どうでもいいけれども、ムーバ端末がネットから切り離されると、CHTMLを正確に展開できるのはiモードHTMLシミュレータだけになりそうですが、インターネット上にiモードHTMLブラウザでしか読めない新天地ができたりとか、iモードが絶滅した後の世界で刊行された小説に、「iモードブラウザで隠されたメッセージを探し出す」とかいうのがあったりすると胸アツなので、今のうちにiモードHTMLシミュレータをダウンロードしておくのはどうでしょうか。

今どきで言うとdocomoのサイトのトップページのようなギャラリーができるjQueryプラグインの、jQuery slideViewerProというのがあります。

jQuery slideViewerPro 1.5 [www.gcmingati.net]

ところが、このjQueryプラグインは、サイトがXHTMLで、MIMEタイプがapplication/xhtml+xml(XHTMLとして処理するにはこのMIMEタイプが必須です)の場合、うまく動かない。

というわけで奥さん、パッチです!

Filename: jquery.slideViewerPro.1.5.js.patch

*** jquery.slideViewerPro.1.5.js.old  2012-02-22 23:08:17.000000000 +0900
--- jquery.slideViewerPro.1.5.js  2012-02-22 23:09:02.000000000 +0900
***************
*** 23,30 ****
    }
  });
  jQuery(function(){
!    jQuery("div.svwp").prepend("<img src='images/svwloader.gif' class='ldrgif' alt='loading...'/ >"); //change with YOUR loader image path
! });
  var j = 0;
  jQuery.fn.slideViewerPro = function(settings) {
      settings = jQuery.extend({
--- 23,30 ----
    }
  });
  jQuery(function(){
!    jQuery("div.svwp").prepend('<img src="images/svwloader.gif" class="ldrgif" alt="loading..." />'); //change with YOUR loader image path
! });
  var j = 0;
  jQuery.fn.slideViewerPro = function(settings) {
      settings = jQuery.extend({
***************
*** 97,103 ****
        jQuery(this).next().after("<a href='#' class='left' id='left" + j + "'><span>"+settings.leftButtonInner+"</span><\/a><a href='#' class='right' id='right" + j + "'><span>"+settings.rightButtonInner+"<\/span><\/a>");

        jQuery(this).find("li").each(function(n) {
!             jQuery("div#thumbSlider" + j + " ul").append("<li><a title='" + jQuery(this).find("img").attr("alt") + "' href='#'><img width='"+ thumbsWidth +"' height='"+ thumbsHeight +"' src='" + jQuery(this).find("img").attr("src") + "' /><p class='tmbrdr'>&nbsp;<\/p><\/a><\/li>");
        });

        jQuery("div#thumbSlider" + j + " a").each(function(z) {
--- 97,103 ----
        jQuery(this).next().after("<a href='#' class='left' id='left" + j + "'><span>"+settings.leftButtonInner+"</span><\/a><a href='#' class='right' id='right" + j + "'><span>"+settings.rightButtonInner+"<\/span><\/a>");

        jQuery(this).find("li").each(function(n) {
!             jQuery("div#thumbSlider" + j + " ul").append('<li><a title="' + jQuery(this).find("img").attr("alt") + '" href="#"><img width="'+ thumbsWidth +'" height="'+ thumbsHeight +'" src="' + jQuery(this).find("img").attr("src") + '" /><p class="tmbrdr">&#160;</p></a></li>');
        });

        jQuery("div#thumbSlider" + j + " a").each(function(z) {

要は、プラグインが挿入しようとしているHTMLは、XMLの文法的に間違っているから入れられないというのです。

パッチ前半の部分は、XMLとしてはimg要素は<img src="~" />と書かなくてはいかんということで、後半の部分は、XHTML5だと文字実体参照として定義されている文字が極端に少なく、&nbsp; というのもXHTML5だとエラーになるので、数値文字参照でノーブレークスペースのコードを直接指定するということです。

こんなに苦労せずとも、ウェブサーバが吐き出すときのMIMEタイプを、application/xhtml+xmlから、text/htmlあたりにすると解決なんですけれども、XHTML5を使っているとはいえなくなるよね。

ともかく、XHTML5として解釈されていると、JavaScriptから挿入するHTMLもXHTML5に矛盾しないようにしなくてはいけないです。めんどくさいが気をつけましょう。

いまさら、ウェブサイトに行動ターゲティングのトラッキングタグが仕込まれているからどうだ、というわけではないのですが[要出典]、はてなブックマークボタンにマイクロアドの行動ターゲティングのトラッキングタグが挿入されるようになっているのに気づいてしまい(→M.C.P.C.: はてなブックマークボタンのJavaScriptがdocument.write使っていてXHTMLで不具合があったのが直った(または9月1日からオプトアウト版はてなブックマークボタンが出ていた件)、これが今年9月からついたわけですけれども、9月より前に設置されたはてなブックマークボタンにも自動的に一定の確率で挿入されるようになっており、ユーザーの知らないところでトラッキングが実施されるようになっています。

source: http://b.st-hatena.com/js/bookmark_button.js ll.734-743

(function () {
var domains = 'www.toyokeizai.net member.toyokeizai.net excite.co.jp exblog.jp mainichi.jp jp.msn.com *.jp.msn.com itmedia.co.jp bizmakoto.jp atmarkit.co.jp eetimes.jp ednjapan.cancom-j.com ednjapan.com barks.jp www.asahi.com *.asahi.com jp.techcrunch.com japanese.engadget.com jp.autoblog.com celebrity.aol.jp www.nikkei.com *.nikkeibp.co.jp japan.cnet.com japan.zdnet.com builder.japan.zdnet.com japan.gamespot.com www.re-source.jp www.yomiuri.co.jp groupon.jp';
var pattern = '^(?:' + domains.replace(/\./g, '\\.').replace(/\*\\\./g, '(?:[^.]+\\.)+').replace(/\s+/g, '|') + ')$';
if (isLocal || !/^https?:$/.test(location.protocol) || new RegExp(pattern).test(location.hostname)) return;
if (Math.random() >= 1 / 2) return;
window.mad_partner_id = '6';
var script = E('script', { type: 'text/javascript', src: '//send.microad.jp/js/conv0000.js' });
var anchor = document.getElementsByTagName('script')[0] || document.getElementsByTagName('head')[0].firstChild;
anchor.parentNode.insertBefore(script, anchor);
})(); 

(今日の場合、1/2の確率でマイクロアドのスクリプトが挿入されるようです。この前は、1/3だった。なぜか確率はコロコロ変わる)

で、はてなブックマークボタンが設置しているサイトでは、マイクロアドの行動ターゲティングのトラッキングを実施していることを説明したほうが、サイト利用者にとって親切だと思うし、更には、マイクロアドのトラッキング行為をやめるようにと、マイクロアドのウェブサイトから指示できる(オプトアウト)ことも提示したほうがいいと思います。

マイクロアドのウェブサイトから行動ターゲティングのためのトラッキングのオプトアウトをするのはこちら。

MicroAdの広告をご覧のお客様へ [send.microad.jp]

そういうわけなんですけれども、企業サイトではてなブックマークボタンを設置している場合だとすると、はてなブックマーク側の2011年9月からの勝手な方針変更のせいで、お客様に対しての説明をするために、企業内では、企業と契約もしていない「マイクロアド」の名前が出てくる文章を用意することは、そもそも「何の目的であるか」「企業にとって何のメリットがあるか」などを説明する必要が出てくるし、「契約もしていないのにデータ取りに利用されている」という解釈をされると大変だなあとか思います。

というわけで、はてなブックマークボタンの場合、そもそもマイクロアドの行動ターゲティングのトラッキングをしないタイプのものがこっそり用意されていますので、こっちを使うことにしました。

(オプトアウト版)はてなブックマークボタンの作成・設置について [b.hatena.ne.jp]

※ちなみに、このブログでは、オプトアウト版はてなブックマークボタンではありません。あくまでも業務で公開しているウェブサイトでそうした、ということです。

ソーシャルボタンを設置すれば、そのボタンを提供している主体にある程度の情報を渡すことを了承していることになるのはいいのだけれども、今回のはてなブックマークボタンのように、設置時には情報提供をすることを認識しない状態で設置していたのに、オプトアウト式にトラッキングの仕組みを入れられると、困る人もいるんじゃないかなーとか思いました。

※見方を変えると、どこの企業にデータを提供することになるか、外部から見るとわかりやすいやり方ではあったかなあ。


(2012-3-10 11:04追記)

  • 発見の発端になったエントリを追記。
  • おとといコード見なおしてみたらMicroAdのトラッキングコードの挿入確率を決めていたコードがなくなったので、「一定の確率で」「確率云々~」をdel。


(2012-3-10 11:12追記)

Webでの反応を下に列記します


(2012-3-10 13:44追記)

要出典タグを追加。


(2012-3-11 15:05追記)

Webでの反応を追加。


(2012-3-12 1:08修正)

「はてなブックマークのトラッキング – ochimusya」がリンク切れになっていたので、新しくリンクを記入した。


(2012-3-13 21:22追記)

当該サービス運営者側からの発表(謝罪、トラッキング開始前の仕様にロールバック)がありました。

XHTMLで書いてサーバからMIMEタイプapplication/xhtml+xmlで送出されるばあい、document.writeやらがうまく動かないので、ソーシャルボタンが動作不良になる件を引き続きやっていきますが、前回のはてなブックマークボタンの件は、行動マーケティング測定用のタグが悪さをしていたということで、「はてなのサービスがこっそり行動マーケティングタグを挿入しているのが見つかった」という、思いもよらぬ展開になってしまったわけですが、今回は、Twitterのツイートボタンを見ていきます。

ツイートボタンについては、Twitter / ツイートボタン [twitter.com] を参照。

さて、まず今回はこんなHTMLを作ってみました。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
  xml:lang="ja" lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>test</title>
  </head>
  <body>
    <p>
      <a href="http://twitter.com/share"
        class="twitter-share-button"
        data-url="http://www.yahoo.co.jp/"
        data-count="horizontal"
        data-lang="ja">
        ツイート</a
      >
      <script type="text/javascript"
        src="http://platform.twitter.com/widgets.js"></script>
    </p>
  </body>
</html>

こんな表示になります。

Twbtn01
▲正常に表示されない

ウェブサイトを、厳格にXHTML化したときに(MIMEタイプをapplication/xhtml+xmlとかにしたとき)、document.writeが使えなくなるという問題があるのですが、最近はやりのソーシャルボタンにも、document.writeやそれに類する仕組みを使っていて、動かなくなることがありました。

というわけで、はてなブックマークボタンも、document.writeを使っていて、コンソールにエラーが出る(動くことは動く)ソーシャルボタンの一つだったのですが、今日見たら直っていた、というお話。

はてなブックマークボタンについては、はてなブックマークボタンの作成・設置について [b.hatena.ne.jp] を参照。

サイトをXHTML5化するために、.htaccessに

AddType "application/xhtml+xml; charset=UTF-8" .html

なんていうのを入れてしまうと、404や403や500のエラーが出たときに表示させるHTML文章も、XMLとして適正(ウェルフォームド)になっていないとブラウザでエラーメッセージが表示できません。

というわけで、BlueOnyxで動いているWebサーバのエラードキュメントを、XHTML5にしました。

なんでBlueOnyxなんだよ、ていうかもしれませんが、「レンタルサーバ」「VPS」「メールサーバ」「ウェブサーバ」「SSHで入れる」「僕がいなくなってもだれかがメンテナンスできる」などの様々な要因を合わせて考えたらこうなったってわけです。

修正前:web/error/404-file-not-found.html(CP932)

<HTML>
<HEAD>
<META NAME="Copyright" VALUE="Copyright (C) 2000, Cobalt Networks, Inc.  All rights reserved.">
<!-- locale-sensitive -->
<TITLE>ファイルが見つかりません</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" >
<BLOCKQUOTE>
 
    <P>&nbsp;</P>
 
  <DIV ALIGN="center">
 
<TABLE WIDTH="400" BORDER="0" CELLSPACING="0" CELLPADDING="0">
        <TR BGCOLOR="#999999">
                <TD>
                        <TABLE WIDTH="400" BORDER="0" CELLSPACING="1" CELLPADDING="5" ALIGN="center">
                                <TR BGCOLOR="#990000">
                                        <TD COLSPAN="2">
<!-- locale-sensitive -->
                                                <DIV ALIGN="left">
                                                        <FONT COLOR="#FFFFFF" SIZE="3"><B>ファイルが見つかりません</B> </FONT>
                                                </DIV>
                                        </TD>
                                </TR>
                                <TR>
                                        <TD BGCOLOR="#FFFFFF" COLSPAN="2" VALIGN="middle">
                                                <TABLE WIDTH="100%" BORDER="0">
                                                        <TR>
                                                                <TD>
                                                                        <IMG SRC="/libImage/warning.gif" WIDTH="40" HEIGHT="40" ALIGN="middle">
                                                                        </TD>
                                                                        <TD>
                                                                                <FONT SIZE="2">
<!-- locale-sensitive -->
                                                                                        リクエストされた URL は、このサーバ上には見つかりません。</FONT>
                                                                        </TD>
                                                                </TR>
                                                        </TABLE>
                                                </TD>
                                        </TR>
                                </TABLE>
                        </TD>
                </TR>
        </TABLE>
  </DIV>
</BLOCKQUOTE>
</HTML>
<!--
Copyright (c) 2003 Sun Microsystems, Inc. All  Rights Reserved.
 
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
 
-Redistribution of source code must retain the above copyright notice, this  list of conditions and the following disclaimer.
 
-Redistribution in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
 
Neither the name of Sun Microsystems, Inc. or the names of contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.
 
This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 
You acknowledge that  this software is not designed or intended for use in the design, construction, operation or maintenance of any nuclear facility.
-->

修正後:web/error/404-file-not-found.html(UTF-8)

<!DOCTYPE html>
<!--#set var="d_page_title" value="ファイルが見つかりません"
--><!--#set var="d_description" value="リクエストされた URL は、このサーバ上には見つかりません。"
--><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
  <head>
    <meta charset="utf-8" />
    <!-- META NAME="Copyright" VALUE="Copyright (C) 2000, Cobalt Networks, Inc.  All rights reserved." / -->
    <!-- locale-sensitive -->
    <title><!--#echo var="d_page_title" --></title>
    <style type="text/css">
    table table td {
      padding: 5px;
    }
    table table table td {
      padding: 1px;
      text-align: left;
    }
    </style>
  </head>
  <body style="background-color: #fff;" >
    <blockquote>
      
      <p>&#160;</p>
      
      <div style="text-align: center; margin: 0px auto;">
        
        <table style="width: 400px; border: 0px; margin: 0px auto; border-spacing: 0px; padding: 0px;">
          <tbody>
            <tr style="background-color: #999;">
              <td>
                <table style="width: 398px; border: 0px; border-spacing: 0px; cellpadding: 5px; text-align: center; margin: 0px auto;">
                  <tbody>
                    <tr style="background-color: #900;">
                      <td style="border-bottom: 1px solid #999;">
                        <!-- locale-sensitive -->
                        <div style="text-align: left;">
                          <span style="color: #fff; font-size: 16px;"><b><!--#echo var="d_page_title" --></b> </span>
                        </div>
                      </td>
                    </tr>
                    <tr>
                      <td style="background-color: #fff; vertical-align: middle;">
                        <table style="width: 100%; border: 0px;">
                          <tbody>
                            <tr>
                              <td>
                                <img src="/libImage/warning.gif" width="40" height="40" style="vertical-align: middle;" alt="Warning" />
                              </td>
                              <td>
                                <span style="font-size: 13px;">
                                <!-- locale-sensitive -->
                                <!--#echo var="d_description" --></span>
                              </td>
                            </tr>
                          </tbody>
                        </table>
                      </td>
                    </tr>
                  </tbody>
                </table>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </blockquote>
  </body>
</html>

MIMEタイプtext/htmlで吐き出していたXHTMLページを、application/xhtml+xmlにすると、あちこち直さなくてはならなくなったのでメモ。

1.真のapplication/xhtml+xmlは、document.writeができない

メンテナンスしているところで、こんなコードがあった。

<script type="text/javascript">
// <![CDATA[
document.write('<input type="hidden" '
             + 'name="thisTITLE" value="'
             + document.title
             + '" />'
);
document.write('<input type="hidden" '
             + 'name="thisURL" value="'
             + document.URL
             + '" />'
);
// ]]>
</script>

とか書いてあった。何年前のコードなんだろう……

document.writeができないのですが、いまどきjQuery時代なんでこうしちゃえばいいんじゃねって思う。

<script type="text/javascript"
  src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"
></script>
 
...
 
<input type="hidden" name="thisTITLE" value="" />    
<input type="hidden" name="thisURL"   value="" />
<script type="text/javascript">
// <![CDATA[
$(function() {
  $('input[name=thisTITLE]').val( document.title );
  $('input[name=thisURL]'  ).val( document.URL   );
});
// ]]>
</script>

あらかじめ要素用意してあって、DOMが生成された後だったら、値の挿入は文句ないよね。

2.真のapplication/xhtml+xmlは、formにつけた名前で値が取得できない

<form name="nForm">
  <dl>
    <dt>
      <label for="email00">メールアドレス</label>
    </dt>
    <dd>
      <input type="text" name="email00" id="email00" value="" />
    </dd>
    <dt>
      <label for="email01">もういっちょ</label>
    </dt>
    <dd>
      <input type="text" name="email01" id="email01" value="" />
    </dd>
  </dl>
</form>
var errstr = '';
if (document.nForm.email00.value != document.nForm.email01.value) {
  errstr += "メールアドレスの確認をしてください。\n";
}

ていうコードがあった。名前の付け方が無駄にキャメルケース。

んで、このコードが、text/htmlからapplication/xhtml+xmlになったとたん、動かなくなる。document.nFormがいなくなる!

これに関しては、document.formsの中にいるので、あわてずにこう書けばOK。

var errstr = '';
if (document.forms['nForm'].email00.value != document.forms['nForm'].email01.value) {
  errstr += "メールアドレスの確認をしてください。\n";
}

さらにこう書ける。

var errstr = '';
if (document.forms.nForm.email00.value != document.forms.nForm.email01.value) {
  errstr += "メールアドレスの確認をしてください。\n";
}

これは、JavaScriptの書き方が古かっただけじゃねえかっていう話ではあります。

以上、XHTMLで書いてあれば、text/htmlからapplication/xhtml+xmlにするだけでいいんじゃね、て思っている人が実際にはまった2点でした。document.writeの話なんて、5年遅れの話題ですよね……

HTML5ではなくて、XHTML5をちょっとやってみているのですけれども、本物のXHTMLは、レスポンスヘッダ内でContent-Type: application/xml もしくは Content-Type: application/xml+xhtml とMIME Typeを出さないといけないということで、.htaccessでこんな風にしてみました。

AddType "application/xhtml+xml" .html
RewriteEngine on
RewriteCond %{HTTP_USER_AGENT} "MSIE 5" [OR]
RewriteCond %{HTTP_USER_AGENT} "MSIE 6" [OR]
RewriteCond %{HTTP_USER_AGENT} "MSIE 7" [OR]
RewriteCond %{HTTP_USER_AGENT} "MSIE 8"
RewriteCond %{REQUEST_URI} \.html$
RewriteRule .* - [T=text/html]

(参考:.htaccessを使ってapplication/xhtml+xmlとtext/htmlに振り分ける。 [www.garunimo.com]
参照先は、.xhtmlをUAによってContent-Type差し替えるやつで、今回のは、.htmlで同様のことをするものです。404エラーすらXHTMLで書く必要があるので、お勧めしません)

これで、拡張子htmlのコンテンツに関して、IE5~8の時は、text/html、それ以外の時は、application/xml+xhtml を出すことになります。

んで、これにしたときは問題があって、Google +1ボタンや、TwitterのTweetボタンなど、JavaScriptからiframeを生成して表示するタイプのソーシャルボタンが動作しなくなります。

たとえば、Google +1のHTML5 ValidなHTMLはこんな感じ。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:g="http://base.google.com/ns/1.0" xml:lang="ja" lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>Google+1 テスト</title>
    <script type="text/javascript" src="https://apis.google.com/js/plusone.js">
    </script>
  </head>
  <body>
    <g:plusone annotation="inline"></g:plusone>
  </body>
</html>

これだと、IEにtext/htmlとして読み込ませた場合でも表示されないので、こっちのコードが普段使い。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>Google+1 テスト</title>
    <script type="text/javascript" src="https://apis.google.com/js/plusone.js">
    </script>
  </head>
  <body>
    <div class="g-plusone" data-size="standard" data-count="true">
    </div>
  </body>
</html>

これらが、Webkit系だと表示されなく、Operaだと表示されます。

正直言って、そこまでしてXHTML5をXMLとして評価するモードは必要ないかもとか思います。なので、普通に text/html で出せばいいんじゃね?ていうことに……

XHTMLは、XML操作でいじれるので個人的には作業しやすいのですが、XML操作しない人にとっては使わないよね……

プラスワン ボタン [www.google.com]

によると、ボタンを表示させたいところに

<!-- 次のタグを +1 ボタンを表示する箇所に貼り付けてください -->
<g:plusone></g:plusone>

て書くらしいのだけれども、これもはやHTMLじゃないので、もしXHTMLの中に書くとしたら、

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:g="http://base.google.com/ns/1.0" xml:lang="ja" lang="ja">

とルート要素でやっておかなければいかんような気がするけれども、

いまさらXHTMLっていう時代でもないし、あんまりこだわらなくていいのかーとも思いました。そもそもGoogleさんはXHTMLにしたがっていなかった気がするし。

問題はGoogle+1ボタン押したあと公開するよーて言う文章が説明不足で後からなんかおこりそうだということですね。

XHTML1.0 Strict文書があったとして、WindowsのIE6ではXML宣言なし、IE7~8ではXML宣言あり、それ以外だったらHTML5(XHTML5でないことに注意)とみなされるような出力がされるSSI記述を用意してみました。

SSIで差し込んで使えるようにしてありますので、使うときには<!--#include virtual="head.inc" -->などして使ってくださいね。

Filename: head.inc

<!--#if expr="$HTTP_USER_AGENT == /MSIE [7-8].0/ "
--><?xml version="1.0" encoding="UTF-8"?>
<!--#endif
--><!--#if expr="$HTTP_USER_AGENT == /MSIE [6-8].0/ "
--><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!--#else
--><!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
  <head>
    <meta charset="utf-8" />
<!--#endif -->

同様にSSIのIF文でbody要素の中のdiv要素をheader要素やsection要素に置き換えていけるのだけれども、素直にhtml5.js使えっていう話ではある。

HTML5 enabling script [remysharp.com]

月別 アーカイブ

ウェブページ

OpenID対応しています OpenIDについて
Powered by Movable Type 5.2.7

このアーカイブについて

このページには、過去に書かれたブログ記事のうちXHTML/XHTML5カテゴリに属しているものが含まれています。

前のカテゴリはWebです。

次のカテゴリはnetatalkです。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。