広告

この広告は30日以上更新がないブログに表示されております。
新規記事の投稿を行うことで、非表示にすることが可能です。
posted by fanblog
「ここにブログの名前を入れます」は更新を終了しました。記事はすべて新ブログ「Big Bang」に移転済みです。記事のタイトルをクリックすると新ブログの該当記事に移動します。そちらでお楽しみください。

カテゴリー別アーカイブ  :  

同じページの特定のIDを別窓で開く

同じページの特定箇所へリンクする方法は、以前「ページの途中にリンクする」という記事に書きました。しかし、最近試してわかったのですが、同じページ内へのリンクを別窓で開こうとしてもうまくいきません。

今開いているページへリンクを張り、target属性を指定して開こうとしてもうまくいかない

同じページ内に id="link" というパートを作り、そこへリンクさせようと、以下の3パターンを試しましたが、全部「別窓で開く」のに失敗しました。

リンク先へは移動するのですが、別窓が開かず、同じウインドウの中で遷移します。

これは、W3Cの仕様なんでしょうか。簡単にググってみましたが、わかりませんでした。ちなみに実行環境はLinux Mint版 Firefox(16.0.2) と Google Chrome(26.0.1410.63) の2つです。

<a href="#link" target="_blank">go link</a>
<a href="#link" target="_new">go link</a>
<a href="#link" target="unique_name">go link</a>

3つ目の target="unique_name" は、「新しいウインドウに自分で好きな名前を付けて開く」という方法です。今回は「unique_name」という名前をつけていますが、名前は自由に決められます。

リンク先を表示するウィンドウの指定(target)」より引用

<a href="index.html" target="morepage">home</a>

target に自分の好みの自由な名前 を付けて、新しいウインドウを表示させ、そのウィンドウに複数のページのリンク先を切り替わり表示させます。 target の値に「morepage」 と付けて三つのページを同じウィンドウに表示させます。
例:
<a href="index.html" target="morepage">home</a>
<a href=cho3.html" target="morepage">タグ教室</a>
<a href="sitoku.html" target="morepage">知っとくコーナー</a>

上の3つのリンク先ページが morepage と名前の付いた 同じ新しいウィンドウの中で切り替わります。最初に選択されたページ(どのページでもいいのですが)には新しいウィンドウで表示されるので、戻るボタンは灰色ですが、次のページからは同じウィンドウに表示されるので戻るボタンは有効になります。

JavaScriptのwindow.openを使えば、同じページも別窓で開ける

当然、JavaScriptが有効でないと使えませんが、window.openで新しい窓を開けます。

window.openの説明は杜甫々さんの「ウィンドウ(Window)」というページが詳しいです。

具体的には下のソースで、新しいウインドウに同じページの id="link" の場所が表示されます。

<a href="javascript:void(0);" onclick="window.open('#link');">リンクへ</a>

実際に、次のリンクをクリックすると新しい窓で、下の赤い部分が開きます →「リンクへ

window.openに入れるURLは、リンク先のidだけで大丈夫です。
window.open('#link')

href="javascript:void(0);" で、リンクを無効にする

aタグはクリックしたらリンク先へ飛ぶものですので、その作用を無効にしないと元のウインドウの画面が、そこへ移動してしまいます。そのために href に、何もしないスクリプトを充てているのがjavascript:void(0);です。

ほとんどの場合、これで問題ありません。しかし、href のリンク先URLを消して、javascript:void(0);を充ててしまったので、JavaScriptが無効だとクリックしてもリンクを表示できません。

それを避けるためにもっとも無難なのは、次の方法です。

  1. HTMLには、通常のリンクを書いておく。
    <a href="#link" onclick="window.open('#link');">リンクへ</a>
  2. それを後から、JavaScriptで書きかえる。

JavaScriptが有効でない場合は、スクリプトが動かないのでリンクが書きかえられません。そうすれば、新しい窓ではなく同じ窓でですが正しくリンク先へ遷移します。

具体的な書きかえ用のJavaScriptは次のようになります。

<a href="#link" onclick="window.open('#link');">リンクへ</a>

<script type="text/javascript"><!--
document.querySelector('a[href="#link"]').href = "javascript:void(0);";
// -->
</script>

