2010年3月3日

画像のオリジナルサイズをJavaScriptで取得する

カテゴリー: JavaScript — admin @ 12:57 AM

getNaturalSizeにimageオブジェクトを投げてあげるとオブジェクトが返る。

var cache = [];
var getNaturalSize = (function(){
    if(Image.naturalWidth || Image.naturalHeight){
        return function(image){
            return {
                "width"  : image.naturalWidth,
                "height" : image.naturalHeight
            };
        }
    }
    else if(window.opera){
        return function(image){
            if(!cache[image.src]){
                var mem = {
                    "w": image.width,
                    "h": image.height
                };
                image.removeAttribute("width");
                image.removeAttribute("height");
                w = image.width;
                h = image.height;
                image.width = mem.w;
                image.height = mem.h;
                cache[image.src] = {
                    "width"  : w,
                    "height" : h
                };
            }
            return cache[image.src];
        };
    }
    else if(window.attachEvent){
        return function(image){
            if (image[key] && image[key].src === image.src) {
                return image[key];
            }
            run = image.runtimeStyle;
            mem = {
                "w" : run.width,
                "h" : run.height
            }; // keep runtimeStyle
            run.width  = "auto"; // override
            run.height = "auto";
            w = image.width;
            h = image.height;
            run.width  = mem.w; // restore
            run.height = mem.h;
            image[key] = {
                "width"  : w,
                "height" : h,
                "src"    : image.src
            };
            return image[key]; // bond
        }
    }
    else{
        return function(image){
            return {
                "width"  : 100,
                "height" : 100
            };
        }
    }
})();

Firefox、IE6~8、Chrome、Operaで動作する。Safari未検証。

2010年2月22日

スクロール位置を取得する

カテゴリー: JavaScript — admin @ 3:19 AM

var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;// ex) 100(px)

2010年2月14日

createElementでtableを挿入するときのIEの挙動

カテゴリー: JavaScript — admin @ 3:48 AM

以下のコードはIE以外では正常に動作する。

var table = document.createElement('table');
var tr = document.createElement('tr');
var td = document.createElement('td');
var img = document.createElement('img');
document.getElementById('container').appendChild(table).appendChild(tr).appendChild(td).appendChild(img);

以下のコードはIEでも動作する。違いはtbodyを明示してあげる事。

var table = document.createElement('table');
var tbody = document.createElement('tbody');
var tr = document.createElement('tr');
var td = document.createElement('td');
var img = document.createElement('img');
document.getElementById('container').appendChild(table).appendChild(tbody).appendChild(tr).appendChild(td).appendChild(img);

html 4.01

11.2.3 行グループ: THEAD、 TFOOT、及びTBODY要素

TBODY開始タグは、表が本体をただ1つだけ含んでいてヘッダもフッタも含まないという場合を除き、常に必要である。

へー

html 5

table 要素

0 個以上の tbody 要素

ふむふむ

参考

http://w3g.jp/xhtml/dic/tbody

2010年1月29日

IEはradioボタンをappendChildしてもつかえない

カテゴリー: JavaScript, jQuery — admin @ 2:03 AM

IE6~7でラジオボタンをappendChildした場合、そのラジオボタンはクリックできない。ラジオボタンとして致命的なバグである。

■失敗例

以下のようにDOM要素を操作する。

var input = document.createElement('input');
var p     = document.createElement('p');
p.appendChild(input);

ちなみにjQueryを使うと以下のようになる。

var input = document.createElement('input');
var p     = document.createElement('p');
$(p).append(input);

■解決策

IEだけinnerHTMLを使う。

var input = document.createElement('input');
var p     = document.createElement('p');
if(!!(!window.opera && window.attachEvent)){
    p.innerHTML = '<input type="radio" name="id" />';
}
else{
    p.appendChild(input);
}

ちなみにjQueryを使うと以下のようになる。

var input = document.createElement('input');
var p     = document.createElement('p');
if($.browser.msie){
    $(p).append('<input type="radio" name="id" />');
}
else{
    $(p).append(input);
}

2010年1月27日

Shadowboxの設定

カテゴリー: JavaScript — admin @ 1:24 AM

以下のようにHTML側で設定する。

<a href="#" id="shadowbox" rel="shadowbox;width=450;height=110;">open window</a>

script側でも以下のように設定した場合、上手く動作しないことがある。

