@blog.justoneplanet.info

JavaScript、PHP、MySQLを使ったり

contentEditableをJavaScriptで変更する際に気をつける事

■結論

以下のように書けばブラウザに関係なく変更が可能である。

document.getElementById('editor').contentEditable = true;//firefox, chrome, opera, safari, IE...OK

jQueryだと以下のようになる。

$('div#editor').attr('contentEditable', true);//firefox, chrome, opera, safari, IE...OK

■失敗例

以下のように書くとIEでは動作しなくなる。

$('div#editor').attr('contenteditable', true);//firefox, chrome, opera, safari...OK

先頭に連番を振るJavaScript

勿体ないから・・・

function perform(){
    var txt = document.getElementById('input').value;
    var ary = txt.split('\n');
    for(var i = 0; i < ary.length; i++){
        ary[i] = (i + 1) + ',' + ary[i];
    }
    txt = ary.join('\n');
    document.getElementById('input').value = txt;
}

JavaScriptの関数の基本

JavaScriptこそ最も大衆に開かれた言語である。ブラウザあれば誰でも実行・開発できる。このサイトに基本をまとめた記事がないので少しずつまとめることにする。

■定義

function文

function func(){
    alert('hello');
};

JavaScriptでは以下のように関数内で関数を定義することができる。

function func(){
    function getStr(){
        return 'hello';
    }
    alert(getStr());
};

但しfunction文を使用する場合は、関数の先頭で宣言したことになる。従って以下のように記述しても文法上は問題がない。

function func(){
    alert(getStr());
    function getStr(){
        return 'hello';
    }
};

またfunction文を使用する場合は、if文の中などで定義できない。定義した場合はブラウザ毎に挙動が異なってしまう。

関数リテラル

var func = function(){
    alert('hello');
};

■引数

以下のように、定義より少ない引数の数で呼び出すことができる。

var func = function(a, b){
    b = (!b)? 'です' : b;
    alert(a + b);
};
func('これはペン');//これはペンです
func('これはペン', 'だ');//これはペンだ

argumentsオブジェクト

引数の長さが可変の場合は、以下のようにargumentsオブジェクトを使用するのも良い。

var max = function(){
    var n = Number.NEGATIVE_INFINITY;
    for(var i = 0, n = arguments.length; i < n; i++){
        if(arguments[i] > n){
            n = arguments[i];
        }
    }
    return n;
};
alert(max(1, -100, 10, 150, 2457));

また以下のようにargumentsオブジェクトのcalleeプロパティには現在実行中の関数の参照が格納されている。

alert((function(n){
    if(n > 1){
        return n * arguments.callee(n - 1);
    }
    else{
        return 1;
    }
})(5));//120
argumentsオブジェクトの特性
  • lengthプロパティを変更しても要素数には変化が及ばない
  • 引数の参照が渡されているため、代入により引数自体を変更できる
  • 同名の引数があった場合は上書きされてしまう

HTMLドキュメントにJavaScriptを埋め込む

JavaScriptこそ最も大衆に開かれた言語である。ブラウザあれば誰でも実行・開発できる。このサイトに基本をまとめた記事がないので少しずつまとめることにする。

■HTML内に書く

以下のように書くのがベストだと思う。

<script type="text/javascript">
//<![CDATA[
alert('hello!');
//]]>
</script>

ちなみに以下のように書いても問題ない。

<script type="text/javascript">
alert('hello!');
</script>

しかしXHTMLとして書く場合は、「<」などの記号がXMLとして認識される。したがって以下のように囲むのが理論上は正しい。但し、現在のブラウザではこれは正しく認識されない。これがXHTMLの現実である。

<script type="text/javascript">
<![CDATA[
alert('hello<>');
]]>
</script>

閉じタグについて

スクリプト内で閉じタグを文字列として書くときは文字列結合を使用しなくてはならない。

//<![CDATA[
alert('<' + '/script>');
//]]>

以下のように記述するとパースエラーとなる。それはHTMLパーサがJavaScriptのリテラルを理解しない為である。

//<![CDATA[
alert('</script>');
//]]>

このような厄介な問題を回避するためのもっとも良い方法は別ファイルにすることである。

■外部ファイルに書く

以下のように記述することにより外部のスクリプトファイルを読み込むことができる。

<script type="text/javascript" src="./js/sample.js"></script>

属性

現在、以下の様なlanguage属性は非推奨である。type属性が存在する場合は常に無視される。

<script language="Javascript" src="./js/sample.js"></script>

■アンカーの属性に書く

以下のようにjavascript:と書いてからjavascriptを記述すると、内容がjavascriptとして認識される。

<a href="javascript:alert('test');"></a>

また以下のようにイベントハンドラを使用することもできる。

<a onclick="alert('test');"></a>

■script要素

要素数

いくつでも書くことができる。

<script type="text/javascript" src="./js/sample.js"></script>
<script type="text/javascript" src="./js/example.js"></script>

実行

