javaScriptとノーブレークスペースについてあれこれ

ノーブレークスペースについて

ノーブレークスペースってなんぞやというと、Web系の人にとってはお馴染みなはずの実体参照" "で表示される空白文字です。Unicode符号は0x00A0。
通常の半角スペース文字は0x0020なので、見た目は同じ空白文字でも実態は違う文字です。

細かいことはwikipedia参照。
ノーブレークスペース - Wikipedia

何でこんな事を書いてるかというと

<!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8">
    <head>
  <body>
    <div id="example">This&nbsp;is&nbsp;a&nbsp;pen</div>
  </body>
</html>

というhtmlに対して

var acutualText = document.getElementById("example").textContent;
var expectedText = "This is a pen";
assertEquals(acutualText, expectedText);

みたいなテストを書いてて通らなかった事がきっかけ。

見分け付ける方法

取り敢えず、文字列をユニコード符号で見てみましょう。

こんな感じのhtmlをブラウザで表示し

<!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8">
    <head>
  <body>
    <div id="whitespace">This is a pen</div>
    <div id="nbsp">This&nbsp;is&nbsp;a&nbsp;pen</div>
  </body>
</html>

コンソールから以下を実行

String.prototype.toCharCode = function (){
    var rtn = "";
    var myself = this;
    this.split("").forEach(function(s, i){
        var c = myself.charCodeAt(i).toString(16).toUpperCase();
        rtn += " U+" + (c.length === 2 ? "00" + c : c);
    })
    return rtn.length > 0 ? rtn.substr(1):"";
}

//ホワイトスペース区切り文字列
var whitespace = document.getElementById("whitespace").textContent;
//ノーブレークスペース区切り文字列
var nbsp = document.getElementById("nbsp").textContent;

console.log("ホワイトスペース(U+0020)区切り:   " + whitespace.toCharCode());
console.log("ノーブレークスペース(U+00A0)区切り: " + nbsp.toCharCode());

結果はこんな感じ

ホワイトスペース(U+0020)区切り: U+0054 U+0068 U+0069 U+0073 U+0020 U+0069 U+0073 U+0020 U+0061 U+0020 U+0070 U+0065 U+006E

ノーブレークスペース(U+00A0)区切り: U+0054 U+0068 U+0069 U+0073 U+00A0 U+0069 U+0073 U+00A0 U+0061 U+00A0 U+0070 U+0065 U+006E

太字強調した部分は区切り文字ですが、ちゃんとノーブレークスペース文字が"U+00A0"として取得出来ている事が分かります。

空白文字の違いなんかどうでもいいからさっくり文字列比較したい

正規表現の"\s"はホワイトスペース文字(U+0020)だけに限らず空白文字全般にマッチするので、String.prototype.replace()を使ってホワイトスペースへ置換するのが一番手っ取り早いかと。

例:

//ホワイトスペース区切り文字列
var whitespace = document.getElementById("whitespace").textContent;
//ノーブレークスペース区切り文字列
var nbsp = document.getElementById("nbsp").textContent;

var nbsp_replaced = nbsp.replace(/\s/g, " ");

console.log("ホワイトスペース(U+0020)区切り:   " + whitespace.toCharCode());
console.log("ノーブレークスペース(U+00A0)区切り: " + nbsp.toCharCode());
console.log("置換後: " + nbsp_replaced.toCharCode());
console.log("置換後に一致するか? " + (whitespace === nbsp_replaced));

結果はこんな感じ

ホワイトスペース(U+0020)区切り: U+0054 U+0068 U+0069 U+0073 U+0020 U+0069 U+0073 U+0020 U+0061 U+0020 U+0070 U+0065 U+006E

ノーブレークスペース(U+00A0)区切り: U+0054 U+0068 U+0069 U+0073 U+00A0 U+0069 U+0073 U+00A0 U+0061 U+00A0 U+0070 U+0065 U+006E

置換後: U+0054 U+0068 U+0069 U+0073 U+0020 U+0069 U+0073 U+0020 U+0061 U+0020 U+0070 U+0065 U+006E

置換後に一致するか? true

ノーブレークスペース(U+00A0)が全てホワイトスペース(U+0020)へ置き換えられ、文字列比較も一致することが確認できました・・・と、ここまではChrome上での話。

ブラウザごとのノーブレークスペースの扱い

jsでテキストノードの値を取得するには、大抵textContentかinnerTextを使うかと思いますが、各ブラウザごとにノーブレークスペースの扱いがどうか見てみましょう。

Chrome Firefox IE11
textContent 0x00A0 0x00A0 0x00A0
innerText 0x00A0 (innerTextは未サポート) 0x0020


IE11ではinnerTextでテキストを取得した時に、ノーブレークスペース("0x00A0")がホワイトスペース("0x0020")として取得されます。textContentがサポートされたのはIE9以降(参照
Node.textContent - Web API インターフェイス | MDN
)となるため、textContentが使えないレガシーIEでノーブレークスペースをノーブレークスペースとして扱うのは手間がかかりそう(innerHTMLで文字列"&nbsp;"として取るか)。