このスクリプトは、href="#link" である aタグを探して、その href を "javascript:void(0);" に書きかえるという動作をします。

新ブログ「Big Bang」で続きを読む

カッコで括るJavaScriptの無名関数 (function(){/*処理*/})();

JavaScriptで、変数名を省略する記法

無名関数について説明する前に、JavaScriptで変数名を省略する書き方を説明します。

例えば、JavaScriptで今年の西暦を調べるには、次のようにします。

var day = new Date;
var year = day.getFullYear();

これは、dayという名前の変数の宣言を省略して、カッコで括っても同じです。つまり、下のようにも書けます。

var year = (new Date).getFullYear();

カッコの中で、new Dateを行ない、その値のメソッドgetFullYearを使って処理をしています。

これは、変数dayを1回しか使わないのであれば、こういう書き方も出来るという例です。

JavaScriptでの関数の宣言方法は2通り

JavaScriptで関数を宣言する方法は、2通りあります。

方法1.  function hoge(){ /*処理*/ }

方法2.  var hoge = function(){ /*処理*/ }

上の2つは、どちらもhogeという名前の関数を宣言しています。

JavaScriptの無名関数

関数宣言の方法2を使って、下のような処理をするとします。

var hoge = function(){ /*処理*/ }
var result = hoge();

これは、今年の西暦を調べたときのように、下のように書けます。

var result = (function(){ /*処理*/ })();

このような、名前を宣言していない関数のことを無名関数と言います。無名関数には、引数も与えられます。

var result = (function(num){ return num * 10; })(2);  /* resultは20になる */

通常、関数は何度も処理を繰り返すために作るので、名前をつけて宣言しますが、1回しか使わない関数の場合は、こういう書き方も出来ます。

1回しか使わない関数とは

1回しか処理を行なわないのであれば、関数にせず、そのまま処理を書いてもいいような気もします。では、HTMLの中で下のように書いたとします。

<script type="text/javascript">
var num = 2;
var result = num * 10;
</script>

これでも、resultは20になります。ですが、もし別の場所でも変数numが使われていたとしたらどうなるでしょう。

ブログでは、好きなようにブログパーツを設置できます。上の処理の前に別の人が作った、次のような内容のブログパーツを貼っていたとします。

いらっしゃいませ!あなたは<span id="elem"></span>人目のお客様です!

<script type="text/javascript">
var num = 1000;

window.onload = function(){
  document.getElementById('elem').innerHTML = num;
};
</script>

この後に、先ほどの処理が行なわれると、グローバル変数のnumは、1000ではなく、2に書きかえられます。

window.onloadは、HTMLのすべてを読み終わってから動作しますので、ページ読み込み後に

「いらっしゃいませ!あなたは1000人目のお客様です!」

と表示されるはずのものが

「いらっしゃいませ!あなたは2人目のお客様です!」

と表示されてしまいます。これでは、あんまりではないですか?

こういう悲しい誤作動をなくすために、無名関数を使って、グローバル変数ではなく、ローカル変数にすることも出来ます。

無名関数を使って、関数を返す

無名関数を使って、よくやる手法で、複数回使う関数を、最初に選択しておくという方法があります。例えば下の関数を見てみましょう。

var hoge = function(elem, event, func){
	if(elem.addEventListener){
		elem.addEventListener(event, func, false);
	}else if(elem.attachEvent){
		elem.attachEvent('on'+event, func);
	}
}

この関数をそのまま使うと、呼び出す度にif文の処理をします。しかし、addEventListenerなのか、attachEventなのかは、変化するはずのない結論なので、下の様にすれば、if文の処理は最初の1回だけで済みます。

var hoge = (function(){
	if(window.addEventListener){
		return function(elem, event, func){
			elem.addEventListener(event, func, false);
		}
	}else if(window.attachEvent){
		return function(elem, event, func){
			elem.attachEvent('on'+event, func);
		}
	}
})();

新ブログ「Big Bang」で続きを読む

なんでもかんでもオブジェクト化すればいいってもんじゃないな。反省!

これは、先日の記事「JavaScriptの無名関数とクラス」の続きです。

