Techracho

ZeroClipboardを使っていて、IEでのみ「未知の実行時エラーです」が発生する場合

このエントリーをはてなブックマーク Share
2010.09.11    javascript, 馬場   タグ: , , , , —    baba   

Webを作っていて、「このタグをコピー」ボタンはよく作ります。

HTML/JavaScriptのみでは、IE以外のブラウザでクリップボードにコピーする機能は対応できないため、基本的にFlashを使うことになります。
それを簡単に実現できるライブラリが、ZeroClipboardです。

使い方は簡単。$関数を作っておき、onloadのタイミングでZeroClipboard.Clientオブジェクトを初期化・設定するだけです。

<html>
<head>
<script type="text/javascript">
function $(id) { return document.getElementById(id); }
function init() {
  clip = new ZeroClipboard.Client();
  clip.setHandCursor(true);
  clip.setText('コピーして欲しい文字列');
  clip.blue('button', 'container');
}
</script>
</head>
<body onload="init();">
  <div id="container">
    <img id="button" src="button.png" />
  </div>
</body>

ところが、HTMLを間違えると、IEでのみ「未知の実行時エラーです」となり、動作しないことがあります。
(Firefox/Chrome/Operaなどでは動作します)

未知の実行時エラーです

未知の実行時エラーです

これは、glue()の第2引数として渡すcontainer要素が、pタグなどになっている場合に発生します。
ZeroClipboardは、内部的にembedタグを生成し、Flashを埋め込んでいるのですが、HTMLの仕様上pのなかにembedは入りません(詳細はDTD参照)。

そのため、HTMLとしては一見Validに見える以下のHTMLでも、

  <p id="container">
    <img id="button" src="button.png" />
  </p>

glue(’button’, ‘container’); とやった時点(InnerHTMLが編集される)で、IEのみ厳密なチェックが入るため、エラーになります。

対策として、container要素は必ずdivなどのembedを含める要素にしましょう。

また当然ですが、内部で$()関数を使うため、jQueryやprototypeとの併用には注意が必要です。

JQuery UI Tabsでタブ表示

このエントリーをはてなブックマーク Share
2010.08.27    HTML, Web, javascript   タグ: , , —    shibachan   

こんにちわ芝原です。

始めてのJavascript関連の投稿です。
お手柔らかにどうぞ。

たくさんのコンテンツをすっきり表示させるための方法として、タブという手法がよくつかわれると思います。
今回は、この便利なタブ表示をJQueryを使って実現する方法を解説します。
動作はこんな感じになります。
http://jqueryui.com/demos/tabs/default.html

必要なファイルのダウンロード

jquery本体とui.core.js、ui.tabs.jsが必要です。

こちらから簡単にダウンロードできますが余計なファイルも多分に入っているので、こちらからファイルを選択してダウンロードしてもいいと思います。

スクリプトの読み込み

必要なファイルを読み込みます。
head内に記述するのが一般的です。

<script type="text/javascript" src="/path/to/jquery.js"></script>
<script type="text/javascript" src="/path/to/ui.core.js"></script>
<script type="text/javascript" src="/path/to/ui.tabs.js"></script>

HTMLの構造

<div id="tabs">
    <ul>
        <li><a href="#tab1">タブに表示する文字</a></li>
        <li><a href="#tab1">タブに表示する文字</a></li>
        <li><a href="#tab3">タブに表示する文字</a></li>
    </ul>
    <div id="tab1">
        タブの中身
    </div>
    <div id="tab2">
        タブの中身
    </div>
    <div id="tab3">
        タブの中身
    </div>
</div>

タブの設定

<script type="text/javascript">
    var $tabs = $('#tabs').tabs();
    $tabs.tabs('select' 1); // 2番目のタブを選択状態にする
                            // デフォルトでは1番目のタブ
</script>

たったこれだけでタブが実装できました。

さらに詳しい情報を知りたい方は、公式ドキュメントをご覧ください。

IEでJavaScriptからButtonのtypeを変更できない

このエントリーをはてなブックマーク Share
2010.07.20    Windows, javascript, 馬場   タグ: , , , —    baba   

JavaScriptで動的にエレメントを作成するときは、

var div = document.createElement('div');

のようにやります。

たとえば、ボタンを作ってそのボタンにクリックイベントを追加、などの良くある処理は、jQueryを使って、

