I'm trying to highlight only matching text within a string at any point.
I have an input box which filters the results. Originally this worked fine to highlight the FIRST character, but the remit has changed to highlight the text within a string at any position.
Fiddle: Highlight matching text
It filters perfectly as it should, but highlights letters starting at the front, not the matching ones. I tried to use indexOf valThis to sort it out but I was probably doing it wrong.
Any help or pointers would be really appreciated.
$('#box').keyup(function () {
var valThis = this.value.toLowerCase();
var length = this.value.length;
$('.objType').each(function () {
var text = $(this).text(),
textL = text.toLowerCase(),
//n = textL.indexOf(valThis);
var htmlR = '<b>' + text.substr(0, length) + '</b>' + text.substr(length);
if(textL.includes(valThis))
{
$(this).html(htmlR).show()
}
else
{
$(this).hide();
}
});
});
I'm trying to highlight only matching text within a string at any point.
I have an input box which filters the results. Originally this worked fine to highlight the FIRST character, but the remit has changed to highlight the text within a string at any position.
Fiddle: Highlight matching text
It filters perfectly as it should, but highlights letters starting at the front, not the matching ones. I tried to use indexOf valThis to sort it out but I was probably doing it wrong.
Any help or pointers would be really appreciated.
$('#box').keyup(function () {
var valThis = this.value.toLowerCase();
var length = this.value.length;
$('.objType').each(function () {
var text = $(this).text(),
textL = text.toLowerCase(),
//n = textL.indexOf(valThis);
var htmlR = '<b>' + text.substr(0, length) + '</b>' + text.substr(length);
if(textL.includes(valThis))
{
$(this).html(htmlR).show()
}
else
{
$(this).hide();
}
});
});
Share
Improve this question
asked Apr 10, 2017 at 16:11
sidestealsidesteal
672 silver badges5 bronze badges
0
3 Answers
Reset to default 5The line
var htmlR = '<b>' + text.substr(0, length) + '</b>' + text.substr(length);
says "take length
characters from the beginning of the string." You want to start with the first matching character, so you need to know where that is. So you want String#indexOf
, not String#includes
.
You also want to save the original text so you can pare against that, not against the text updated by the previous filter operation:
// Grab the original text
$(".objType").each(function() { // ***
$(this).data("original", $(this).text()); // ***
}) // ***
$('#box').keyup(function() {
var valThis = this.value.toLowerCase();
var length = this.value.length;
$('.objType').each(function() {
var text = $(this).data("original"), // ***
textL = text.toLowerCase(),
index = textL.indexOf(valThis); // ***
if (index !== -1) {
var htmlR = text.substr(0, index) + '<b>' + text.substr(index, length) + '</b>' + text.substr(index + length); // ***
$(this).html(htmlR).show()
} else {
$(this).hide();
}
});
});
Updated Fiddle, snippet:
// Grab the original text
$(".objType").each(function() { // ***
$(this).data("original", $(this).text()); // ***
}) // ***
$('#box').keyup(function() {
var valThis = this.value.toLowerCase();
var length = this.value.length;
$('.objType').each(function() {
var text = $(this).data("original"), // ***
textL = text.toLowerCase(),
index = textL.indexOf(valThis); // ***
if (index !== -1) {
var htmlR = text.substr(0, index) + '<b>' + text.substr(index, length) + '</b>' + text.substr(index + length); // ***
$(this).html(htmlR).show()
} else {
$(this).hide();
}
});
});
input[type="text"] {
width: 50%;
font-size: 110%;
margin: 10px;
padding: 5px;
float: left;
clear: left;
}
span {
float: left;
clear: left;
margin: 10px 10px;
}
<script src="https://ajax.googleapis./ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input placeholder="Filter results" id="box" type="text" />
<span class="objType" id="item1">Accepted Event Relation</span>
<span class="objType" id="item2">Case Status Value</span>
<span class="objType" id="item3">External Data Source</span>
<span class="objType" id="item4">Navigation Link Set</span>
Try this. It does non-destructive copying
$('#box').keyup(function () {
var valThis = this.value.toLowerCase(),
length = this.value.length;
$('.objType').each(function () {
var text = $(this).text(),
textL = text.toLowerCase(),
textStart = textL.indexOf(valThis),
textEnd = textStart+valThis.length;
//n = textL.indexOf(valThis);
var htmlR = text.substring(0,textStart)+'<b>' + text.substring(textStart,textEnd) + '</b>' + text.substring(textStart+length);
if(textStart!=-1) {
$("#"+this.id+"r").html(htmlR).show()
$(this).hide();
}
else {
$("#"+this.id+"r").empty();
$(this).hide();
}
});
});
input[type="text"] {
width: 50%;
font-size: 110%;
margin:10px;
padding: 5px;
float:left;
clear:left;
}
span{
float:left;
clear:left;
margin:10px 10px;
}
<script src="https://ajax.googleapis./ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input placeholder="Filter results" id="box" type="text" />
<span class="objType" id="item1">Accepted Event Relation</span><span id="item1r"></span>
<span class="objType" id="item2">Case Status Value</span><span id="item2r"></span>
<span class="objType" id="item3">External Data Source</span><span id="item3r"></span>
<span class="objType" id="item4">Navigation Link Set</span><span id="item4r"></span>
I guess you need to use a bination of indexOf
, RegEx
and substring
to find and highlight the position of the word in each string.
See Fiddle here: https://jsfiddle/sxk7qj40/
$('#box').keyup(function () {
const valThis = this.value;
const length = this.value.length;
$('.objType').each(function () {
const text = $(this).text();
const textL = text.toLowerCase();
const position = textL.indexOf(valThis.toLowerCase());
if (position !== -1) {
const matches = text.substring(position, (valThis.length + position));
const regex = new RegExp(matches, 'ig');
const highlighted = text.replace(regex, `<mark>${matches}</mark>`);
$(this).html(highlighted).show();
} else {
$(this).text(text);
$(this).hide();
}
});
});
You would not want to hardcode your substring
position at 0, as this will always begin at the start of the string regardless.
Instead, you need to use indexOf
, which will return the starting position. You can then use that in the substring to find the starting point that you want to replace
. Note that indexOf
will return -1
if not found, hence the if
statement.
Where to end though? Well, this will e in as a bination of the start position we already have + the string length you just typed.
NOTE: For my own convenience, I used some es2015 features like const and template literals, but you can't use es2015, simply run it through Babel, or manually replace these parts with var and string concatenation respectively.
Hope this makes sense?
e.g. ES5 safe:
$('#box').keyup(function () {
var valThis = this.value;
var length = this.value.length;
$('.objType').each(function () {
var text = $(this).text();
var textL = text.toLowerCase();
var position = textL.indexOf(valThis.toLowerCase());
if (position !== -1) {
var matches = text.substring(position, (valThis.length + position));
var regex = new RegExp(matches, 'ig');
var highlighted = text.replace(regex, '<mark>' + matches + '</mark>');
$(this).html(highlighted).show();
} else {
$(this).text(text);
$(this).hide();
}
});
});