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

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

1.023world Facebook

結果 Oh! Life (旧ブログ)

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

完全CSSオンリーのツールチップテスト

先日の CSSで三角形を作る→ツールチップへの応用 の実験がなかなか面白かったので、調子に乗って今度はJavaScriptを使わない完全CSSオンリーのツールチップのテストをおこなってみました。

サンプルページ ← クリックして別ウィンドウでご覧ください
詳細は後記しますが、現状で Safari、Chrome、Opera 以外のブラウザでは、ツールチップのポインタ部(突起)が崩れたり未表示となります。(但しツールチップ自体は表示される)
尚、IE 6 に至っては何故かアンカー自体が消失してるかも(汗)

但し前置きしておきますが、これは恐らく実用には耐えない仕様かも知れません(汗)
と言うのも、結果的に SEO スパム的な手法(?)になっちゃうので、よい子の皆さんは絶対に真似しない方が賢明かと思いますです。

と言うわけで、「あくまでも実装可能なアイデアのひとつ」としてご覧ください。

まず、以下のようなソースを用意しました。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS" />
<title>CSS TEST</title>
<style type="text/css">
<!--
a.tooltip {
  position:relative;
  text-decoration:none;
}
a.tooltip:hover:after {
  content:'';
  position:absolute;
  z-index:1;
  left:10px;
  top:20px;
  display:block;
  width:0;
  height:0;
  border-top:0;
  border-right:solid 10px transparent;
  border-bottom:solid 10px #369;
  border-left:0;
}
a.tooltip span {
  position:absolute;
  z-index:2;
  left:0;
  top:30px;
  display:none;
  width:120px;
  padding:4px 6px;
  line-height:1.1;
  font-size:13px;
  color:#369;
  background:#cde;
  border:solid 1px #369;
}
html>body a.tooltip span {
  width:108px;
}
a.tooltip:hover span {
  display:block;
}
a.tooltip span:before {
  content:'';
  position:absolute;
  left:10px;
  top:-9px;
  display:block;
  width:0;
  height:0;
  border-top:0;
  border-right:solid 9px transparent;
  border-bottom:solid 9px #cde;
  border-left:0;
}
-->
</style>
</head>
<body>
  <p>
    <a href="#" class="tooltip">
      ここ
      <span>このツールチップはCSSのみで実現しています</span>
    </a>
  </p>
</body>
</html>

このテストは、予めツールチップのテキストをアンカーに忍ばせておいて、マウスオーバーに応じて CSS の疑似クラス(:hover)で可視制御をおこなうというものです。要するに、ツールチップ用テキストもアンカーテキストもどちらもアンカーに入れちゃう形となります。

また前回同様、ツールチップのポインタ(突起)も再現する訳ですが、この部分を CSS の疑似要素(:after, :before)で再現することにしました。これにより無駄にタグを使うことなく、必要最小限のマークアップでポインタ部が実現されます。
尚、この実験ソースでは :before:after を使い分けていますが、いずれも絶対配置してるので、各々でどちらを使っても通用すると思います。

ちなみに border-radius (角丸)については今回は目的外として省略しました。

さて、このソースをツールチップも含めポインタ(突起)まで全てまともにレンダリングできたのは、以下のブラウザだけでした。

CSSの疑似要素でツールチップのポインタが実装できたブラウザ

最近、Safari はレベル高いなぁ。。。かなり高評価ですよ。
Chrome もさすが Google のクオリティを受け継いでいます。
Opera は期待してなかっただけに感動!!

しかし。。。 Firefox は 2.0 も 3.0 もポインタは全滅でした!
原因を調べてみたら、どうやら疑似要素に対しての絶対配置(position:absolute;)が処理できていないようでした。
さらに調べてみたら、CSS2 の仕様の時点では疑似要素に対しての position や float は確かに無視すべきとあり、その制限が無くなったのは CSS2.1 になってからのようです。
と言うわけで、現時点の Firefox は CSS2に準拠していると思われます。

また、Firefox が全滅と言うことで、Netscape 等の Gecko 系も全滅です。

