noteやcodocなど、文章の途中に有料エリアを設定できるプラットフォームが増えてきました。
大体どのプラットフォームでも課金前から有料エリアの文字数を知ることができるようにデザインされています。
この文字数カウントを自作する際、かつ、有料エリアがHTMLで返ってくる場合。
例えば、
<a href="https://kimizuka.fm">こちら</a>をご覧ください。
というHTMLが返ってくる場合を考えてみると、有料エリアの文字数は「こちらをご覧ください。」の11文字になって欲しいですが、
const text = '<a href="https://kimizuka.fm">こちら</a>をご覧ください。'; const length = text.length; // -> 45
と、text.lengthを取得してしまうと、45文字になってしまいます。
今回は、これを11文字とカウントする方法を考えてみます。
結論
フロントで計測する場合は、一度DOMのinnerHTMLに代入し、textContentのlengthをカウントすることで、ほぼほぼ期待通りのテキスト長を取得することができます。
const div = document.createElement('div'); const text = '<a href="https://kimizuka.fm">こちら</a>をご覧ください。'; div.innerHTML = text; const length = div.textContent.length; // -> 11
関数にするのであればこんな感じです。
function getTextLengthFromHTML(html) { const div = document.createElement('div'); div.innerHTML = html; return div.textContent.length; }
簡易的にサロゲートペアに対応するのであれば、一旦配列に入れてしまうのも良いと思います。
function getTextLengthFromHTML(html) { const div = document.createElement('div'); div.innerHTML = html; return [ ...div.textContent ].length; }
今回は以上です。
関連記事
追記
Intl.Segmenterを使うと、より正確な値が期待できます。
function getJaTextLengthFromHTML(html) { const div = document.createElement('div'); const segmenter = new Intl.Segmenter('ja-JP', { granularity: 'grapheme' }); div.innerHTML = html; return [ ...segmenter.segment(div.textContent) ].length; }