外から呼び出せないなら、中でやればいいじゃない!

今しがた、他の事をしていたら突然に閃いた。すごい思い込みで無駄な事をしていた。こないだ書いた記事、あれオブジェクト化する必要はなかったんだ。

自分がやりたかった事は、ボタンをonclickしたら、setCookie関数を走らせたかっただけで、そのために無名関数の中のfunctionをprototypeにするということを考えた。なぜなら、無名関数の中の関数はonclickで呼び出せないから。

記事の中で、ボタンのないバージョンと、あるバージョンの2つを走らせたいという考えもあった。「2つ走らせるならオブジェクト」という発想が先行した。

でも、そうじゃなかった。「無名関数の中の関数はonclickで呼び出せない」のなら、「無名関数の中でonclickのコールバックを書き込めばいい」だけだったんだ!

つまり、元のソースの終盤の部分を、少し書き換えるだけでよかった。

 if( !targetDay ) {
   targetDay = addDay( LIMIT_DAY ).getTime();
   setCookie( COOKIE_NAME, targetDay + '', SHELF_LIFE );
 }

 (function () {
  var text = '次回まで約';
  var s = (targetDay - (new Date).getTime()) / 1000 |0;

  if (s < 0)
   text = TIMEOUT_MESS;

  else {
   text += padding( s % 86400 / 3600 |0) + '時間' +
    padding( s % 3600 / 60 |0) + '分' +
    padding( s % 60 |0) + '秒です。';
    setTimeout (arguments.callee, 1000);
  }

  node.firstChild.nodeValue = text;
 })();

上のソースは、下のように書き換えても同じ。

 function reset(){
   targetDay = addDay( LIMIT_DAY ).getTime();
   setCookie( COOKIE_NAME, targetDay + '', SHELF_LIFE );
 }

 function load() {
  var text = '次回まで約';
  var s = (targetDay - (new Date).getTime()) / 1000 |0;

  if (s < 0)
   text = TIMEOUT_MESS;

  else {
   text += padding( s % 86400 / 3600 |0) + '時間' +
    padding( s % 3600 / 60 |0) + '分' +
    padding( s % 60 |0) + '秒です。';
    setTimeout (load, 1000);
  }

  node.firstChild.nodeValue = text;
 }

  if( !targetDay ) {
    reset();
  }

  load();

あとは、<input type="button" id="messBtn" value="リセット" /> とでもして、次のcallbackを無名関数の中から書き込むだけで事足りた。

 document.getElementById('messBtn').onclick=function(){
    reset();
    load();
  };

これで実際に動いている。

次回

タイマーを

これなら前回、global宣言したvar countdownも必要ない。出来る限りglobalではなくlocalで宣言は済ませたい。

もし、2つ走らせたいなら、無名関数をやめて、関数に名前をつければいい。その時、表示領域のidを引数に加えるだけでよかった。ただし、prototypeと違って無駄なメモリーを食うけれど。そのメモリー消費も微々たるもの。ブログのたったひとつの記事のためにオブジェクトにするのが効率的かどうかという話だな。実際、今やってみたら、この書き換え作業は2〜3分で出来た。前回はthisをつけるのに数分要している。

今回は元のソースを書いたbabu_babu_babooさんにやんわりと指摘を受けていた。けど、その真意を理解できないでいた。ずっと頭の中で気になっていたんだけど、突然に閃いた。こういうことだったんだ。なんでもかんでもオブジェクトにすればいいってもんじゃないな。

これはプログラミングに限らず言えることだけど、自分一人で考えていると、ひとりよがりの結論になりがちだ。指摘をされるまで気がつかない事というものはある。せっかくブログがあるんだから情報は表に出して、いろいろな意見を聞いた方がいいのかもしれない。と、今回は思いました。

最終的なソースコード

前回のコードでも動作は問題ないので、前回のソースを使っているマスクドライダー17号さんが下のソースに書き換える必要はありません。(書き換えてもいいけど、同じ事です)

<style type="text/css">
p#mess{ font-size:200%; }
</style>

<p id="mess">次回</p>
タイマーを<input type="button" id="messBtn" value="リセット" />

