0

Here is my code

And I am trying to change the color of any match in the <li> elements that matches the text in the <input> element. So if you type lets say "this is a simple text" the result should look like this:

<input value="this is a simple text" id="term"/> 
<ul id="ul-id" >
    <li id="li-id-1"> hello budy <span style="color:red">this</span> <span style="color:red">is</span> really <span style="color:red">simple</span> stuff </li>
    <li id="li-id-2"> <span style="color:red">this</span> <span style="color:red">is</span> it</li>
    <li id="li-id-3"> there <span style="color:red">is</span> something here</li>
    <li id="li-id-4"> plain <span style="color:red">text</span> file</li>
</ul>

Any help would be appreciated.

Thank you.

Nassim
  • 2,879
  • 2
  • 37
  • 39

7 Answers7

3

You can remove the delay function if you like, but this would lead to a performance loss:

// https://stackoverflow.com/a/9310752/1636522
RegExp.escape = function (text) {
    return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
};

window.addEventListener('load', function () {
    var wrapper = '<span style="background:yellow">$&</span>';
    var input = document.getElementById('term');
    var list = document.getElementById('ul-id');
    var items = list.getElementsByTagName('li');
    var l = items.length;
    var source = Array.prototype.map.call(
        items, function (li) { return li.textContent; }
    );
    var cmp = function (a, b) {
        return b.length - a.length;
    };
    var delay = function (fn, ms) {
        var id, scope, args;
        return function () {
            scope = this;
            args = arguments;
            id && clearTimeout(id);
            id = setTimeout(function () { 
                fn.apply(scope, args); 
            }, ms);
        };
    };
    term.addEventListener('keyup', delay(function () {
        var i, re, val;
        if (val = this.value.match(/[^ ]+/g)) {
            val = val.sort(cmp).map(RegExp.escape);
            re = new RegExp(val.join('|'), 'g');
            for (i = 0; i < l; i++) {
                items[i].innerHTML = source[i].replace(re, wrapper);
            }
        }
        else {
            for (i = 0; i < l; i++) {
                items[i].textContent = source[i];
            }
        }
    }, 500));
});
<input value="" id="term"/> 
<ul id="ul-id" >
    <li id="li-id-1"> hello budy this is really simple stuff </li>
    <li id="li-id-2"> this is it</li>
    <li id="li-id-3"> there is something here</li>
    <li id="li-id-4"> plain text file</li>
</ul>

Similar topic: https://stackoverflow.com/a/20427785/1636522.

Community
  • 1
  • 1
  • @Nassim Cause you are expected to bring a little effort to the table: http://stackoverflow.com/help/quality-standards-error ;-) –  Jun 18 '15 at 09:44
  • 1
    @Nassim Don't take it personal, people here worry about the quality of this (free) service for the benefit of the entire community, I guess that's why you've been downvoted. This is just an assumption, I'm not the downvoter. –  Jun 18 '15 at 10:01
  • 1
    @Nassim > I was the one to downvote the question as I don't think it fits SO's "rule" of "show us what you've tried" . I wasn't the one downvoting your other questions though, I usually don't bother to "witch hunt", it can happen to anybody especially when you don't have any idea where to start, but it then would indicate SO is not the place to ask. – Laurent S. Jun 18 '15 at 10:27
  • 1
    Of course, leave it up, especially as they're some valuable answers in it. Downvote is not an invitation to remove it, just some kind of "flag" which, when raised by enough people, deletes the question automatically. If you understood my (our) point, I'm already glad with it, there's nothing personnal, that's just my small contribution to the community :-) – Laurent S. Jun 18 '15 at 10:37
1

I don't know if it is possible with only RegEx, but here is a jQuery solution:

$('#term').change(function() {
    var inpArr = $(this).val().split(" ");

    $('#ul-id li').each(function() {
        var liArr = $(this).text().split(" ");
        var txt = "";
        $.each(liArr, function(i, v) {
            if(inpArr.indexOf(v) > -1) {
                txt += "<span class='red'>"+ v +"</span> ";
            } else {
                txt += v + " ";
            }
        });
        $(this).html(txt);
    });
});

span.red {
     color: red; 
}

And the working fiddle: https://jsfiddle.net/ermk32yc/1/

Wa Kai
  • 466
  • 5
  • 12
1

Plain JS solution

var list = document.querySelector('#ul-id'),
    listItem,
    listItems,
    term = document.querySelector('#term'),
    oldRef = list.innerHTML,
    oldValue;

term.addEventListener('keyup', function () {
    var regExp,
        value = term.value;

    if (oldValue !== value) {
        oldValue = value;
    
        // Reset
        list.innerHTML = oldRef;
    
        if (value.trim() !== '') {
            listItems = list.querySelectorAll('#ul-id li');
            regExp = new RegExp(term.value, 'g');

            // Perform matching
            for (var i = 0; i < listItems.length; i++) {
                listItem = listItems[i];
                listItem.innerHTML = listItem.innerHTML.replace(regExp, function (match) {
                    return '<span class="matched">' + match + '</span>';
                });
            }
        }
    }

}, false);
.matched {
    color: red;
}
<input  id="term"/> 

