简体   繁体   中英

Javascript RegExp replace text between two tags if the opening tag has specified class

I have the following HTML code:

<span class="whatever-class custom-class-name" attribute="Whatever 1 AAA">AAA BBB</span>
<span class="search-text custom-class-name" attribute="Whatever 2 AAA">Text AAA</span>

I want to replace the text between the span tags, but only if the span tag has the class " search-text ". So in my case, I have a string containing an HTML code with two spans. I want to replace the text from the second span if it contains a searched text.

I search for: " aa " and I want to replace it with <span class="highlight-text">aa</span> . So the final result should be:

<span class="whatever-class custom-class-name" attribute="Whatever 1 AAA">AAA BBB</span>
<span class="search-text custom-class-name" attribute="Whatever 2 AAA">Text <span class="highlight-search">AA</span>A</span>

Right now I'm doing something like:

var paint = $.proxy(this._paint, this);
var regex = /(<span class="search-text[^>]+>|<\/span>)/g;
item.node.innerHTML = item.html.replace(regex, paint);

where " value " is: " aa " and " item.html " is the HTML presented at the beginning of my question.

the _paint function:

_paint: function($0) {
   return '<span class="highlight-text">' + $0 + '</span>';
},

At this moment the result is that the second span is entirely wrapped into the '<span class="highlight-text">' + $0 + '</span>'; . This is the result:

<span class="whatever-class custom-class-name" attribute="Whatever 1 AAA">AAA BBB</span>
<span class="highlight-text"><span class="search-text custom-class-name" attribute="Whatever 2 AAA">Text AAA</span></span>

I want only the text match to be wrapped inside the hghlight span, like this:

<span class="whatever-class custom-class-name" attribute="Whatever 1 AAA">AAA BBB</span>
<span class="search-text custom-class-name" attribute="Whatever 2 AAA">Text <span class="highlight-text">AA</span>A</span>

Any ideas? Thanks.

Regex is notoriously the wrong tool for most of this job; it's designed for manipulating strings, not structured data such as HTML. Fortunately, you're already in the browser, so you have an entire toolset designed for DOM manipulation available: may as well use it. (You've also tagged the question with jQuery, which makes it even easier.)

Update: I'd misread a detail in the question, and was pulling the search string from a parent node's attribute instead of externally; I also failed to make the search case-insensitive. Both now corrected in the below:

 // Make a case-insensitive regex from the search string let str = 'aa'; let re = new RegExp(str, "gi"); // operate only on the .search-text nodes: $('.search-text').each(function(i, el) { // get the current contents of the element: let text = $(el).html(); // Add your highlights: text = text.replace(re, '<span class="highlight-text">$&</span>'); // insert the modified text back into the DOM: $(el).html(text); }) 
 .highlight-text { background-color: #FFC } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <span class="whatever-class custom-class-name" attribute="Whatever 1 AAA">AAA BBB</span> <span class="search-text custom-class-name" attribute="Whatever 2 AAA">Text AAA</span> I 

This is only really safe if the .search-text elements have no child nodes. It will generally work even if they contain some HTML, but only if:

  • You're certain that the strings you're highlighting will never match portions of the HTML itself, and
  • there aren't any event bindings attached to the DOM elements (this script replaces the contents of .search-text wholesale.)

For example, trying to highlight the word "span" in an html string containing <span> elements would result in invalid html:

 // same script as above $('.search-text').each(function(i, el) { let text = $(el).html(); let highlights = $(el).attr("attribute").split(" "); for (str of highlights) { text = text.replace(str, '<span class="highlight-text">' + str + '</span>'); } $(el).html(text); }) 
 .highlight-text { background-color: #FFC } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <span class="search-text" attribute="span">Text AAA <span>test</span></span> 

Starting with a string

If your starting point is an HTML string instead of an already-built DOM tree, all you need to do is convert that string into a document fragment first so you can use these DOM tools on it:

let fragment = $('<template>');
fragment.html($yourStringHere);
/* manipulate fragment contents as above, then */
return fragment.html();

A partial solution inspired by Daniel Beck's answer. This solution doesn't manipulate the DOM. (I just displayed the result on the DOM for demonstration purposes)

https://jsfiddle.net/mt2yz90L/3/

HTML:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="result">
</div>

JS:

  let searchedText = 'aa';
  let html ='<span class="whatever-class custom-class-name" attribute="Whatever 1 AAA">AAA BBB</span><span class="search-text custom-class-name" attribute="Whatever 2 AAA">Text AAA</span> I';
  var htmlParts = html.split(/(<span class="search-text[^>]+>|<\/span>)/g);
  var htmlPartsIndex = 0;
  for(var i=0; i < htmlParts.length; i++) {
    if(htmlParts[i].indexOf('search-text') !== -1) {
        htmlPartsIndex = ++i;
      break;
    }
  }
  if(htmlPartsIndex > 0) {
     htmlParts[htmlPartsIndex] = htmlParts[htmlPartsIndex].toLowerCase().replace(searchedText, '<span class="highlight-text">' + searchedText + '</span>')
  }
  $('#result').html(htmlParts.join(''));

CSS:

.highlight-text {
  background-color: red;
}

My only issue is that in my case (over 300 items to parse) it blocks the browser. So this is quite slow. I posted it in the idea that maybe someone will share a faster solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM