javascriptjqueryindexof

JQuery: how to wrap given section of text in a span?


I have a series of title elements with text in them and a text input. When I put a string into the text input I want to highlight (wrapp in <span> with a class) the responding character(s) within the title elements.

For example, if the text input contains the word "world" then I want that same word to be wrapped in high light span, eg

<div class="title">Hello <span class="highlight">World</span>! This is a title</div>

I have achieved this somewhatr with .replace() eg:

// value is .val recieved from input
function highlight(value) {
    $('.title').each(function() {
        var original = $(this).text();
        var newText = original.replace(value, "<span class='highlight'>" + value + "</span>");
        $(this).html(newText);
    });
};

However, this has a couple of limitations:

  1. It doesn't work with capitalisation (if user enters world the string World won't be highlighted).

  2. It only works on the first instance of the word (if there are multiple instances of the word world in the string it will only highlight the first.

I tried to approach this a different way by inserting the <span>s by getting the start and end positions of each inputted string, but this just created a mess:

function highlight(value, el) {
    $(el).each(function() {
        var original = $(this).text();
        var startPosition = original.indexOf(value);
        
        if (startPosition !== 0 || startPosition !== -1) {
            var endPosition = (startPosition + value.length);
                            
           var newString = original.slice(0, startPosition)
            + '<span class="highlight">'
            + original.slice(startPosition, endPosition)
            + '</span>'
            + original.slice(startPosition);
            
            $(this).html(newString);
        }
    });
};

Would anyone know if this is possible to do? Can anyone point me in the right direction?

(PS: I'd rather not use plugins if possible)


Solution

  • You can use a regexp

    There is not much gained by only grabbing titles containing the word, since that will loop too

    function highlight(value) {
      $('.title').each(function() {
        let textContent = $(this).text(); 
        const wordArr = [...new Set(textContent.match(new RegExp(value, 'ig')))];
        if (wordArr.length > 0) { 
          wordArr.forEach(word => textContent = textContent.split(word).join(`<span class="highlight">${word}</span>`));
          $(this).html(textContent);
        }
      });
    };
    highlight("wor");
    .highlight  {
      color: red
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <h1 class="title">Hello world</h1>
    <h1 class="title">Hello World</h1>
    <h1 class="title">Hello World Hello world</h1>