<script type="text/javascript">
<!--
(function (doc) {

 function addDay ( day, date ) {
  if( 'number' !== typeof day ) day = 0;
  if( 'object' !== typeof date ) date = new Date;
  date.setDate( date.getDate() + day );
  return date;
 }

 function getCookie ( name ) {
  name = encodeURIComponent( name ).replace( /([.*()]) /g, '\\$1' );
  var value = doc.cookie.match( RegExp( name + '\\s*=\\s*(.*?)(?:[\\s;,]|$)' ) );
  return value ? decodeURIComponent( value[1] ): '';
 }

 function setCookie ( name, value, day, path, domain ) {
  return doc.cookie = encodeURIComponent (name) + '=' + encodeURIComponent (value) +
   '; ' + 'expires=' + ( addDay(day) ).toUTCString () + '; ' + (path ? 'path=' + encodeURI (path) + '; ': '') +
   (domain ? 'domain=' + encodeURI (domain) + '; ': '');
 }
 function padding ( n ) { return n < 10 ? '0' + n: n; }
  var COOKIE_NAME = 'myCount';
 var LIMIT_DAY = 1;
 var SHELF_LIFE = 10;
 var TIMEOUT_MESS = '24時間以上経過しました!!';
 var node = doc.getElementById( 'mess' );
 var targetDay = parseInt( getCookie( COOKIE_NAME ) );

 function reset(){
   targetDay = addDay( LIMIT_DAY ).getTime();
   setCookie( COOKIE_NAME, targetDay + '', SHELF_LIFE );
 }

 function load() {
  var text = '次回まで約';
  var s = (targetDay - (new Date).getTime()) / 1000 |0;

  if (s < 0)
   text = TIMEOUT_MESS;

  else {
   text += padding( s % 86400 / 3600 |0) + '時間' +
    padding( s % 3600 / 60 |0) + '分' +
    padding( s % 60 |0) + '秒です。';
   setTimeout (load, 1000);
  }

  node.firstChild.nodeValue = text;
 }

  if( !targetDay ) {
    reset();
  }

  load();

  document.getElementById('messBtn').onclick=function(){
    reset();
    load();
  };
})(this.document);

//-->
</script>

新ブログ「Big Bang」で続きを読む

JavaScriptの無名関数とクラス

2013年5月3日追記
この記事の反省をふまえた続編もあります。この記事で私の書き換えたソースは、非常に無駄な事をしていますので、この記事だけだと恥ずかしいから、そちらもお読み頂ければ幸いです。

なんでもかんでもオブジェクト化すればいいってもんじゃないな。反省!

経過時間を計算してくれるタイマー

マスクドライダー17号さんが、彼のブログで面白いことをやっていて、最初にそのページを開いてからカウントダウンをはじめて、24時間経過したかどうか、残り時間を教えてくれるというスクリプトを走らせています。

その記事によると「教えて!gooにて回答されていた、babu_babooさんのScriptを参考にしました」ということで、babu_babu_babooさんのスクリプトをアレンジしたそうです。

教えて!gooの内容は、期限が来たかどうかカウントしたいだけなので、そのスクリプトは必要な目的を達成しています。

現在、マスクドライダー17号さんが使っているスクリプトは、おそらく24時間で正しく止まると思います。(止まるという表現は正確でないですがわかりやすく言っています)ただ、それを彼のブログで使う場合は不足している機能があります。それは、リセット機能です。

クッキーにデータが残っている

そのスクリプトは、クッキー(cookie)を使って、実現されています。ブラウザを閉じても、最初の時間をクッキーが記憶しているので、もう一度ブログにアクセスした時に、最初の時間からの残りを計算できます。そして、残り時間がなくなると「24時間以上経過しました!!」と表示して終了します。

カウントダウンが1度だけなら、今のままで、まったく問題ないのですが、マスクドライダー17号さんの場合は、そのタイマーを24時間経過する度に、リセットして使いまわしたいのだと思います。でも、今のままだと、最初の24時間が過ぎると「24時間以上経過しました!!」と表示されて、そのまま動かなくなります。その後、もう一度使おうとしても、24時間たってしまったので動きません。

