ゼロ幅スペースと改行制御 - ‍ や ­ など見えない文字の使い方

HTML には画面上に表示されないけれど、テキストの振る舞いを制御する「見えない文字」がいくつか存在します。改行位置のヒントを与えたり、絵文字を合成したり、単語の区切りを明示したりと、それぞれ異なる役割を持っています。

見えない文字とは

通常の文字はブラウザに表示されて目で確認できますが、Unicode にはレンダリング上の幅を持たない制御用の文字が定義されています。これらは画面には何も表示されないものの、テキストの折り返しや結合の挙動に影響を与えます。

HTML ではエンティティ名や番号参照を使ってこれらの文字をソースコードに埋め込めます。代表的なものを見ていきましょう。

­ - ソフトハイフン

­(Soft Hyphen)は「ここで改行してもよい」というヒントをブラウザに伝える文字です。通常は何も表示されませんが、その位置で実際に改行が発生したときだけハイフン(-)が現れます。

<p style="width: 120px; border: 1px solid #ccc; padding: 8px;">
  Donau&shy;dampf&shy;schiff&shy;fahrt
</p>

この例ではドイツ語の長い複合語に &shy; を挿入しています。表示幅に余裕があればハイフンは見えませんが、幅が狭くなると &shy; の位置で改行されてハイフンが表示されます。

HTML
CSS
JavaScript
<div id="demo-shy">
    <p class="wide">広い: Donau&shy;dampf&shy;schiff&shy;fahrt&shy;gesellschaft</p>
    <p class="narrow">狭い: Donau&shy;dampf&shy;schiff&shy;fahrt&shy;gesellschaft</p>
</div>
#demo-shy p {
    border: 1px solid #aaa;
    padding: 8px;
    font-size: 15px;
    margin-bottom: 8px;
    font-family: monospace;
}
.wide {
    width: 360px;
}
.narrow {
    width: 140px;
}

幅が広い場合はハイフンが表示されず、狭い場合だけ改行位置にハイフンが現れることがわかります。長い URL や専門用語がレイアウトを崩してしまう場面で便利です。

&zwsp; - ゼロ幅スペース

&#8203;(Zero Width Space、略して ZWSP)は幅ゼロの空白文字です。&shy; と同様に改行可能な位置を示しますが、改行されてもハイフンは表示されません。

&shy;(ソフトハイフン)

改行位置のヒントを与える。改行が発生するとハイフンが表示される。欧文の複合語に適している。

&#8203;(ゼロ幅スペース)

改行位置のヒントを与える。改行が発生してもハイフンは表示されない。URL やファイルパスなど、ハイフンを見せたくない場面に適している。

<p style="width: 200px;">
  /home/&#8203;user/&#8203;documents/&#8203;project/&#8203;src/&#8203;index.html
</p>

ファイルパスの区切りにゼロ幅スペースを挿入しておくと、幅が足りないときにスラッシュの直後で自然に改行されます。ハイフンが表示されないため、パスの見た目を損なうことがありません。

HTML
CSS
JavaScript
<div id="demo-zwsp">
    <p class="path-wide">/home/&#8203;user/&#8203;documents/&#8203;project/&#8203;src/&#8203;index.html</p>
    <p class="path-narrow">/home/&#8203;user/&#8203;documents/&#8203;project/&#8203;src/&#8203;index.html</p>
</div>
#demo-zwsp p {
    border: 1px solid #aaa;
    padding: 8px;
    font-size: 14px;
    margin-bottom: 8px;
    font-family: monospace;
    word-break: normal;
}
.path-wide {
    width: 420px;
}
.path-narrow {
    width: 180px;
}

日本語のテキストでも、ブラウザが適切に改行位置を判断できないケースにゼロ幅スペースを挿入することがあります。

&zwj; - ゼロ幅接合子

&zwj;(Zero Width Joiner)は前後の文字を「結合」させる指示を出す文字です。最も身近な使い方は絵文字の合成です。

<!-- 絵文字の合成例 -->
<p>👨‍👩‍👧‍👦</p>

この家族の絵文字は、実際には 👨 + ZWJ + 👩 + ZWJ + 👧 + ZWJ + 👦 という 7 つの文字の並びです。ZWJ が間に入ることで、対応するブラウザやOS では 1 つの家族絵文字として描画されます。