IE は。。。ま、疑似要素自体できない子なので、言うに及ばず(苦笑)

ただ、いずれのブラウザでもツールチップ自体は正常に表示される(IE 6 はダメっぽい?)ので、ポインタ(突起)さえ諦めればこれはこれでツールチップのアイデアのひとつとして成立するかな?と思います。

さて、この完全CSSツールチップですが、アイデア自体なかなかのもんだと思うのですが、やっぱり実用には無茶ですかね?
でも完全非表示ではなくて、マウスオーバーで表示されるから、実はセーフなのかな?
とは言えこれがセーフなら、堂々とキーワードをアンカーに詰め込めちゃいますね(爆)
ま、僕はやりませんけど。

あとは人柱さんに期待してます。

おまけ

お時間とご興味のある方は、以下のサンプルもお試しください。
上記の優秀なブラウザなら、うまく解釈されるはずです♪

サンプルページ2

あとはこれでブラウザデフォルトのツールチップが消せたら完璧なんだけど。。。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS" />
<title>CSS TEST 2</title>
<style type="text/css">
<!--
a.tooltip {
  position:relative;
  text-decoration:none;
}
a.tooltip[title]:hover:after {
  content:attr(title);
  position:absolute;
  left:0;
  top:60px;
  display:block;
  width:108px;
  padding:4px 6px;
  line-height:1.1;
  font-size:13px;
  color:#cde;
  background:#369;
}
a.tooltip[title]:hover:before {
  content:'';
  position:absolute;
  left:20px;
  top:50px;
  display:block;
  width:0;
  height:0;
  border-top:0;
  border-right:solid 10px transparent;
  border-bottom:solid 10px #369;
  border-left:0;
}
-->
</style>
</head>
<body>
  <p>
    <a href="#" class="tooltip"
     title="このツールチップはCSSのみで実現しています">ここ</a>
  </p>
</body>
</html>

これはさすがにまだ早いかな。
でもこれが一番スッキリしてる(笑)

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

CSSで三角形を作る→ツールチップへの応用

2011/10/15 JavaScriptソース修正

­ ­

この三角形、CSS だけで実現されているって判ります?
実はボーダーを配したレイヤの角で得られるエンボス効果を利用したものなんですね。
言われてみれば納得ですが、これを三角形に応用しようなんて、僕も思いもつきませんでした。

ちなみにこれは、マイコミジャーナルの記事からネタ元(英語)を見て知りました。
さらにそちらでは、この三角形をツールチップに応用したソースも公開されていました。
この方法なら、角丸は別としても(爆)、吹き出しフレームに不可欠なポインター(尖ったとこ)まで、画像を使わず CSS のみで実現可能になります。
いやぁ、なんでも考える人がいるもんですね。感心させられます。

ところで僕自身はツールチップと言うもの自体あまり好きくないのですが(笑)、せっかくなのでこれをさらに進化させて、CSS + JavaScript で自動的にツールチップを生成する実験をしてみました。

サンプルページ

このアンカー(リンク)のソースはこうなってます。

<a href="#" class="tooltip"
 title="このツールチップは画像を使わずCSSのみで実現しています">ここ</a>

ツールチップを出したいアンカーに、class="tooltip" と、title="テキスト" を設定しておけば自動的に JavaScript でツールチップへ変換してくれるという仕組みです。構成は外部CSSファイルと外部JSファイルを予めヘッダ内で読み込ませるだけです。あとは上記の方法でリンクを書けば、勝手にツールチップが生成されます。要するに、ブラウザデフォルトのツールチップをオリジナルのモノに置き換えると言う方式ですね。

ネタ元の方法ではツールチップの内容を直接フィールドに配置していますが、僕の場合は本来の用途そのままにアンカーのタイトルに入れました。これはいろいろ考えた末の結果ですが、世知辛い時代ですので、どちらが良いのかの判断はみなさまにお任せしたいと思います(爆)

ツールチップ自動生成ツールの概要

