Как я могу использовать Javascript для изменения содержимого узла? - PullRequest
1 голос
/ 03 апреля 2009

Мне нужно использовать Javascript, чтобы сделать три вещи:

  1. Выберите все узлы с классом "foo".
  2. Найдите все слова внутри этих узлов, которые начинаются с "*".
  3. Окружите эти слова <span class="xyz"> ... </span>, где xyz - само слово.

Например, содержание:

<ul>
  <li class="foo">
    *abc def *ghi
  </li>
  <li class="bar">
    abc *def *ghi
  </li>
</ul>

станет

<ul>
  <li class="foo">
    <span class="abc">*abc</span> def <span class="ghi">*ghi</span>
  </li>
  <li class="bar">
    abc *def *ghi    <!-- Not part of a node with class "foo", so
  </li>                     no changes made. -->
</ul>

Как я могу это сделать? (P.S. Решения, включающие jQuery, тоже работают, но кроме этого я бы предпочел не включать никаких дополнительных зависимостей.)

Ответы [ 3 ]

2 голосов
/ 03 апреля 2009

JQuery не требуется:

UE_replacer = function (node) {

   // just for performance, skip attribute and
   // comment nodes (types 2 and 8, respectively)
   if (node.nodeType == 2) return;
   if (node.nodeType == 8) return;

   // for text nodes (type 3), wrap words of the
   // form *xyzzy with a span that has class xyzzy
   if (node.nodeType == 3) {

      // in the actual text, the nodeValue, change
      // all strings ('g'=global) that start and end
      // on a word boundary ('\b') where the first
      // character is '*' and is followed by one or
      // more ('+'=one or more) 'word' characters
      // ('\w'=word character). save all the word
      // characters (that's what parens do) so that
      // they can be used in the replacement string
      // ('$1'=re-use saved characters).
      var text = node.nodeValue.replace(
            /\b\*(\w+)\b/g,
            '<span class="$1">*$1</span>'   // <== Wrong!
      );

      // set the new text back into the nodeValue
      node.nodeValue = text;
      return;
   }

   // for all other node types, call this function
   // recursively on all its child nodes
   for (var i=0; i<node.childNodes.length; ++i) {
      UE_replacer( node.childNodes[i] );
   }
}

// start the replacement on 'document', which is
// the root node
UE_replacer( document );

Обновлено: Чтобы противопоставить направление ответа Страгера , я избавился от испорченного jQuery и сохранил регулярное выражение как можно более простым. Этот «сырой» подход к JavaScript оказался гораздо проще, чем я ожидал.

Хотя jQuery явно хорош для манипулирования структурой DOM, на самом деле нелегко понять, как манипулировать текстовыми элементами.

1 голос
/ 03 апреля 2009

Не пытайтесь обрабатывать innerHTML / html () элемента. Это никогда не сработает, потому что регулярное выражение недостаточно мощно для анализа HTML. Просто пройдитесь по узлам Text в поисках того, что вы хотите:

// Replace words in text content, recursively walking element children.
//
function wrapWordsInDescendants(element, tagName, className) {
    for (var i= element.childNodes.length; i-->0;) {
        var child= element.childNodes[i];
        if (child.nodeType==1) // Node.ELEMENT_NODE
            wrapWordsInDescendants(child, tagName, className);
        else if (child.nodeType==3) // Node.TEXT_NODE
            wrapWordsInText(child, tagName, className);
    }
}

// Replace words in a single text node
//
function wrapWordsInText(node, tagName, className) {

    // Get list of *word indexes
    //
    var ixs= [];
    var match;
    while (match= starword.exec(node.data))
        ixs.push([match.index, match.index+match[0].length]);

    // Wrap each in the given element
    //
    for (var i= ixs.length; i-->0;) {
        var element= document.createElement(tagName);
        element.className= className;
        node.splitText(ixs[i][1]);
        element.appendChild(node.splitText(ixs[i][0]));
        node.parentNode.insertBefore(element, node.nextSibling);
    }
}
var starword= /(^|\W)\*\w+\b/g;

// Process all elements with class 'foo'
//
$('.foo').each(function() {
    wrapWordsInDescendants(this, 'span', 'xyz');
});


// If you're not using jQuery, you'll need the below bits instead of $...

// Fix missing indexOf method on IE
//
if (![].indexOf) Array.prototype.indexOf= function(item) {
    for (var i= 0; i<this.length; i++)
        if (this[i]==item)
            return i;
    return -1;
}

// Iterating over '*' (all elements) is not fast; if possible, reduce to
// all elements called 'li', or all element inside a certain element etc.
//
var elements= document.getElementsByTagName('*');
for (var i= elements.length; i-->0;)
    if (elements[i].className.split(' ').indexOf('foo')!=-1)
        wrapWordsInDescendants(elements[i], 'span', 'xyz');
0 голосов
/ 03 апреля 2009

Регулярное выражение будет выглядеть примерно так (синтаксис sed-ish):

s/\*\(\w+\)\b\(?![^<]*>\)/<span class="\1">*\1</span>/g

Таким образом:

$('li.foo').each(function() {
    var html = $(this).html();
    html = html.replace(/\*(\w+)\b(?![^<]*>)/g, "<span class=\"$1\">*$1</span>");
    $(this).html(html);
});

Сегмент \*(\w+)\b является важной частью. Он находит звездочку, за которой следуют один или несколько символов слова, за которыми следует какой-либо термин слова (например, конец строки или пробел). Слово вводится в $1, который затем используется как текст и класс вывода.

Часть сразу после этого ((?![^<]*>)) выглядит негативно. Он утверждает, что закрывающая угловая скобка не следует, если перед ней нет открывающей угловой скобки. Это предотвращает совпадение, когда строка находится внутри тега HTML. Это не работает с искаженным HTML, но это не должно быть так или иначе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...