繰り返しますが、教えて!gooの場合は、これで目的を達成出来ているので問題ありません。

現在、マスクドライダー17号さんは、クッキーの有効期限を10日にしているので、止まってから、さらに9日経てば、またスクリプトは使えるようになりますが、かといって、有効期限を短くすると、ブログを開いたときにスクリプトがリセットされてしまい、24時間経ったのかどうか、わからなくなります。ですから「24時間以上経過しました!!」という表示を読むまでは、リセットしたくありません。読み終わったら、ボタンを押して、リセット(カウントダウンの再スタート)をさせるのが、いちばんいいと思います。

元のソースコード

出来れば、動いている今のソースコードをそのまま流用したいです。ありがたいことに、機能ごとに、細かく関数に分けてくれています。この中のsetCookie関数を使えばクッキーはリセット出来ます。

<style type="text/css">
p#mess{ font-size:200%; }
</style>

<p id="mess">次回</p>

<script type="text/javascript">
<!--
(function (doc) {

 function addDay ( day, date ) {
  if( 'number' !== typeof day ) day = 0;
  if( 'object' !== typeof date ) date = new Date;
  date.setDate( date.getDate() + day );
  return date;
 }

 function getCookie ( name ) {
  name = encodeURIComponent( name ).replace( /([.*()]) /g, '\\$1' );
  var value = doc.cookie.match( RegExp( name + '\\s*=\\s*(.*?)(?:[\\s;,]|$)' ) );
  return value ? decodeURIComponent( value[1] ): '';
 }

 function setCookie ( name, value, day, path, domain ) {
  return doc.cookie = encodeURIComponent (name) + '=' + encodeURIComponent (value) +
   '; ' + 'expires=' + ( addDay(day) ).toUTCString () + '; ' + (path ? 'path=' + encodeURI (path) + '; ': '') +
   (domain ? 'domain=' + encodeURI (domain) + '; ': '');
 }
 function padding ( n ) { return n < 10 ? '0' + n: n; }
  var COOKIE_NAME = 'myCount';
 var LIMIT_DAY = 1;
 var SHELF_LIFE = 10;
 var TIMEOUT_MESS = '24時間以上経過しました!!';
 var node = doc.getElementById( 'mess' );
 var targetDay = parseInt( getCookie( COOKIE_NAME ) );

 if( !targetDay ) {
   targetDay = addDay( LIMIT_DAY ).getTime();
   setCookie( COOKIE_NAME, targetDay + '', SHELF_LIFE );
 }

 (function () {
  var text = '次回まで約';
  var s = (targetDay - (new Date).getTime()) / 1000 |0;

  if (s < 0)
   text = TIMEOUT_MESS;

  else {
   text += padding( s % 86400 / 3600 |0) + '時間' +
    padding( s % 3600 / 60 |0) + '分' +
    padding( s % 60 |0) + '秒です。';
   setTimeout (arguments.callee, 1000);
  }

  node.firstChild.nodeValue = text;
 })();
})(this.document);

//-->
</script>

しかし、弱ったことにソースは無名関数で括られているので、setCookie関数を使えません。無名関数の外に出してもいいのですが、この際、オブジェクト(クラス)化して、無名関数内のfunctionを、クラスのprototypeに移動させる事にします。

オブジェクト指向で書き換えたコード

関数の中身は、ほとんどいじっていませんし、スクリプトのロジックは一緒です。

<style type="text/css">
p#mess{ font-size:200%; }
</style>

<p id="mess"></p>
タイマーを<input type="button" onclick="countdown.reset();" value="リセット" />

<script type="text/javascript">
<!--
var timerCookie=function(id){
 this.id=id;
 this.COOKIE_NAME = 'myCount'+id;
 this.LIMIT_DAY = 1;
 this.SHELF_LIFE = 10;
 this.TIMEOUT_MESS = '24時間以上経過しました!!';
 this.targetDay = parseInt( this.getCookie( this.COOKIE_NAME ) );

 if( !this.targetDay ) {
   this.reset();
 }else{
   this.load();
 }
};

