Как обернуть все вхождения известного текста в документ HTML? - PullRequest
1 голос
/ 29 ноября 2011

Я хочу найти все TEXT или HREF, совпадающие с RegExp, в документе HMTL и обернуть их тегом (например, преобразовать обычный текст в ссылки).

Рассмотрим следующий HTML:

<body>
  <!-- test1 <div>test2 <a href="test3">test4</a></div> -->
  test5
  <a href="test6">notest</a>
  <div>
    test8
    <p>
      test9 notest test10
      <a href="notest">test12</a>
      <input type="text" name="test13">test14</input>
    </p>
    test15
  </div>
</body>

Тогда это будет моей необходимой заменой:

<body>
  <!-- test1 <div>test2 <a href="test3">test4</a></div> -->
  <div class="wrapped">test5</div>
  <div class="wrapped"><a href="test6">notest</a></div>
  <div>
    <div class="wrapped">test8</div>
    <p>
      <div class="wrapped">test9</div> notest
      <div class="wrapped">test10</div>
      <div class="wrapped"><a href="notest">test12</a></div>
      <input type="text" name="test13">test14</input>
    </p>
    <div class="wrapped">test15</div>
  </div>
</body>

Обратите внимание, что тесты 5, 6, 8, 9, 10, 12, 15 обернуты.

Недопустимо вставлять в поля ввода или любые другие специальные теги HTML, которые не отображаются (например, <script> <doctype> и т. д.)

До этого я работал с принципом стека:

  1. Вставить тело в стек.

  2. e = stack.pop().

  3. Поместить все дочерние элементы e элемента типа в стек, кроме ссылок (<a> узлов) и элементов class="wrapped".

  4. Проверьте все оставшиеся e.children ссылки типа для совпадения href или текста и переноса.

  5. Завершение всех внутренних совпадений во всехe.children типа текста.

  6. Если стек не пуст, перейдите к 2.

  7. Завершено

JavaScript требуется только для запуска в Firefox 8.

Я хотел бы выполнить перенос без обхода дерева, линейный будет оптимальным

Ответы [ 2 ]

1 голос
/ 29 ноября 2011

Почему вы не хотите обход дерева? Я думаю, что ваш текущий алгоритм так же хорош, как он получает.

Проблема в том, что DOM не предлагает какого-либо сложного метода для получения всех текстовых узлов.

Я не запускал никаких тестов производительности, но этот может иметь примерно ту же скорость:

  1. nodes := getElementsByTagName('*')
  2. excludes := document.querySelectorAll('a, a *, .wrapped, .wrapped *, script, style, input, textarea [, ...]')
    (querySelectorAll должен работать очень хорошо)
  3. targets := nodes - excludes
    (не уверен насчет производительности здесь)
  4. Итерация по targets
    • Перебрать детей
    • Обернуть каждый текстовый узел
  5. ручка <a> элементы отдельно
1 голос
/ 29 ноября 2011

Вам повезло!Я на самом деле написал что-то вроде этого некоторое время назад.

Может потребоваться некоторая модификация, чтобы заставить его работать с регулярными выражениями, но это не должно быть слишком сложно.Код был протестирован в IE7,8,9, Chrome и Firefox

/*global window document addEvent*/

function textContent(node) {
    if (typeof node.textContent !== "undefined") {
        return node.textContent;
    } else {
        return node.nodeValue; // IE
    }
}
function setTextContent(node, new_value) {
    if (typeof node.textContent !== "undefined") {
        node.textContent = new_value;
    } else {
        node.nodeValue = new_value; // IE
    }
}

function replace_stuff(context_element) {
    var i = 0, node, pos, before, after, div;
    if (1 === context_element.nodeType) {
        for (; i < context_element.childNodes.length; i += 1) {
            replace_em(context_element.childNodes[i]);
        }
    } else if (3 === context_element.nodeType) {
        node = context_element;
        pos = textContent(node).indexOf("YOURSTRING"); // replace with regex if you so desire
        while (-1 !== pos) {
            before = textContent(node).substring(0, pos);
            after = textContent(node).substring(pos + 10, textContent(node).length);  // 10 is the lenth of YOURSTRING
            context_element.parentNode.insertBefore(document.createTextNode(before), node);
            div = document.createElement("div");
            div.appendChild(document.createTextNode("YOURSTRING")); // reinsert original content
            context_element.parentNode.insertBefore(div, node);
            setTextContent(node, after);
            pos = textContent(node).indexOf("YOURSTRING"); // find next occurance
        }
    }
}

addEvent(window, "load", function () {  // you may need to change this line
    replace_stuff(document.getElementById("main"));
});

В основном он проверяет textContent узла для YOURSTRING, разбивает текст на два узла: текст перед YOURSTRING итекст после.Затем он оборачивает YOURSTRING в div и вставляет его между двумя другими узлами.

...