Category Archives: Web技術

フロントエンドをJavaScript化してるページを検索してみた

 昨日アップしたテストページがGoogleで検索できるようになっていました。(現時点でbingにはまだインデックスされていませんでした。)

 残念ながら、JavaScriptによって挿入された動的な部分では検索できませんでした。枠として出力した静的なHTMLの部分では表示されますが、そりゃそうですね。となると、metaタグのkeywordとかdescriptionで頑張る方式しかないです。

 例えば、ECショップの商品ページを一個のHTMLで作成して、商品情報は後からJSONPで動的に取得して表示するといったアプローチは取れない、取っても商品ごとに検索にヒットさせられないということです。商品一つ一つに静的ページを作るのは無理(それが大変だからシステム化するのだから)なので、結局サーバーサイドテンプレートエンジンが必要になってくるのかな。で、それを使うとappengineでは遅いという。

 プレビューはできてるんだから、ページ表示後一定時間以内に動的出力された文言はキーワード化するとかしてくれたらいいのに。もう少し、現代のサーチエンジンには頑張って欲しいです。(笑)

 「もはや検索の時代じゃない、Likeボタンだ」

という向きもあるかもしれませんね。だけど、

Googleの提供するプラットフォーム(appengine)上で最適なアーキテクチャ(→http://d.hatena.ne.jp/higayasuo/20101109/1289290143)でページを作ると、Googleの主役の検索エンジンでヒットしないページが出来る

などということは、冗談も厳しいのではなかと思うのですが。

サーチエンジン頑張ってください

代替案

検索エンジン用のページを作って、それをクロールさせるとかそういった逃げがあるかもしれませんね。Rhinoかなんかを裏で走らせたら重いだろうと思ったけど、単なるプレーンテキストに近いページをpythonかなんかでデータだけ並べて取ってくればいいかも。
しかし、完全に静的なHTMLだとリダイレクト(それもmetaタグによる)しか遷移する方法がないので、どうやって検索エンジンをそっちを誘導するかが結局問題になることに気がついた。

appengine 1.4pr が出てた

おおぉぉぉ!
http://blog.virtual-tech.net/2010/11/google-app-engine-prerelease-sdk-140-is.html

これで、インスタンスを常に維持してればいいってこと?



↑ちなみに、これができても僕の仕事上(なぜ数日間にわたって一連のエントリを書いていたのかのワケ)はあんまりメリットがない。

[javascript] javascriptテンプレートエンジン(?)表示の繰り返し更新対応

どうも、懲りない僕です。JavaScriptでビュー書いてるんだから、動的にビュー動かしたいよね?だから、TemplateEngineRunnerは繰り返し実行できなきゃ意味ないよね?なんてわけで、対応しました。
それにしてもアレなサンプル……
http://s-ishigami.appspot.com/files/test.html

ソース

TemplateEngine = new function() {
var currentNode;
this.setCurrentNode = setCurrentNode;
this.propertyModelValue = function(object, propName) { return new PropertyModel(object, propName).getValue; };
this.writeText = function (value) { return new WriteText(value); };
this.writeHtml = function (value) { return new WriteHtml(value); };
this.setAttribute = function (name, value) { return new SetAttribute(name, value); };
this.loopWhile = function(condition, subController) { return new LoopFor(emptyFunction, condition, emptyFunction, subController); }
this.loopFor = function(init, condition, after, subController) { return new LoopFor(init, condition, after, subController); }
function emptyFunction() {}
function setCurrentNode(node) {
currentNode = node;
}
function functionOrValue(f) {
if (typeof f == 'function') {
return f();
}
return f;
}
function PropertyModel(object, propName) {
this.getValue = getValue;
function getValue() {
return object[propName];
}
}
function WriteText(value) {
this.execute = execute;
function execute() {
currentNode.text(functionOrValue(value));
}
}
function WriteHtml(value) {
this.value = value;
this.execute = execute;
function execute() {
currentNode.html(functionOrValue(value));
}
}
function SetAttribute(name, value) {
this.execute = execute;
function execute() {
currentNode.attr(name, functionOrValue(value));
}
}
function LoopFor(init, condition, after, subController) {
this.execute = execute;
function execute() {
init();
while (condition()) {
var clone = currentNode.clone(true);
clone.attr('id', null);
clone.addClass('GENERATED_NODE');
clone.addClass(currentNode.attr('id') + "_CLONE");
if (subController != null) {
var thisNode = currentNode;
TemplateEngineRunner.run(subController, clone);
setCurrentNode(thisNode);
}
clone.find('*').attr('id', null);
clone.insertBefore(currentNode);
after();
}
currentNode.addClass('ORIGINAL_NODE');
currentNode.hide();
}
}
}
TemplateEngineRunner = new function() {
this.run = run;
function run(controller, parentNode) {
clearGeneratedNodes(parentNode);
if (typeof parentNode == 'String') {
parentNode = $('#' + parentNode);
}
for (var key in controller) {
if (parentNode == null) {
TemplateEngine.setCurrentNode($('#' + key));
} else {
TemplateEngine.setCurrentNode(parentNode.find('#' + key));
}
controller[key].execute();
}
}
function clearGeneratedNodes(parentNode) {
if (parentNode == null) {
$('.GENERATED_NODE').remove();
$('.ORIGINAL_NODE').show();
} else {
if (typeof parentNode == 'String') {
parentNode = $('#' + parentNode);
}
parentNode.find('.GENERATED_NODE').remove();
parentNode.find('.ORIGINAL_NODE').show();
}
}
}
var myModel = {
name: 'テスト',
sex: 'male',
position: 0,
goNext: function() {
this.position += 1;
},
nameChange: function() {
this.name = this.name == "テスト" ? "太郎" : "テスト";
}
};
with (TemplateEngine) {
var yamanote = ['大崎', '品川', '田町', '浜松町', '新橋', '有楽町', '東京', '神田', '秋葉原', '御徒町', '上野', '鶯谷', '日暮里', '西日暮里', '田端', '駒込', '巣鴨', '大塚', '池袋', '目白', '高田馬場', '新大久保', '新宿', '代々木', '原宿', '渋谷', '恵比寿', '目黒', '五反田'];
var i = 0;
var controller = {
// オブジェクトのプロパティ出力(動的)
name: writeText(propertyModelValue(myModel, 'name')),
// 固定値出力
test: writeText('生麦生米生卵'),
// 動的値出力
now: writeText(function() {return new Date().toString();}),
// 固定HTML出力
testHtml: writeHtml('<span style="color: blue;">青</span>巻紙<span style="color: red;">赤</span>巻紙<span style="color: yellow;">黄</span>巻紙'),
// 属性変更
attr: setAttribute('style', 'font-size: x-large; color: blue;'),
// ループ(indexを外側の変数で定義しなければいけないことが課題)
'for': loopFor(
function() {i = 0},
function() {return i < 10},
function() {i++;},
{
counterFor: writeText(function() {return yamanote[(myModel.position + i) % yamanote.length];})
}
)
}
}
TemplateEngineRunner.run(controller);
<!DOCTYPE html>
<html lang="ja">
<head>
   <meta charset="utf-8">
   <title>Template Engine Test</title>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<a href="javascript: myModel.goNext(); TemplateEngineRunner.run(controller)">次の駅へ</a>
<p>現在日時: <span id="now"></span></p>
<table border="1">
<col width="200">
<col width="200">
<tr>
<th bgcolor="silver">key</th>
<th bgcolor="silver">val</th>
</td>
<tr>
<th>氏名</th>
<td align="center"><a href="javascript: myModel.nameChange(); TemplateEngineRunner.run(controller)"><span id="name"></span></a></td>
</td>
<tr>
<th>得意技</th>
<td><span id="attr"><span id="test"></span></span></td>
</td>
<tr>
<th>得意技2</th>
<td><span id="testHtml"></span></td>
</td>
<tr id="for">
<th>次は</th>
<td id="counterFor"></td>
</tr>
</table>
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">google.load("jquery", "1.4.2");</script>
<script type="text/javascript" src="templateEngine.js"></script>
<script type="text/javascript" src="test.js"></script>
</body>
</html>


propertyModelValueとか、とてもWicket風。Mayaaを目指したらWicketに似てきた。最初からWicket風につくるべきだったか?

Javascriptクライアントサイドテンプレートエンジンのプロトタイプ

前回の続きです。

とりあえず、それっぽく動くようになってきたので、公開してみます。
http://s-ishigami.appspot.com/files/test.html

作ってて思ったんですが、テンプレートエンジンというより、jQueryベースのHTML変換ライブラリだなと。JSONベースのXSLTだなと。jQueryわかってるJavaScriptプログラマーならいらないんじゃないかと。

でも、僕の周りでMayaaが大ヒットしているのを見ると、こういうアプローチもありなのではと思いもします。

つくり込むなら、JSONPを受け取るfunctionとか色々やりたいですね。
JavaScriptなので横からfunction足し放題なので、アプリごとにカスタマイズもありじゃないかと、

ちなみに、上記URLは、見ての通りappengine(py)の物で、indexにアクセスすると、python練習のために作ったFizzBuzzが出力されますが、見ないでね!