如何将所有文本包装在独特的<span>标签中?

4

我希望你能将所有正文文本(每个单词)都包裹在独特的标签中。

包裹前

<body>    
<div>
  <p>word word </p>
  <div>word word</div>
    <ul>
        <li>word word</li>
    </ul>
  </ul>
  <p>word word <strong>word</strong> </p>
</div>
</body>

包装后

<body>
<div>
    <p><span id="1">word</span> <span id="2">word</span> </p>
    <div><span id="3">word</span> <span id="4">word</span></div>
    <ul>
        <li><span id="5">word</span> <span id="6">word</span></li>
    </ul>
    </ul>
    <p><span id="7">word</span> <span id="8">word</span> <strong><span id="9">word</span></strong> </p>
</div>
</body>

我尝试使用jQuery实现,但结果并不如预期。
$('body *').each(function(){

        var words = $(this).text().split(" ");

            $(this).empty();
            $t=$(this);
            $.each(words, function(i, v) {
                $t.append('<span>'+v+'</span>');
            });

});

感谢您的提前帮助,Logan。

就性能而言,您不必要地调用了$(this) 3次,而您本可以在顶部设置$t=$(this),然后稍后重复使用$t。 - Zathrus Writer
还有,您关闭了两次无序列表(ul标签)。 - Zathrus Writer
你可以给id,$t.append('<span id="'+i+'">'+v+'</span>')。 - Jitender
@amit <span id="n'+i+'">' 最好不要以数字开头。 - elclanrs
2个回答

11
(function (count) {
  'use strict';
  (function wrap(el) {
    $(el).contents().each(function () {
      // Node.* won't work in IE < 9, use `1`
      if (this.nodeType === Node.ELEMENT_NODE) {
        wrap(this);
      // and `3` respectively
      } else if (this.nodeType === Node.TEXT_NODE) {
        var val = $.trim(this.nodeValue);
        if (val.length > 0) {
          $(this).replaceWith($.map(val.split(/\s+/), function (w) {
            return $('<span>', {id: count = count + 1, text: w}).get();
          }));
        }
      }
    });
  }('body'));
}(0));

更新: 此版本不会静默删除空格 ;)

http://jsfiddle.net/LNLvg/3/

(function (count) {
  'use strict';
  (function wrap(el) {
    $(el).filter(':not(script)').contents().each(function () {
      if (this.nodeType === Node.ELEMENT_NODE) {
        wrap(this);
      } else if (this.nodeType === Node.TEXT_NODE && !this.nodeValue.match(/^\s+$/m)) {
        $(this).replaceWith($.map(this.nodeValue.split(/(\S+)/), function (w) {
          return w.match(/^\s*$/) ? document.createTextNode(w) : $('<span>', {id: count = count + 1, text: w}).get();
        }));
      }
    });
  }('body'));
}(0));

http://jsfiddle.net/mtmqR/1/


ELEMENT_NODE和TEXT_NODE有什么区别?如果我alert ELEMENT_NODE会返回什么,如果我alert TEXT_NODE呢? - Jitender
@amit 抱歉,我不太明白你的问题。 - Yoshi
这似乎会丢失单词之间的空格,并且也会丢失任何前导或尾随的空格。 - jfriend00
空格对于自动换行非常重要,我认为这不能通过CSS模拟。我提到这一点是因为在我的答案中,保留原来存在的空格并将它们放在span标签外面是一项有意义的工作。 - jfriend00
@jfriend00,我包括了第二个版本,它将保留空格。 - Yoshi
显示剩余3条评论

1
这是一个使用纯JavaScript的解决方案,它将单词包装在span中,并保留了文本中的空格,但将它们留在span之外,因此只有单词本身在span中。
function splitWords(top) {
    var node = top.firstChild, words, newNode, idCntr = 1, skipChild;
    var re = /\S/;
    while(node && node != top) {
        skipChild = false;
        // if text node, check for our text
        if (node.nodeType == 3) {
            if (re.test(node.nodeValue)) {
                newNode = null;
                words = node.nodeValue.split(" ");
                for (var i = 0; i < words.length; i++) {
                    if (words[i] === "") {
                        newNode = document.createTextNode(" ");
                        node.parentNode.insertBefore(newNode, node);
                    } else {
                        newNode = document.createElement("span");
                        newNode.id = "word" + idCntr++;
                        newNode.innerHTML = words[i];
                        node.parentNode.insertBefore(newNode, node);
                        if (i < words.length - 1) {
                            newNode = document.createTextNode(" ");
                            node.parentNode.insertBefore(newNode, node);
                        }
                    }
                }
                if (newNode) {
                    node.parentNode.removeChild(node);
                    node = newNode;
                    // don't go into the children of this node
                    skipChild = true;
                }
            }
        } else if (node.nodeType == 1) {
            if (node.tagName == "SCRIPT") {
                skipChild = true;
            }
        }        

        if (!skipChild && node.firstChild) {
            // if it has a child node, traverse down into children
            node = node.firstChild;
        } else if (node.nextSibling) {
            // if it has a sibling, go to the next sibling
            node = node.nextSibling;
        } else {
            // go up the parent chain until we find a parent that has a nextSibling
            // so we can keep going
            while ((node = node.parentNode) != top) {
                if (node.nextSibling) {
                    node = node.nextSibling;
                    break;
                }
            }
        }
    }
}

还有一个可用的演示: http://jsfiddle.net/jfriend00/3mms7/

顺便提一下,我将ID值设为“word1”,“word2”等,因为在HTML5之前,ID值不允许以数字开头。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接