headのタイトルにページ番号とカテゴリー名を追加する

2014年1月22日にA8.netのファンブログはSeesaa Blogのシステムをベースにして生まれ変わりました。よってファンブログ関連の記事は今では当てはまらない可能性があります。新しいファンブログのカスタマイズは別ブログ「ファンブログ テック」で詳しく書いていますので、そちらをご覧ください。

先日の記事「ブログ移転で rel=”canonical” は有効か?」で、このブログではヘッダのタイトルに「ページ番号」や「カテゴリー名」を追加していると書きました。よく考えたら、その方法を知りたい方がいるかもしれないと思いますので、この記事にまとめます。

先日の記事に書いた通り、このスクリプトを適用したら、ほんの数日でGoogleの検索結果に反映されるようになりました。この記事は、このブログを書いているファンブログを例にしていますが、他のブログでも充分応用が効く内容だと思います。

現在、このブログのスキンは下の構成になっています。(2013年3月16日時点)

このスキンは独自仕様なので、ファンブログの物とは違います

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="Shift_JIS" />
<meta name="description" content="{$BlogDescription$}" />
<meta name="keywords" content="HTML,css,JavaScript,スタイルシート" />
<meta name="author" content="Tom" />
<meta name="copyright" content="Copyright {$BlogName$}" />
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
<link rel="start" href="{$BlogUrl$}" title="{$BlogName$}" />
<link rel="stylesheet" type="text/css" href="{$BlogUrl$}style.css" charset="Shift_JIS" media="all" />
<link rel="alternate" type="application/rss+xml" title="RSS" href="{$BlogUrl$}index1_0.rdf" />
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
<title>{$BlogEntryTitle$} | {$BlogName$}</title>
<!--[if lte IE 8]>
<link rel="stylesheet" type="text/css" href="" media="all" />
<script type="text/javascript" src=""></script>
<![endif]-->
<script type="text/javascript" src=""></script>
</head>
<body>
(省略)
</body>
</html>

この中のtitleタグの中身{$BlogEntryTitle$} | {$BlogName$}です。

{$BlogEntryTitle$}は、記事のタイトルに、
{$BlogName$}ブログのタイトルに自動で変換されて表示されます。

ファンブログのスキンの初期状態

ただし、ファンブログのスキンの初期設定は
{$BlogName$} だけが書き込まれていて、{$BlogEntryTitle$}はありません。このままだと「ブログ名」だけが表示され、「記事のタイトル」は表示されません。これは検索結果にも同じように反映されます。

たとえば次のキーワードで検索してみます。

キーワード

その結果、今現在このブログの記事が1位に表示されています(2013年3月17日時点)。下のどちらかが実際の表示結果、もう片方は画像を加工した物ですが、この2つを比べてみてクリックされやすいと思うタイトルはどちらでしょう?

実際の検索結果表示画面
ブログタイトルのみの場合

左が実際の表示。右がファンブログのスキンのままだと表示される状態を再現したものです。検索した人の入力した2つのキーワード。これがタイトルに含まれる方がクリックされやすいと思います。ブログ名だけで、キーワードのない右は、せっかく1位にあってもそのままスルーされそうな気はしませんか?

ファンブログのスキンに「記事のタイトル」を追加する方法

ボタン

まず、ファンブログの管理画面左の「スタイルの編集」をクリック。
画面が変わったら「フリースキンの編集」と言う項目から「1記事」を選択。
「フリースキンの編集」は画面左にあり、次の一覧になっています。

  • スタイルシート
  • メイン
  • ポータル
  • アーカイブ
  • 1記事
  • プロフィール

この中の「1記事」をクリックします。「1記事」の画面になったら画面右のタイトルが「1記事テンプレートの編集」になります。この右側の枠の中を編集します。

1記事テンプレートの編集で一番上から見ていくと、途中で下のように書かれた行があります。

<title>{$BlogName$}</title>

これを、次のように変更します。

<title>{$BlogEntryTitle$} | {$BlogName$}</title>

これで保存すれば、「記事のタイトル | ブログの名前」という風に表示されます。

タイトルに「ページ番号」を追加するスクリプト

ようやくここからが、今回の本題です。今回やるのは「ページ番号」の追加と、「カテゴリー名」の追加です。「ページ番号」を追加するスクリプトは「メイン」スキンに書き加えます。まず、上の要領で「フリースキンの編集」から「メイン」を選択して開きます。ちなみに、このブログのメインスキンはtitleタグを下のように書き直してあります。

<title>{$BlogName$} | ブログ インデックス</title>

メインスキンのheadに、次のコードを書き加えます。ソースの中の「ayzfqir5」はこのブログの場合です。各自、自分のブログのURLに変更してください。

自分のブログのURLから、赤字の部分を抜き出して、下のコードの「ayzfqir5」を書きかえます。
http://fanblogs.jp/ayzfqir5/
<script type="text/javascript"><!--
url=window.location.href.match(/^https?:\/\/fanblogs\.jp\/ayzfqir5(\/(([0-9]+)\/?)?)?$/);

