ラベル JavaScript の投稿を表示しています。 すべての投稿を表示
ラベル JavaScript の投稿を表示しています。 すべての投稿を表示

2006年10月4日水曜日

巨大なFLVを再生中に他のリンクをクリックしてもなかなか移動できない現象を回避する方法

FlipClipでクリップを見ていると、再生の途中で画面内のリンクをクリックして他のページに移動しようとしても、なかなか移動できなくてイライラすることがあったので、これを回避する方法がないものかと考えていたのですが、今日試した方法が有効だったので紹介します。

この現象は、再生している動画のサイズが大きい場合によく起こる現象で、リンクをクリックしてもブラウザはこの大きな動画の再生に忙しいのか、なかなか画面を切り替えてくれません。

そこで考えたのが、クリックした時に、再生している動画をけしてしまうという方法です。
試した方法は簡単で、リンクなどをクリックしてページが切り替わるタイミングで、再生中のフレームを含むdiv要素のinnnerHTMLを空にしてしまうというものです。
コードのイメージはこんな感じです。

>

<script type="text/javascript"><:!--
Event.observe(window, 'beforeunload', function(){
$('clipPlayer').innerHTML='';
});
--></script>
<

最初はbeforeunloadではなくunloadで試してみましたが、タイミングが遅いらしく、効果がありませんでした。
beforeunloadはブラウザによっては動かないといった情報をどこかで見たような気がしたのですが、IE, FireFox, Safariでうまく動いたので、互換性に問題なしと判断し、FlipClipでもこの方法を早速採用しました。

画面の移動がスムースになって、いい感じです。



2006年5月23日火曜日

JSON::Syckで改行を含むデータをダンプすると改行の後にスペースが2個入る

Ajaxる時のサーバとのデータ交換フォーマットとして、JSONを使う時、perlでサーバ側を実装する際にはJSONとかJSON::Syckというモジュールを使うとperlのデータ構造をJSONフォーマットに変換してくれるので便利です。

昨日これを使って、サーバからデータを取得し、textareaに取得したデータを入れるということをしたところ、改行を含んだテキストだと、改行の後にスペースが2個入ってしまうという現象に遭遇しました。
要はこうなってほしいところが、

><

こうなってしまうんです。

><

調べてみると、クライアントで受け取ったJSONデータをevalした段階ですでにスペースを含んでいたので、サーバ側でJSONデータを作成するところに問題がありそうだということがわかりました。
そこで以下のようなスクリプトを書いて、JSON,JSON::SyckがどんなJSONデータを吐き出すか見てみました。
>

#!/usr/bin/env perl
use strict;
use JSON::Syck;
use JSON;

my $data = {
key => "foo\nbar\nbaz\n"
};

print "# Dumped by JSON::Syck $JSON::Syck::VERSION:\n";
print JSON::Syck::Dump($data), "\n";

print "# Dumped by JSON $JSON::VERSION:\n";
print objToJson($data), "\n";

<

結果は以下のとおり。

>

# Dumped by JSON::Syck 0.12:
{"key":"foo\n\
bar\n\
baz\n"}
# Dumped by JSON 1.05:
{"key":"foo\nbar\nbaz\n"}

<

どうやらJSON::Syckを使ってダンプしたデータは、整形のため、改行の後に、スペースが2個入っているようです。それでこれをJavaScriptでevalするとこのスペースもデータとして含まれてしまうため、上記のような現象が起きたみたいです。
そもそも改行後にスペースが入ってしまうのが問題なのか、スペースが入るのは問題なくて、クライアント側のJavaScriptでのパースに問題があるのかわかってませんが、試した限りだと、IEとFirefoxでスペースつきのデータの扱い方が異なるようで、挙動が変わってしまい、困りました。

とりあえず、サーバ側で改行コードを[BR]とかに変換しておいて、クライアント側でそれを改行コードとか、<br />に変換するという風にして対処しましたが、ちょっと付け焼刃的ですかねー。

2006年3月15日水曜日

_blankを使わないで別ウィンドウを開くにはrel="external"を使うのが美しいと思う。

はてなブックマークをみていたら、気になるエントリーを発見。


[戯] target="_blank" を使わないで新しいウィンドウでリンクを開く方法


target="_blank"という書き方がXHTML 1.1 や XHTML Basicに準拠していないので、これらに準拠するようにしつつ、別ウィンドウで開くにはどうすればよいかという話です。

別ウィンドウで開くにはJavaScriptを使えってのが推奨される方法なんですが、onclickを使って定義するのはめんどうということで、この記事では、aタグにclass="popup"という属性を与えておけば、JavaScriptで別ウィンドウを開くということをしています。

この件については友人のHTML、CSSマスターなkawachi君と話したことがあって、そのときは、下のエントリで紹介されているrel="external"という方法を使うのがいいんじゃないかという結論に達しました。


Opening a link in a new window - the valid way


僕は上のエントリを参考に以下のようなjsファイルを用意して使っています。


>