Shadowbox.open({
    "content" : 'this is a shadow box',
    "player"  : "html",
    "width"   : 300,
    "height"  : 200,
    "options" : {
        "onFinish" : function(){
            alert('test');
        }
    }
});

onFinishが上手く動作しなかった。設定ミスに起因するが、見つけにくいミスでもある。

2010年1月23日

JavaScriptで画像を縦横比を維持しつつ指定サイズに丸める

カテゴリー: JavaScript, jQuery — admin @ 6:02 PM

SVGやcanvasを使えば確かそのままトリミングもできた気がするが、別にそこまでしたくない用の関数。画像のオリジナルサイズを取得し計算する感じだ。面倒なのでjQueryを使う。

var frameWidth  = 700;
var frameHeight = 400;
$('li').css({
    "overflow" : "hidden"
});
$('li img').each(function(){
    var nWidth  = this.naturalWidth  || getNaturalSize(this).width;//ff : ie
    var nHeight = this.naturalHeight || getNaturalSize(this).height;//ff : ie
    if(nHeight < nWidth * (frameHeight / frameWidth)){
        this.width  = nWidth * frameHeight / nHeight;
        this.height = frameHeight;
        $(this).css({
            "width"  : nWidth * frameHeight / nHeight + 'px',
            "height" : frameHeight + 'px',
            "position" : "relative",
            "left" : -((nWidth * frameHeight / nHeight) - frameWidth) / 2 + 'px'
        });
    }
    else{
        this.width  = frameWidth;
        this.height = nHeight * frameWidth / nWidth;
        $(this).css({
            "width"  : frameWidth + 'px',
            "height" : nHeight * frameWidth / nWidth + 'px',
            "position" : "relative",
            "top" : -((nHeight * frameWidth / nWidth) - frameHeight) / 2 + 'px'
        });
    }
});

以下の部分でブラウザ分岐をしている。IEとOpera以外はimgオブジェクトにオリジナルのサイズが格納されたプロパティ(naturalWidth、naturalHeight)を持つ。

var nWidth  = this.naturalWidth  || getNaturalSize(this).width;//ff : ie
var nHeight = this.naturalHeight || getNaturalSize(this).height;//ff : ie

getNaturalSize関数は以下のようになる。

var getNaturalSize = function(image){
    var w, h, key = "actual", run, mem;
    if(window.opera){
    }
    if (image[key] && image[key].src === image.src) {
        return image[key];
    }
    run = image.runtimeStyle;
    mem = {
        "w" : run.width,
        "h" : run.height
    }; // keep runtimeStyle
    run.width  = "auto"; // override
    run.height = "auto";
    w = image.width;
    h = image.height;
    run.width  = mem.w; // restore
    run.height = mem.h;
    image[key] = {
        "width"  : w,
        "height" : h,
        "src"    : image.src
    };
    return image[key]; // bond
};

ちなみにOperaには対応していない。

2010年1月17日

Shadowboxでショートカットキーを使えなくする

カテゴリー: JavaScript — admin @ 9:53 PM

Shadowboxでは「x」や「w」のキーを押すとウィンドが閉じる。しかし、入力欄などがShadowbox内に存在していた場合、この挙動が不具合を生じさせる。そこで以下のようにenableKeysオプションをfalseにする。

Shadowbox.init({
    "language" : 'ja',
    "players"  : ['img', 'html', 'iframe', 'qt', 'wmp', 'swf', 'flv'],
    "enableKeys" : false
});

2010年1月17日

tinyMCEはjQueryのcloneで複製できない

カテゴリー: JavaScript, jQuery — admin @ 3:06 AM

jQueryのtinyMCEプラグインを使用する。

■cloneメソッド

そっくりそのままコピーができるが、コピーされたtinyMCEは機能しないはずだ。この不具合は、tinyMCEをsortable要素にした時にも生じる。恐らく、tinyMCEが内部的にiframeを使用していることに起因するのではないだろうか。