var button = $(document.createElement('button'))
.attr('type', 'button').text('ぼたん');
button.click(function() {
    alert('hello');
});
$('#hoge').next(button);

のようにやると思います。typeを設定しているのは、デフォルトだとsubmit扱いになってしまうからです。

しかしこれ、IEだとエラーになります。

type property can’t be changed

buttonのtypeはなぜか読み取り専用プロパティらしい・・・

ということで、ちょっとダサイですが、以下のようにやって解決。

var button = $('<button type="button">ぼたん</button>');

以上、ちょっとした注意点でした。

nyroModalの使い方

このエントリーをはてなブックマーク Share
2010.06.21    javascript, 馬場   タグ: , , —    baba   

JavaScriptを使ったライトボックスはたくさんありますが、単に表示させるだけでなく、開いたボックス内で簡単な操作をしたいこともありますよね。

そんなときは、jQueryプラグインのnyroModalが高機能で便利です。

http://nyromodal.nyrodev.com/

使い方は簡単。ダウンロードしたJSとCSSを適切に読み込んだら、以下のように書くだけです。

<a href="hoge1.html" class="nyroModal" rel="gal"><img src="hoge1.jpg" /></a>
<a href="hoge2.html" class="nyroModal" rel="gal"><img src="hoge2.jpg" /></a>
<a href="hoge3.html" class="nyroModal" rel="gal"><img src="hoge3.jpg" /></a>

JSを書く必要はありません。class指定のみで大丈夫です。

このうち、rel=”gal” の指定が、グループ化してギャラリーにする(前へ・次へボタンを有効にする)設定です。

galという文字列は何でも良く、もし複数ギャラリーを作りたいときは、グループ名を入れればOKです。

たとえば、以下のようにすれば、hogeギャラリーとpiyoギャラリーができて、それぞれ3枚ずつ画像がある状態に出来ます。

<a href="hoge1.html" class="nyroModal" rel="hoge"><img src="hoge1.jpg" /></a>
<a href="hoge2.html" class="nyroModal" rel="hoge"><img src="hoge2.jpg" /></a>
<a href="hoge3.html" class="nyroModal" rel="hoge"><img src="hoge3.jpg" /></a>

<a href="piyo1.html" class="nyroModal" rel="piyo"><img src="piyo1.jpg" /></a>
<a href="piyo2.html" class="nyroModal" rel="piyo"><img src="piyo2.jpg" /></a>
<a href="piyo3.html" class="nyroModal" rel="piyo"><img src="piyo3.jpg" /></a>

なお、nyroModalは、リンク先の拡張子を見て、動作を変更しています。
.jpgなどの拡張子では、自動的にサイズを調べて、適切なリサイズを行ってくれます。

プログラムから動的に画像を出力する際など、以下のようなURLでは、誤作動を起こすので気をつけて下さい。
http://bpsinc.jp/image/test.jpg?width=50

この場合、以下のようにすれば解決できます。
http://bpsinc.jp/image/test.jpg?width=50&ext=.jpg

jQuery.formとJSONView

このエントリーをはてなブックマーク Share
2010.06.17    HTML, javascript, 馬場   タグ: , , , —    baba   

JSONViewは、Firefoxで application/json なコンテンツを綺麗に表示するプラグインです。
とても便利ですね。

ところが、jQuery.formプラグインなどと組み合わせると、不具合を起こすことがあるので注意が必要です。

一般的なajaxでは、

$.ajax({
  url: 'hogehgoe',
  success: function(data, dataType) {
  }
});

といったやりかたをしますが、このときは特に問題ありません。

しかし、jQuery.formで $(’form’).ajaxSubmit() を使う場合、内部的には$.ajaxは使用されていません。
XMLHttpRequestでは画像データ等のmultipart/form-dataを送信できないため、ダミーのiframeを作って、そこをターゲットにformの送信を実行しています。

//jquery.form.jsの内部コードのイメージ
var iframe = '<iframe id="hoge" onload="irame-onload"></iframe>';
$('form').attr('target', 'hoge');
$('form').submit();

これで、ajaxSubmitをすると、結果がiframeに入ります。
iframeのonloadイベントで、内部のinnerHTMLを見て、結果を呼び出し元に返す処理を行っています。

ここで、iframeの中身がどんな値になるかは、ブラウザの実装依存です。
一般に、プレインテキストやJSON形式が渡ってきたとしても、DOMツリーを構築するためにbodyタグまでは作ってしまうようです。