if(url){
	var number=url[3]?url[3]:1;

	if(document.title){
		document.title+=' page'+number;
	}else{
		document.querySelector('title').innerHTML+=' page'+number;
	}
}
//-->
</script>

“headのタイトルにページ番号とカテゴリー名を追加する”の続きを読む

HTMLを指定文字数で切り詰めて表示する

前の記事「記事の先頭にアイコンを挿入する方法」の最後で、記事の文字数を切り詰める方法を簡単に書きました。この記事では、その部分だけ詳しく説明します。

普通に、文章を指定文字数で切り詰めるなら次のようにすれば実現できます。

function hoge(str,len){
	return str.length>len?str.slice(0,len)+'…':str;
}

第1引数strには文字列、第2引数lenに切り詰めたい長さを指定します。lenより長ければ、len文字で切り詰め、その後に「…」を加えます。ただしマルチバイト文字列の場合、どの文章も同じ文字数で切られない可能性があります。その場合は文字数ではなく、バイト数で切られていると思います。

文章と、文字数を入れて実行ボタンを押してください。

文章:

文字数:

HTMLの文章切り詰め

HTMLの場合はタグを消去しないと表示が崩れる可能性があります。文字列を切り詰めた結果、終了タグがなくなってしまったり、タグの途中で切れてしまったりするからです。

<p>こんにちは。</p>
  -> <p>こんに… &nbsp終了タグがなくなってしまった!

<p>こんにちは。<span style=”color:#0000ff;”>今日はいい天気ですね!</span></p>
  -> <p>こんにちは。<span style=”col…  タグの途中で切れてしまった!

そこで、文字を切り詰める前にHTMLタグを正規表現で消去します。

str.replace(/\<script.*?\>.*?(\<\/script\>)|\<style.*?\>.*?(\<\/style\>)|\<.+?\>/g,'');