//external.js
function externalLinks() {
if (!document.getElementsByTagName) return;
var anchors = document.getElementsByTagName("a");
for (var i=0; i<anchors.length; i++) {
var anchor = anchors[i];
if (anchor.getAttribute("href") &&
anchor.getAttribute("rel") == "external")
anchor.target = "_blank";
}
}

Event.observe(window,'load', externalLinks, false);

<


使い方は簡単でこのjsファイルをインクルードするだけです。


>


<script type="text/javascript" src="/path/to/prototype.js"></script<
<script type="text/javascript" src="/path/to/external.js"></script>

<


load時にこのファンクションを実行するためにprototype.jsのEvent.observeを使っているため、
prototype.jsもインクルードしています。ここはwindow.onload=externalLinks;に書き換えれば、
prototype.jsは必要ありません。
このファイルをインクルードすると、rel="external"と指定したaタグをクリックすると別ウィンドウで開くようになります。


>


<a rel="external" href="http://www.google.com/">別ウィンドウで開きます。</a>

<


rel="external"というこのリンク先は外部のサイトだよという関連性を示す記述に対して、JavaScriptで別ウィンドウで開くんだよと定義してあげるというアプローチが美しいなと思っています。




2005年9月30日金曜日

続・onload時に複数のfunctionを実行するJavaScript

ちょっと前に書いたonload時に複数のfunctionを実行するJavascriptというエントリーへのkoさんのコメントでaddEventListenerというのを初めて知りました。

これは何なんだろうということで、調べてみると、W3C DOM Level2 のイベントモデルでイベントハンドリングを行う際に使う関数だということがわかりました。

もう少し調べてみると、このDOM Level2、FirefoxなどMozilla系のブラウザには実装されているようなのですが、IEには実装されていないため、addEventListenerが使えず、そのかわりに同等の機能をもったattachEventという関数が定義されているそうです。

将来的にはIEもDOM Level2をサポートして、addEventListenerが使えるようになると思いますが、現状では、ブラウザを判定してaddEventListenerを使うか、attachEventを使うかを切り替える必要がありそうです。うーん、めんどくさい。。

なので、ブラウザに依存しない前回書いた方法もまんざら無駄骨ということもなかったかな、と思っていたんですが、昨日友人にイベントの登録はprototype使えば簡単にできるよといわれ、無駄骨だったことが確定しましたw

prototype.jsを使ったonload時に実行するfunctionの登録はこんな感じです。

>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Event observe sample</title>
<meta http-equiv="Content-Type" content="text/html; charset=euc-jp">
<script type="text/javascript" src="/js/prototype-1.3.1.js"></script>
<script type="text/javascript">
function foo() {alert('foo')}
function bar() {alert('bar')}
function baz() {alert('baz')}

Event.observe(window, 'load', foo, false);
Event.observe(window, 'load', bar, false);
Event.observe(window, 'load', baz, false);
</script>
</head>
<body></body>
</html>
<

prototype.jsで定義されているEvent.observeを使って関数を登録します。引数は最初に対象となるオブジェクトを渡すところ以外はaddEventListenerと同じです。IE6とFirefox1.0.7で動作することを確認しました。
IEと、Firefoxでは関数の実行順が逆になってましたが、DOM Level2でもイベントの実行順は定義されてないことと、そもそも実行順に結果が依存するような関数はひとつにまとめておくべきだと思うので、これは問題にならなそうです。

prototypeにはこれ以外にもまだまだ知らない便利な関数、クラスがたくさんありそうです。prototype恐るべし。

2005年9月19日月曜日

onload時に複数のfunctionを実行するJavaScript

昨日に引き続きJavaScriptねたです。
ページを表示した時点でJavaScriptを実行したい場合、
>

window.onload=function(){ alert('called when window is loaded.'); }
<
のようにwindowオブジェクトのonloadイベントに実行したいfunctionをセットしてやればいいのですが、これだと、オンロード時にひとつのfunctionしか実行できません。
そこで、オンロード時に複数のfunctionを実行できるようなスクリプトを書いてみました。今回のコードはnaoyaさんのprototype.js でデザインパターン - IteratorのエントリにあるIteratorパターンのコードをそのまま借りたリスペクト指向プログラミングになってますw

>

//multiple_onload.js
var OnloadFunction = Class.create();
OnloadFunction.prototype = {
initialize : function(func) {
this.func = func;
},
getFunc : function() {
return this.func;
}
};

var OnloadFunctions = Class.create();
OnloadFunctions.prototype = {
initialize : function() {
this.last = 0;
this.functions = new Array();
},
getFunctionAt : function(index) {
return this.functions[index];
},
appendFunction : function(func) {
this.functions[this.last] = func;
this.last++;
},
getLength : function() {
return this.last;
},
iterator : function() {
return new OnloadFunctionIterator(this);
}
}