<ul id="ul-id" >

    <li id="li-id-1"> hello budy this is really simple stuff </li>
    <li id="li-id-2"> this is it</li>
    <li id="li-id-3"> there is something here</li>
    <li id="li-id-4"> plain text file</li>

</ul>
Vigneswaran Marimuthu
  • 2,452
  • 13
  • 16
  • I like this solution because it highlights on each character set, but I it doesn't "this is simple " but the solution of @Arun-p-jhony hilights each word, it would be great to combine the 2 solutions – Nassim Jun 18 '15 at 09:23
1

You might do something like this

$('#term').change(function (i) {
    var terms = $('#term').val().split(" ");
    $('#ul-id > li').each(function (i, el) {
        var val = $(el).html().replace(/<[^<]+>/g, ''),
            match;
        terms.forEach(function (term) {
            val = val.replace(new RegExp(term, 'g'),
                '<span style="color:red">' + term + '</span>');
        });
        $(el).html(val);
    });
});

https://jsfiddle.net/1vm0259x/5/

Juho
  • 427
  • 2
  • 12
0

With jQuery

if (!RegExp.escape) {
  RegExp.escape = function(value) {
    return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&")
  };
}

$('#term').on('change keyup', function() {
  //$('#ul-id li .highlight').contents().unwrap();//remove previous highlights

  var parts = this.value.split(' ').map(function(value) {
    return '\\b' + RegExp.escape(value) + '\\b';
  });
  var regex = new RegExp(parts.join('|'), 'g');

  $('#ul-id li ').each(function() {
    var text = $(this).text();
    $(this).html(text.replace(regex, function(part) {
      return '<span class="highlight">' + part + '</span>'
    }))
  })
});
.highlight {
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input id="term" />
<ul id="ul-id">
  <li id="li-id-1">hello budy this is really simple stuff</li>
  <li id="li-id-2">this is it</li>
  <li id="li-id-3">there is something here</li>
  <li id="li-id-4">plain text file</li>
</ul>
Arun P Johny
  • 384,651
  • 66
  • 527
  • 531
0

You can use the below solution if there is no html contents in the li elemnets

if (!RegExp.escape) {
  RegExp.escape = function(value) {
    return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&")
  };
}

var lis = [].slice.call(document.querySelectorAll('#ul-id li'));
lis.forEach(function(el) {
  el.dataset.text = el.innerHTML;
});

document.querySelector('#term').addEventListener('change', function() {
  var parts = this.value.split(' ').map(function(value) {
    return '\\b' + RegExp.escape(value) + '\\b';
  });
  var regex = new RegExp(parts.join('|'), 'g');

  lis.forEach(function(el) {
    el.innerHTML = el.dataset.text.replace(regex, function(part) {
      return '<span class="highlight">' + part + '</span>'
    })
  });
});
.highlight {
  color: red;
}
<input id="term" />
<ul id="ul-id">
  <li id="li-id-1">hello budy this is really simple stuff</li>
  <li id="li-id-2">this is it</li>
  <li id="li-id-3">there is something here</li>
  <li id="li-id-4">plain text file</li>
</ul>
Arun P Johny
  • 384,651
  • 66
  • 527
  • 531
  • 1
    Maybe that's only to help people, but that doesn't help SO as such, so is posting 2 different answers to the same question while both codes could be part of the same answer... anyway that's always bothering me : I take some time as many others trying to educate posters, and such answers although helpfull to the OP makes people like me looking like they rude people that don't want to help, while we're only trying to enforce SO rules... – Laurent S. Jun 18 '15 at 10:20
  • @Bartdude Sorry if the posts looked offensive to you.... this time it was just fun... found the question little interesting so just gave it a try... about the 2 answers first the jQuery one was did without noticing that jQuer tag was not there... then posted a new answer since the code block is pretty long... – Arun P Johny Jun 18 '15 at 10:24
  • I don't find the answer offensive as such and totally understand the feeling : sometimes it just feels funny to try to code something, or you happen to have the snippet somewhere. It just makes it even harder to explain to some people why they're downvoted. Cfr the remark of the OP on the accepted answer... – Laurent S. Jun 18 '15 at 10:33
0

you can handle blur event or you can copy paste the inner function code to wherever it is required. this is the guide code here you can more explore match function as per your requirement and then can traverse your li elements as shown below.

$('#term).blur(function() {

  $('#ul-id li').foreach(function()
  {
   if($(this).text().match($("#term").text()))
   {
     ///set/change here  color of  li element
      $(this).css('color', 'red');
   }
  }
}
Ashok Rathod
  • 840
  • 9
  • 24