楽天市場の検索結果を表示する

 ここでは楽天市場の検索結果を表示させてみましょう。楽天もInfoseekも同じですので、CGIも送信するURL部分を変更するだけです。ブラウザ側から送信された日本語文字列はEUCのコードに変換してから楽天の検索サーバーに送信します。(サンプルを実行する

#!/usr/local/bin/ruby
require "kconv"
require "cgi-lib"
input = CGI.new
inputdata = input["query"]
inputdata = Kconv::toeuc(inputdata)
bom = "\xef\xbb\xbf"
print "Content-type: text/html\n\n"
print bom
fh = open("| curl 'http://esearch.rakuten.co.jp/rms/sd/esearch/vc?sitem="+inputdata+"'")
while !fh.eof
str = fh.gets
print Kconv::toutf8(str)
end
fh.close

 たくさんウィンドウが表示されると、どのウィンドウがアクティブ(有効になっているか)が分かりにくくなります。今回のサンプルのような場合には、最前面にウィンドウが表示されるものがアクティブウィンドウになります。そこで、アクティブウィンドウはタイトルバーを黒色で、インアクティブウィンドウ(アクティブではないウィンドウ)は灰色のタイトルバーにする処理を追加してみましょう。
 現在、サンプルで使用しているウィンドウのタイトルバーはdivタグの背景画像として指定しています。つまり背景画像を入れ替えればタイトルバーのアクティブ、インアクティブを切り替えて見せることができます。背景画像はスタイルシートで指定しているので、スタイルシートのbackground-imageプロパティを操作することになります。JavaScriptではstyle.backgroundImageプロパティにアクセスすることで背景画像を操作できます。背景画像を指定するには単純に画像のURLを指定するのではなくurl(背景画像URL)のようにurl()のカッコ内に表示したい背景画像のURLを指定します。実際には、文字列としてurl(の文字と画像のURLを連結し指定します。
 ここでは、ウィンドウを全てインアクティブにしてから、アクティブにするウィンドウの背景画像を設定するという方法をとります。ウィンドウを全てインアクティブにするには以下のようにウィンドウの最大数だけfor()を使って繰り返し処理を行います。

for (var i=0; i<winNum; i++)
{
try {
$("window"+i).style.backgroundImage = "url(titlebar2.gif)";
}catch(e){}
}

 変数winNumにウィンドウの最大数が入っていますが、これは今までに開かれたウィンドウの総数であって、現在開かれているウィンドウの枚数ではありません。このため、存在しないウィンドウを参照してしまう(存在しないdivタグを指定してしまう)ことになりエラーが発生します。ここでは、エラーを発生させたくありませんので、try...catch()を使ってエラーが発生しても無視して処理を続けるようにします。(この方法は非効率的です。ちなみにdragObj.windowに配列として現在存在するウィンドウリストがありますので、これを参照し処理するように改良してみるのも良いでしょう。)
 ウィンドウがアクティブになるのは、新規にウィンドウが開かれた場合とドラッグが開始された場合です。この時は、まず全てのウィンドウをインアクティブにしてから該当ウィンドウをアクティブにします。新規ウィンドウの場合には、winNumで示されるウィンドウをアクティブにするだけです。ドラッグの場合には、イベントハンドラを設定しドラッグ開始時にウィンドウをアクティブにする処理を行います。

inactiveWindow();
var obj = getEventTarget(evt);
if (obj.id.indexOf("window") == -1) return;
obj.style.backgroundImage = "url(titlebar.gif)";

 ドラッグ開始時には、どのウィンドウ(イベントターゲット)がクリックされたかを知る事ができます。そのターゲットの背景画像をアクティブであることを示す背景画像に入れ替えればできあがりです。(実際のサンプルを実行する

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>楽天市場の検索結果をマルチウィンドウで表示する</title>
<link rel="stylesheet" href="main.css" type="text/css" media="all">
<script type="text/javascript" src="xmlhttp.js"></script>
<script type="text/javascript"><!--
function rakutenSearch()
{
srchStr = document.getElementById("query").value;
if (srchStr == "") return;
srchStr = srchStr.replace(/ /g,"%20");
httpObj = createXMLHttpRequest(displayData);
if (httpObj)
{
httpObj.open("GET","curl.rb?query="+encodeURI(srchStr)+"&cache="+(new Date()).getTime(),true);
$("progress").style.visibility = "visible";
httpObj.send(null);
}
}
function displayData()
{
if ((httpObj.readyState == 4) && (httpObj.status == 200))
{
newWindow();
$("result"+ winNum).innerHTML = httpObj.responseText;
winNum++;
}
}
function newWindow()
{
$("progress").style.visibility = "hidden";
var dObj = $("window0").cloneNode(true);
var winObj = $("contents").appendChild(dObj);
winObj.id = "window"+winNum;
winObj.childNodes[0].id = "result"+winNum;
addEvent("window"+winNum,"click",closeWindow,false);
addEvent("window"+winNum,"mousedown",activeWindowDrag,false);
addEvent("window"+winNum,"mousedown",dragObj.dragStart,false);
winObj.style.left = 10+Math.floor(Math.random()*400)+"px";
winObj.style.top = 150+Math.floor(Math.random()*200)+"px";
winObj.style.visibility ="visible";
winObj.style.zIndex = dragObj.zIndex + 1;
winObj.childNodes[0].style.visibility = "visible";
dragObj.window.push("window"+winNum);
dragObj.maxLayer = dragObj.window.length;
inactiveWindow();
activeWindow(winNum);
}
// ウィンドウをアクティブにする処理
function activeWindow(n)
{
$("window"+n).style.backgroundImage = "url(titlebar.gif)";
}
function activeWindowDrag(evt)
{
inactiveWindow();
var obj = getEventTarget(evt);
if (obj.id.indexOf("window") == -1) return;
obj.style.backgroundImage = "url(titlebar.gif)";
}
// ウィンドウを全てインアクティブにする処理
function inactiveWindow()
{
for (var i=0; i<winNum; i++)
{
try {
$("window"+i).style.backgroundImage = "url(titlebar2.gif)";
}catch(e){}
}
}
// ウィンドウを閉じる処理
function closeWindow(e)
{
// クローズボックス上でマウスボタンが押されたか?
if ((dragObj.offsetX < 2) || (dragObj.offsetX >14) || (dragObj.offsetY < 4) || (dragObj.offsetY > 16)) return;
wObj = getEventTarget(e);
contObj = $("contents");
// ウィンドウをノードから削除
for (var i=0; i<contObj.childNodes.length; i++)
{
if(contObj.childNodes[i].id == wObj.id) contObj.removeChild(contObj.childNodes[i]);
}
// ウィンドウリストから削除
var temp = new Array();
for (i=0; i<dragObj.window.length; i++)
{
if (dragObj.window[i] != wObj.id) temp.push(dragObj.window[i]);
}
dragObj.window = temp;
}
function initObj()
{
window.document.onmousemove = dragObj.dragProc;
window.document.onmouseup = dragObj.dragEnd;
winNum = 1;
dragObj.window = [];
dragObj.maxLayer = 1; // 最大レイヤー枚数
}
// --></script>
</head>
<body onload="initObj()" oncontextmenu="return false">
<h1>楽天市場の検索結果をマルチウィンドウで表示する</h1>
<p>ウィンドウ、アクティブなものとそれ以外でタイトルバーの状態(画像)が変わります</p>
<form method="get" name="ajaxForm" onsubmit="rakutenSearch();return false;">
<input type="text" value="" id="query">
<input type="button" value="楽天市場検索" onClick="rakutenSearch()">
</form>
<div id="contents"></div>
<div id="window0" class="windowBorder"><div id="result0" class="windowContents"></div>
</div>
<div id="progress"><img src="ring.gif"> 検索結果を読み込み中です...</div>
</body>
</html>

 実際に検索してみるとウィンドウサイズが小さいため見にくい場合があります。サイズを変更する場合にはスタイルシート(サンプルではmain.css)を操作するだけです。(表示位置はスクリプトでランダムに設定しているので、スタイルシートで指定しても、その位置には表示されません。)

.windowBorder {
position:absolute;
left:0px;
top:100px;
width:800px;
height:600px;
overflow:hidden;
border:1px black solid;
background-color:#ddd;
background-image:url(titlebar.gif);
background-repeat:no-repeat;
visibility:hidden;
z-index:1;
}
.windowContents {
position:absolute;
left:0px;
top:20px;
width:800px;
height:580px;
overflow:scroll;
background-color:#fff;
visibility:hidden;
z-index:1;
}

 検索する文字を次々と入力すると連続して送信されてしまい、期待通りの検索結果が表示されません。このような場合には、検索結果が表示されるまで検索語を入力できないようにする、ボタンをクリックできないようにするといった処理を行います。このようにすれば連続して通信を行うことができなくなります。
 テキストフィールドやボタンなどをディスエーブル(無効化)するにはdisabledプロパティにtrueを設定します。falseを設定すると有効(普通の状態)になります。HTTP通信を開始したら無効化し、データを受信し終わったら有効化します。特定のオブジェクト(テキストフィールドやボタン)のdisabledプロパティの状態を見て送信可能かどうか判断することもできますが、ここでは安全のため通信中かどうかのフラグ変数loadFlagを用意しています。このフラグがtrueであれば通信中、falseであれば通信していないことを示しています。ボタン等の無効化、有効化の時にこのフラグも同時に設定します。(実際のサンプルを見る

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>楽天市場の検索結果をマルチウィンドウで表示する(3)</title>
<link rel="stylesheet" href="main.css" type="text/css" media="all">
<script type="text/javascript" src="xmlhttp.js"></script>
<script type="text/javascript"><!--
loadFlag = false; // 読み込み状態フラグ
function rakutenSearch()
{
if (loadFlag) return;
srchStr = document.getElementById("query").value;
if (srchStr == "") return;
srchStr = srchStr.replace(/ /g,"%20");
httpObj = createXMLHttpRequest(displayData);
if (httpObj)
{
httpObj.open("GET","curl.rb?query="+encodeURI(srchStr)+"&cache="+(new Date()).getTime(),true);
$("progress").style.visibility = "visible";
loadFlag = true;
$("sendButton").disabled = true;
$("query").disabled = true;
httpObj.send(null);
}
}
function displayData()
{
if ((httpObj.readyState == 4) && (httpObj.status == 200))
{
newWindow();
$("result"+ winNum).innerHTML = httpObj.responseText;
winNum++;
loadFlag = false;
$("sendButton").disabled = false;
$("query").disabled = false;
}
}
function newWindow()
{
$("progress").style.visibility = "hidden";
var dObj = $("window0").cloneNode(true);
var winObj = $("contents").appendChild(dObj);
winObj.id = "window"+winNum;
winObj.childNodes[0].id = "result"+winNum;
addEvent("window"+winNum,"click",closeWindow,false);
addEvent("window"+winNum,"mousedown",activeWindowDrag,false);
addEvent("window"+winNum,"mousedown",dragObj.dragStart,false);
winObj.style.left = 10+Math.floor(Math.random()*400)+"px";
winObj.style.top = 150+Math.floor(Math.random()*200)+"px";
winObj.style.visibility ="visible";
winObj.style.zIndex = dragObj.zIndex + 1;
winObj.childNodes[0].style.visibility = "visible";
dragObj.window.push("window"+winNum);
dragObj.maxLayer = dragObj.window.length;
inactiveWindow();
activeWindow(winNum);
}
// ウィンドウをアクティブにする処理
function activeWindow(n)
{
$("window"+n).style.backgroundImage = "url(titlebar.gif)";
}
function activeWindowDrag(evt)
{
inactiveWindow();
var obj = getEventTarget(evt);
if (obj.id.indexOf("window") == -1) return;
obj.style.backgroundImage = "url(titlebar.gif)";
}
// ウィンドウを全てインアクティブにする処理
function inactiveWindow()
{
for (var i=0; i<winNum; i++)
{
try {
$("window"+i).style.backgroundImage = "url(titlebar2.gif)";
}catch(e){}
}
}
// ウィンドウを閉じる処理
function closeWindow(e)
{
// クローズボックス上でマウスボタンが押されたか?
if ((dragObj.offsetX < 2) || (dragObj.offsetX >14) || (dragObj.offsetY < 4) || (dragObj.offsetY > 16)) return;
wObj = getEventTarget(e);
contObj = $("contents");
// ウィンドウをノードから削除
for (var i=0; i<contObj.childNodes.length; i++)
{
if(contObj.childNodes[i].id == wObj.id) contObj.removeChild(contObj.childNodes[i]);
}
// ウィンドウリストから削除
var temp = new Array();
for (i=0; i<dragObj.window.length; i++)
{
if (dragObj.window[i] != wObj.id) temp.push(dragObj.window[i]);
}
dragObj.window = temp;
}
function initObj()
{
window.document.onmousemove = dragObj.dragProc;
window.document.onmouseup = dragObj.dragEnd;
winNum = 1;
dragObj.window = [];
dragObj.maxLayer = 1; // 最大レイヤー枚数
}
// --></script>
</head>
<body onload="initObj()" oncontextmenu="return false">
<h1>楽天市場の検索結果をマルチウィンドウで表示する(3)</h1>
<p>サイズが大きいバージョン</p>
<form method="get" name="ajaxForm" onsubmit="rakutenSearch();return false;">
<input type="text" value="" id="query">
<input type="button" value="楽天市場検索" id="sendButton" onClick="rakutenSearch()">
</form>
<div id="contents"></div>
<div id="window0" class="windowBorder"><div id="result0" class="windowContents"></div>
</div>
<div id="progress"><img src="ring.gif"> 検索結果を読み込み中です...</div>
</body>
</html>

 実際に使ってみると、いくらか不具合があります。ウィンドウ内をクリックするとウィンドウがインアクティブになってしまったりするのも、その1つです。ここらへんはウィンドウの実装方法などに依存する部分もあるので、この項ではこのままの状態にしておきましょう。

 次の項目では、検索でなくページ内に任意のURLのページを表示させてみます。

[第六章 5:他のサイトをページ内に表示するへ]
[目次へ]

(2006.1.20)