var OnloadFunctionIterator = Class.create();
OnloadFunctionIterator.prototype = {
initialize : function(functions) {
this.functions = functions;
this.index = 0;
},
hasNext : function () {
return this.index < this.functions.getLength();
},
next : function() {
return this.functions.getFunctionAt(this.index++);
}
}

var onloadFunctions = new OnloadFunctions();

function multipleOnload () {
var it = onloadFunctions.iterator();
while (it.hasNext()){
var func = it.next();
func.func();
}
}

window.onload = multipleOnload;

<

使用方法はこんな感じです。
動作させるにはprototype.jsが必要なので最初にインクルードしています。

>


<html>
<head>
<script type="text/javascript" src="/js/prototype-1.3.1.js" ></script>
<script type="text/javascript" src="/js/multiple_onload.js" ></script>
<script type="text/javascript">
functon foo() {alert('foo');}
functon bar() {alert('bar');}
functon baz() {alert('baz');}

onloadFunctions.appendFunction(new OnloadFunction(foo) );
onloadFunctions.appendFunction(new OnloadFunction(bar) );
onloadFunctions.appendFunction(new OnloadFunction(baz) );
</script>
</head>
<body>
...
</body>
</html>

<
まず、multiple_onload.jsをインクルードしておきます。そして、定義したfunctionをappendFunctionメソッドを使ってセットしておきます。
これでfoo,bar,bazがオンロード時に順番に実行されます。


2005年9月18日日曜日

画像のスワップをクラス名で制御するJavaScript(プリロード付き)

ちょっとJavaScriptをまじめに勉強しようかと思い始めました。
というわけで、手始めにマウスオーバーすると別画像に切り替わるスクリプトを書いてみました。
マウスオーバーで画像が切り替わるなんて、Dreamweaver使えばJavaScriptを知らなくてもできてしまうんですが、Dreameweaverが吐き出すコードはHTMLタグの中にonload="xxxxxxxx"とか、onmouseover="xxxxx"をやたらと追加して、非常に見づらいのです。
これだとテキストエディタでちょろっとHTMLを編集したい時に大変ですし、きっとGoogleさんもクロールしにくいでしょうから、imgタグに予めスワップ用に設定しておいたクラス名を記述しておけば、マウスオーバー、マウスアウトで画像が切り替わるようにしてみました。

作成したスクリプトはこんな感じです。クラス名から要素を取得するところはprototype.jsから借りてきました。

>

// swap_image.js
var imagesNormal = new Object();
var imagesHilite = new Object();

function SwapImage (name,normalSrc,hiliteSrc) {
this.name = name;
this.normalSrc = normalSrc;
this.hiliteSrc = hiliteSrc;
}

function setupSwapImages(swapImages) {
preloadSwapImages(swapImages);
setupSwapEvent(swapImages);
}


function preloadSwapImages (a) {
for (var i=0; i < a.length; i++){
imagesNormal[a[i].name] = new Image();
imagesNormal[a[i].name].src = a[i].normalSrc;
imagesHilite[a[i].name] = new Image();
imagesHilite[a[i].name].src = a[i].hiliteSrc;
}
}

function setupSwapEvent (a) {
for (var i=0; i<a.length; i++){
var images = getElementsByClassName(a[i].name);
for (var j=0; j<images.length; j++){
var image = images[j];
if (image.getAttribute("src")){
image.onmouseover = function () {swapImage( this, "hilite" );}
image.onmouseout = function () {swapImage( this, "normal" );}
}
}
}
}

// 2006-05-15追記
function swapImage(image,type){
if (type=="hilite") {
image.src = imagesHilite[image.className].src;
} else if (type=="normal") {
image.src = imagesNormal[image.className].src;
}
}
// 追記ここまで(こぴぺしわすれ・・。)

// copied from prototype.js v1.3.1
// http://prototype.conio.net/
function getElementsByClassName (className) {
var children = document.getElementsByTagName('*') || document.all;
var elements = new Array();

for (var i = 0; i < children.length; i++) {
var child = children[i];
var classNames = child.className.split(' ');
for (var j = 0; j < classNames.length; j++) {
if (classNames[j] == className) {
elements.push(child);
break;
}
}
}

return elements;
}
<

実際の使用例はこんな感じです。クラス名により画像のスワップを制御するようにすることで、HTMLがすっきりしました。さらにhead要素内のscriptタグを削除してもValidなHTMLのままってところもいいですよね。

>


<html>
<head>
<script type="text/javascript" src="/js/swap_image.js" ></script>
<script type="text/javascript">

var swapImages = [
new SwapImage('class01','/images/foo.gif', '/images/foo_onmouse.gif'),
new SwapImage('class02','/images/bar.gif', '/images/bar_onmouse.gif'),
new SwapImage('class03','/images/baz.gif', '/images/baz_onmouse.gif')
];

window.onload = function () {setupSwapImages(swapImages);};
</script>
</head>
<body>

<img src="/images/foo.gif" class="class01" />
<img src="/images/bar.gif" class="class02" />
<img src="/images/baz.gif" class="class03" />

</body>
</html>
<