Reputation: 1128
I'm trying to write a script that highlights everything on a webpage that matches the input to a search box whilst ignoring case in the search for matches.
My html page looks like -
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Index Page</title>
<!--<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">-->
<!-- Bootstrap core CSS -->
<link href="Content/bootstrap.min.css" rel="stylesheet">
<!-- Your custom styles (optional) -->
<link href="Content/style.css" rel="stylesheet">
<!-- custom javascript -->
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.quicksearch/2.3.1/jquery.quicksearch.js"></script>
<script src="Scripts/script.js"></script>
</head>
<body>
<!--Main Navigation-->
<header>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<form class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" id="txt_query" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
</nav>
</header>
<!--Main Navigation-->
<div class="container-fluid">
<div class="searchable">
Hello
<div>
test1
</div>
<div>
test2
</div>
<div>
Test3
<div>
teest4
</div>
</div>
</div>
</div>
</body>
</html>
And the javascript (JQuery) code is -
$(function () {
$('input#txt_query').on('input', function () {
if (this.value !== null) {
var search_value = this.value;
var search_regexp;
if (this.value !== "") {
search_regexp = new RegExp(search_value + "+(?![^<]*\>)", "gi");
}
$('.searchable').each(function () {
$(this).find('*').each(function () {
if ($(this).data('old-state') == null) {
$(this).data('old-state', $(this).html());
}
if (this.value !== "") {
var html = $(this).data('old-state').replace(search_regexp, "<span class = 'highlight'>" + search_value + "</span>");
//alert(html);
$(this).html(html);
}
});
});
}
});
});
And the CSS to highlight the word is -
.highlight {
font-weight: bold;
color: green;
}
This currently has 2 problems.
Where matching terms are adjacent, the items are treated as a single item. So when searching for 'e', the 'ee' gets replaced with a highlighted 'e'.
A search for 't' finds the capital 'T' but replaces it with a highlighted 't' (small t).
I think both problems could be solved by modification of the following line of code:
var html = $(this).data('old-state').replace(
search_regexp,
"<span class = 'highlight'>" + search_value + "</span>"
);
Instead of 'search_value' I'd like to use the text found by the regex pattern, but I don't know how to access that text.
Upvotes: 0
Views: 83
Reputation: 19154
Try replace callback function to capture matches and replace with it so e
become ee
and t
keep original case or T
var html = $(this).data('old-state').replace(search_regexp,
function(match, contents, offset) {
return "<span class = 'highlight'>" + match + "</span>";
});
demo:
$(function() {
$('input#txt_query').on('input', function() {
if (this.value !== null) {
var search_value = this.value;
var search_regexp;
if (this.value !== "") {
search_regexp = new RegExp(search_value + "+(?![^<]*\>)", "gi");
}
$('.searchable').each(function() {
$(this).find('*').each(function() {
if ($(this).data('old-state') == null) {
$(this).data('old-state', $(this).html());
}
if (this.value !== "") {
var html = $(this).data('old-state').replace(search_regexp, function(match, contents, offset) {
return "<span class = 'highlight'>" + match + "</span>";
});
//alert(html);
$(this).html(html);
}
});
});
}
});
});
.highlight {font-weight: bold;color: green;}
.searchable {font-size: 24px}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="txt_query" type="search">
<div class="container-fluid">
<div class="searchable">
Hello
<div>
test1
</div>
<div>
test2
</div>
<div>
Test3
<div>
teest4
</div>
</div>
</div>
</div>
Upvotes: 1
Reputation: 44068
\b
Put a \b
at both ends of you word and you will match only what is between them. The otherside of \b
must be a non-word like a space.
In order to use a variable in a Regex pattern use the RegExp constructor not the Regex literal.
👍
var keyword = new RegExp(variable, 'gi');
👎
var keyword = /variable/gi;
If you do use a variable, you need to escape it first. Note the \b
s are escaped: \\b
var escaped = `(?!(?:[^<]+>|[^>]+<\\/a>))\\b(${keyword})\\b`;
I noticed a lack of template literals in the question so if you aren't familiar with them, it's worth getting to know them, they are strings with a better syntax.
This demo will highlight what it matches by wrapping them in <mark>
tags.
document.getElementById('search').addEventListener('change', function(e) {
highlight(this.value, '#content');
});
function highlight(keyword, selector) {
var node = document.querySelector(selector);
var html = node.innerHTML;
var clean = html.replace(/(<mark>|<\/mark>)/, '');
var escaped = `(?!(?:[^<]+>|[^>]+<\\/a>))\\b(${keyword})\\b`;
var regex = new RegExp(escaped, `gi`);
var hits = clean.replace(regex, `<mark>$1</mark>`);
node.innerHTML = hits;
}
<input id='search' type='search'><input type='button' value='search'>
<article id='content'>
<ol>
<li>aircrew</li>
<li><u>air</u> crew</li>
<li>playground</li>
<li><u>play</u> ground</li>
<li>underground</li>
<li><u>under</u> ground</li>
<li>hacksaw</li>
<li><u>hack</u> saw</li>
<li>toothpaste</li>
<li><u>tooth</u> paste</li>
</ol>
</article>
Upvotes: 0