JavaScriptのすぐ使える7つのユーティリティ関数

久々に[自ブログからの転載](https://ka2.org/ready-to-usable-the-7-javascript-utility-functions/)。

私が自作アプリケーション内のJavaScriptで使っているもので、すぐに使えて、わざわざ外部ライブラリを読み込むまでもないユーティリティ関数がいくつかあるので、それらを紹介してみようかと。

オリジナルのソースでは、いくつかはjQueryの`.ready()`イベントのハンドラー内に定義しているのだが、特にjQueryに完全依存するようなスクリプトでもないので、素のJavaScriptだけでも動作するように修正するのはそこまで難しくはないと思う。まぁ、記述的に簡潔になるのでjQueryありきのままで掲載していく。


## 1. 自己のURLのGETパラメータを連想配列（オブジェクト）に格納する

```js:
/**
 * Return as an object by parsing the query string of the current URL
 */
$.QueryString = (function(queries) {
  if ('' === queries) { return {}; }
  var results = {};
  for (var i=0; i<queries.length; ++i) {
    var param = queries[i].split('=');
    if (param.length !== 2) { continue; }
    results[param[0]] = decodeURIComponent(param[1].replace(/\+/g, ' '));
  }
  return results;
})(window.location.search.substr(1).split('&'));
```

これは、現在表示している自身のWEBページのURL文字列をパースして、GETパラメータ部を連想配列（JavaScriptのオブジェクト）形式として返してくれる関数だ。カプセル化して自動実行するようにしてあるので、jQueryの`.ready()`イベント発生後であればハンドラー内のどこからでも`$.QueryString`と変数的に参照できる。

例えば、自己URLが`http://example.com/index.html?a=b&c=2#top`としてアクセスされていた場合、`$.QueryString`には`{ a:"b", c:"2" }`が格納されていることになる。


## 2. CSSスタイルの`em`単位をピクセル値に変換して取得する

```js:
/**
 * Convert unit from em to pixels
 *
 * @param int em [optional]
 */
$.em2pxl = function(em) {
  if (em === undefined) { em = 1; }
  var div = $('<div style="width:'+em+'em;"></div>').appendTo('body');
  var pixel = div.width();
  div.remove();
  return pixel;
};
```

CSSスタイルでプロパティの定義値として使われる`em`という単位は、`1em`が **親要素の** 1文字の高さと同じサイズになる。最上位のルート要素を起点とする`rem`と異なり、「 **親要素** 」を起点にするというのが曲者で、HTMLの入れ子構造が複雑になってくるとある特定の要素についてその親要素を起点とした`1em`が一体実際どれくらいのサイズなのかが不明瞭になることがままある。そこで、この関数を使うことで、計測したい`em`値を`<body>`タグを起点として再計測を行い、ピクセル値として取得できる。jQuery等で要素のサイジングを行う場合などに参照値として利用できる。
なお、引数に何も指定しないで、`$.em2pxl()`とコールすると`1em`のピクセルサイズが返ってくる仕様だ。


## 3. 指定した文字列の幅を計測する

```js:
/**
 * Survey the width of strings
 *
 * @param string str
 * @param bool stripTags [optional]
 */
$.strWidth = function(str) {
  if ('' === arguments[0]) { return 0; }
  var ruler = $('<span style="visibility:hidden;position:absolute;white-space:nowrap;"></span>').appendTo('body');
  var width;
  if (arguments[1] !== undefined && arguments[1]) {
    str = $('<div/>').html(str).text();
  }
  width = ruler.text(str).get(0).offsetWidth;
  ruler.remove();
  return width;
};
```

WEBページのフォントとしてプロポーショナル・フォント（可変幅フォント）を利用していると、文字数×フォントサイズの式で計算された文字列幅は、実際の文字列幅と異なってしまう。そこで特定の文字列についてその全体の正確な横幅が欲しい時などにこの関数を使うことで、文字列全体の横幅を返してくれる。jQueryなどで要素のサイズをきっちり指定したい時などに利用できる関数だ。

例えば、等幅フォントと可変幅フォントでの文字列サイズの差は次のようになる。

```js:
// 等幅フォント
$('body').css({ fontFamily: 'monospace' });
console.info([ $.strWidth('abc'), $.strWidth('ａｂｃ'), $.strWidth('ABC'), $.strWidth('ＡＢＣ') ]);
//=> [ 20, 39, 20, 39 ]

// 可変幅フォント
$('body').css({ fontFamily: 'sans-serif' });
console.info([ $.strWidth('abc'), $.strWidth('ａｂｃ'), $.strWidth('ABC'), $.strWidth('ＡＢＣ') ]);
//=> [ 24, 28, 31, 36 ]

```


## 4. 画像サイズを計測する

```js:
/**
 * Survey the size of image
 *
 * @param string src [optional]
 */
$.imageSize = function(src) {
  var imgSize = { w: 0, h: 0 };
  var img = new Image();
  img.src = src;
  imgSize.w = img.width;
  imgSize.h = img.height;
  return imgSize;
};
```

画像ソースのオリジナルサイズを計測する関数だ。`<img>`タグの`src`属性に指定する値を引数として与えてあげれば、外部URLの画像だろうが、base64エンコードされた画像バイナリの文字列だろうが、読み込んでサイズを計測して`{w:(横幅ピクセル値),h:(縦幅ピクセル値)}`のオブジェクトを返してくれる。呼び出し元での使い方は次のようになる。

```js:
var imgSize = $.imageSize( $('img').attr('src') );
$('img').attr( 'width', imgSize.w/2 ).attr( 'height', imgSize.h/2 );
```

なお、指定した`src`の値が無効だったり、画像URLがNot Foundだった場合はサイズとして縦横それぞれ0ピクセルのオブジェクトが返るので、画像のエラー判定にも使える。

内部処理でjQueryに依存していないので、そのまま素のJavaScript関数として利用できる。


## 5. 数値エンティティと実体文字列の相互変換

```js:
/**
 * Perform mutual conversion between the actual strings and the html entity
 * 
 * @param string str [required]
 * @param string proc [optional] "decode" (is default) or "encode"
 */
$.htmlEntities = function(str, proc) {
  if ( 'encode' === proc ) {
    var buffer = [];
    for ( var i=str.length-1; i>=0; i-- ) {
      buffer.unshift( ['&#', str[i].charCodeAt(), ';'].join('') );
    }
    return buffer.join('');
  } else {
    return str.replace( /&#(\d+);/g, function( match, dec ) {
      return String.fromCharCode( dec );
    });
  }
};
```

指定した文字列を数値エンティティにエンコード（`$.htmlEntities( '文字列', 'encode' )`）したり、数値エンティティの文字列をデコード（`$.htmlEntities( ’数値エンティティ文字列’ )`）する関数。この関数はPHPの`mb_encode_numericentity`や`mb_decode_numericentity`の関数と互換性があるので、JavaScriptでエンコードした文字列をPHP側でデコードしたり、その逆も可能だ。この関数やHTMLエンティティについての詳しい事については[以前Qiitaにまとめ記事を書いた](http://qiita.com/ka215/items/ace36f55c3ad1297de81)のでそちらも参照してほしい。

変換例は下記の通り：

```js:
console.info( $.htmlEntities( '<em>強調文字</em>', 'encode' ) );
//=> &#60;&#101;&#109;&#62;&#24375;&#35519;&#25991;&#23383;&#60;&#47;&#101;&#109;&#62;

console.info( $.htmlEntities('&#60;&#101;&#109;&#62;&#24375;&#35519;&#25991;&#23383;&#60;&#47;&#101;&#109;&#62;') );
//=> <em>強調文字</em>
```

なお、この関数もjQueryに依存していないので、素のJavaScriptでそのまま使える。


## 6. マルチバイト文字に対応した文字列処理

```js:
/**
 * Functions supported multibyte string
 */
$.isSurrogatePear = function( upper, lower ) {
  return 0xD800 <= upper && upper <= 0xDBFF && 0xDC00 <= lower && lower <= 0xDFFF;
};
$.mb_strlen = function( str ) {
  var ret = 0;
  for (var i = 0; i < str.length; i++,ret++) {
    var upper = str.charCodeAt(i);
    var lower = str.length > (i + 1) ? str.charCodeAt(i + 1) : 0;
    if ( $.isSurrogatePear( upper, lower ) ) { i++; }
  }
  return ret;
};
$.mb_substr = function( str, begin, end ) {
  var ret = '';
  for (var i = 0, len = 0; i < str.length; i++, len++) {
    var upper = str.charCodeAt(i);
    var lower = str.length > (i + 1) ? str.charCodeAt(i + 1) : 0;
    var s = '';
    if( $.isSurrogatePear( upper, lower ) ) {
      i++;
      s = String.fromCharCode( upper, lower );
    } else {
      s = String.fromCharCode( upper );
    }
    if ( begin <= len && len < end ) { ret += s; }
  }
  return ret;
};
```

Unicodeの4バイト文字にも対応している`strlen`（文字列長取得）関数と`substr`（文字切り出し）関数だ。日本語などのマルチバイト文字を処理する時用の関数となる。使い方はそれぞれ下記を参照してほしい。

### マルチバイト文字対応型の文字列長を取得する

PHPの`mb_strlen()`メソッドと同等の関数で、実際には下記の例がこの関数の処理を端的に表している。
Unicodeでは1文字で3～4バイトとなるマルチバイト文字があるため、その文字が含まれる文字列に対して切り捨てや抽出を行う場合、正確に文字数をカウントする関数が必要になるのだ。この`$.mb_strlen()`はそのためのものだ。

```js:
var str = '三国志の人物・彭𣴎の「𣴎」の字は4バイト文字だ'; //:23文字
console.info([ str.length, $.mb_strlen(str) ]);
//=> [ 25, 23 ]
```

### マルチバイト文字対応型の文字列の切り出し

PHPの`mb_substr()`メソッドと同等の関数で、これも実際の下記の切り出し処理の例を見るのが一番理解しやすい。
文字列を切り出す範囲にUnicodeの3～4バイト文字が含まれていると、従来のsubstrで文字列を切り出すと分割点にあるマルチバイト文字がバイナリ的に分断されてしまい、文字化けが発生してしまうことがある。これを防ぐためには正確な文字数を算出して切り出しを行う必要がある。この`$.mb_substr()`はそのための関数である。

```js:
var str = '三国志の人物・彭𣴎の「𣴎」の字は4バイト文字だ';
console.info([ str.substr(0, 9), $.mb_substr(str, 0, 9) ]);
//=> [ '三国志の人物・彭�', '三国志の人物・彭𣴎' ]
```

<div class="clearfix"><br></div>

なお、これらの関数はともにjQueryに依存していないので、JavaScriptが有効ならどこでも利用可能である。


## 7. 文字列からタグを除去する

```js:
/**
 * Strip tags from specified strings
 */
$.strip_tags = function( str, allowed ) {
  allowed = ( ( ( allowed || '' ) + '' ).toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join('');
  var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi, commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
  return str.replace( commentsAndPhpTags, '' ).replace( tags, function ($0, $1) {
    return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
  });
};
```

指定の文字列からHTMLタグを除去する。第二引数に除去しないタグを指定することもできる。実際の使用例は下記の通りだ。

```js:
var mixedStr = '<address><strong>Full Name</strong><br><a href="mailto:#">first.last@example.com</a></address>';
console.info( $.strip_tags(mixedStr) );
//=> Full Namefirst.last@example.com

console.info( $.strip_tags(mixedStr, '<a><br>') );
//=> Full Name<br><a href="mailto:#">first.last@example.com</a>
```

これもjQueryに依存していないので、JavaScriptが有効ならどこでも利用可能だ。

<div class="clearfix"><br><hr></div>

──以上、実質の関数的には8つあったのだが、まぁ、_7つ道具_という語呂にも掛けて、マルチバイト文字列制御関数は1つにまとめてしまった次第。結構有用な関数が多いと思うので、必要に応じて是非利用してみてほしい。