JavaScriptで一文字ずつspanで囲む方法【主に一文字ずつのアニメーションに使用】

文字を一文字ずつアニメーションさせようとする場合は一文字ずつspanタグで囲んでcssでアニメーションさせるのが一般的だと思いますが、そのアニメーションさせる文字数が5文字程度であれば手作業でspanタグで囲んでもいいのですが20や30文字と文字数が多くなるにつれてその作業は大変になります。

この記事では文字を span タグで手作業で囲む作業を JavaScript を使用して楽にする方法を紹介します。

前提

この記事では【簡易版】と【ES6版(class構文)】を紹介します。
前提として以下のHTMLをサンプルとして使用します。

HTML

<p class="js-span-wrap-text">
  これは一文字ずつアニメーションする<br />
  もじのサンプルです
</p>

デモ

この記事で紹介するjsを使用してspanタグで一文字ずつ囲みCSSでのアニメーションデモ。

JavaScriptで一文字ずつspanで囲む方法(簡易版)

まずは簡易版を紹介します。

特徴

  • 特定の要素一つのみ対応
  • コピペですぐに使える
  • 改行コード削除
  • brなどのHTML要素は除外

JavaScript

以下のJavaScriptをコピペしてください。
それそれだけで動きます。

function spanWrap() {
  const target = document.querySelector('.js-span-wrap-text');
  const nodes = target.childNodes;

  let convert = [];

  for (let node of nodes) {
    if (node.nodeType == 3) {//テキストの場合
      let text = node.textContent.replace(/\s+/g, '');
      text.split('').forEach((v) => {
        convert.push("<span>" + v + "</span>");
      });

    } else {//テキスト以外
      convert.push(node.outerHTML);
    }
  }
  target.innerHTML = convert.join("");
}

spanWrap();

これだけでdocument.querySelector()に指定した要素(今回は.js-span-wrap-text)の中のテキストがspanで囲まれます。

実行結果

実行すると以下のように自動で全てのテキストをspanで囲ってくれます。

<p class="js-span-wrap-text"><span>こ</span><span>れ</span><span>は</span><span>一</span><span>文</span><span>字</span><span>ず</span><span>つ</span><span>ア</span><span>ニ</span><span>メ</span><span>ー</span><span>シ</span><span>ョ</span><span>ン</span><span>す</span><span>る</span><br><span>も</span><span>じ</span><span>の</span><span>サ</span><span>ン</span><span>プ</span><span>ル</span><span>で</span><span>す</span></p>

解説

要点のみを簡単に解説します。

要素のノードタイプごとに分割

const nodes = target.childNodes;

要素をノードタイプで分割します。

HTMLは「1」
テキストは「3」
を返します。

テキストかHTMLで処理を分岐

for (node of nodes) {
  if (node.nodeType == 3) {//テキストの場合
    let text = node.textContent.replace(/\s+/g, '');
    text.split('').forEach((v) => {
      convert.push("<span>" + v + "</span>");
    });

  } else {//テキスト以外
    convert.push(node.outerHTML);
  }
}
テキストの場合

テキストから空白を削除しspanで囲んで配列に追加します。
※空白を削除しないとカラのspanができてしまうため。

テキスト以外(HTML)

テキスト以外だとその要素をそのまま配列に追加します。

配列を連結して元テキストを差し替え

target.innerHTML = convert.join("");

join()で配列の中身を連結して挿入。

JavaScriptで一文字ずつspanで囲む方法(ES6版(class構文))

続いてES6版(class構文)の場合を紹介します。

特徴

  • 複数の要素に対応
  • 空白削除
  • brなどのHTML要素は除外

簡易版と違うのは複数の要素に対応していることです。

以下のようなHTMLにも.js-span-wrap-text要素全てに対して適応できます。

<p class="js-span-wrap-text">
  これは一文字ずつアニメーションする<br />
  もじのサンプルです
</p>
<p class="js-span-wrap-text">
  サンプルテキストです
</p>

JavaScript

以下のJavaScriptをコピペしてください。

class SpanWrap {
  constructor() {
    this.settings = {
      target: ".js-span-wrap-text"
    }
    this.targets = "";
    this.targetLength = 0;
    this.nodes = [];
    this.convertContents = [];
  }

  init(options) {
    this.setup(options);
    this.getNodes();
    this.convert();
    this.set();
  }

  setup(options) {
    this.settings = Object.assign({
      target: this.settings.target
    }, options || {});
    this.targets = document.querySelectorAll(this.settings.target);
    this.targetLength = this.targets.length;
  }

  getNodes() {
    for (let target of this.targets) {
      let nodes = target.childNodes;
      this.nodes.push(nodes);
    }
  }

  convert() {
    for (let i = 0; i < this.targetLength; i++) {
      this.convertContents.push([]);//カラの配列を追加
      for (let node of this.nodes[i]) {
        if (node.nodeType == 3) {//テキストの場合
          let text = node.textContent.replace(/\s+/g, '');//テキストから空白削除
          text.split('').forEach((v) => {
            this.convertContents[i].push("<span>" + v + "</span>");
          });

        } else {//テキスト以外
          this.convertContents[i].push(node.outerHTML);
        }
      }
    }
  }

  set() {
    for (let i = 0; i < this.targetLength; i++) {
      this.targets[i].innerHTML = this.convertContents[i].join("");
    }
  }

}

簡易版との違いは複数の要素に対しての処理を加えているだけです。

実行

const spanWrap = new SpanWrap();
spanWrap.init();

デフォルトは.js-span-wrap-textに対してですが、変更したい場合は以下のようなオブジェクトを渡してください。

以下は.textのテキストに対してspanで囲む処理を適応します。

spanWrap.init({
  target: ".text"
});

参考にさせていただいた記事

テキストをタイピング風に一文字ずつ表示する

最後に

色々なことに言えますが、こういった同じ処理の繰り返しはプログラムを組んである程度自動化したほうが良いと思います。
今回のspanで囲む処理は元のテキストに変更があったとしてもテキストを差し替えるだけで新しいテキストにspanで囲む処理を適応されますので修正なども楽になると思います。