このツールは、外部CSSファイルと外部JSファイルをヘッダ内に読み込ませるだけで動作します。

大まかなフローだけ説明しますと、ページ読み込み完了時に JavaScript でページ内をスキャンして、クラス名に「tooltip」を持つアンカーで且つタイトル属性が設定されていれば、そのタイトルをツールチップに変換するというものです。

なるべくソースを簡潔にするために、必要最小限の構成になっています。あまり厳密なことはやってないので、気になる方は気の済むように弄ってください(汗)。親切な方からのアドバイス等も頂けたら嬉しいです。

注意点としては、変換されたツールチップは body 末尾に不可視状態で蓄積され、アンカーへのオンカーソルに応じて該当のツールチップを可視制御しています。アンカーとツールチップには連番の ID が割り当てられますので、対象ページでの既存 ID との衝突にご注意ください (滅多なことは無いと思うけど)

アンカー ID 連番: ancher-0 ~ ancher-n
ツールチップ ID 連番: tooltip-0 ~ tooltip-n

その他、動作環境ですが、一応以下のブラウザで動作確認ができました。いずれも Windows のみでの確認。

  • IE 7 OK (角丸無し)
    IE 6 は transparent の処理が変だけど OK (角丸無し)
  • Firefox 2以上 OK
  • Safari 3 以上 OK
  • Netscape 7.1 以上 OK
  • Opera 9.6 OK (角丸無し)
  • Chrome 1.0 OK

以下、汚いソース(汗)で恐縮ですが、興味があればご自由にどうぞ。
但し角丸は IE と Opera では再現されませんのであしからず(汗)

外部CSSファイル用ソース

適当に名前を付けて保存してください (tooltip.css とか)

div.tooltip {
  position:absolute;
  z-index:999;
  display:none;
  width:120px;
  height:0;
  text-align:left;
  line-height:0;   /* for IE */
}

div.tooltip p {
  position:absolute;
  bottom:0;
  width:120px;
  margin:0;
  padding:4px 6px;
  text-align:left;
  line-height:1.1em;
  font-size:12px;
  font-family:Osaka,'MS UI Gothic',sans-serif;
  color:#369;
  background:#cde;
  border:solid 1px #369;
  -moz-border-radius:4px;   /* for Mozilla */
  -webkit-border-radius:4px;   /* for Safari */
  -khtml-border-radius:4px;   /* for Old Safari, KHTML */
  border-radius:4px;   /* for CSS3 */
}
html>body div.tooltip p {
  width:108px;
}

div.tooltip div {
  position:absolute;
  width:0;
  height:0;
  line-height:0;   /* for IE */
  border-right:0;
  border-bottom:0;
}
div.tooltip div.point-border {
  z-index:1;
  top:-1px;
  left:70px;
  border-top:solid 10px #369;
  border-left:solid 10px transparent;
}
div.tooltip div.point-inner {
  z-index:2;
  top:-2px;
  left:71px;
  border-top:solid 8px #cde;
  border-left:solid 8px transparent;
}

色とか自由に書き換えてください。

外部JSファイル用ソース

適当に名前を付けて保存してください (tooltip.js とか)

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

/* ↓ 2011/10/15追加 */
if(!ie){
 (function(){
  var evt = ['mousedown','mouseover','mouseout','mousemove',
             'mousedrag','click','dblclick'];
  for(var i=0; i<evt.length; i++){
   window.addEventListener(evt[i], function(e){window.event=e;}, true);
  }
 }());
};