良くあるパターン

<html>
<head></head>
<body>{"result":"これが結果です"}</body>
</html>

jquery.formプラグインは、ブラウザがbodyタグの中に直接値を入れるか、またはpreやtextareaに値を入れることを期待しています。

しかし、JSONViewを使うと、以下のような綺麗なHTMLを勝手に作ってくれてしまいます。

<html>
<head></head>
<body>
<doctype html="">
  <div id="json">
    {
    <ul class="obj collapsible">
      <li><span class="prop">result</span>: <span class="num">0</span></li>
    </ul>
    }
  </div>
</doctype>
</body>

人間様が見るには良いですが、これではjquery.formプラグインが認識できません。
そんなわけで、doctype html… といった値を丸ごとJSONパースしようとして、エラーになってしまいます。

解決策は、Content-Typeを変えるか、jquery.formを改造するくらいしかありません。

Content-Typeの変更が、一番みんなハッピーになれるはずです。

これに気づかず、JSONViewを入れると動かなくなるWebサイトがあるかもしれませんね。

CakePHP hiddenでclassを設定できない

このエントリーをはてなブックマーク Share
2010.06.15    CakePHP, PHP, javascript, 馬場      baba   

Formヘルパーを使う際、inputタグなどなら、

$form->input('User.name', array('class' => 'test'));

のようにclassを指定できますが、

hiddenタグの場合、class指定は無視されます。

$form->input('User.id', array('type' => 'hidden', 'class' => 'test'));

これは、formヘルパーが以下のように意図的に無視している為で、仕様のようです。

function hidden($fieldName, $options = array()) {
        //(略)
	return sprintf(
		$this->Html->tags['hidden'],
		$options['name'],
		$this->_parseAttributes($options, array('name', 'class'), '', ' ')
	);
}

JavaScriptから制御する際など、hiddenにもclassを付けたいことは多々ありますが、考慮されていないようですね。

直接タイプしても面倒じゃないので、まあそれほど困らないのですが。

<input type="hidden" name="data[User][id]" class="test" />

jQuery.qTipをjQuery1.4で使う

このエントリーをはてなブックマーク Share
2010.06.13    javascript, 馬場   タグ: , —    baba   

よく欲しくなるJavaScriptライブラリに、ツールチップがあります。

jQueryにもたくさんのTooltipプラグインがありますが、使い方が簡単でデザインが綺麗なものということで、qTipを選んでみました。
http://craigsworks.com/projects/qtip/

とても良くできていますが、現状(1.0.0-rc3)では、jQuery 1.4に対応していないようです。
そのまま使うと、以下のエラーが発生します。

$(this).data(”qtip”) is null
Line 138

これは、jQueryのdata()メソッドの挙動が変更になったためです。

以下のサンプルコードを実施すると分かりますが、jQuery 1.3では「undefined」が表示され、1.4 では「null」が表示されます。

<!DOCTYPE html>
<html>
<script type="text/javascript" src="jquery14.js"></script>
</head>
<body>
<h1>test</h1>
<script type="text/javascript">
	alert($('h1').data('hoge'));
</script>
</body>
</html>

JavaScriptでは、nullのtypeはobjectなので、qTipの判定式が誤作動しています。

これを修正するには、qTipのdevelopment版(jquery.qtip-1.0.0-rc3.js)で133行目にあたる箇所に、次の赤い部分を書き加えます。

// Check if element already has qTip data assigned
if(typeof $(this).data(’qtip’) == ‘object’ && $(this).data(’qtip’))
{
// Set new current interface id
if(typeof $(this).attr(’qtip’) === ‘undefined’)
$(this).data(’qtip’).current = $(this).data(’qtip’).interfaces.length;

// Push new API interface onto interfaces array
$(this).data(’qtip’).interfaces.push(obj);
}

これで、jQueryのバージョンを落とさずに便利なライブラリを使えますね。

もちろん、ライセンスは要確認です。

true/false一覧君

このエントリーをはてなブックマーク Share
2010.06.12    C#, C++, PHP, Python, Ruby, java, javascript, プログラミング言語, 馬場   タグ: —    baba   

当然ながら、言語ごとに真偽判定(true/false)の基準が違います。

特にスクリプト系の言語では、明示的に型を指定しないことが多いので、たまに問題になります。