timerCookie.prototype.addDay=function( day, date ){
  if( 'number' !== typeof day ) day = 0;
  if( 'object' !== typeof date ) date = new Date;
  date.setDate( date.getDate() + day );
  return date;
};

timerCookie.prototype.getCookie=function( name ){
  name = encodeURIComponent( name ).replace( /([.*()]) /g, '\\$1' );
  var value = document.cookie.match( RegExp( name + '\\s*=\\s*(.*?)(?:[\\s;,]|$)' ) );
  return value ? decodeURIComponent( value[1] ): '';
};

timerCookie.prototype.setCookie=function( name, value, day, path, domain ){
  return document.cookie = encodeURIComponent (name) + '=' + encodeURIComponent (value) +
   '; ' + 'expires=' + ( this.addDay(day) ).toUTCString () + '; ' + (path ? 'path=' + encodeURI (path) + '; ': '') +
   (domain ? 'domain=' + encodeURI (domain) + '; ': '');
};

timerCookie.prototype.padding=function( n ){ return n < 10 ? '0' + n: n; }

timerCookie.prototype.reset=function(){
   this.targetDay = this.addDay( this.LIMIT_DAY ).getTime();
   this.setCookie( this.COOKIE_NAME, this.targetDay + '', this.SHELF_LIFE );
   this.load();
};

timerCookie.prototype.load=function () {
  var text = '次回まで約';
  var s = (this.targetDay - (new Date).getTime()) / 1000 |0;

  if (s < 0)
   text = this.TIMEOUT_MESS;

  else {
   text += this.padding( s % 86400 / 3600 |0) + '時間' +
    this.padding( s % 3600 / 60 |0) + '分' +
    this.padding( s % 60 |0) + '秒です。';
   (function(obj){
	setTimeout (function(){ obj.load(); }, 1000);
   })(this);
  }

  document.getElementById( this.id ).innerHTML = text;
};

var countdown=new timerCookie('mess');
//-->
</script>

これで、setCookie関数を使えるようになりました。setCookie関数を使って作ったのが、上のprototypeにあるreset関数です。元のコードは本来、例文として作られたものですし、1回しか使わないという前提に立ったものです。そういう場合は無名関数で括ってしまうのが便利ですし、使いまわす時はオブジェクト化が便利だと思います。

ちなみに、オブジェクトは表示領域のidを引数にとるようにしています。これで、同じページで時間の違う、複数のタイマーを走らせることも出来ます。

タイマーを

新ブログ「Big Bang」で続きを読む

マウスが乗った写真を拡大表示する商品レイアウト

ファンブログにアップロードした画像URLの調べ方は「ファンブログでアップロードした画像の場所」に書いてあります。

Amazonなんかでやっている商品レイアウトで小さな写真を並べておいて、どれかにマウスを乗せると、その写真の拡大図を表示するという手法があります。その実現方法です。この下に6つ並んだダースベイダーのどれかにマウスを合わせると、その写真が下に大きく表示されます。

レゴ (LEGO) スターウォーズ ダースベイダー Star Wars Darth Vader Alarm Clock 目覚まし時計 9002113

LEGOとスターウォーズがコラボレーションしたアラームクロック

  • 手足が可動式なのでお気に入りのスタイルで飾れます。
  • 頭の部分を押してアラームストップ&ライト点灯。
  • デスクサイド、ベッドサイドを演出してくれるクロックです。
【メーカー名】LEGO
【品番・型番】9002113

サイズ:(約)前長23cm、幅15cm / 重量:(約)535g
材質:プラスチック / 生産国:中国
スペック:アラーム(スヌーズ機能付き)ライト付き
     単4乾電池×2本使用(付属)
箱・保証書:有り(6ヶ月)
特記事項:実際の商品とは若干異なる場合がございます。
     予めご了承ください。

3,675円(税抜:3,500円)

送料525円(お買い上げ金額合計8,000円以上で送料無料)

レゴ (LEGO) スターウォーズ ダースベイダー Star Wars Darth Vader Alarm Clock 目覚まし時計 9002113  レゴ (LEGO) スターウォーズ ダースベイダー Star Wars Darth Vader Alarm Clock 目覚まし時計 9002113をカートに入れる

