I would like to have a specific text colored differently in HTML/PHP/CSS/JS.
I have a text similar to this structure:
#john please move the invite to the next week.
What i would like to achieve is to have the wording "#john" colored in green while the rest of the text characters should remain in black. In other words the "#" and the first space " " right after the #, are the delimiters of the green text.
Can you please advise on how to achieve this?
Thanks,
Slim
You can use Regular expressions for this; the character \# will just match a "#", and the character \w+ will match a word - a word is a set of characters which are unseparated by commas, spaces, full stops, etc.
Lastly, you will need to use a String Iterator to find all matches and loop through them, and, of course, re-color them.
Here is how you would do this:
function color_text(text) {
let matches = text.matchAll(/\#\w+/g);
let current_match;
while(!current_match?.done) {
current_match = matches.next();
if(current_match?.done) {
break;
}
let value = current_match?.value[0];
text = text.replace(value, "<span style='color: #00db58'>" + value + "</span>");
}
return text;
}
// TESTING HERE -- - - -- - - -
document.write(color_text("#john please move the invite to the next week."));
document.write("<br>");
document.write(color_text("#john please tell #josh to stop being annoying."));
Related
I am trying to highlight the remaining of a word if the text value from an input includes the start of the word. For example, we have a string of "Nutrition is great!", and if the user types in "Nutr" then I would like for "Nutrition" to be highlighted. I am having quite a lot of difficulty with this and was wondering if it's possible to substring to the next available whitespace? Or if anyone could give me any pointers for a different/better approach.
I have taken some inspiration from This post but unfortunately, it doesn't match the full word as I'd like.
I have created a sandbox to demonstrate my example, you'll notice that if you type in "Nutr" that only 2 options get highlighted when all 3 options should if possible?
https://codesandbox.io/s/dry-wind-17mko
You have a few issues in that others have pointed out about upper/lower case. You are also including the period at the end of the sentence, which is unlikely your intent.
This solution doesn't require changing the case of the input or anything.
I would like to offer a simpler approach using Regular expressions and the .match() method from the String Object.
This will allow a case insensitive match while only highlighting the word. In the code below, you'll see the matched searched word placed into the HTML with a simple call to .replace() adding the <span> tags.
The match is done based upon finding the typed characters and then finding the word boundary (the space you mentioned in your question).
if (titleRef.current) {
let resultsText = titleRef.current.innerHTML;
const regex = new RegExp("(?=" + searchTerm + ")\\w*", "i");
const found = resultsText.match(regex);
if (found) {
titleRef.current.innerHTML = resultsText.replace(found[0],`<span>${found[0]}</span>`);
}
}
There is one remaining problem. If the search no longer matches (say you type 'nutrdddd', the original match remains highlighted. So to overcome this, you'll need to remove the span tags and I'll leave that up to you.
Hope this is helpful. Here is the CodeSandBox
Comparison has to be based on String either being uppercase or lower case so that we can select the text String irrespective of being upper case of lower case.
UPDATE:
Also we need to select only the term based on match but not full length of text in the span.
UPDATED CODESANDBOX: https://codesandbox.io/s/highlighting-z216x
Logic to get the specific text updated to work dynamically.
Code Changes in useEffect() in Result.js
useEffect(() => {
console.log(searchTerm);
if (titleRef.current) {
let resultsText = titleRef.current.textContent.toUpperCase();
const index = resultsText.toUpperCase().indexOf(searchTerm);
const text = resultsText.substr(index, resultsText.length).split(" ")[0];
if (index >= 0) {
resultsText =
resultsText.substring(0, index) +
"<span>" +
text +
"</span>" +
resultsText.substring(index + text.length);
titleRef.current.innerHTML = resultsText;
}
}
}, []);
And in App.js we need to make sure to pass searchTerm as uppercase props
<Result
text="This is nutritional value"
searchTerm={searchTerm.toUpperCase()}
/>
Well, first what you can do is you need to change your string and input both to either Lowercase or Uppercase so that both will be compared on the same basis. rest all is fine you can check using .includes() method and you can trim your string using .trim() method as then can only highlight the desired the word.
I've created my own autocomplete feature and I've come across a bug I'd like to fix. Here's an example of an incomplete sentence I might want to autocomplete the final word for:
let text = 'Hello there, I am her'
In my functionality the user clicks ctrl + enter and it autcompletes the word with a suggestion displayed on the page. In this case let's say the suggestion is 'here'. Also my controller knows where the user is based on the insertion cursor (so I have the index).
If I use replace like so:
text.replace(word, suggestion);
(Where word is 'her' and suggestion is 'here') it will replace the first occurrence. Obviously there are endless combinations of where this word might be in the text, how do I replace one at a certain index in text string? I know I can do it through some messy if conditions, but is there an elegant way to do this?
(If it is relevant I am using angular keydown/keyup for this)
EDIT>>>>>
This is not a duplicate on the question linked as in that case they are always replacing the last occurrence. If I did that then my program wouldn't support a user going back in their sentence and attempting to autocomplete a new word there
So, you have a position in a string and a number of characters to replace (=length of an incomplete word). In this case, would this work?
let text = 'appl and appl and appl'
function replaceAt(str, pos, len, replace) {
return str.slice(0, pos) + replace + str.slice(pos + len);
}
console.log(replaceAt(text, 0, 4, 'apple'))
console.log(replaceAt(text, 9, 4, 'apple'))
Gonna point you in a direction that should get you started.
let sentence = 'Hello lets replace all words like hello with Hi';
let fragments = sentence.split(' ');
for (let i=0; i<fragments.length; i++){
if(fragments[i].toLowerCase() == 'hello')
fragments[i] = 'Hi'
}
let formattedsentence = fragments.join(' ');
console.log(formattedsentence); //"Hi lets replace all words like Hi with Hi"
I was pointed out to this post, which does not seem to follow the criteria I have:
Replace a Regex capture group with uppercase in Javascript
I am trying to make a regex that will:
format a string by adding uppercase for the first letter of each word and lower case for the rest of the characters
ignore HTML markup
Accept swedish characters (åäöÅÄÖ)
Say I've got this string:
<b>app</b>le store östersund
Then I want it to be (changes marked by uppercase characters)
<b>App</b>le Store Östersund
I've been playing around with it and the closest I've got is the following:
(?!([^<])*?>)[åäöÅÄÖ]|\s\b\w
Resulted in
<b>app</b>le Store Östersund
Or this
/(?!([^<])*?>)[åäöÅÄÖ]|\S\b\w/g
Resulted in
<B>App</B>Le store Östersund
Here's a fiddle:
http://refiddle.com/refiddles/598aabef75622d4a531b0000
Any help or advice is much appreciated.
It is not possible to do this with regexp alone, since regexp doesn't understand HTML structure. [*] Instead, we need to process each text node, and carry through our logic for what is the beginning of the word in case a word continues across different text nodes. A character is at start of the word if it is preceded by a whitespace, or if it is at the start of the string and it is either the first text node, or the previous text node ended in whitespace.
function htmlToTitlecase(html, letters) {
let div = document.createElement('div');
let re = new RegExp("(^|\\s)([" + letters + "])", "gi");
div.innerHTML = html;
let treeWalker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT);
let startOfWord = true;
while (treeWalker.nextNode()) {
let node = treeWalker.currentNode;
node.data = node.data.replace(re, function(match, space, letter) {
if (space || startOfWord) {
return space + letter.toUpperCase();
} else {
return match;
}
});
startOfWord = node.data.match(/\s$/);
}
return div.innerHTML;
}
console.log(htmlToTitlecase("<b>app</b>le store östersund", "a-zåäö"));
// <b>App</b>le Store Östersund
[*] Maybe possible, but even if so, it would be horribly ugly, since it would need to cover an awful amount of corner cases. Also might need a stronger RegExp engine than JavaScript's, like Ruby's or Perl's.
EDIT:
Even if just specifying really simple html tags? The only ones I am actually in need of covering is <b> and </b> at the moment.
This was not specified in the question. The solution is general enough to work for any markup (including simple tags). But...
function simpleHtmlToTitlecaseSwedish(html) {
return html.replace(/(^|\s)(<\/?b>|)([a-zåäö])/gi, function(match, space, tag, letter) {
return space + tag + letter.toUpperCase();
});
}
console.log(simpleHtmlToTitlecaseSwedish("<b>app</b>le store östersund", "a-zåäö"));
I have a solution which use almost only regex. It may be not the most intuitive way to do it, but it should be effective and I find it funny :)
You have to append at the end of your string every lowercase character followed by their uppercase counterpart, like this (it must also be preceded by a space for my regex) :
aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZåÅäÄöÖ
(I don't know which letters are missing, I know nothing about swedish alphabet, sorry... I'm counting on you to correct that !)
Then you can use the following regex :
(?![^<]*>)(\s<[^/]*?>|\s|^)([\wåäö])(?=.*\2(.)\S*$)|[\wåÅäÄöÖ]+$
Replace by :
$1$3
Test it here
Here is a working javascript code :
// Initialization
var regex = /(?![^<]*>)(\s<[^/]*?>|\s|^)([\wåäö])(?=.*\2(.)\S*$)|[\wåÅäÄöÖ]+$/g;
var string = "test <b when=\"2>1\">ap<i>p</i></b>le store östersund";
// Processing
result = string + " aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZåÅäÄöÖ";
result = result.replace(regex, "$1$3");
// Display result
console.log(result);
Edit : I forgot to handle first word of the string, it's corrected :)
I am currently doing a regex comparison to remove words (rude words) from a text field when written by the user. At the moment it performs the check when the user hits space and removes the word if matches. However it will remove the word even if it is part of another word. So if you type apple followed by space it will be removed, that's ok. But if you type applepie followed by space it will remove 'apple' and leave pie, that's not ok. I am trying to make it so that in this instance if apple is part of another word it will not be removed.
Is there any way I can perform the comparison on the whole word only or ignore the comparison if it is combined with other characters?
I know that this allows people to write many rude things with no space. But that is the desired effect by the people that give me orders :(
Thanks for any help.
function rude(string) {
var regex = /apple|pear|orange|banana/ig;
//exaple words because I'm sure you don't need to read profanity
var updatedString = string.replace( regex, function(s) {
var blank = "";
return blank;
});
return updatedString;
}
$(input).keyup(function(event) {
var text;
if (event.keyCode == 32) {
var text = rude($(this).val());
$(this).val(text);
$("someText").html(text);
}
}
You can use word boundaries (\b), which match 0 characters, but only at the beginning or end of a word. I'm also using grouping (the parentheses), so it's easier to read an write such expressions.
var regex = /\b(apple|pear|orange|banana)\b/ig;
BTW, in your example you don't need to use a function. This is sufficient:
function rude(string) {
var regex = /\b(apple|pear|orange|banana)\b/ig;
return string.replace(regex, '');
}
I'm working on an autocomplete component that highlights all ocurrences of searched text. What I do is explode the input text by words, and wrap every ocurrence of those words into a
My code looks like this
inputText = 'marriott st';
text = "Marriott east side";
textSearch = inputText.split(' ');
for (var i in textSearch) {
var regexSearch = new RegExp('(?!<\/?strong>)' + textSearch[i]), "i");
var textReplaced = regexSearch.exec(text);
text = text.replace(regexSearch, '< strong>' + textReplaced + '< /strong>');
}
For example, given the result: "marriott east side"
And the input text: "marriott st"
I should get
<strong>marriot< /strong > ea < strong >st < /strong > side
And i'm getting
<<strong>st</strong>rong>marriot</<strong>st </strong>rong>ea<<strong>st</strong> rong>s</strong> side
Any ideas how can I improve my regex, in order to avoid ocurrences inside the html tags? Thanks
/(?!<\/?strong>)st/
I would process the string in one pass. You can create one regular expression out of the search string:
var search_pattern = '(' + inputText.replace(/\s+/g, '|') + ')';
// `search_pattern` is now `(marriot|st)`
text = text.replace(RegExp(search_pattern, 'gi'), '<strong>$1</strong>');
DEMO
You could even split the search string first, sort the words by length and combine them, to give a higher precedence to longer matches.
You definitely should escape special regex characters inside the string: How to escape regular expression special characters using javascript?.
Before each search, I suggest getting (or saving) the original search string to work on each time. For example, in your current case that means you could replace all '<strong>' and '</strong>' tags with ''. This will help keep your regEx simple, especially if you decide to add other html tags and formatting in the future.