
[code]

    <section>あいう<b>えおかきく</b>けこさしすせそ</section>
    
[/code]

このようなHTMLがあったとき、５文字ずつに区切りたいとします。  
結果として、`section` タグ内のHTMLを

[code]

    <div>あいう<b>えお</b></div><div><b>かきく</b>けこ<div>さしすせそ</div>
    
[/code]

のようにもともと囲まれている `b` タグは維持しつつ、`div` タグで囲む方法はありますか？  
よろしくお願いします。


CC BY-SA 3.0
2017-10-09T15:59:49.353
38551
2020-12-18T12:03:25.443
2017-10-09T16:06:47.227
19687
19687
question
javascript
JSで指定文字数ごとにタグで囲みたい


> b タグは維持しつつ、div タグで囲む方法はありますか？

あります。数行程度の短いコードで書けるような簡単な方法はないかと思います。

どこが`b`内か調べて、その結果を元にDOMを再構築するのが簡単でしょう。前提条件でコードは変わりますが、

  * 1文字はJavaScriptの1文字とみなす(絵文字などサロゲートペアやNFDは考えない)
  * `section` 要素の直下には `b` 要素とテキスト以外は入らない
  * `b` 要素の直下にはテキストしか入らない

以上のようなシンプルなケースでは、次のようになります。

[code]

    function getBoldFlags(section) {
      var flags = [];
      var offset = 0;
      for (var child = section.firstChild; child; child = child.nextSibling) {
        if (child.nodeType == Node.TEXT_NODE || child.nodeType == Node.CDATA_SECTION_NODE) {
          for (var i = 0; i < child.length; ++i)
            flags.push(false);
        } else if (child.nodeType == Node.ELEMENT_NODE) {
          if (child.tagName == 'B') {
            for (var i = 0; i < child.textContent.length; ++i)
              flags.push(true);
          } else {
            for (var i = 0; i < child.textContent.length; ++i)
              flags.push(true);
          }
        }
      }
      return flags;
    }
    
    function foldWithin(text, flags, width) {
      console.assert(text.length == flags.length, 'getBoldFlags() has a bug?');
      var fragment = document.createDocumentFragment();
      for (var i = 0; i < text.length; i += width) {
        var div = document.createElement('div');
        var end = Math.min(text.length, i + width);
        for (var j = i; j < end;) {
          if (flags[j]) {
            var chunkEnd = j
            for (; chunkEnd < end; ++chunkEnd) {
              if (!flags[chunkEnd])
                break;
            }
            div.appendChild(document.createElement('b')).textContent = text.substring(j, chunkEnd);
            j = chunkEnd;
          } else {
            var chunkEnd = j
            for (; chunkEnd < end; ++chunkEnd) {
              if (flags[chunkEnd])
                break;
            }
            div.appendChild(document.createTextNode(text.substring(j, chunkEnd)));
            j = chunkEnd;
          }
        }
        fragment.appendChild(div);
      }
      return fragment;
    }
    
    var section = document.querySelector('section');
    var boldFlags = getBoldFlags(section);
    var fragment = foldWithin(section.textContent, boldFlags, 5);
    section.textContent = '';
    section.appendChild(fragment);
[/code]

[code]

    b {
      color: red;
    }
[/code]

[code]

    <section>あいうえ<b>おかきく</b>けこ<b>さしすせそ</b>た<b>ち</b>つて<b>となにぬねのは</b>ひふへほま</section>
[/code]


CC BY-SA 3.0
2017-10-18T13:35:16.317
38839
2017-10-18T13:35:16.317
3475
38551
answer