<!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>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>無題ドキュメント</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.js"></script>
<script type="text/javascript" src="tiny_mce/tiny_mce.js"></script>
<script type="text/javascript" src="tiny_mce/jquery.tinymce.js"></script>
<script type="text/javascript">
$(function(){
	$('textarea.tinymce').tinymce({
		theme : "advanced",
		plugins : "safari,pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions,iespell,inlinepopups,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template",
		theme_advanced_buttons1 : "save,newdocument,|,bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,styleselect,formatselect,fontselect,fontsizeselect",
		theme_advanced_buttons2 : "cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numlist,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code,|,insertdate,inserttime,preview,|,forecolor,backcolor",
		theme_advanced_buttons3 : "tablecontrols,|,hr,removeformat,visualaid,|,sub,sup,|,charmap,emotions,iespell,media,advhr,|,print,|,ltr,rtl,|,fullscreen",
		theme_advanced_toolbar_location   : "top",
		theme_advanced_toolbar_align      : "left",
		theme_advanced_statusbar_location : "bottom",
		theme_advanced_resizing : true,
		init_instance_callback : function(){
			var clone = $('div.parts').clone(true);
			$('div.parts').after(clone);
		}
	});
});
</script>
</head>
<body>
<div class="parts">
<form method="post" action="">
<textarea class="tinymce" name="" rows="15" cols="60"></textarea>
</form>
</div>
</body>
</html>

■解決策

document.createElementでtextareaから生成したtextareaに対して、tinymce()する。

<!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>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>無題ドキュメント</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.js"></script>
<script type="text/javascript" src="tiny_mce/tiny_mce.js"></script>
<script type="text/javascript" src="tiny_mce/jquery.tinymce.js"></script>
<script type="text/javascript">
$(function(){
	$('textarea.tinymce').tinymce({
		theme : "advanced",
		plugins : "safari,pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions,iespell,inlinepopups,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template",
		theme_advanced_buttons1 : "save,newdocument,|,bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,styleselect,formatselect,fontselect,fontsizeselect",
		theme_advanced_buttons2 : "cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numlist,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code,|,insertdate,inserttime,preview,|,forecolor,backcolor",
		theme_advanced_buttons3 : "tablecontrols,|,hr,removeformat,visualaid,|,sub,sup,|,charmap,emotions,iespell,media,advhr,|,print,|,ltr,rtl,|,fullscreen",
		theme_advanced_toolbar_location   : "top",
		theme_advanced_toolbar_align      : "left",
		theme_advanced_statusbar_location : "bottom",
		theme_advanced_resizing : true,
		init_instance_callback : function(){
			var div      = document.createElement('div');
			var form     = document.createElement('form');
			var textarea = document.createElement('textarea');
			$(div).attr('class', 'parts');
			$(div).append(form);
			$(form).append(textarea);
			$(textarea).attr('class', 'tinymce');
			$(textarea).attr('rows', '15');
			$(textarea).attr('cols', '60');
			$('div.parts:last').append(div);
			$(textarea).tinymce({
				theme : "advanced",
				plugins : "safari,pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions,iespell,inlinepopups,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template",
				theme_advanced_buttons1 : "save,newdocument,|,bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,styleselect,formatselect,fontselect,fontsizeselect",
				theme_advanced_buttons2 : "cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numlist,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code,|,insertdate,inserttime,preview,|,forecolor,backcolor",
				theme_advanced_buttons3 : "tablecontrols,|,hr,removeformat,visualaid,|,sub,sup,|,charmap,emotions,iespell,media,advhr,|,print,|,ltr,rtl,|,fullscreen",
				theme_advanced_toolbar_location   : "top",
				theme_advanced_toolbar_align      : "left",
				theme_advanced_statusbar_location : "bottom",
				theme_advanced_resizing : true
			});
		}
	});
});
</script>
</head>
<body>
<div class="parts">
<form method="post" action="">
<textarea class="tinymce" name="" rows="15" cols="60"></textarea>
</form>
</div>
</body>
</html>

しっかり機能するtinyMCEが生成されるはずだ。

init_instance_callback

エディタが完成するとコールされる。tinymce()から完成までは時間がかかり、気をつけなくてはいけないのは、その間にscript処理が止まらない。従って、完成した(直後の)エディタに対してscriptからアクセスする場合は、init_instance_callbackを使用する必要がある。

2010年1月11日

JavaScriptでマルチスレッドプログラミング

カテゴリー: JavaScript — admin @ 11:43 PM

JavaScriptで複雑な演算を行った場合、結果が帰ってくるまで他の処理は行えない。それを可能にするには以下の様なコードを使う。

■Workerオブジェクト

メイン

var worker = new Worker('sample.js');
worker.onerror = function(error){
    alert(error.message);//alert is not defined
    alert(error.filename);//file:///C:Desktop/sample.js
    alert(error.lineno);//1
}
worker.onmessage = function(event){
    alert('finished');
}

