子孫テキストノードを取得してテキストを1文字ずつ分割しタグで囲う

子孫テキストノードを取得して、テキストを1文字ずつ分割しタグで囲うには、次のような方法で実装できます。

関数定義

JavaScriptt (ES6以降)

/**
 * テキストを1文字ずつ分割しタグで囲う
 * @param {Node} baseElem 基準となる親要素
 * @param {string} [wrapTagName="span"] 囲う要素のタグ名
 */
const splitTextCharWrapEachTag = (baseElem, wrapTagName = 'span') => {
	/**
	 * 子孫テキストノードを取得
	 * @param {Node} parentNode 親要素
	 * @returns {Node[]} 取得したテキストノードを返す
	 */
	const getDescendantTextNodes = (parentNode, childNodes = []) => {
		parentNode.childNodes.forEach(childNode => {
			if (childNode.nodeType === 3) {
				childNodes.push(childNode);
			} else if (childNode.nodeType === 1) {
				getDescendantTextNodes(childNode, childNodes);
			}
		});

		return childNodes;
	};

	const textLineNodes = getDescendantTextNodes(baseElem);

	textLineNodes.forEach(textLineNode => {
		const chars = textLineNode.textContent.split('');

		if (!chars.length) return true;

		chars.forEach(char => {
			let charNode;

			if (char.match(/^\s+$/)) {
				charNode = document.createTextNode(char);
			} else {
				charNode = document.createElement(wrapTagName);
				charNode.textContent = char;
			}

			textLineNode.parentNode.insertBefore(charNode, textLineNode);
		});

		textLineNode.remove();
	});
};

ポイント

12行目~22行目:childNodesプロパティで子テキストノードを取得します。
ノードの種類が要素の場合はさらにその子テキストノードを取得しています。

26行目~45行目:取得したテキストノードから文字列を取得し、splitメソッドで1文字ずつ分割します。
分割した文字列はインデントや改行はそのままにし、それ以外の文字列はspan要素で囲います。

使い方

引数

引数名 初期値 説明
第一引数
必須
baseElem Node   基準となる親要素
第二引数 wrapTagName string 'span' 囲う要素のタグ名

サンプルコード

JavaScript

splitTextCharWrapEachTag(document.querySelector('.code-block code'));

JavaScript逆引きリファレンス一覧へ戻る