たまたま手元にあった環境で、どんな値が真に判定されるのか、というのをまとめてみました。
新しい言語に突撃するときのメモ代わりにでもして頂ければ幸いです。

検証コードイメージ

a = 0
if a
  print 'true'
else
  print 'false'
end

truefalse

凡例:
× は、コンパイルエラー
ー は、言語仕様にその値が存在しない

備考:
*”" というのは、ナル文字の値を指す(\0なので、実体はゼロという整数値)
array[] というのは、各言語での空の配列を指す
※1 C言語にfalseは存在しない
※2 配列のアドレスを指すため、真と評価される
※3 noticeが発生するが、設定や@で抑制可能

こうしてみると、やはりPHPは変態です。’0′ をfalseと判定する言語はあまりないですね。Webに特化しているのがよくわかります。$_GETなどは文字列で渡ってくるので、確かに手抜きに便利仕様です。

空配列をfalseと判定するのが、PythonとPHPというなんだかおもしろい組み合わせになりました。
良く忘れるけど、rubyはゼロという数字をtrueに判定するので注意が必要ですね。

結局、ミスのしようが無い Java / C# は安全で好きです。
また、ゼロは false、ほかは true と割り切ったC言語も、シンプルで好きです。

jQuery.tablesorter

このエントリーをはてなブックマーク Share
2010.04.20    javascript, 馬場   タグ: , , —    baba   

テーブル並び替えのJSはたくさんありますが、jQuery.tablesorter が簡単に使えて良さそうです。

これの注意点です。

Ajaxで動的にテーブルのデータを追加するようなとき、最初に空のテーブルに対して

$('#table').tablesorter();

を実行して、あとで要素を追加したくなりますが、これだと上手くソートされません。
初期化時に、データを見て並び替えアルゴリズムを判定しているようです。

必ず、データが1件以上あるテーブルに対して初期化しましょう。

ということで、データ追加時に tablesorter() を実行することになりますが、2回以上これを実行すると、クリック時に複数回ソートが行われてしまいます。
2回登録してあると、「昇順→降順」が一瞬で行われ、実質ソートの切り替えができなくなってしまいます。

destroy系の関数が用意されていないので、手動でイベントを削除するのが良いですね。

$('#table th').unbind();
$('#table').tablesorter();

Javascriptでイベントハンドラに好きな値を渡す

このエントリーをはてなブックマーク Share
2010.04.14    javascript, 馬場      baba   

他の言語に慣れていると、Javascriptのスコープチェーンが分かりづらいですね。

たとえば、jQueryでクリックイベントに関数を登録する際、外の変数を参照すると予期せぬ結果になってしまうことがあります。

例:すべてのボタンで3が表示されてしまう

<html>
<head>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
$(function() {
	for (var i = 0; i < 3; i++) {
		$('#button' + i).click(function() {
			alert(i);
		});
	}
});
</script>
</head>
<body>
<button id="button0">0</button>
<button id="button1">1</button>
<button id="button2">2</button>
</body>
</html>

このような場合、以下のようにすることで、意図した動作が実現できます。

for (var i = 0; i < 3; i++) {
	$('#button' + i).click((function(i) {
		return function() {
			alert(i);
		}
	})(i));
}

しかし、毎回これをやるのも面倒なので、好きな引数を渡せるような関数を作ってしまうのも良いですね。
これなら可変長引数にもそのままで対応できます。

$.func = function() {
    var fn = arguments[arguments.length - 1];

    var arg = new Array(arguments.length - 1);
    for (var i = 0; i < arguments.length - 1; i++) {
        arg[i] = arguments[i];
    }

    return function() {
        fn.apply(this, arg);
    }
};

for (var i = 0; i < 3; i++) {
	$('#button' + i).click($.func(i, function(i) {
		alert(i);
	}));
}

jQueryにはdojo.hitch相当のものがないですが、これで似たようなことはできそうです。

4/17追記

jQuery1.4 で追加された jQuery.proxy が dojo.hitch 相当なので、これを使っても良かったですね。
まあお好みで・・・
というより、よく 1.3 までこの関数なしでやってきましたよね。必須だと思うのですが。

古い投稿 »

COPYRIGHT [C] 2009 BEYOND PERSPECTIVE SOLUTIONS LTD. ALL RIGHTS RESERVED.