ちなみにこれは「カートに入れる」と本当に買うことができます

え!欲しいですか?困ったなぁ。そんなつもりじゃなくて、ただのサンプルなんだけど!いやぁ困った!でも仕方がない。どうしても欲しい方は レゴ (LEGO) スターウォーズ ダースベイダー Star Wars Darth Vader Alarm Clock 目覚まし時計 9002113をカートに入れる をクリックしてお買い上げください。

ソースコード

今回説明するのは、左上のダースベーダーの写真だけのブロックです。その右の「説明文」や「カートに入れる」ボタンがあるブロックと、写真の下の「ストーム・トルーパー」や「Amazon」「楽天」等へのリンクがまとまっているブロック、この2つは説明には含めません。

先にソースだけをまとめて書きます。これをまるごとブログの記事にコピペして、画像URL1から6に表示したい画像のURLを入れるだけでも上のようになります。ただし、編集画面のオプションで改行を有効にしていると動かないと思います。改行を有効にしていても動くソースは記事の最後です。

新ブログ「Big Bang」で続きを読む

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>こんに…  終了タグがなくなってしまった!

<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;
}

新ブログ「Big Bang」で続きを読む

ソースのバグの作り方 -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が存在しないかもしれません。その場合はエラーになります。だから他の人のブログで使えばエラーが出るかもしれません。

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

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

新ブログ「Big Bang」で続きを読む

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

これは、かこに教わったんですが、Google Analyticsでクリック数を計測できるんですね。知りませんでした。

今回の記事の元ネタは「ファンブログハック」のおーとえす氏の記事
googleアナリティクスで簡単にクリックカウント出来るコード」です。