function setTooltip(){
  var a = document.getElementsByTagName('a');
  for(var i=0;i<a.length;i++){
    if(a[i].getAttribute('class') == 'tooltip' || a[i].getAttribute('className') == 'tooltip'){
      if(!a[i].getAttribute('title')) continue;
      var tooltip = document.createElement('div');
      tooltip.setAttribute('id','tooltip-' + i);
      setClass(tooltip,'tooltip');
      var text = document.createElement('p');
      text.appendChild(document.createTextNode(a[i].getAttribute('title')));
      tooltip.appendChild(text);
      var pointBorder = document.createElement('div');
      setClass(pointBorder,'point-border');
      tooltip.appendChild(pointBorder);
      var pointInner = document.createElement('div');
      setClass(pointInner,'point-inner');
      tooltip.appendChild(pointInner);
      document.body.appendChild(tooltip);
      a[i].setAttribute('id','ancher-' + i);
      a[i].setAttribute('title','');
      a[i].style.position = 'relative';
      a[i].onmouseover = function(){
        var id = this.getAttribute('id').split('-')[1];
        var tooltip = document.getElementById('tooltip-' + id).style;
        var pos = getPosition();
        tooltip.top = (pos.y - 20) + 'px';
        tooltip.left = (pos.x - 80) + 'px';
        tooltip.display = 'block';
      };
      a[i].onmouseout = function(){
        var id = this.getAttribute('id').split('-')[1];
        document.getElementById('tooltip-' + id).style.display = 'none';
      };
    }
  }
}

function setClass(obj,value){
  (ie && ie < 8)? obj.setAttribute('className',value): obj.setAttribute('class',value);
}

function getPosition(){
  var obj = new Object;
  obj.x = (ie)? window.event.clientX + (document.documentElement.scrollLeft|document.body.scrollLeft):
                (window.event.clientX + window.pageXOffset|window.event.pageX);
  obj.y = (ie)? window.event.clientY + (document.documentElement.scrollTop|document.body.scrollTop):
                (window.event.clientY + window.pageYOffset|window.event.pageY);
  return obj;
}

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

setOnload(setTooltip);

うまくコピペできないときは、以下のファイルを参考にしてください。

CSSファイル:tooltip.css
JavaScriptファイル:tooltip.js

念のためヘッダへの記述方法

<link type="text/css" rel="stylesheet" href="tooltip.css" />
<script type="text/javascript" src="tooltip.js"></script>

パスはきちんと通るように記述してくださいね。絶対パスが無難でしょう。

何かご意見ご指摘等あればコメントをお願いします。

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

静的サイトのためのRORサイトマップ生成CGI

最近はアクアショップサイトなんかでもCMSの利用が増えてきましたが、それでもまだ古式ゆかしく静的なHTMLをソフトまたは手打ちでシコシコ作成しておられるショップサイトも少なくないでしょう (間違ってもバカにしてるつもりはありませんし、むしろ尊敬します)

そんなショップさんを応援すべく、少しでも他の CMS サイトに負けないためにも、ROR 形式のサイトマップXML(検索エンジン対策)を自動で生成してくれる CGI を作ってみました。
まずは流れを知るために、以下の解説を順にご覧ください。

ROR サイトマップとは?

以下のソースのような XML ファイルです。

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:ror="http://rorweb.com/0.1/">
  <channel>
    <title>ROR Sitemap for http://www.1023world.net/</title>
    <link>http://www.1023world.net/</link>
    <item>
      <link>http://www.1023world.net/</link>
      <ror:updated>2009-02-28T18:54:48+00:00</ror:updated>
      <ror:updatePeriod>weekly</ror:updatePeriod>
      <ror:sortOrder>0</ror:sortOrder>
      <ror:resourceOf>sitemap</ror:resourceOf>
    </item>
  </channel>
</rss>
</rss>

これは当サイトの場合のソースの抜粋例ですが、実際にはページ数の分だけソースは繰り返し長くなります。
内容的には、どのページがいつ更新されたか、そして更新頻度はどうか、みたいな情報がXMLの構文で羅列されています。
この XML ファイルをサーバーに置いて検索エンジンに渡すことで、リストされたサイト内のページをもれなくクロールさせることができます。

ROR 形式のサイトマップは、Google、Yahoo、MSN、すべての検索エンジンで利用可能な汎用フォーマットなので、これからサイトマップの準備を検討される方にはおすすめなフォーマットです。
ちなみにアクアショップさんじゃない方でも、必要であればご自由にお使いください (サポートはしませんが)