sample.js

alert('test');

エラーになる。sample.jsではwindowオブジェクトを始めブラウザが生成するオブジェクトにはアクセスできない。従って、以下の様なサンプルを用いる。

メイン

3628800とalertされるはずだ。

var worker = new Worker('sample.js');
worker.onerror = function(){
    alert('error');
}
worker.onmessage = function(event){
    alert(event.data);
}

sample.js

var n = 10;
var func = function(n){
    if(n === 1){
        return 1;
    }
    else{
        return n * func(n - 1);
    }
}
var result = func(n);
postMessage(result);

onmessageで受け取った引数のメンバ

  • data : 3628800
  • QueryInterface : function QueryInterface() { [native code] }
  • type : message
  • target : [object Worker]
  • currentTarget : [object Worker]
  • eventPhase : 2
  • bubbles : false
  • cancelable : false
  • timeStamp : 1263220393472000
  • stopPropagation : function stopPropagation() { [native code] }
  • preventDefault : function preventDefault() { [native code] }
  • initEvent : function initEvent() { [native code] }
  • CAPTURING_PHASE : 1
  • AT_TARGET : 2
  • BUBBLING_PHASE : 3
  • origin :
  • source : null
  • initMessageEvent : function initMessageEvent() { [native code] }

以下のコードで調査した。

var worker = new Worker('sample.js');
worker.onerror = function(){
    alert('error');
}
worker.onmessage = function(event){
    alert(event.data);
    for(var i in event){
        var data   = document.createTextNode(' : ' + event[i]);
        var member = document.createTextNode(i);
        var li     = document.createElement('li');
        li.appendChild(member);
        li.appendChild(data);
        document.getElementById('members').appendChild(li);
    }
}

残念ながらWorkerオブジェクトが使えるのは今のところFirefoxだけである。IE8、Chrome、Opera10、Safari4ではいずれも使えない。

参考

2010年1月5日

二分木(Binary Tree)

カテゴリー: JavaScript, アルゴリズム — admin @ 12:08 AM

全ての接点(ノード)が左と右の子を持つ事ができる。

■Node

まずはノードを定義(二分木用)。

function Node(key, element){
    this._key        = key;
    this._parentNode = null;
    this._lChildNode = null;
    this._rChildNode = null;
}
Node.prptotype = {
    "getKey" : function(){
        return this._key;
    },
    "hasParent" : function(){
        return (this._parentNode != null);
    },
    "getParent" : function(){
        return this._parentNode;
    },
    "setParent" : function(element){
        this._parentNode = element;
    },
    "isEmpty" : function(){
        return ((this._lChildNode == null) && (this._rChildNode == null));
    },
    "hasLeftChild" : function(){
        return (this._lChildNode != null);
    },
    "hasRightChild" : function(){
        return (this._rChildNode != null);
    },
    "getLeftChild" : function(){
        return this._lChildNode;
    },
    "getRightChild" : function(){
        return this._lChildNode;
    },
    "setLeftChild" : function(element){
        this._lChildNode = element;
    },
    "setRightChild" : function(element){
        this._lChildNode = element;
    }
}

なるほど!

■二分木

ノードを利用して二分木を定義。

function BinaryTree(){
    this._root = new Node(null, null);
}
BinaryTree.prototype = {
    "isEmpty" : function(){
        return (this._root.getKey() == null);
    },
    "isRoot" : function(node){
        return (this._root == node);
    },
    "getRoot" : function(){
        return this._root;
    },
    "replaceRoot" : function(node){
        this._root = node;
    },
    "hasParent" : function(node){
        return (node.hasParent());
    },
    "getParent" : function(node){
        return node.getParent();
    },
    "setParent" : function(node, element){
        return node.setParent(element);
    },
    "isEmpty" : function(node){
        return node.isEmpty();
    },
    "hasLeftChild" : function(node){
        return node.hasLeftChild();
    },
    "hasRightChild" : function(node){
        return node.hasRightChild();
    },
    "getLeftChild" : function(node){
        return node.getLeftChild();
    },
    "getRightChild" : function(node){
        return node.getRightChild();
    },
    "setLeftChild" : function(node, element){
        return node.setLeftChild(element);
    },
    "setRightChild" : function(node, element){
        return node.setRightChild(element);
    }
}

デザインパターンでいうところのadapterパターンだ。

参考

次ページへ »