パーサーはHTMLドキュメント内での出現順に処理をするため、head要素内のscriptからbody要素内の要素に以下のようにアクセスすることはできない。

//<![CDATA[
alert(document.getElementById('container').innerHTML);
//]]>

以下のようにロード完了時に実行するようにしなければならない。

//<![CDATA[
window.onload = function(){
    alert(document.getElementById('container').innerHTML);
}
//]]>

E4Xなどはこの記事では解説しない。

tinyMCEのプラグインを作ってみる

samplepluginを作るとする。

■メインの JavaScript

  • plugins/sampleplugin
(function(){
    /**
     * include language files
     */
    tinymce.PluginManager.requireLangPack('sampleplugin');

    /**
     * define plugin
     */
    tinymce.create(
        "tinymce.plugins.SamplePlugin",
        {
            /**
             * init
             * @param {Object} editor : tinymce
             * @param {string} url
             */
            "init" : function(editor, url){
                editor.addCommand(
                    "sample",
                    function(){
                        //コマンドが実行された時のアクション
                    }
                );
                editor.addButton(
                    "sample",
                    {
                        "title" : "sampleplugin.desc",
                        "cmd"   : "sample"
                    }
                )
            },

            /**
             * getInfo
             * information of plugin
             */
            "getInfo" : function (){
                return {
                    "longname"  : "Sample tinyMCE Plugin",
                    "author"    : "Mitsuaki Ishimoto",
                    "authorurl" : "http://justoneplanet.info",
                    "infourl"   : "http://justoneplanet.info",
                    "version"   : tinymce.majorVersion + "." + tinymce.minorVersion
                }
            }
        }
    );

    /**
     * setup plugin
     */
    tinymce.PluginManager.add(
        "sampleplugin",
        tinymce.plugins.SamplePlugin
    );
})();

■ 言語ファイル

  • plugins/sampleplugin/langs/ja.js
  • plugins/sampleplugin/langs/en.js
tinyMCE.addI18n('ja.sampleplugin',{
    desc : 'さんぷる'
});
tinyMCE.addI18n('en.sampleplugin',{
    desc : 'sample'
});

<mm_file>から</mm_file>を抜き出す

Dreamweaverのテキスト検索結果を保存するとxmlファイルになる。そっからファイル名だけを抜き出す。

$(function(){
    $('input#button').click(function(){
        var str = '';
        var txt = $('textarea#textarea').val();
        var ary = txt.split('<mm_file>');
        for(var i = 0; i < ary.length; i++){
            ary[i] = ary[i].substring(0, ary[i].indexOf('</mm_file>'));
        }
        var txt = ary.join("\n");
        $('textarea#textarea').val(txt);
    });
});

jQueryを使用。

JavaScriptで要素のスタイルを取得する

var str = '';
var declarations = getComputedStyle(document.getElementById('header'), '');
for(var i in declarations){
    if(!i.match(/[0-9]+/) && declarations[i] && typeof declarations[i] === 'string'){
        str += i + ':' + declarations[i] + '\n';
    }
}
alert(str);

以上。

JavaScriptからstylesheetにアクセスする

JavaScriptからlinkで読み込んだstylesheetにアクセスできる。

var str = '';
for(var i = 0; i < document.styleSheets.length; i++){
    var stylesheet = document.styleSheets[i];
	for(var i2 = 0; i2 < stylesheet.cssRules.length; i2++){
		var rule = stylesheet.cssRules[i2];
		str += rule.cssText + "\n";
	}
}

以上。

svgをやりたいからRaphaëlを使ってみる

実践あるのみ。o(^O^*=*^O^)oでも面倒だからjQueryを使用する。

■円を描く

JavaScript

$(function(){
    // 描画エリアをidで指定
    var paper = Raphael("notepad", 320, 50);
    // 円を描く
    var circle = paper.circle(20, 20, 10);
});

HTML

<div id="notepad"></div>

サンプル

おー。できた(*^-^)

■パスを描く

JavaScript

$(function(){
    // 描画エリアをidで指定
    var paper = Raphael("notepad", 320, 200);
    // パスを描く
    var path = paper.path("M25 25L20 65L25 105L65 110L105 105L110 65L105 25L75 20L45 25L40 55L45 85L65 90L85 85L90 65L85 45L75 40L65 45L60 55L65 65");
});

サンプル

ふむふむ。分かってきた。パスの書き方を見てみると相対的でも座標を指定できるらしい。

■文字を描く

JavaScript

$(function(){
    // 描画エリアをidで指定
    var paper = Raphael("notepad", 320, 30);
    // 文字を描く
    var string = paper.text (90, 10, "なめらかなベクターグラフィックス。\nSVGです。");
});

サンプル

おー。どうやらセンタリングされるらしいな★

■ドラッグ&ドロップしてみる

JavaScript