<と>のエスケープは正規表現自体には必要ないかもしれませんが、JavaScriptをHTML内に書いた場合にトラブらないようにエスケープしておきました。たとえば、よくdocument.write(‘<scr’+’ipt’というような記述をしますが、これはdocument.write(‘\<script’でも大丈夫です。

HTMLに対応した修正版

function hoge(str,len){
	str=str.replace(/\<script.*?\>.*?(\<\/script\>)|\<style.*?\>.*?(\<\/style\>)|\<.+?\>/g,'');
	return str.length>len?str.slice(0,len)+'…':str;
}

記事の先頭にアイコンを挿入する方法

2014年1月22日にA8.netのファンブログはSeesaa Blogのシステムをベースにして生まれ変わりました。よってファンブログ関連の記事は今では当てはまらない可能性があります。新しいファンブログのカスタマイズは別ブログ「ファンブログ テック」で詳しく書いていますので、そちらをご覧ください。

このブログでは、下のキャプチャのように記事のカテゴリーごとに違うアイコンを表示しています。その実現方法です。このブログはファンブログというサービスに書いているので、ファンブログを例にとりますが、他のブログでも応用できます。

記事ごとにアイコンを配置したindexページ

アイコンを入手する

先に結論を書くと、このブログで使っているアイコンはFree Icon set – Icon Edenからダウンロードした無料アイコンセット「iCandies」です。

インターネットを探すと、無料で使えるアイコンは沢山見つかります。今回、自分はGIGAZINEの記事「フリーでハイクオリティな「IconEden」による商用利用可能な無料アイコンセットいろいろ」で、このアイコンを見つけました。GIGAZINEを見ると関連記事で、まだまだたくさん色々なアイコンを紹介してくれています。

他にもPhotoshop VIPの「RESOURCE/Icon まとめ記事/アイコンまとめ」など、アイコンを画像付で紹介してくれているサイトは沢山見つかるので、そういうところで探すと、お気に入りのものが見つかると思います。

アイコン画像をサーバーにアップロードする

自分の使うアイコンを入手できたら、それをサーバーにアップロードします。ファンブログの場合、記事の中にアップロードするしかないのですが、ひとつの記事にアップロードできる画像は5個までです。何十個もアップロードする場合は、とても大変な作業になります。

なので、私は自分の契約している別サーバーにFTPでアップロードしました。私は使ったことがないので、わかりませんが無料で使えるファイルサーバーはいくつかあるようです。

カテゴリーとアイコンを紐付ける

今回は、カテゴリーごとに違うアイコンを表示させるため、まずその設定をします。つまり、どのカテゴリーでどのアイコンを表示させるかの設定です。具体的にはJavaScriptのオブジェクトを使って連想配列で処理します。このブログの場合は次のようになります。

var categories={
	"category_5":"37_64x64.png",
	"category_28":"43_64x64.png",
	"category_6":"41_64x64.png",
	"category_7":"51_64x64.png",
	"category_27":"50_64x64.png",
	"category_30":"45_64x64.png",
	"category_34":"40_64x64.png",
	"category_35":"36_64x64.png",
	"category_33":"44_64x64.png",
	"category_20":"42_64x64.png",
	"category_13":"2_64x64.png",
	"category_32":"55_64x64.png",
	"category_24":"34_64x64.png",
	"category_18":"25_64x64.png",
	"category_15":"26_64x64.png",
	"category_26":"9_64x64.png",
	"category_16":"56_64x64.png",
	"category_19":"48_64x64.png",
	"category_25":"54_64x64.png",
	"category_29":"5_64x64.png",
	"category_31":"19_64x64.png"
};

左側の “category_xx” がキーで、右の “xx_64x64.png” が、呼び出すファイル名です。

キーは、カテゴリーごとのURL「http://fanblogs.jp/ユーザー名/category_xx/」の最後「category_xx」を記入します。たとえば、カテゴリー「超初心者HTML入門」のURLは「http://fanblogs.jp/ayzfqir5/category_28/」なので「category_28」になります。

私はアイコンを、すべて同じディレクトリにアップロードしたので右側はファイル名だけですが、ファンブログにアップロードした場合は、ファイル5個ごとにディレクトリ構成が違うので、フルパスで記入する必要があります。

挿入スクリプトの本体

今、このブログのスキンは完全自作でファンブログのものとは違う構成なのですが、ファンブログのスキンなら下のようになっていると思います。

<BlogEntries>
<div class="entry">
<div class="entryDate">{$BlogEntryDateformat="%Y年%m月%d日" language="jp"$}</div>
<h2 class="entryTitle"><a href="{$BlogEntryPermalinkUrl$}">{$BlogEntryTitle$}</a></h2>

<div class="entryBody clr">{$BlogEntryBody$}</div>
<BlogEntryIfExtended>
	<div class="entryContinue"><a href="{$BlogEntryPermalinkUrl$}#BlogEntryExtend">続きを読む...</a></div>
</BlogEntryIfExtended>
<div class="entryInfo">
	投稿者:<a href="{$BlogEntryWriterUrl$}" target="_blank">{$BlogEntryWriterName$}</a>|{$BlogEntryDate format="%H:%M"$}
	<BlogEntryIfCategory>|<a href="{$BlogEntryCategoryLink$}">{$BlogEntryCategory$}</a></BlogEntryIfCategory>
	<BlogEntryIfAllowComments>|<a href="{$BlogEntryPermalinkUrl$}#comments">コメント({$BlogEntryCommentCount$})</a></BlogEntryIfAllowComments>
	<BlogEntryIfAllowPings>|<a href="{$BlogEntryPermalinkUrl$}#Trackback">トラックバック({$BlogEntryTrackbackCount$})</a>	 </BlogEntryIfAllowPings>
 </div>
</div><!-- entry -->
</BlogEntries>

この最後の「</BlogEntries>」の直後に次のコードを追加します。それがスクリプトの本体です。書き換えるスキンは「メイン」「アーカイブ」「1記事」の3つです。

<script type="text/javascript"><!--
(function(){
	var categories={
		"category_5":"37_64x64.png",
		"category_28":"43_64x64.png",
		"category_6":"41_64x64.png",
		"category_7":"51_64x64.png",
		"category_27":"50_64x64.png",
		"category_30":"45_64x64.png",
		"category_34":"40_64x64.png",
		"category_35":"36_64x64.png",
		"category_33":"44_64x64.png",
		"category_20":"42_64x64.png",
		"category_13":"2_64x64.png",
		"category_32":"55_64x64.png",
		"category_24":"34_64x64.png",
		"category_18":"25_64x64.png",
		"category_15":"26_64x64.png",
		"category_26":"9_64x64.png",
		"category_16":"56_64x64.png",
		"category_19":"48_64x64.png",
		"category_25":"54_64x64.png",
		"category_29":"5_64x64.png",
		"category_31":"19_64x64.png"
	};

	var icon_url='ここにアイコンのディレクトリへのPATHを記入';
	var entries=document.querySelectorAll('div#entries div.entry');

	for(var i=0;i<entries.length;i++){
		var a=entries[i].querySelectorAll('div.entryInfo a');
		var icon=categories[a[1].href.replace(/^.+?\/(category_[0-9]+).*$/,'$1')];
		var img='string'==typeof icon?'<img src="'+icon_url+icon+'" alt="" class="category_icon" />':'';
		var body=entries[i].querySelector('div.entryBody');

		body.innerHTML=img+body.innerHTML;
	}
})();//-->
</script>

var categories の中身は自分のブログに合わせてください。

「ここにアイコンのディレクトリへのPATHを記入」と書いてある場所には、アイコンセットをアップロードしたサーバーのディレクトリへのPATHを記入します。ファンブログにアップロードしている場合は、アイコンのファイル名にPATHも含まれているので、空文字列にします。

a[1]の部分は、スキンがデフォルトの場合です。スキンを改造している場合は、添字の1を適切なものに訂正してください。

表示されるアイコンには、すべて「category_icon」というクラス名がつけられています。間隔などの調整はスタイルシートで行なってください。

本文を指定文字数で切り詰める

ここまでで、スキンは完成しています。ただ、うちのブログの様に、本文を切り詰めて、最後に「この記事を読む」と付ける場合は、上のスクリプトを一部変更します。

この行を削除して、下のソースに差し替えます。

body.innerHTML=img+body.innerHTML;

差し替えるソース

var str=body.innerHTML.replace(/\<script.*?\>.*?(\<\/script\>)|\<style.*?>.*?(\<\/style\>)|\<.+?\>/g,'');
body.innerHTML=img+(str.length>150?str.slice(0,149)+'…':str)+'<p class="read_more"><a href="{$BlogEntryPermalinkUrl$}">この記事を読む</a></p>';

上は、文字数を150字にする場合です。150字より長ければ、149文字で切って、150文字目に「…」を入れます。文字数は自分の都合に合うように調整してください。

もちろん、この最後の処理は「1記事」には適用しません。1記事の場合、アイコンを先頭に挿入することで、表示のバランスが狂うことがあります。それはレイアウト的な問題ですので、各自で修正してください。このブログでは「1記事」のみ、アイコンを記事本文でない場所に配置して解決しました。

appendChildやinsertBeforeでノードを移動させる

DOMのappendChildは子ノードの追加、insertBeforeはノードの挿入のためのメソッドですが、これを使ってノードの場所を変更できます。

appendChildで移動する

たとえば、次のようなHTMLがあったとします。

<div id="outer">
<p id="morning">おはよう</p>
<p>こんにちは</p>
<p id="night">こんばんは</p>
</div>

ここで、下のコードを実行すると「おはよう」が「こんばんは」の後に移動します。

document.querySelector('#outer').appendChild(document.querySelector('#morning'));

実行例

おはよう

こんにちは

こんばんは

実行すると「おはよう」が「idがouterであるdiv」に追加されます。その結果、元の位置から最後にずれます。「おはよう」の位置がずれたので、「こんにちは」が先頭になります。

insertBeforeで移動する

上の例のHTMLに今度は次のスクリプトを適用してみます。すると最後の「こんばんは」が先頭にある「おはよう」の前に移動します。

var outer=document.querySelector('#outer');
var night=outer.querySelector('#night');
var morning=outer.querySelector('#morning');

outer.insertBefore(night,morning);

実行例

おはよう

こんにちは

こんばんは

これを知っていると、すごく便利だと思います。このブログのコメント欄の改造はこのメソッドを多用して実現しました。

ファンブログでの404ページ表示法

2014年1月22日にA8.netのファンブログはSeesaa Blogのシステムをベースにして生まれ変わりました。よってファンブログ関連の記事は今では当てはまらない可能性があります。新しいファンブログのカスタマイズは別ブログ「ファンブログ テック」で詳しく書いていますので、そちらをご覧ください。

この記事はファンブログ専用情報です。2013年3月10日、記事を大幅に書き換えました。

今ご覧いただいている、このブログで実際に使っている方法は下のやり方とは違います。このブログではappendChildという方法で「404の時だけ文章を追加」しています。下の説明は、元々書き込まれている文章の「表示・非表示の切替え」をしています。SEO的には、不要な文章がない方が望ましいですが、説明が難しくなるので簡単な方法を書いています。

今回は、ファンブログ純正のスキンを使っているブログ「八百屋勘太のおもしろ体験談」のキャプチャを使わせていただきました。
ページがある時

ファンブログのページはarchive/1から始まるので、archive/0というページは存在しません。

 <- このページは存在しない。

ファンブログだと、何も設定しない場合、存在しないページは次のように表示されます。

記事がないページ

記事がないので、その下の「HOMEへ行くナビ」や、「トラックバック」「コメント」が上に上がって表示されています。これだと、記事がないのがわかりづらいので、「記事が見つからない」というメッセージを表示するように変更します。

設定方法

まず、サイドバーに1項目追加して、次のソースをそのまま貼り付けてください。サイドバーに項目を追加する方法がわからなければ、別記事「モジュールの設置方法」を参照してください。

<div id="message404" style="display:none;">
<h2 class="entryTitle">ご指定のページが見つかりません</h2>
<div class="entryBody">
ここに好きなメッセージを書いてください
</div>
</div>

<script type="text/javascript"><!--
if(-1==window.location.href.indexOf('profile')&&
	-1==window.location.href.indexOf('portal')&&
	!document.querySelector('.entryTitle a').innerHTML.length){

	var message=document.querySelector('#message404');
	var entries=document.querySelector('#entries');

	entries.parentNode.insertBefore(message,entries);
	entries.parentNode.removeChild(entries);

	message.id='entries';
	message.style.display='block';
}//-->
</script>

このソースの赤字と青字を好きな文章に書き換えてください。赤字がタイトル、青字が説明文です。今回はタイトルは変更せず、説明文のところで画像を使って説明しました。

完成したのは下のソースです。青字部分の場所だけ書き換えています。

<div id="message404" style="display:none;">
<h2 class="entryTitle">ご指定のページが見つかりません</h2>
<div class="entryBody">
<p style="text-align:center;">
<img alt="ページが存在しません" src="画像のURL" width="400" height="126" />
</p>
</div>
</div>

<script type="text/javascript"><!--
if(-1==window.location.href.indexOf('profile')&&
	-1==window.location.href.indexOf('portal')&&
	!document.querySelector('.entryTitle a').innerHTML.length){

	var message=document.querySelector('#message404');
	var entries=document.querySelector('#entries');

	entries.parentNode.insertBefore(message,entries);
	entries.parentNode.removeChild(entries);

	message.id='entries';
	message.style.display='block';
}
//-->
</script>

これを保存すれば、記事がないURLを指定された時に下のように表示されます。

ページがない時の表示

スクリプトの説明

初期状態では、追加したメッセージは非表示になっています。

<div id="message404" style="display:none;">
<h2 class="entryTitle">ご指定のページが見つかりません</h2>
<div class="entryBody">
<p style="text-align:center;">
<img alt="ページが存在しません" src="画像のURL" width="400" height="126" />
</p>
</div>
</div>

<script type="text/javascript"><!--

//開いているページがプロフィールでもポータルでもない場合、
//タイトルをクラス名entryTitleで探し、a タグの中身があるかを調べる
//なければ記事はない。その場合は括弧の中を実行
if(-1==window.location.href.indexOf('profile')&&
	-1==window.location.href.indexOf('portal')&&
	!document.querySelector('.entryTitle a').innerHTML.length){

	//非表示になっているメッセージを探す
	var message=document.querySelector('#message404');

	//記事の表示場所を探す
	var entries=document.querySelector('#entries');


	//本来記事が表示される場所の直前にメッセージを移動
	entries.parentNode.insertBefore(message,entries);

	//記事表示領域を削除(トラックバックやコメント欄も削除される)
	entries.parentNode.removeChild(entries);

	//メッセージのidをentriesに変更(スタイルシートを適用するため)
	message.id='entries';

	//メッセージを表示
	message.style.display='block';

}
//-->
</script>

このスクリプトを使うのは1記事ページだけなので、サイドバーではなく、1記事のスキンに書き込んでもかまいません。その場合は、スキンの最後に書いてください。entryTitleやentriesの前に書くと、それを見つけられないので正しく動きません。

今回のinsertBeforeの使い方は、少し特殊ですが、それに関しては別記事「appendChildやinsertBeforeでノードを移動させる」で詳しく説明しています。

余談ですが本来ペーがないときにはステータス404を返すのが決まりです。しかしファンブログは404ではなく200を返してきます。200というのは問題なしOKのコードです。確かに、記事がない空のページですが、ページそのものは表示されています。しかし、この場合200でいいのでしょうか?私は別のスクリプトで404が返って来れば、それ用の処理、200なら通常の処理という内容のものを書いたのですが、どうしてもうまく行かないので調べた結果404が返っていないことに気がつきました。

Google Analyticsのイベント トラッキングはaddEventListenerやattachEventでは計測されない

先日書いた記事「Google Analyticsでクリック数を計測するスクリプト」で、どこへのリンクがクリックされたのかのログをonclickイベントで拾うコードを書きました。ちゃんと動いていたんですが、Tweetボタンやmixiボタンが動作しなくなっていたので修正しました。

動かなくなった理由はonclickを上書きしていたからです。普通のaタグはonclickがnullなので、書いたコードで問題なかったんですが、Tweetボタンやmixiボタンはhref=”javascript:void(0);”でリンクを無効にして、onclickで作動していたからです。そのスクリプトを消してしまっていたから、ボタンを押しても動作しませんでした。正確に言えばボタンを押すと、違う動きになった。つまりGoogle Analytics用のデータ作成に使われるようになったということです。

そこで、addEventListener 或いは attachEventを使って、アクションを「上書き」ではなく「追加」したんですが、それではログが記録されていないことに気がつきました。

Google Analyticsのイベント トラッキングはonclickでなくては計測されない

確認するために丸一日、下のコードで置いておきました。その結果計測されたイベントはゼロです。ただ、そのコードはすでに消してしまっていたので、下のコードは記憶を元に書いています。元のソースでは_gaq.pushのところをalertに変えてテストしましたが、意図どおりに動いていました。

<script type="text/javascript">
<!--
function add_ga(elem){
	var type=(-1==elem.href.indexOf('/ayzfqir5/')&&elem.href.match(/^[^\?]*\/\//))?'Out':'In';
	_gaq.push(['_trackEvent',type+'bound Link','click',elem.href]);
};

var set_event=(function(){
	if(document.addEventListener){
		return function(elem,func){
			elem.addEventListener('click',func,false);
		};
	}

	if(document.attachEvent){
		return function(elem,func){
			elem.attachEvent('onclick',func);
		};
	}

	return null;
})();

if('function'==typeof set_event){
	var a=document.querySelectorAll('a');

	for(var i=0;i<a.length;i++){
		set_event(a[i],(function(elem){
			return function(){add_ga(elem);};
		})(a[i]));
	}
}

delete set_event;
// -->
</script>

対処として、上のコードを次のものに変更したところ、addEventListenerのfirefoxでもattachEventのIEでも計測されるようになりました。滅多に押されないところを自分でクリックして記録されているかどうかを見たのですが、偶然誰かが他のブラウザでクリックしていないのならaddEventListenerでもattachEventでも記録されているのだと思います。

<script type="text/javascript">
<!--
function add_ga(elem){
	var type=(-1==elem.href.indexOf('/ayzfqir5/')&&elem.href.match(/^[^\?]*\/\//))?'Out':'In';
	_gaq.push(['_trackEvent',type+'bound Link','click',elem.href]);
};

var set_event=(function(){
	if(document.addEventListener){
		return function(elem){
			if(null!==elem.onclick){
				elem.addEventListener('click',elem.onclick,false);
			}

			elem.onclick=function(){add_ga(elem);};
		};
	}

	if(document.attachEvent){
		return function(elem){
			if(null!==elem.onclick){
				elem.attachEvent('onclick',elem.onclick);
			}

			elem.onclick=function(){add_ga(elem);};
		};
	}

	return null;
})();

if('function'==typeof set_event){
	var a=document.querySelectorAll('a');

	for(var i=0;i<a.length;i++){
		set_event(a[i]);
	}
}

delete set_event;
// -->
</script>

元々のonclickの内容をelem.addEventListener(‘click’,elem.onclick,false);elem.attachEvent(‘onclick’,elem.onclick);という形でコピーしてから、elem.onclick=function(){add_ga(elem);};で新しいonclickに_gaq.pushする関数を上書きするという内容です。正直、これで動くか心配だったんですが、出来るんですね。

結論として、addEventListenerattachEventではダメで、onclickでなくてはイベントは計測されないようです。

ただ、検索してみるとaddEventListenerでうまくいっていると言う記事がたくさんあったのですが、他の方は動いてるんでしょうか?

ソースのバグの作り方 -if文のカッコ-

先日の記事「最新記事だけ日付を消す新スクリプト -お知らせ枠の設置-」のソースを実際にマスクドライダー17号さんが設置したところ、次の事情でうまくいきませんでした。(記事のソースは、うまくいくように修正済みです)

最初のソースコードは、次のものでした。

<script type=”text/javascript”>date=document.querySelector(‘div.entryDate’);if(date){date.style.display=’none’;}</script>

このコードは、間違っていません。
実際に、マスクドライダー17号さんのブログのソースをメモ帳にコピペして、上の通り作業してブラウザで開くと、ちゃんと消えたのに、本当のブログにアップロードすると、消えませんでした。

なぜかというと、{} の2つのカッコが、おかしな形で変換されていたのです。

ファンブログのエディタを通すと、時々なぜかわからない形でソースコードが変わってしまうことがあります。それで、ソースを書き換えて対処しました。

変更点は、次の通りです。(記事のソースは変更済みです)

{} を削除する。
おかしく変換されてしまう2つの文字を削除することで、おかしな部分がなくなったので、正しく動きました。

なぜ、{} を削除できたのか?

逆に言うと、{} は不要なものだったのか?という疑問があるかもしれません。この場合は、なくてもいいものでした。だから、消すだけで対処できました。これは次の条件にあっていたためです。つまり幸運だったのです。

if文の次の処理は条件にあえば実行される。実行する処理が複数ある時はその部分を、{ と } で囲む。

JavaScriptに限らず、C系統のif文の書き方は次の構文です。

if(条件){ 実行内容 }

今回の場合は、if(date){date.style.display=’none’;} が、その部分です。

これは、if(‘object’==typeof date){date.style.display=’none’;} と書きかえても同じです。

この文を日本語に直すと、dateがオブジェクトなら、dateのスタイルシートdisplayをnoneにしなさいとなります。

これは実行内容がひとつです。「dateのスタイルシートdisplayをnoneにしなさい」のひとつだけを実行するので、カッコがなくても正しく動きます。しかし、もし実行内容が複数だと意図どおりには動きません。

たとえば、「dateがオブジェクトなら、dateのスタイルシートdisplayをblockに、色を赤にしなさい」という内容であったらどうでしょう。ソースコードはこうなります。

if(date){
	date.style.display='block';
	date.style.color='#ff0000';
}

改行と行のはじめの空白(インデント)は見やすくするためにつけていますが、コンピュータの内部処理では無視されます。if(date){date.style.display=’block’;date.style.color=’#ff0000′;}と書いたのと同じようにコンピュータは処理するのですが、人間が見やすくするためにつけています。

JavaScriptでは、{から}までのブロックは、ひとつの命令として扱います。

これで、dateがオブジェクトなら、{から}までのブロックを実行しなさいとなります。ブロックの内容は「displayをblockに」と「色を赤に」の2つです。

ここでカッコをとってみます。

if(date)
	date.style.display='block';
	date.style.color='#ff0000';

これはどのように扱われるか?次のようになります。

処理1. dateがオブジェクトなら、dateのスタイルシートdisplayをblockにしなさい。
処理2. dateのスタイルシートのcolor(色)を#ff0000(赤)にしなさい。

処理1はdateがオブジェクトならという条件判断をされますが、条件判断はひとつ目の処理で終了します。よって、処理2はdateがオブジェクトであろうとなかろうと実行します。

この例だとdateというオブジェクトがあれば、結果は同じです。カッコがあってもなくても、「dateがオブジェクトであった場合」の結果は、「スタイルシートのdisplayをblock」にして「スタイルシートのcolorを#ff0000」にします。

dateがオブジェクトである限り、カッコがあってもなくても、思ったとおりの結果になるのです。だから例えば、この命令をマスクドライダー17号さんのブログに適用すれば正しく、意図どおりに動きます。なぜなら、マスクドライダー17号さんのブログでは日付が消えているのだから、dateオブジェクトがあるのです。

しかし、dateがオブジェクトでなかったら、どうなるでしょう?適用されるはずのdate.style.colorが存在しないかもしれません。その場合はエラーになります。だから他の人のブログで使えばエラーが出るかもしれません。

つまり、これはカッコがないことによって気がつかないバグになっています。

カッコのあるなしで、意図とは違う結果を生み出すことがあります。バグというのはそうやって生み出されるのです。

Google Analyticsでクリック数を計測するスクリプト

Google Analyticsでクリック数を計測できるんですね。知りませんでした。

今回の記事の元ネタは「ファンブログハック」のおーとえす氏の記事「googleアナリティクスで簡単にクリックカウント出来るコード」です。おーとえす氏の場合は、JQueryを使っているんですが、使わないコードを書いてみました。

このスクリプトだと、Tweetボタンを押しても反応しなくなります。下にある修正版を使ってください。

<script type="text/javascript">
<!--
(function(){
  var a=document.querySelectorAll('a');

  for(var i=0;i<a.length;i++){
    a[i].onclick=(function(elem){
      return function(){
        var type=(-1==elem.href.indexOf('/ayzfqir5/')&&'javascript:void(0)'!=elem.href)?'Out':'In';
        _gaq.push(['_trackEvent',type+'bound Links','click',elem.href,0]);
      };
    })(a[i]);
  }
})();
// -->
</script>

青い字で書いてある0の数字をスキンごとに変えると、どのスキンでクリックされたかがわかります。数字は0以上であれば何でも構いませんが、マイナスの値だとダメみたいです。
これは、合計値が表示されるみたいで、この使い方では意味ありませんでした。

上のコードでTweetボタンが動かなくなった技術的説明

Tweetボタンは、aタグを使って、href=”javascript:void(0)”onclick=”ここでスクリプト処理”というソースですが、上のコードは、onclick=function()という形で、onclickを上書きしてしまいました。

修正コードでは、上書きではなく追加という形に変更しました。詳しくは別記事「aタグをボタン代わりに使う」と「addEventListenerとattachEvent」を、ご覧いただくのがいいと思います。

修正版はこの下のものです。

<script type="text/javascript">
<!--
function add_ga(elem){
	var type=(!elem.href.match(/^[^\?]+ayzfqir5/)&&elem.href.match(/^[^\?]*\/\//))?'Out':'In';
	_gaq.push(['_trackEvent',type+'bound Link','click',elem.href]);
};

(function(){
	var set_event=(function(){
		if(document.addEventListener){
			return function(elem){
				if(null!==elem.onclick){
					elem.addEventListener('click',elem.onclick,false);
				}

				elem.onclick=function(){add_ga(elem);};
			};
		}

		if(document.attachEvent){
			return function(elem){
				if(null!==elem.onclick){
					elem.attachEvent('onclick',elem.onclick);
				}

				elem.onclick=function(){add_ga(elem);};
			};
		}

		return null;
	})();

	if('function'==typeof set_event){
		var a=document.querySelectorAll('a');

		for(var i=0;i<a.length;i++){
			set_event(a[i]);
		}
	}
})();
// -->
</script>

ちょっと変なスクリプトに見えるかもしれませんが、単にイベントを追加しただけではクリック数は保存されませんでした。詳細な説明は別記事にしましたので、よければご覧ください。「Google Analyticsのイベント トラッキングはaddEventListenerやattachEventでは計測されない

このコードを、スキンの最後の方にある</body>の直前に追加すれば、すべてのクリックが計測出来るようになります。

ただし、赤い字で書いてあるayzfqir5を自分のURLに差し替えてから使ってください。

この記事を書いた移転前のブログはhttp://fanblogs.jp/ayzfqir5/というサブディレクトリ方式のURLでした。このayzfqir5の部分を自分のURLを見て、自分のブログだけに出現する物に書き換えてください。

URLの中にayzfqir5が含まれているかどうかを判断材料にして、内部リンクか外部リンクかのログを取っています。内部リンク(自分のブログへのリンク)ならInbound Link、外部リンク(自分のブログ以外へのリンク)ならOutbound Linkというイベント カテゴリにわけて記録されます。

Google Analyticsでの見方

「コンテンツ」タブの「イベント」でログを閲覧できます。

下は、このブログでどこへのリンクがクリックされたかを表示した物です。今つけたばかりなので数は少ないです。

この表のイベントの値というのが、上でスキンごとに変えた数字です。(合計値が表示されて意味がないので今は使っていません)

Google Analyticsの管理画面
クリックすると別画面で大きく見られます

下のInbound Linksが内部リンク、Outbound Linksが外部リンクです。

内部リンクと外部リンク
クリックすると別画面で大きく見られます

querySelectorとquerySelectorAll

スタイルシートのセレクタを使ってDOMにアクセス出来る2つのメソッドが超便利な件。

querySelectorquerySelectorAllの2つ。

どちらも同じで、document.querySelector(‘div.entry p’)のようにセレクタでアクセスできる。

この2つのメソッドの違いは、querySelectorの場合、指定条件にあった最初の1個だけを抽出する。
一方、querySelectorAllの時は、指定条件にあうすべての要素を抽出する。

たとえば、次のようなHTMLがあったとする。

<div class="entry">
<p class="hoge">こんにちは。<br />
今日はとっても天気がいいですね!</p>
<p>これはペンです。</p>
<p class="hoge">さようなら。</p>
</div>

これに対してpタグを見つけたい場合

document.querySelector(‘div.entry p’)だと、最初の
<p class=”hoge”>こんにちは。<br />
今日はとっても天気がいいですね!</p>

だけを抽出する。

一方、document.querySelectorAll(‘div.entry p’)なら<div class=”entry”>~</div>の中にある、
すべてのpタグをまとめて抽出してくれる。

そして、querySelectorAllは、配列のように添字でアクセスできる。

var p=document.querySelector('div.entry p');
p.style.color='#ff0000';

とすると、
こんにちは。
今日はとっても天気がいいですね!

というように、最初のpタグだけが赤くなる。

一方

var p=document.querySelectorAll('div.entry p');

for(var i=0;i<p.length;i++){
	p[i].style.color='#ff0000';
}

とすれば、すべてのpタグが赤くなる。

querySelectorAllには、lengthがあるが、querySelectorには、lengthがないので注意。

セレクタでアクセス出来るので、var p=document.querySelectorAll(‘div.entry p.hoge‘);と指定すれば、
hogeクラスのpタグだけが抽出される。

CSSのセレクタに関しては次のページが参考になります。
意外と知らない!?CSSセレクタ20個のおさらい|Webpark

本当にこれは便利です!
実際にこのメソッドを使ってソースを書いてみました。下がその記事です。

自動で正月だけメッセージ表示

自動で正月の三が日だけブログにお祝いメッセージを表示させる方法です。

1. まず、画像でも文章でもいいので正月に表示するメッセージを完成させてください。

表示場所はどこでも構いません。当然ですが、記事の中だとその記事が開かれないと実行されません。

2. 完成されたメッセージを次のようにHTMLで囲みます。

<div id="new_year_message">ここにメッセージを入れます</div>

次のステップで正月まで非表示になるので、その前にブラウザで表示内容を確認しておいてください。

3. 2で作ったHTMLの直後に下のソースを追加します。

<style type="text/css">
div#new_year_message{display:none;}
</style>

<script type="text/javascript">
<!--
var new_year1=(new Date(2013,0,1)).getTime();
var new_year2=(new Date(2013,0,4)).getTime();
var now=(new Date).getTime();

if(new_year1<=now&&now<new_year2){
  var div=document.getElementById('new_year_message');
  if(div){
    div.style.display='block';
  }
}
// -->
</script>

これで2013年の1月1日から1月3日の間だけ、メッセージが表示されます。期間が過ぎた後は表示されませんが、無駄なソースになるので忘れずに削除してください。

注:万が一表示されなかった場合もTomさんは責任を負わないものとします。それをご了承いただける場合のみご使用ください。

誤解があるといけないので、明記しますが、1月1日の午前0時0分0秒から1月4日になる直前までの間にページが読み込まれると、メッセージを表示します。それ以前に開かれていたページには何も起こりません。

既に開いてあるページには、元旦になると同時にメッセージが表示されたりはしませんし、開いているページに表示されているメッセージが1月4日になると自動で消えたりもしません。