Заменить содержимое в элементах без замены HTML

Предположим, у меня есть следующая структура HTML:

<test>
    <div>
        This is a test
        </div>
    <div>
        This is another test
        <button>
            Button test
        </button>
    </div>
</test>

Теперь я использую следующий код jQuery для замены, например, 'T':

$("test *").each(function(index, value) {
    $(this).html($(this).html().replace(new RegExp('t', "ig"), "<b>t</b>"));
});

Однако это приводит к следующей структуре HTML (что неожиданно, см. <button> тег, который нарушает мой HTML):

<test>
    <div>
        <b>T</b>his is a <b>t</b>es<b>t</b>
        </div>
    <div>
        <b>T</b>his is ano<b>t</b>her <b>t</b>es<b>t</b>
        <bu<b>t</b><b>t</b>on>
            Bu<b>t</b><b>t</b>on <b>t</b>es<b>t</b>
            </bu<b>t</b><b>t</b>on>
        </div>
    </test>

Чего я хочу достичь:

<test>
    <div>
        <b>T</b>his is a <b>t</b>es<b>t</b>
        </div>
    <div>
        <b>T</b>his is ano<b>t</b>her <b>t</b>es<b>t</b>
        <button>
            Bu<b>t</b><b>t</b>on <b>t</b>es<b>t</b>
            </button>
        </div>
    </test>

По сути, я хочу заменить внутри всего элемента, но сохранить теги HTML и все атрибуты HTML.

3 ответа

Решение

С помощью jQuery это можно сделать довольно просто. Создайте функцию, которая принимает элемент, текст которого вы хотите обновить, текст, который вы хотите заменить, и то, чем вы хотите заменить его. Затем в функции вы хотите удалить дочерний HTML и обновить любой текст, оставшийся в элементе, с вашим текстом замены. Затем вы можете рекурсивно запустить эту же функцию для каждого из ваших дочерних элементов, прежде чем добавлять их обратно в родительский элемент.

function replaceTextInHtmlBlock($element, replaceText, replaceWith)
{
  var $children = $element.children().detach();
  //Now that there should only be text nodes left do your replacement
  $element.html($element.text().replace(replaceText, replaceWith));
  //Run this function for each child element
  $children.each(function(index, me){
    replaceTextInHtmlBlock($(me), replaceText, replaceWith);
  });
  $element.append($children);
}

$(document).ready(function(){
  $("#doReplace").click(function(){
    replaceTextInHtmlBlock($("#top"), $("#replace").val(), $("#with").val());
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="top">
  <div>
    This is a test
  </div>
  <div>
    This is another test
    <button>
            Button test
            </button>
  </div>
</div>
<br />
<br />
<div>
  <label>Replace</label>
  <input id="replace" value="t" />
  <label>with</label>
  <input id="with" value="<strong>t</strong>" />
  <button id="doReplace">Do Replace</button>
</div>

Вы можете избежать jQuery и не беспокоиться о button в частности (так как это не поможет, если вы нажмете input тэг и т. д.), если заменить только в текстовых узлах.

Мы ищем текстовые узлы, в каждом случае замечая первые "t" (или "T"), которые мы видим, оборачивая их в <b> и двигаться дальше. Другие совпадения в той же области будут найдены в последующих текстовых узлах.

var wrapText = "t";

var el = document.getElementsByTagName('test')[0];

replaceIn(el);

function replaceIn(el) {
  var i = 0;

  while (i < el.childNodes.length) {
    var cn = el.childNodes[i];

    if (cn.nodeType == 3) {   // text node
      var p = cn.textContent.toLowerCase().indexOf(wrapText);

      if (p >= 0) {
        var range = new Range();
        range.setStart(cn, p);
        range.setEnd(cn, p + 1);
        range.surroundContents(document.createElement('b'));
        ++i;   // don't check the <b> we just created
      }
    } 
    else {
      replaceIn(cn);
    }

    ++i;
  }
}
<test>
  <div>
    This is a test
  </div>
  <div>
    This is another test
    <button>
      Button test
   </button>

    <input value="input test" />
  </div>
</test>

Похоже, вы хотите выделить все символы T, но ваше регулярное выражение также ловит ваш HTML.

Выполните операцию для innerText, а не innerHTML. Я не использовал jQuery в течение нескольких лет, так что может быть более оптимальный способ сделать это, но это должно сделать это.

$("test *").each(function(index, value) {
    $(this)[0].innerText = $(this)[0].innerText.replace(new RegExp('t', "ig"), "<b>t</b>");
});
Другие вопросы по тегам