$(function(){
    var paper = Raphael("notepad", 500, 100);
    var circle = paper.circle(50, 40, 10);
    circle.attr("fill", "#55ccff");
    circle.attr("stroke", "#55ccff");
    circle.drag(
        function(dx, dy){// move
            this.attr({
                "cx" : this.ox + dx,
                "cy" : this.oy + dy
            });
        },
        function(){// start
            this.ox = this.attr("cx");
            this.oy = this.attr("cy");
            this.attr({"opacity" : 0.5});
        },
        function(){// end
            this.attr({"opacity" : 1});
        }
    );
});

サンプル

おー。キター(゜∀゜)

■DOM

DOMが取り出せるらしいぞ!

JavaScript

$(function(){
    var paper = Raphael("notepad", 320, 50);
    var circle = paper.circle(20, 20, 10);
    circle.attr('fill', 'blue');
    circle.node.onclick = function(){
        alert('hello!');
    }
});

サンプル

jQueryと組み合わせは・・・

$(function(){
    var paper = Raphael("notepad", 320, 50);
    var circle = paper.circle(20, 20, 10);
    circle.attr('fill', 'blue');
    $(circle.node).click(function(){
        alert('hello!');
    });
});

サンプル

むはっ★

$(function(){
    var paper = Raphael("notepad", 320, 50);
    var circle = paper.circle(20, 20, 10);
    circle.attr('fill', 'blue');
    $(circle.node).click(function(){
        circle.hide();
    });
});

サンプル

■参考

Raphael Reference

もっともっとFile APIを使ってサーバ側で受け取ってみる

前回の記事に以下の質問がありました。

質問なのですがバイナリデータはsendでは送れないのでは?
自分も同じようなことをしているのですがFireFox限定のsendAsBinaryでないと無理でした。
どのようにして転送してるのでしょうか?

えーと。。。(・Θ・;)。。。あの、その。。。うかれててサーバ側を考えてませんでした。すみませんm(_ _)m

■クライアント側のコード

前回のコードとおなじ。

function dragStart(e){
    //e.preventDefault();
}
function dragEnter(e){
    //e.preventDefault();
}
function dragOver(e){
    e.preventDefault();
}
function drop(e){
    var files = e.dataTransfer.files;
    for(var i = 0; i < files.length; i++){
        var request = new XMLHttpRequest();
        request.upload.onprogress = function(e){
            document.getElementById('progress').style.width = ((e.loaded / e.total) * 100 + "%");
        }
        request.upload.onload = function(e){
            document.getElementById('progress').style.width = '0px';
            alert('finished');
        }
        request.open('post', "./index.html");
        request.send(files[i]);
    }
    e.preventDefault();
}

サーバー側

今回のケースは、$_FILESなどでファイルを取得することはできない。以下のようにして生のPOSTデータを取得しなければならない。

$post = fopen("php://input", "r");
file_put_contents('./test.png', $post);//ファイル名はテストなので固定=3
fclose($post);

ちゃんと受け取れるヽ(^◇^*)/。簡易的なコードなので実環境ではフィルタリングしてください。

■sendAsBinaryとsendを比較して使ってみる

以下はsendAsBinaryを使ったサンプルである。

request.open('post', "./index.html");
request.sendAsBinary(files[i].getAsBinary());

上述のコードが以下のコードに対応する。

request.open('post', "./index.html");
request.send(files[i]);

まとめ

  1. 引数がFileオブジェクトの時はsendを使用する
  2. 引数がBinary文字列の時はsendAsBinaryを使用する
  3. 引数がBinary文字列の時にsendを使用すると途中でデータがカットされてしまう

なるほど!

sendAsBinaryとは

A variant of the send() method that sends binary data.

sendAsBinaryはsendメソッドの変異体らしい。

This data is converted to a string of single-byte characters by truncation (removing the high-order byte of each character).

シングルバイト文字列に変換されるらしい。

$_FILESで受け取りたい時

以下のようにsendAsBinaryを使用する。

request.open('post', "./index.php", true);
var boundary = '------multipartformboundary1276452374015';
request.setRequestHeader(
    'content-type',
    'multipart/form-data; boundary=' + boundary
);
request.setRequestHeader(
    'content-length',
    files[i].size
);
request.sendAsBinary(
    '--' + boundary + '\n' +
    'Content-Disposition: form-data; name="file"; filename="test.png"\n\n' +
    'Content-Type: application/octet-stream\n\n' +
    files[i].getAsBinary() + '\n' +
    '--' + boundary + '--'
);

以下のようにsendAsBinaryを使用する。ちなみにboundaryとは

マルチパートのデータは、この例のように「境界(boundary)」となる行でデータ項目が区切られ、それぞれがContent-Dispositionという説明情報などのあとに実際のデータが続くという形を取ります。

とのことである。

PHPでは以下のように受け取れる。

var_dump($_FILES);
/*
array(1) {
  ["file"]=>
  array(5) {
    ["name"]=>
    string(8) "test.png"
    ["type"]=>
    string(0) ""
    ["tmp_name"]=>
    string(14) "/tmp/sdhfhsg"
    ["error"]=>
    int(0)
    ["size"]=>
    int(10)
  }
}
*/
参考

勉強になったー=3