HTML
CSS
JavaScript
<div id="demo-zwj">
    <p class="emoji">結合あり: 👨‍👩‍👧‍👦</p>
    <p class="emoji">結合なし: 👨👩👧👦</p>
    <p class="small">※ 環境によって表示が異なる場合があります</p>
</div>
#demo-zwj .emoji {
    font-size: 28px;
    margin-bottom: 8px;
}
#demo-zwj .small {
    font-size: 12px;
    color: #888;
}

ZWJ による絵文字合成は OS やブラウザの対応状況に依存します。未対応の環境では個別の絵文字が並んで表示されるだけなので、表示が壊れるわけではありません。

&zwnj; - ゼロ幅非接合子

&zwnj;(Zero Width Non-Joiner)は &zwj; の逆で、前後の文字が結合しないようにする文字です。アラビア語やペルシア語など、文字が自動的に連結する言語で使われます。

たとえばアラビア語では、隣り合う文字が自動的につながって表示されますが、略語の各文字を独立させたい場合に &zwnj; を挿入して合字(リガチャー)を防ぐことがあります。

隣接する文字同士が一体化して 1 つの字形になる仕組み。アラビア文字やデーヴァナーガリー文字で頻繁に起こる。

英語圏や日本語圏の Web 制作で &zwnj; を使う場面はほとんどありませんが、多言語サイトを扱う場合には知っておくと役立ちます。

&wj; とゼロ幅改行なしスペース

&#8288;(Word Joiner)は「ここでは改行しないでほしい」という指示を出す文字です。ゼロ幅スペースの逆の働きをします。

文字番号参照改行への影響
ZWSP&#8203;改行を許可する
Word Joiner&#8288;改行を禁止する

似た機能を持つ文字に U+FEFF(BOM / Zero Width No-Break Space)がありますが、現在の Unicode 仕様ではこの文字は BOM(バイト順マーク)としての用途に限定されており、改行禁止の目的には Word Joiner を使うことが推奨されています。

<!-- この位置では改行させない -->
<p>価格:&#8288;1,200&#8288;</p>

「価格:」と金額の間、金額と「円」の間で改行されることを防ぎたい場合に使えます。ただし、同じ目的であれば &nbsp; のほうが広く知られており、サポートも安定しています。

見えない文字の一覧

ここまでに紹介した文字を整理します。

&shy;(U+00AD)

ソフトハイフン。改行可能位置を示し、改行時にハイフンを表示する。長い単語や URL の折り返し制御に使う。

ZWSP(U+200B)

ゼロ幅スペース。改行可能位置を示すが、ハイフンは表示しない。パスや日本語テキストの折り返しに使う。

ZWJ(U+200D)

ゼロ幅接合子。前後の文字を結合させる。絵文字の合成やアラビア文字の連結制御に使う。

ZWNJ(U+200C)

ゼロ幅非接合子。前後の文字の結合を防ぐ。アラビア語やヒンディー語の合字制御に使う。

Word Joiner(U+2060)

改行を禁止する。数値と単位のように途中で切りたくない箇所に使う。

実務での注意点

見えない文字はその名のとおり画面に表示されないため、デバッグが難しくなることがあります。意図せずコピー&ペーストで紛れ込むと、文字列の比較が失敗したり、検索にヒットしなくなったりする原因になります。

ゼロ幅スペース(ZWSP)が文字列に含まれていると、見た目が同じでも JavaScript の === 比較で false になることがあります。この問題をデバッグするのに最も有効な方法はどれですか?

  • ブラウザの表示を拡大して確認する
  • console.log で文字列を出力する
  • 文字列の各文字のコードポイントを調べる
  • CSS で文字色を変更する
__RESULT__

見えない文字は拡大しても表示されず、console.log でも見えません。charCodeAt() や codePointAt() で各文字のコードポイントを確認すれば、不可視文字が混入しているかどうかを特定できます。

テキストエディタの「不可視文字を表示」機能を有効にしておくと、ソースコード上でこれらの文字の存在を視覚的に確認できます。意図的に挿入する場面以外では、見えない文字がコードに紛れ込まないよう注意しましょう。