JavaScriptで全角2文字、半角1文字でカウントする

前の「JavaScriptで文字数でなくてバイト数を求める」だとUTF8ベースなので、1文字が3バイトや4バイトになってしまう…

なので、今回は全角2文字、半角1文字でカウントする関数を作ってみました。
他の国の言葉は知らないので、完全ではないでしょうが、日本語だったらこれで大丈夫かなぁ…

不味かったらご指摘ください。

function bytes(str) {
	str = str.replace(/[。-゚]/g, 'K');
	var hex = '';
	for (var i = 0; i < str.length; i++) {
		hex += str.charCodeAt(i).toString(16);
	}
	return hex.length/2;
}

軽く解説:
まず引数の半角カタカナを1バイトでカウントするために、'K'に置き換えています。
次は、charCodeAt&toString(16)で文字コードを16進数に変換してます。
最後に、16進数に変換した文字列の文字数を2で割ってるだけです。
ちなみに、charCodeAtはUTF16の文字コードを10進数で返してくれます。
んでもって、toString(16)はNumberを16進数にしてくれます。
toString(16)の0埋めは、入力できる文字は0x20からだから気にしなくて良さそうだ。

JavaScriptでダウンロード。 iPhoneフルスクリーンではできない!?

先日作った「みんな大好き!くじ引きメーカー」。
このウェブアプリにくじの内容をダウンロードとアップロードができる機能を追加しようとしたら、iPhoneでフルスクリーンの時にダウンロードができないという事態に…。
今日はその解決方法を記録しとこうと思います。

●まずはダウンロードさせる関数

function downloadData(data) {
	var a = document.createElement('a');
	a.download = 'kuji.txt';
	a.href = 'data:application/octet-stream,'+encodeURIComponent(data);
	a.click();
}

軽く解説:
aタグを作って、download属性、href属性をセットして、強制的にクリックしてるだけです。
ちなみにdownload属性はHTML5から使えるみたいなので、IE9以前とかは使えないみたいですね。
あと、data:スキームも…。

●で、iPhoneフルスクリーンでの挙動
ボタンがピコんってなるだけで、無反応。
クリックイベントは来てるのに…。(.addEventListenerで確認)

●ちなみに、iPhone Safariでは
download属性は効いていないけれどダウンロードできました。
ファイル名がUnknown.txtになってしまう。(ここはあきらめた)

●で、最終どうしたか
downloadDataを呼び出すところで、iPhoneとそれ以外で分岐するようにして、
iPhoneの場合はdownload.htmlにジャンプさせて、download.html側でdownloadDataと同じ処理をするようにしましたとさ。

・分岐部分

var userAgent = window.navigator.userAgent.toLowerCase();
if (userAgent.indexOf('iphone') != -1)	{
	var a = document.createElement('a');
	a.href = './download.html?data='+encodeURIComponent(data);
	a.click();	
} else
{
	downloadData(data);
}

軽く解説:
window.navigator.userAgentでユーザーエージェントが取ってこれるので、toLowerCaseで全て小文字にしてます。
小文字のユーザーエージェントにiphoneという文字列が入ってたら、download.htmlにジャンプ。
入っていなければdownloadDataを呼ぶようにしています。

・download.html

<html>
<head>
<script>
	var data = (window.location.search.substring(1)).replace(/^.*data=/, '').replace(/&.*$/, '');
	var a = document.createElement('a');
	a.download = 'kuji.txt';
	a.href = 'data:application/octet-stream,'+data;
	a.click();
</script>
</head>
<body>
</body>
</html>

軽く解説:
window.location.searchは、URLの?で始まるクエリ部分が取ってこれます。
なので、正規表現で先頭からdata=までを削除して、&から最後まで削除します。
で、残ったのがダウンロードさせたいデータです。
あとはdownloadData関数と同じです。
ちなみに、dataにはURLエンコードされた文字列が入っているので、encodeURIComponentは通しません。

◆まとめ
ファイル名がUnknow.txtになったり、別途Safariが起動したり、気に入らないところはあるものの、ひとまずダウンロード&アップロードができるようになったので、良しとすることにしました。
もっと良い方法があれば、だれか教えて欲しいなぁ・・・。

JavaScriptでスマホアプリもどき

先日公開した「みんな大好き!くじ引きメーカー」を作ってみて思ったこと。

とにかくスマホアプリっぽいのを少ない工数で作りたかったので、JavaScript+CSS+HTML5だけで実装した。
この方法の良かった点と良くなかった点をまとめてみました。

●良かった点
 ・JavaScriptなので、ビルドとかいらない。故に工数がかなり減らせる。
 ・審査とか全くないので、不具合があればすぐに直せて反映できる。
 ・スマホ限定でなく、PC向けも同時開発できる。
 ・Webプログラマーであれば作れる。
●良くなかった点
 ・音の制御がいまいち。特にマナーモードでも鳴ってしまうのが最悪。
 ・画面を縦向き固定にしたかったのだが、できない。
 ・プログラムソースが丸見え。
 ・ブラウザによって、多少動きが違う。

まとめてみて思ったんだけど、やっぱりスマホアプリはXCodeやAndroidStudioでネイティブアプリを作るのが一番かな…。
だけど、工数が減らせるのはかなり魅力的!
あと、メンテナンス性も抜群なので、なしとは言い難い…。

悩ましいところだ。

とにかく少ない工数でできるので、もう一つアプリを作ってみました。
良かったら、こちらもお試しあれ!

みんな大好き!計算ドリルメーカー

JavaScriptで配列をシャッフル

PHPには、shuffleという配列をシャッフルしてくれる関数があるけど、JavaScriptにはない。
なので、Fisher-Yates shuffleというアルゴリズムでシャッフルする関数を作ってみた。

/**
 * 関数:array_shuffle
 * 説明:引数の配列をシャッフルした結果を返す
 * 引数:
 *   array arr 配列
 * 戻り:
 *   array arr シャッフルした配列
 */
function array_shuffle(arr) {
    ret = arr.concat();
    for(var i = ret.length - 1; i > 0; i--){
        var r = Math.floor(Math.random() * (i + 1));
        var tmp = ret[i];
        ret[i] = ret[r];
        ret[r] = tmp;
    }
    return ret;
}

// array_shuffleを実行
var arr = [0,1,2,3,4,5,6,7,8,9];
var result = array_shuffle(arr);
console.log(arr);
console.log(result);

JavaScriptで文字数でなくてバイト数を求める

JavaScriptで、テキストの文字数でなくてバイト数を求める方法。

●まずは文字数
var str = 'This is a test. これはテストです。';
alert(str.length);
●こちらがバイト数
var str = 'This is a test. これはテストです。';
alert(encodeURIComponent(str).replace(/%../g,'c').length);
軽く解説:
encodeURIComponentは特定の文字をUTF8としてURLエンコードする関数。(%xxという形にしてくれる)
これを利用して、%xxを正規表現で'c'という文字に置き換えた文字列の文字数を計算するとバイト数になるというわけ。

jQueryでタグの階層

レンダリング後のタグの階層とその数を調べたかったので、作ってみました。

<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
var myTag = {};
function myTags(elm, tg) {
	tg += (tg===''?'':'>')+$(elm).prop('tagName');
	if (typeof(myTag[tg])==='undefined') myTag[tg] = 0;
	myTag[tg]++;
	if ($(elm).children().length) {
		$(elm).children().each(function() {
			myTags(this, tg);
		});
	}
}
$(function() {
	var tg = '';
	myTags($('html'), tg);
	for (var tg in myTag) {
		console.log(tg + ':' + myTag[tg]);
	}
});
</script>