おーとえす氏の場合は、JQueryを使っているんですが、
ファンブログには標準ではJQueryが使われていないので、それを使わないコードを書いてみました。

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

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

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

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

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

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){
	a=document.querySelectorAll('a');

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

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

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

ファンブログはスキンが、メイン、ポータル、アーカイブ、1記事、プロフィールの5つあるので全部に追加します。

ただし、次の箇所を修正してから使ってください。

1.赤い字で書いてあるayzfqir5を自分のものに差し替える。

これは自分のブログのURLを見て、書きかえます。

ファンブログの場合はサブディレクトリ方式のURLですので、このブログのURLは
https://fanblogs.jp/ayzfqir5/となっています。このayzfqir5の部分を自分のURLを見て、書き換えてください。

本当のことを言うと、書き換えなくても動きます。ただ、URLの中にayzfqir5が含まれているかどうかを判断材料にして、内部リンクか外部リンクかのログを取っています。

これを書き換えると、内部リンク(自分のブログへのリンク)ならInbound Link
外部リンク(自分のブログ以外へのリンク)ならOutbound Linkというイベント カテゴリにわけて記録されます。

2.青い字で書いてある0の数字をスキンごとに変える。

これも実は、別にやらなくても、構いません。やると、どのスキンでクリックされたかがわかります。

わたしは、1記事を0、メインを1、アーカイブを2、プロフィールを3、ポータルを4にしました。

数字は0以上であれば何でも構いませんが、マイナスの値だとダメみたいです。

これは、合計値が表示されるみたいで、この使い方では意味ないので削除しました。

Google Analyticsでの見方

「コンテンツ」タブの「イベント」でログを閲覧できます。
下は、このブログでどこへのリンクがクリックされたかを表示した物です。今つけたばかりなので数は少ないです。

この表のイベントの値というのが、上記の2で設定した数字です。(今は使っていません)

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

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

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

こんなに簡単に出来るとは知らなかった。かこと、おーとえす氏に感謝です。

需要があるかどうかを知りたいので、使うときはコメント欄に「使う」と書いてもらえるとうれしいです。

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

Tweetボタンは、aタグを使って、href="javascript:void(0)"onclick="ここでスクリプト処理"というソースでした。

最初のコードは、onclick=function(){処理}という形で、onclickを上書きしてしまいました。

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

この修正に関するさらに詳細な説明を別記事にしました。よければご覧ください。
Google Analyticsのイベント トラッキングはaddEventListenerやattachEventでは計測されない

新ブログ「Big Bang」で続きを読む

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

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

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日になると自動で消えたりもしません。

新ブログ「Big Bang」で続きを読む

お知らせ欄一括変更スクリプト

現在(2012年12月18日)新しいスキンに変更中なのですが、すべてのページの上部に「お知らせ」欄を設けて、「作業中につき、ご迷惑をお掛けします」という内容のお断りを書いています。

今回はこの「お知らせ」欄を変更する際に1個の原稿でいっぺんに済ませる方法です。

というのも、記事の上に項目を追加するには、通常はスキン(テンプレート)に直接変更を書き込むからです。ただし、ブログのサービスによっては、元々の設定で「お知らせ」欄を一元管理できます。

livedoorブログ
ブログの「設定画面」から「基本設定」のページを開いて「メッセージボード」という項目に書き込むとすべてのページの上部に、そのメッセージが表示されます。
Seesaaブログ
管理画面の「デザイン」から「コンテンツ」を選択し、開いたページで記事の上下に自由に項目を追加できます。

上記ブログは、それですべてのページの「お知らせ」を一元管理できます。

それ以外のブログでも同じように一元管理をしたければ、次のようにします。

1. まず、サイドバーに「お知らせ」の原稿を書いたパーツを設置する。
2. その「お知らせ」原稿をスタイルシートで非表示にする。
3. JavaScriptで非表示の文章を表示したい場所にコピーしてから、表示する。

お気づきだと思いますが、サイドバーに表示するものはすべて一元管理が出来るので、サイドバーに表示できればいいのなら、一つ目の作業だけで完了です。

これから説明するのは、サイドバーではなく、ブログの記事の上にお知らせを表示する方法です。

「お知らせ」原稿を一元管理せず、ページごとに変える場合は、前回このブログがスキンを変更した時の記事「ブログ記事の前にお知らせ欄を作る」にやり方を書いてあります。

表示領域の設定

上で作業内容として3つの項目を列挙しました。しかし、実際にはその前にひとつ、下準備が必要です。これは、このブログを書いているファンブログを例に説明します。

ブログ記事の前にお知らせ欄を作る」と同じ要領で実際に表示する場所をスキンに作ります。

ファンブログのスキンには下の箇所があります。

<div id="content">
<div id="entries">
<BlogEntries>

この<div id="content"><div id="entries">の間に「<div id="message" style="display:none;"><h2>お知らせ</h2></div>」と追加します。

<div id="content">
<div id="message" style="display:none;"><h2>お知らせ</h2></div>
<div id="entries">
<BlogEntries>

<h2>お知らせ</h2>は「お知らせ」欄のタイトルです。不要なら削除してください。

style="display:none;"と書くことで、その部分は非表示になります。普段「お知らせ」がない場合はそこにタイトルも何も表示しません。

下準備は以上です。ファンブログ以外でもテンプレートを見ながら表示場所を確かめて、追加します。

サイドバーに設置するパーツの内容

サイドバーに次のとおり書いた項目を追加します。これで先ほど3つ列挙した作業をまとめて行ないます。

<div id="message_hidden" style="display:none;">ここにメッセージ本文</div>

<script type="text/javascript">
<!--
var message_area=document.getElementById('message');
var text_area=document.getElementById('message_hidden');

if(message_area&&text_area){
  message_area.innerHTML+=text_area.innerHTML.replace(/\r?\n/g,'<br />');
  message_area.style.display='block';
}
// -->
</script>

.replace(/\r?\n/g,'<br />')とあるのは自動で改行を<br />に変換するオプションです。不要なら削除してください。この意味が分からなければ削除しないでください。

「お知らせ」の内容を「ここにメッセージ本文」と書いた部分に記入します。先ほど説明したstyle="display:none;"があるので、サイドバーに文章は表示されません。

今後、変更がある時は「メッセージ本文」だけを変更します。表示するお知らせがないときはサイドバーのパーツを取り外せばOKです。

参考記事:
表テーブルが下がる原因と対処法 <br />タグについて説明しています
モジュールの設置方法 ファンブログでサイドバーに項目を追加する方法を説明しています

新ブログ「Big Bang」で続きを読む