Highlight results when searching divs
I have a div setting like this:
<input id="search">
<div class="entry">
<div class="title">hello world test 123</div>
<div class="description">lorem ipsum test test1 testing</div>
</div>
<div class="entry">
<div class="title">attack on titan</div>
<div class="description">fullmetal alchemist</div>
</div>
And I allow the user to search for the div with:
jQuery(document).ready(function() {
jQuery("#search").on("keyup click input", function () {
var val = jQuery(this).val();
if (val.length) {
jQuery(".entry").hide().filter(function () {
return jQuery('.title, .description',this).text().toLowerCase().indexOf(val.toLowerCase()) != -1;
}).show();
}
else {
jQuery(".entry").show();
}
});
});
My question is, how can I highlight search terms? For example, if the user is searching test
, I want to wrap the text test
in tags <span>
.
EDIT: Please note that I know how to search / replace text, but I cannot get it to work correctly with my search function.
source to share
Optimized solution
After all the problems discussed in the comments and trying to optimize the solution, so there will be no shortage of possible errors, I refactored the code and optimized it:
$(document).ready(function() {
$("#search").on("keyup click input", function() {
var val = jQuery(this).val();
var regExp = new RegExp(val, 'ig');
var reg = new RegExp('<span class="highlight">(.+)<\/span>', 'ig');
if (val.length) {
$(".entry").hide().filter(function() {
var found = $('.title, .description', this).text().toLowerCase().indexOf(val.toLowerCase()) != -1;
if (val.length > 3) {
$('.title, .description', this).each(function(k, v) {
if ($(v).text().match(regExp)) {
$(v).html($(v).text().replace(regExp, '<span class="highlight">$&</span>'));
} else {
$(v).html($(v).text().replace(reg, '$&'));
}
});
} else {
$('.title, .description', this).each(function(k, v) {
$(v).html($(v).text().replace(reg, '$&'));
});
}
return found;
}).show();
} else {
$('.title, .description').each(function(k, v) {
$(v).html($(v).text().replace(reg, '$&'));
});
$(".entry").show();
}
});
});
.highlight {
background-color: blue
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="search">
<div class="entry">
<div class="title">hello world test 123</div>
<div class="description">lorem ipsum test test1 testing</div>
</div>
<div class="entry">
<div class="title">attack on titan</div>
<div class="description">fullmetal alchemist</div>
</div>
It iterates over the elements and uses a RegExp with the appropriate group, and if the content of the iterated element matches the Regex, replace the matched text with the same content enclosed in a range, otherwise just set the content to its original form.
Original Answer
Here's how you should do it:
var val = jQuery(this).val();
if (val.length) {
$(".entry").hide().filter(function() {
var found = $('.title, .description', this).text().toLowerCase().indexOf(val.toLowerCase()) != -1;
var regExp = new RegExp(val, 'ig');
$('.title, .description', this).each(function(k, v) {
if ($(v).text().toLowerCase().indexOf(val.toLowerCase()) != -1) {
var newHTML = $(v).text().replace(regExp, '<span class="highlight">$&</span>');
$(v).html(newHTML);
}
});
return found;
}).show();
} else {
$(".entry").show();
}
You need to iterate over the elements and use RegExp with the appropriate group, and if the content of that element matches your Regex, replace the matched text with the same content enclosed in a gap.
Demo:
This is a working demo:
$(document).ready(function() {
$("#search").on("keyup click input", function() {
var val = jQuery(this).val();
if (val.length) {
$(".entry").hide().filter(function() {
var found = $('.title, .description', this).text().toLowerCase().indexOf(val.toLowerCase()) != -1;
var regExp = new RegExp(val, 'ig');
$('.title, .description', this).each(function(k, v) {
if ($(v).text().toLowerCase().indexOf(val.toLowerCase()) != -1) {
var newHTML = $(v).text().replace(regExp, '<span class="highlight">$&</span>');
$(v).html(newHTML);
}
});
return found;
}).show();
} else {
$('.title, .description').each(function(k, v) {
var reg = new RegExp('<span class="highlight">(.+)<\/span>', 'ig');
var newHTML = $(v).text().replace(reg, '$&');
$(v).html(newHTML);
});
$(".entry").show();
}
});
});
.highlight {
background-color: blue
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="search">
<div class="entry">
<div class="title">hello world test 123</div>
<div class="description">lorem ipsum test test1 testing</div>
</div>
<div class="entry">
<div class="title">attack on titan</div>
<div class="description">fullmetal alchemist</div>
</div>
Edit:
This is a demo that only highlights sentences when you type more than two letters:
$(document).ready(function() {
$("#search").on("keyup click input", function() {
var val = jQuery(this).val();
if (val.length) {
$(".entry").hide().filter(function() {
var found = $('.title, .description', this).text().toLowerCase().indexOf(val.toLowerCase()) != -1;
var regExp = new RegExp(val, 'ig');
if (val.length > 2) {
$('.title, .description', this).each(function(k, v) {
if ($(v).text().toLowerCase().indexOf(val.toLowerCase()) != -1) {
var newHTML = $(v).text().replace(regExp, '<span class="highlight">$&</span>');
$(v).html(newHTML);
}
});
} else {
$('.title, .description').each(function(k, v) {
var reg = new RegExp('<span class="highlight">(.+)<\/span>', 'ig');
var newHTML = $(v).text().replace(reg, '$&');
$(v).html(newHTML);
});
}
return found;
}).show();
} else {
$('.title, .description').each(function(k, v) {
var reg = new RegExp('<span class="highlight">(.+)<\/span>', 'ig');
var newHTML = $(v).text().replace(reg, '$&');
$(v).html(newHTML);
});
$(".entry").show();
}
});
});
.highlight {
background-color: blue
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="search">
<div class="entry">
<div class="title">hello world test 123</div>
<div class="description">lorem ipsum test test1 testing</div>
</div>
<div class="entry">
<div class="title">attack on titan</div>
<div class="description">fullmetal alchemist</div>
</div>
source to share
Try to hide all divs contains(text)
instead of filter()
.inintile.Then Show only text contains div
. And apply the span element to the corresponding letter in the children usingnew RegExp()
To ignore case matching ig
in regex and also added code for case insensitive forcontains
Updated Fixed with .title, .description
for kids
jQuery(document).ready(function() {
jQuery("#search").on("input", function() {
var val = jQuery(this).val()
jQuery(".entry").hide()
jQuery(".entry:contains(" + val + ")").show()
jQuery(".entry").each(function() {
if ($(this).find(".title, .description:contains(" + val + ")")) {
$(this).find(".title, .description:contains(" + val + ")").html(function() {
return $(this).text().replace(new RegExp('('+val+')', 'ig'), '<span>$1</span>')
})
}
})
});
})
jQuery.expr[':'].contains = function(a, i, m) {
return jQuery(a).text().toUpperCase()
.indexOf(m[3].toUpperCase()) >= 0;
};
.entry {
background: #fff;
margin-bottom: 10px;
width: 300px;
}
span {
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="search">
<div class="entry">
<div class="title">hello world test 123</div>
<div class="description">lorem ipsum test test1 testing</div>
</div>
<div class="entry">
<div class="title">Attack on titan</div>
<div class="description">fullmetal alchemist</div>
</div>
<div class="entry">
<div>
<div class="title">For nested element on titan</div>
<div>
<div class="description">fullmetal alchemist nested</div>
</div>
</div>
</div>
source to share
Try the following:
document.getElementById('search').onkeyup = userInput;
document.getElementById('search').onclick = userInput;
document.getElementById('search').oninput = userInput;
var allEntries = document.querySelectorAll('.entry');
function userInput () {
var val = this.value;
for (var i = 0; i < allEntries.length; i++) {
var entryElement = allEntries[i];
var title = entryElement.querySelector('.title');
var description = entryElement.querySelector('.description');
var noHtmlSearchStr = '';
if (title) noHtmlSearchStr += title.innerText;
if (description) noHtmlSearchStr += description.innerText;
if (noHtmlSearchStr.length > 0) {
if (noHtmlSearchStr.toLowerCase().indexOf(val.toLowerCase()) != -1) {
// Remove existing <b> tags.
var regexp1 = new RegExp('(<b>|<\/b>)', 'gi');
if (title) title.innerHTML = title.innerHTML.replace(regexp1, '');
if (description) description.innerHTML = description.innerHTML.replace(regexp1, '');
if (val.length > 3) {
var regexp2 = new RegExp('(' + val + ')(?!>)', 'gi');
if (title) title.innerHTML = title.innerHTML.replace(regexp2, '<b>$1</b>');
if (description) description.innerHTML = description.innerHTML.replace(regexp2, '<b>$1</b>');
}
entryElement.style.display = 'block';
} else {
entryElement.style.display = 'none';
}
}
}
}
.entry {
background: #fff;
margin-bottom: 10px;
width: 300px;
}
<input id="search">
<div class="entry">
<div class="title">hello world test 123</div>
<div class="description">div lorem <span>ipsum</span> test <div>test1</div> testing span</div>
</div>
<div class="entry">
<div class="title">attack on titan</div>
<div class="description">fullmetal alchemist</div>
</div>
<div class="entry"></div>
<div class="entry">
<div class="title">attack on titan</div>
</div>
<div class="entry">
<div class="description">Let not go to Camelot, 'tis a silly place</div>
</div>
Explanation of JS code
- Bind all events to a function
userInput()
. - Get all the elements of the class
.entry
and store them inallEntries
. - Get user input and save to
val
. - Iterate through
allEntries
. - Get search text from
title
anddescription
and save tonoHtmlSearchStr
. - If it
val
matches some partnoHtmlSearchStr
, then show itentryElement
, otherwise hide it. - Remove tags
<b>
fromtitle
anddescription
. - If the user search length (
val
) is longer than three characters, select the text matches, otherwise do not select anything.
source to share
All Articles