I am trying to get highlighting on keyword searching working right. A couple issues I am having.
Case insensitive is working for the first word, but would like it to replace with original case word, not the lowercase searched word.
i.e. search trend, it replaces Trend with trend, I know why, but would like to figure out how to replace back the found word, not the searched word
The second word is not matching case insensitive.
i.e. search trend micro is not matching trend Micro.
Here is jsFiddle: http://jsfiddle.net/hh2zvjft/1/
if ($(".ProjectSearch").val().length > 0) {
var searchedText = $(".ProjectSearch").val();
var wordList = searchedText.split(" ");
$.each(wordList, function (i, word) {
$(".ProjectTaskGrid:contains('" + word + "')").each(function (i, element) {
var rgxp = new RegExp(word, "gi");
var repl = '<span class="search-found">' + word + '</span>';
element.innerHTML = element.innerHTML.replace(rgxp, repl);
});
});
}
Can you please help identify the issues, and offer improvements? Thanks!
Some refererences used to arrive at code:
https://stackoverflow.com/a/120161/2727155
https://stackoverflow.com/a/10011639/2727155
Highlight multiple words (ignore HTML tags)
const regEscape = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const EL_input = document.querySelector("#input");
const EL_area = document.querySelector("#area");
const org = EL_area.innerHTML; // Store the current HTML state
const highlight = () => {
const val = EL_input.value;
if (!val) return EL_area.innerHTML = org;
const pts = regEscape(val.trim()).split(/ +/);
const reg = new RegExp("(?![^<]+>)(" + pts.join("|") + ")", "ig");
const res = org.replace(reg, '<span class="highlight">$1</span>');
EL_area.innerHTML = res;
};
EL_input.addEventListener("input", highlight);
highlight();
div {
padding: 5px;
border: solid 1px #CCC;
}
.highlight {
background: gold;
}
<input id="input" autocomplete=off type="text" value="tren pan com br" />
<div id="area">
Renew Trend Worry-Free Business Security license
that someweb.com will expire in 60 days.<br>
Activate BR like breakline trend
and [ confirm <span>SOME <span>SPAN</span> IS HERE</span>
upon electronic<br> delivery notification
from Trend Micro
</div>
You were close! Change line 7 to:
var repl = '<span class="search-found">$&</span>';
Notice the $& in there. It's a reference to the matched word and preserves the case.
http://jsfiddle.net/hh2zvjft/2/
And as Roko pointed out in the comment below, you'll get a constant padding increase of 3px on either side if you keep clicking Find. To fix this I'd recommend removing the padding from the CSS, or disabling the Find button if the words have already been highlighted.
Related
Let's say I have an HTML document with the following body:
<style>
.highlighted {
color: red;
background-color: yellow;
}
</style>
<article>My text in an element</article>
I have the challenge to style the i letter from the in word inside the <article> tag with javascript, and I'm required to do it by it's index [8].
So far I can only think of starting with this...
<script>
const article = document.querySelector('article').innerText
console.log(article[8]);
</script>
Expected output:
<article>My text <span class="highlighted">i</span>n an element</article>
// Console
>>>> "i"
...although I've never tried anything like this before, so I'm kinda lost with the following steps.
I supose I need to insert a <span> tag by this index, but I'm not sure how I would set the closing </span> tag or update the DOM.
What would be a good way to achieve this kind of styling?
//get text of article
const article = document.querySelector('article').innerText;
//find index of word 'in'
const index = article.indexOf('in');
//opening and closing tags
const openingTag = '<span style="color:red">'
const closingTag = '</span>'
//insert tags into article
const newHTML
= article.slice(0, index)
+ openingTag + 'in' + closingTag
+ article.slice(index + 2);
document.querySelector('article').innerHTML = newHTML;
This code styles the first occurrence of the word "in" by setting the text color to red. If you want to do something else, change the style attribute of the opening tag.
article.slice(0, index) returns everything before the word "in." In your example, this would evaluate to 'My text '. article.slice(index + 2) returns everything after the word "in" because "in" is 2 letters long. In your example, this would evaluated to ' an element'. When all the strings are concatenated together, the result is 'My text <span style="color:red">in</span> an element'.
const HIGHLIGHT_IDX = 8;
const article = document.querySelector('article').innerText;
const words = article.split(' ');
let highlightCheck = words[0].length;
let wordToHighlight = words[0].length >= HIGHLIGHT_IDX && '0';
let wordIdx = 1;
while (!wordToHighlight) {
highlightCheck += 1 + words[wordIdx].length;
if (highlightCheck >= HIGHLIGHT_IDX) {
wordToHighlight = wordIdx;
} else {
wordIdx += 1;
}
}
words[wordToHighlight] =
`<span class="highlight">${words[wordToHighlight]}</span>`;
document.querySelector('article').innerText = words.join(' ');
I'm working on a contenteditable innerHTML string in JavaScript and tried to remove pairs of
<div><br></div>
It appears when user presses Enter for a newline.
Example String : (will be dynamic)
<div><br></div><div><br></div><div><br></div>Example <br> String d<<div><br></div><div><br></div><div><br></div></div>
so I can get
Example <br> String d<
I tried my regex by searching Stack Overflow too:
let final_text = document.getElementById('chat_room_input_box').innerHTML.trim();
final_text = final_text.replace(/^\s*(?:<br\s*\/?\s*>)+|(?:<br\s*\/?\s*>)+\s*$/gi, '');
final_text = final_text.replace(/^(?: |\s)+|(?: |\s)+$/ig,'');
final_text = final_text.replace(/^\s*(?:<div\s*\/?\s*>)+|(?:<div\s*\/?\s*>)+\s*$/gi, '');
I can remove <br> or at start/ end but fails to remove <div><br></div>
Don't use regex to handle HTML. Regex is not powerful enough for this.
Use an HTML parser. Luckily the world's most capable HTML parser is right at your finger tips - a browser. All that is necessary to parse a piece of HTML is to set an element's .innerHTML property.
Then you can easily remove any elements you define, for example <div> that are empty - which includes those that only contain <br> and/or whitespace.
As a bonus, you even are guaranteed to get valid HTML back from the operation, so the invalid String d< will be returned as the valid String d<.
function sanitizeHtml(html) {
const container = document.createElement('DIV');
// Step 1: Parse the HMTL
container.innerHTML = html;
// Step 2: Modify the structure
for (let div of container.querySelectorAll('div')) {
if (div.textContent.trim() === '') div.parentNode.removeChild(div);
// ...over time, I'm sure you'll find more things you'd like to correct
}
// Step 3: return the modified HTML
return container.innerHTML;
}
function showResult() {
const result = sanitizeHtml(this.innerHTML);
document.getElementById('outputHtml').textContent = result;
document.getElementById('outputDisplay').innerHTML = result;
}
const input = document.getElementById('input');
input.contentEditable = true;
input.addEventListener('input', showResult);
showResult.call(input);
#input, #outputDisplay {
border: 1px solid red;
}
#outputHtml {
border: 1px solid blue;
}
<div id="input"><div><br></div><div><br></div><div><br></div>Example <br> String d<<div><br></div><div><br></div><div><br></div></div></div>
<pre id="outputHtml"></pre>
<div id="outputDisplay"></pre>
so, by what I see, it would make sense to do it in steps
first remove elements like br (self closing) elements
str="<div><br></div><div><br></div><div><br></div>Example String d<<div><br></div><div><br></div><div><br></div></div>"
############## REMOVING SELF CLOSING ELEMENTS
# here is a list of self closing elements, you can add more that you want to remove
str = str.replace(/<(br|img)>/g, "")
# another way of removing self closing elements, is to look for the closure when they are properly coded <br />
str = str.replace(/<br\s*\/?>/g, "")
# if wanted, you can use both
str = str.replace(/(<br\s*\/?>|<(br|img)>)/g, "")
########## REMOVE ELEMENTS THAT CLOSE EMPTY (divs)
str = str.replace(/<([^>]*)>\s*<\/\1>/g, "")
# this will return
# str = str.replace(/<([^>])>\s*<\/\1>/g, "")
If what you want it to look for specific elements, you can use
str = str.replace(/<\/?\s*(br|img|div)\s*\/?>/g, "")
## this returns
"Example String d<"
To learn more, I recomend
https://www.regular-expressions.info/tutorial.html
https://cheatography.com/davechild/cheat-sheets/regular-expressions/
I have a single line of text which is the post title and it looks like this:
<h2 class="title">
Report 10/2019 The name of the report
</h2>
I'd like to be able to:
a) split the name of the link title after the year;
b) style separately the "Report 10/2019" and the rest of the name in two different ways (preferable adding a class to each part - or at least the first part).
I managed to split the text using this:
var breakAt = 17;
var brokenString = $(".title a").html();
brokenString = brokenString.substring(0, breakAt) + "<br>" + brokenString.substring(breakAt);
$(".title a").html(brokenString);
But this divides the name by number of characters, whereas it would be safer to split after X number of spaces (in this case after second space) - the number of the month will be one character or two.
And don't know how to apply different styling to the two parts of the title link.
Loop over all the titles, get their content, apply a regular expression and wrap the substring from the beginning to the year (4 digits) into a span (or another element if you prefer)
Then apply a style to the span (e.g a bolder font and display: block) so the remaining part starts on a new line)
var titles = document.querySelectorAll('.title a');
[...titles].forEach((title) => {
var content = title.textContent;
content = content.replace(/(^.+?\d{4})/, '<span>$1</span>');
title.innerHTML = content;
})
.title a {
color: #333;
font-weight: 400;
}
.title span {
font-weight: 700;
display: block;
}
<h2 class="title">
Report 10/2019 The name of the report
</h2>
<h2 class="title">
Another Report 3 / 2019 The name of the report
</h2>
You can try using split function and second space from string:
$(document).ready(function(){
var string = $('.title a').text();
var report_date = string.split(' ').slice(0, 2).join(' ');
var report_title = string.replace(report_date, '').trim();
});
There's two key things you need:
RegExp, for better and more dynamic pattern matching
span tags, since you want to add classes (you can't do this if you just split the two parts with a br - they're still just text nodes)
So, the question for the RegExp is, how do we define the breakpoint? Well, it seems sensible to use the datestamp for that. First we'll grab it:
let a = $(".title a"),
let part_1 = a.text().match(/^.+\d{2}\/\d{4}/);
Then we need to remove what we just matched from the overall string, to give us the second part. We'll also remove the whitespace between the two parts.
let part_2 = a.text().replace(part_1[0], '').trim();
The last thing is to put these two parts into span tags inside the a.
$('<span>').addClass('part-1').text(part_1[0]).appendTo(a);
$('<span>').addClass('part-2').text(part_2).appendTo(a);
You might want to use regular expression, since the length of the first part of the string might be dynamic. This pattern should do the work of splitting your string into two parts: one with the report + month/year, and the other with the report name:
/^(.*?\/\d{4})(.*?)$/
See proof-of-concept here:
const links = document.querySelectorAll('h2.title a');
Array.prototype.forEach.call(links, (link) => {
const textRegex = /^(.*?\/\d{4})(.*?)$/gi;
const matches = textRegex.exec(link.textContent);
const reportYear = matches[1].trim();
const reportName = matches[2].trim();
link.innerHTML = `<span class="year">${reportYear}</span><span class="name">${reportName}</span>`;
});
.title a span {
display: block;
}
.year {
color: red;
}
.name {
color: green;
}
<h2 class="title">
Report 10/2019 The name of the report
</h2>
If you want to break the string based on the number of spaces, you can use split.
Here is an example:
var stringToBreak = $(".title a").html();
var brokenArr = stringToBreak.split(' ');
var txtToStyle1 = brokenArr.slice(0, 2).join(' ');
var txtToStyle2 = brokenArr.slice(2).join(' ');
$(".title a").empty();
$(".title a").append(
$("<span>").html(txtToStyle1).css('color', 'red')
).append(
$("<span>").html(' ' + txtToStyle2)
);
a {
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h2 class="title">
Report 10/2019 The name of the report
</h2>
here is a solution.
var brokenString = $(".title a").html();
var stringArray = brokenString.split(" ");
var firstPart = stringArray[0] + " " + stringArray[1];
// firsPart delete from array
stringArray.shift();
stringArray.shift();
// now assign in html
$(".title a").html(firstPart + ' <br>' + stringArray.join(' '));
const anchorText = document.getElementsByTagName('a')[0].innerText;
const splitBy = '2019 ';
const splittedText = anchorText.split(splitBy);
const firstElement = "<span class='first'>" + splittedText[0] + splitBy + "</span>";
const secondElement = "<span class='second'>" + splittedText[1] + "</span>";
document.getElementsByTagName('a')[0].innerHTML = firstElement + secondElement;
.first {
color: red;
}
.second {
color: green;
}
<h2 class="title">
Report 10/2019 The name of the report
</h2>
If 2019 it's a dynamical value like (current year) you can retrieve that from the new Date() date object and add it there.
In my project I have some html with comments surrounding text so I can find the text between particular comments and replace that text whilst leaving the comments so I can do it again.
I am having trouble getting the regex to work.
Here is an html line I am working on:
<td class="spaced" style="font-family: Garamond,Palatino,sans-serif;font-size: medium;padding-top: 10px;"><!--firstname-->Harrison<!--firstname--> <!--lastname-->Ford<!--lastname--> <span class="spacer"></span></td>
Now, here is the javascript/jquery that I have at the moment:
var thisval = $(this).val(); //gets replacement text from a text box
var thistoken = "firstname";
currentTemplate = $("#gentextCodeArea").text(); //fetch the text
var tokenstring = "<!--" + thistoken + "-->"
var pattern = new RegExp(tokenstring + '\\w+' + tokenstring,'i');
currentTemplate.replace(pattern, tokenstring + thisval + tokenstring);
$("#gentextCodeArea").text(currentTemplate); //put the new text back
I think I'm pretty close, but I don't have the regex right yet.
The regex ought to replace the firstname with whatever is entered in the textbox for $thisval (method is attached to keyup procedure on textbox).
Using plain span tags instead of comments would make things easier, but either way, I would suggest not using regular expressions for this. There can be border cases that may lead to undesired results.
If you stick with comment tags, I would iterate over the child nodes and then make the replacement, like so:
$("#fname").on("input", function () {
var thisval = $(this).val(); //gets replacement text from a text box
var thistoken = "firstname";
var between = false;
$("#gentextCodeArea").contents().each(function () {
if (this.nodeType === 8 && this.nodeValue.trim() === thistoken) {
if (between) return false;
between = true;
} else if (between) {
this.nodeValue = thisval;
thisval = '';
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
New first name: <input id="fname">
<div id="gentextCodeArea">
<!--firstname-->Harrison<!--firstname-->
<!--lastname-->Ford<!--lastname-->
<span class="spacer"></span></div>
What went wrong in your code
By using text() you don't get the comment tags. To get those, you need to use html() instead
replace() does not mutate the variable given in the first argument, but returns the modified string. So you need to assign that back to currentTemplate
It would be better to use [^<]* instead of \w+ for matching the first name, as some first names have non-letters in them (hyphen, space, ...), and it may even be empty.
Here is the corrected version, but I insist that regular expressions are not the best solution for such a task:
$("#fname").on("input", function () {
var thisval = $(this).val(); //gets replacement text from a text box
var thistoken = "firstname";
currentTemplate = $("#gentextCodeArea").html(); //fetch the html
var tokenstring = "<!--" + thistoken + "-->"
var pattern = new RegExp(tokenstring + '[^<]*' + tokenstring,'i');
currentTemplate = currentTemplate.replace(pattern, tokenstring + thisval + tokenstring);
$("#gentextCodeArea").html(currentTemplate); //put the new text back
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
New first name: <input id="fname">
<div id="gentextCodeArea">
<!--firstname-->Harrison<!--firstname-->
<!--lastname-->Ford<!--lastname-->
<span class="spacer"></span></div>
here is a function which will generate an appropriate Regular expression:
function templatePattern(key) {
return new RegExp(`<!--${key}-->(.*?)<!--${key}-->`);
}
the (.*?) means "match as little as possible," so it will stop at the first instance of the closing tag.
Example:
'<!--firstname-->Harrison<!--firstname--> <!--lastname-->Ford<!--lastname-->'
.replace(templatePattern('firstname'), 'Bob')
.replace(templatePattern('lastname'), 'Johnson') // "Bob Johnson"
$(function(){
function onKeyUp(event)
{
if(event.which === 38) // if key press was the up key
{
$('.firstname_placeholder').text($(this).val());
}
}
$('#firstname_input').keyup(onKeyUp);
});
input[type=text]{width:200px}
<input id='firstname_input' type='text' placeholder='type in a name then press the up key'/>
<table>
<tr>
<td ><span class='firstname_placeholder'>Harrison</span> <span class='lastname_placeholder'>Ford</span> <span class="spacer"></span></td>
</tr>
</table>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
[blog]: https://jsfiddle.net/kb413ae8/ "click here" for working fiddle
Kids Markers & Crayons is a response string for which highlighting of text should append, based on input we pass in searchQuery variable in code.
if Markers is provided as searchQuery input, Markers should be highlighted, which is currently working fine in above fiddle link.
if space is provided after word markers and pass has a searchQuery input in code, Kids 'b tag with class='boldTxt'>Markers & Crayons is appended. which is weird. Happens same for all word with space.
Highlight of text/word should happen if first character in searchQuery input matches with first character of word in response. does not matter where the word is. Thanks
var data = {"ResultSet":{"result":[
{"rank":"999999","term":"Kids Markers & Crayons"},
{"rank":"999999","term":"Crayola Fine Line Markers"},
{"rank":"999999","term":"Crayola Super Tips Washable Markers"},
{"rank":"999999","term":"Crayola Washable Markers Broad Line Classic"},
{"rank":"999999","term":"Post-it Assorted Page Markers"},
{"rank":"999999","term":"Crayola Markers Broad Line Classic"},
{"rank":"999999","term":"Crayola Crayons"},
{"rank":"999999","term":"Crayola Washable Markers Classic Colors"},
{"rank":"999999","term":"Crayola Washable Crayons Large"}]
,"totalResultsAvailable":"32","totalResultsReturned":11}};
for(i=0; i<9; i++) {
var result = data.ResultSet.result[i].term;
//console.log('result: ' + result);
var searchQuery = 'Markers ';
/*searchQuery inputs
searchQuery = 'c'; o/p: c should be highlighted at beginning of word, In classic 'c' is highlighting twice which should not be highlighted at end or in mid of word.
searchQuery = 'markers '; o/p: <b> tag is added which has to be removed.
searchQuery = 'markers & c '; o/p: <b> tag is added which has to be removed.
searchQuery = 'ola'; o/p: ola should be highlighted at beginning of word, In Crayola 'ola' is highlighting, which should not be highlighted at end or in mid of word.
*/
var searchResult = result;
words = searchQuery.split(' ');
$.each(words, function() {
var word = this.trim();
var regex = new RegExp('(' + word + ')(?!>|b>)', 'gi');
searchResult = searchResult.replace(regex, "<b class='boldTxt'>$1</b>");
});
console.log(searchResult);
$('#wag-typeaheadlists').append('<li><a>' + searchResult + '</a></li>')
}
.boldTxt {
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<ul id="wag-typeaheadlists">
</ul>
In your fiddle try adding the following line after you split the searchQuery.
words = words.filter(function(word){ return word != "" });
Hope it solves your problem!