RORサイトマップ作成CGI 特長

  • CGI をサーバーに置いて呼び出すだけでRORサイトマップを自動生成
  • 簡易認証機能があるので、他人に実行させない
  • 他のサーバーにURLを送ったり、生成時にソースをコピーしたりファイルを作成したり等の手間が一切不要

RORサイトマップ作成CGI 利用条件

  • 独自ドメインの方
  • サーバーで CGI が使えること
  • サーバーに Jcode.pm がインストールされていること

RORサイトマップ作成CGI 設置手順

  1. CGI のプログラムソースをメモ帳(あるいはテキストエディタ等)にコピペして「setSitemap.cgi」と言う名前で保存します。
  2. 必要であればご利用のサーバーに合わせて1行目の Perl のパスを書き換えます。
    #!/usr/bin/perl または #!/usr/local/bin/perl など)
  3. 次に8行目のパスワードをあなただけのモノに必ず書き換えておいてください。
  4. 万一、サイトマップで除外したいページがある場合は、14行目の @no と言う配列にファイル名またはディレクトリ名をルートからの絶対パスで追加してください。
    例えば、http://あなたのドメイン/secret.html を除外したければ、 /secret.html を追加します。
    また、http://あなたのドメイン/member/ 内のすべてのページを除外したければ、/member/ を追加します。
    (これらの例を既にソースに入れてあります)
  5. 作成した CGI ファイルを、サーバーのルート(トップページを置いてる階層)にアップロードします。
  6. CGI ファイルのパーミッション(アクセス権)を 755 にします。
    (サーバーによっては 755 以外の場合があります)
  7. http://あなたのドメイン/setSitemap.cgi?パスワード にアクセスします。このURLが今後サイトマップを更新するURLとなります。
    仮にあなたのドメインが abc.com で、パスワードを abcd1234 としたなら、http://abc.com/setSitemap.cgi?abcd1234 へのアクセスとなるはずです。
    このアクセスにより、その時点でのサイトマップが ror.xml と言うファイル名でルートに生成(更新)されます。
    生成後のサイトマップのURLは http://あなたのドメイン/ror.xml です。
    (Firefoxだとタイトルしか出ないかも知れませんが、ソースを開けばサイト内のページがすべてリストアップされているのが確認できると思います)
  8. 続いて、あなたのサイトのトップページのヘッダ内(<head>この中</head>)に、以下のタグを埋め込んでおきます。改行せずに一行で書いてもOKです。
    <link rel="alternate" type="application/rss+xml"
    href="http://あなたのドメイン/ror.xml" title="ROR 0.1" />

    これで、次回の検索エンジンのクロールでサイトマップが読み込まれ、リストされたすべてのページが今後のクロール対象となるでしょう。
    あとは、今後サイトを更新するたびに 7. へアクセスして、その都度サイトマップを最新のモノに更新しておいてください。

RORサイトマップ作成CGI のプログラムソース

#!/usr/bin/perl

### make: http://YOURDOMAIN/setSitemap.cgi?(password)
### view: http://YOURDOMAIN/ror.xml
##################################################

### password
my $pwd = "abcd1234";   # set your original password.

### ROR file name
my $ror = "ror.xml";

### noindex files or directory, from root path.
my @no = (
  "/secret.html",
  "/member/",
);

### updatePeriod:
### always,hourly,daily,weekly,monthly,yearly,never
my $up = "monthly";

##################################################

use Jcode;

&viewXML if ($ENV{'QUERY_STRING'} ne $pwd);

my $no = ",." . join(",.",@no) . ",";

my(@html,$xml);
&getHTML("./");
&setXML;

print qq|Content-type: text/xml; charset="UTF-8"\n\n$xml|;
exit;

sub viewXML {
  print "Location: http://$ENV{'HTTP_HOST'}/$ror\n\n";
  exit;
}

sub getHTML {
  my $dir = $_[0];
  my @file = &getDir($dir);
  foreach my $file (@file) {
    my $path = "$dir$file";
    next if ($no =~ /,\Q$path\E\/?,/);
    &getHTML("$path/") if (-d $path);
    next if ($file !~ /\.html?$/i);
    my $last = (stat($path))[9];
    $path =~ s/^\.+|(\/)index\.html?$/$1/ig;
    push(@html,"$last\t$path\t$up\t\n");
  }
}

