mtのカスタムブロックでアコーディオンの開く部分をテキストボックスから出力したい。 innerHTMLで要素の中身を取って来るのですが、表示する時に改行コードが抜けます
ポイントは、
innerHTMLで取ったテキストは、そのままでは「改行=表示上の改行」にならない
というところです。
「抜けている」というより、「HTMLとしては存在するけどレイアウト上折り返されていない/正規化されている」状態になっている可能性が高いです。
なぜ改行が消えたように見えるのか?
① innerHTML は「HTML文字列」を返す
たとえばエディタ側のDOMがこうだとして:
<div class="acc-body">1行目
2行目
3行目</div>
const body = document.querySelector('.acc-body').innerHTML;
-
bodyの中身は"1行目\n2行目\n3行目"のような文字列になります(環境によっては\r\n)。 - これをそのまま
MTBlockEditorSetCompiledHtml(`
<div class="accordion__panel">${body}</div>
`);
とすると、フロント側では HTMLのテキストノード内の改行 になります。
HTMLの標準動作では、white-space: normal の要素内では
- 連続スペース・改行・タブなどは「1つのスペース」にまとめられる
ので、見た目上は
1行目 2行目 3行目
みたいに全部つながって見える=「改行が消えたように見える」わけです。
解決パターン 3つ
パターンA:CSSで white-space: pre-line を指定する(JSいじり少なめ)
表示側のアコーディオンの中身にクラスをつけて、CSSで改行を活かす方法です。
1. カスタムブロックスクリプト例
<script>
document.addEventListener('DOMContentLoaded', () => {
if (document.body.dataset.hasCompiledHtml) return;
const title = document.querySelector('.acc-title')?.textContent || '';
const body = document.querySelector('.acc-body')?.textContent || '';
MTBlockEditorSetCompiledHtml(`
<div class="c-accordion">
<button class="c-accordion__head">${title}</button>
<div class="c-accordion__panel js-accordion-panel">
${body}
</div>
</div>
`);
});
</script>
※ .acc-body が <textarea> の場合は .value を使います:
const body = document.querySelector('.acc-body')?.value || '';
2. CSSで改行を活かす
.c-accordion__panel {
white-space: pre-line;
}
-
pre-lineは- 改行コード(
\n)→ 実際の改行として表示 - それ以外のスペースは通常通り
- 改行コード(
という挙動なので、「テキストエリアの感覚」に近くなります。
まずは これを試すのが一番楽 です。
パターンB:JSで \n を <br> に変換してしまう
「どうしても <br> にしておきたい」「HTMLメール的な出力にしたい」という場合は、JS側で変換します。
<script>
document.addEventListener('DOMContentLoaded', () => {
if (document.body.dataset.hasCompiledHtml) return;
const title = document.querySelector('.acc-title')?.textContent || '';
const bodyRaw = document.querySelector('.acc-body')?.value || ''; // textarea想定
const bodyHtml = bodyRaw
.replace(/\r\n/g, '\n') // 改行コードを揃える(Windows対策)
.replace(/\r/g, '\n')
.replace(/\n/g, '<br>'); // 改行 → <br>
MTBlockEditorSetCompiledHtml(`
<div class="c-accordion">
<button class="c-accordion__head">${title}</button>
<div class="c-accordion__panel js-accordion-panel">
${bodyHtml}
</div>
</div>
`);
});
</script>
- この方法では、保存されるHTML自体が
<br>に置き換わります。 - CSSでの
white-space指定は不要です。
パターンC:innerHTML ではなく textContent / value を使う
今、
innerHTMLで要素の中身を取って来るのですが…
と書いてくれたので、ここも重要ポイントです。
-
テキストボックス / textarea の値 →
.value - 通常の要素内テキスト →
.textContent - HTML(タグ含め)ごと欲しい →
.innerHTML
アコーディオンの「開く部分」は基本テキストだけのことが多いので、
-
安全・素直 なのは
.textContent/.value -
.innerHTMLは「ユーザーが<br>や<strong>など直接入れる」ケース向き
です。
もし今こんな感じなら:
const body = document.querySelector('.acc-body').innerHTML;
いったんこう変えてみてください:
// テキストエリアの場合
const body = document.querySelector('.acc-body').value || '';
// divなどの場合
const body = document.querySelector('.acc-body').textContent || '';
その上で
- CSSで
white-space: pre-lineを指定する(パターンA) - もしくは JS で
<br>に変換する(パターンB)
のどちらか、という構成にすると安定します。
「ブログ記事のサンプル」っぽくまとめる書き方(おまけ)
もしこのネタを自分のブログやメモに残すなら、こんな感じで構成しておくと後で見返しやすいです:
- カスタムブロック構成(親:アコーディオン、子:アコーディオンユニット)
- エディタ内HTML構造(
.acc-title,.acc-body) - カスタムスクリプト(
textContent/value+white-space: pre-lineパターン) - フロント側のCSS(アコーディオン開閉+
white-space)