sub setXML {
  @html = reverse sort @html;
  foreach (@html) {
    my($last,$path,$up) = split(/\t/,$_);
    my($year,$mon,$day,$hour,$min,$sec) = &getTime($last);
    $last = "$year-$mon-$day" . "T$hour:$min:$sec+09:00";
    $xml .= <<"EOF";
		<item>
			<link>http://$ENV{'HTTP_HOST'}$path</link>
			<ror:updated>$last</ror:updated>
			<ror:updatePeriod>$up</ror:updatePeriod>
			<ror:sortOrder>0</ror:sortOrder>
			<ror:resourceOf>sitemap</ror:resourceOf>
		</item>
EOF
  }
  $xml = <<"EOF";
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:ror="http://rorweb.com/0.1/">
	<channel>
		<title>ROR Sitemap for $ENV{'HTTP_HOST'}</title>
		<link>http://$ENV{'HTTP_HOST'}/</link>
$xml	</channel>
</rss>
EOF
  &setFile("./$ror",Jcode->new($xml)->utf8);
}

sub getDir {
  my $dir = $_[0];
  opendir(DIR,$dir) || return ();
  @_ = sort readdir DIR;
  closedir(DIR);
  splice(@_,0,2);
  return @_;
}

sub setFile {
  my($file,@data) = @_;
  open(OUT,"> $file");
  seek(OUT,0,0);
  print OUT @data;
  truncate(OUT, tell(OUT));
  close(OUT);
  chmod(0666,$file);
}

sub getTime {
  my $time = $_[0];
  $ENV{'TZ'} = "JST-9";
  my($sec,$min,$hour,$day,$mon,$year,$week) = localtime($time);
  $year += 1900;
  $mon = sprintf("%02d",$mon+1);
  $day = sprintf("%02d",$day);
  $hour = sprintf("%02d",$hour);
  $min = sprintf("%02d",$min);
  $sec = sprintf("%02d",$sec);
  $week = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat')[$week];
  return ($year,$mon,$day,$hour,$min,$sec,$week);
}

うまくコピペできない場合は、こちらを開いてご利用ください。

注意事項など

  • 静的HTMLではないサイト構造の方(ブログ等)は利用できません。あくまでHTMLファイルが対象です。
  • サイトマップの生成にはサーバーにある程度の負荷が掛かるので、必ずパスワードを設定して自分だけがアクセスできるようにしてください。また、自分以外の方が触るパソコンでは、サイトマップ更新用URLはブックマークしない方が無難です。
    ちなみにパスワード無し(あるいは間違えると)でアクセスすると、ror.xml にジャンプする仕組みになっています。
  • サイト内のすべてのページ(拡張子が .htm.html のもので大文字小文字問わず)が全て対象になるので、都合の悪いページは予め @no に設定しておいてください。
  • サイトマップの生成に要する時間はページの量に応じて長くなります。
  • サイトマップはページの更新日時順に降順でソートされます。
  • CGI ファイルを作成する際の文字コードは意識する必要はありません (Shift_JIS でOK)
  • updatePeriod 要素は全ページ共通の設定となります。weekly か monthly あたりで良いと思いますけど。
  • sortOrder 要素は全ページ 0 を割り当てています。これは0以外を入れたらどうなるのかよく判りません(汗)
  • プログラムはソースを簡潔にするため、必要最小限の構成となっています。不便な点や改良点があれば自由にいじってお使いください。
  • なるべくサーバーに依存しないように組んでありますが、最低限 Jcode が使えるサーバーが必要です。
  • 尚、全てのサーバーでの動作を保証するモノではありませんので、解説通りに設置して動かない場合は諦めてください(汗)
    但し、海水関連ショップの方ならご相談に載ります。
    info@1023world.net まで。

その他、何かあればコメント欄にどうぞ。

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