Caps and Lowercase input that also generates in output - javascript

I've been trying to put together an input where the text automatically capitalizes the first letter of each word and makes all other letters lowercase for that word. I had some success using that for just making everything lower case after the first letter for the input with this:
<input type = "text" size="8" name="textfield1" id="textfield1" />
with the javascript being
document.getElementById('textfield1').addEventListener("keyup", () => {
var inputValue = document.getElementById('textfield1')['value'];
if (inputValue[0] === ' ') {
inputValue = '';
} else if (inputValue) {
inputValue = inputValue[0].toUpperCase() + inputValue.slice(1).toLowerCase();
}
document.getElementById('textfield1')['value'] = inputValue;
});
I tried adding map(), split(), and join() in various ways based off of lessons I've found (I'm learning on my own, no formal training since high school) for use in a string with the console.log methods but I'm confused on how I can apply this to an input. It would take too long to note everything I've tried but one thing I did was this:
document.getElementById('textfield1').addEventListener("keyup", () => {
var inputValue = document.getElementById('textfield1')['value'];
if (inputValue[0] === ' ') {
inputValue = '';
} else if (inputValue) {
input.content = input.content.split(' ').map(function(inputValue) {
return inputValue[0].toUpperCase() + inputValue.slice(1).toLowerCase();
}).join(' ');
}
document.getElementById('textfield1')['value'] = inputValue;
});
I'm not sure what I'm missing here. I'm sure there's something that I'm not seeing or understanding. I also tried looking to see if there was something similar listed on here or elsewhere in relation to this and inputs but I didn't see anything specific to what I was looking for.
I want the input to remain the same for what comes up with the output into another box when it gets copied over.
Example of what I'm trying to do is:
input of textfield: heLlo OuT thERe!
output to another textarea with the click of a button: Hello Out There!

Your second version would be correct. However in this code:
else if (inputValue) {
input.content = input.content.split(' ').map(function(inputValue) {
return inputValue[0].toUpperCase() + inputValue.slice(1).toLowerCase();
}).join(' ');
}
You started to use some input.content instead of the inputValue variable. Most probably yout mistake lies there.

You can use this regex replace to change a sentence to Proper Case:
const regex = /\b(\w)(\w*)/g;
const input = 'Fix this lower and UPPER to Proper'
let result = input.replace(regex, function(m, c1, c2) {
return c1.toUpperCase() + c2.toLowerCase()
});
console.log('result: ', result);
// => 'Fix This Lower And Upper To Proper'
Explanation of regex:
\b -- word boundary
(\w) -- capture group 1: a singe word character
(\w*) --capture group 2: zero to multiple word characters
g -- flag for global, e.g. run pattern multiple times
replace function: parameter c1 contains capture group 1, c2 contains capture group 2

Related

Vue: Make matching part of input bold, including special hyphens

I have made a simple select component in Vue with a search/filter system. Based on the user input I'm showing some Belgium city suggestions.
Working example: https://codesandbox.io/s/wandering-lake-lecok?file=/src/components/Select.vue (Sometimes there is an error message in Codesandbox. Refresh the build in browser and it should work)
I want to take the UX one step further and show the matching part of the user input bold and underlined. Therefore I have a working makeBold function. By splitting the suggestion string into multiple parts I can add a bold and underline tag and return the suggestion.
computed: {
results() {
return this.options.filter((option) =>
option.display_name
.replaceAll("-'", "")
.toLowerCase()
.includes(this.searchInput.replaceAll("-'", "").toLowerCase())
);
},
},
methods: {
makeBold(str, query) {
const n = str.toUpperCase();
const q = query.toUpperCase();
const x = n.indexOf(q);
if (!q || x === -1) {
return str;
}
const l = q.length;
return (
str.substr(0, x) + "<b><u>" + str.substr(x, l) + "</u></b>" + str.substr(x + l)
);
},
}
One problem, a lot of cities in Belgium use dashes and/or apostrophes. In the suggestions function I'm removing this characters so a user doesn't need to type them. But in the makeBold function I would like to make this characters bold and underlined.
For example:
When the input is 'sint j', 'sintj' or 'Sint-j' I want the suggestions to look like 'Sint-Jans-Molenbeek' and 'Sint-Job in't Goor'
Is there someone who can give me a breakdown on how to achieve this?
I would propose using a mask, to save the city name structure, and after you find the start and end index of substring in city name, restore the original string from mask, inserting the appropriate tags at the start and end index using a replacer function. this way you would not worry about any other non-word characters or other unexpected user input.
Here is the makeBold function:
makeBold(str, query) {
// mask all word characters in city name
const city_mask = str.replace(/\w/g, "#");
// strip city and query string from any non-word character
let query_stripped = query.toLowerCase().replace(/\W/g, "");
let string_stripped = str.replace(/\W/g, "");
// find the index of querystring in city name
let index = string_stripped.toLowerCase().indexOf(query_stripped);
if (index > -1 && query_stripped.length) {
// find the end position of substring in stripped city name
let end_index = index + query_stripped.length - 1;
// replacer function for each masked character.
// it will add to the start and end character of substring the corresponding tags,
// replacing all masked characters with the original one.
function replacer(i) {
let repl = string_stripped[i];
if (i === index) {
repl = "<b><u>" + repl;
}
if (i === end_index) {
repl = repl + "</u></b>";
}
return repl;
}
let i = -1;
// restore masked string
return city_mask.replace(/#/g, (_) => {
i++;
return replacer(i);
});
}
return str;
}
And here is the working sandbox. I've changed a bit your computed results to strip all non-word characters.
One way is to transform your search string into a RegExp object and use replace(regexp, replacerFunction) overload of string to achieve this.
For example the search string is "sintg"
new RegExp(this.searchInput.split("").join("-?"), "i");
Turns it into /s-?i-?n-?t-?g/gi
-? indicates optional - character and
"i" at the end is the RegExp case insensitive flag
Applied to codesandbox code you get this
computed: {
results() {
const regex = new RegExp(this.searchInput.split("").join("-?"), "i");
return this.options.filter((option) => option.display_name.match(regex));
},
},
methods: {
makeBold(str, query) {
const regex = new RegExp(query.split("").join("-?"), "i");
return str.replace(regex, (match) => "<b><u>" + match + "</u></b>");
},
},
Which gives this result
However there is a caveat: There will be errors thrown if the user puts a RegExp special symbol in the search box
To avoid this the initial search input text needs to get RegExp escape applied.
Such as:
new RegExp(escapeRegExp(this.searchInput).split("").join("-?"), "i");
But there is no native escapeRegExp method.
You can find one in Escape string for use in Javascript regex
There is also an escapeRegExp function in lodash library if it's already in your list of dependencies (saves you from adding another function)
You could create a function that removes all spaces and - in the query and city string. If the city includes the query, split the query string on the last letter and get the occurences of that letter in the query. Calculate the length to slice and return the matching part of the original city string.
const findMatch = (q, c) => {
const query = q.toLowerCase().replace(/[\s-]/g, "");
const city = c.toLowerCase().replace(/[\s-]/g, "");
if (city.includes(query)) {
const last = query.charAt(query.length - 1); // last letter
const occ = query.split(last).length - 1; // get occurences
// calculate slice length
const len = c.toLowerCase().split(last, occ).join(" ").length + 1;
return c.slice(0, len);
}
return "No matching city found."
}
const city = "Sint-Jan Test";
console.log(findMatch("sint j", city));
console.log(findMatch("sintj", city));
console.log(findMatch("Sint Jan t", city));
console.log(findMatch("sint-j", city));
console.log(findMatch("Sint-J", city));
console.log(findMatch("SintJan te", city));

Assign ID to html text

How do I go about assigning certain words a unique id tag using vanilla javascript? For example:
<p>All the king's horses ran away.</p>
The word "All" gets the id "term1", "King's" gets "term2", "away" gets "term3", etc. but not every word in the sentence will get assigned an id.
I am currently using the replace method but I think it's the wrong approach:
var str = document.getElementsByTagName('p')[0].innerHTML;
function addId() {
txt = str.replace(/all/i, '<span id="term1">$&</span>').replace(/king's/i, '<span id="term2">$&</span>').replace(/away/i, '<span id="term3">$&</span>');
document.getElementsByTagName('p')[0].innerHTML = txt;
}
window.onload = function() {
addId();
};
<p>All the king's horses ran away.</p>
This forces me to chain a bunch of replace commands, changing the id name each time. I don't think this is the best solution. What is best way to do this? Thanks for your help!
I believe this should make the job done. You can replace blacklist with whitelist. That will depend on your use case. Also, addIds can use Array.map instead of Array.forEach that will make the whole function a one-liner. This example is imperative because it will be more readable.
// string - string where we want to add ids
// blackList - words we want to skip (can be whiteliste black list is more general)
function addIds(string, blackList = ['the', 'a']) {
const stringArray = string.split(' ') // split string into separate words
let stringWithIds = string // this will be final string
stringArray.forEach((item, index) => {
// skip word if black listed
if (blackList.indexOf(item.toLowerCase()) !== -1) {
return
}
stringWithIds = stringWithIds.replace(item, `<span id="term${index}">${item}</span>`) // add id to word if not black listed
})
// replace string with our string with ids
document.getElementsByTagName('p')[0].innerHTML = stringWithIds;
}
window.onload = function() {
const str = document.getElementsByTagName('p')[0].innerHTML;
addIds(str); // you can pass custom blacklist as optional second parameter
};
<p>All the king's horses ran away.</p>
I think this function would be flexible enough for the task:
// Here, your logic to decide if the word must be tagged
const shouldBeTagged = word => {
return true
}
function addId(str) {
// Split the string into individual words to deal with each of them
// one by one
let txt = str.split(' ').map((word, i) => {
// Should this word be tagged
if (shouldBeTagged(word)) {
// If so, tag it
return '<span id="term' + i + '">' + word + '</span>'
} else {
// Otherwise, return the naked word
return word
}
// Join the words together again
}).join(' ')
document.getElementsByTagName('p')[0].innerHTML = txt;
}
window.onload = function() {
addId(document.getElementsByTagName('p')[0].innerHTML);
};
<p>All the king's horses ran away.</p>

RegExp doesn't work fine

I'm working on a template engine, I try to catch all strings inside <% %>, but when I work it on the <%object.property%> pattern, everything fails.
My code:
var render = function(input, data){
var re = /<%([^%>]+)?%>/g;
var templateVarArray;
// var step = "";
while((templateVarArray = re.exec(input))!=null){
var strArray = templateVarArray[1].split(".");
// step+= templateVarArray[1]+" ";
if(strArray.length==1)
input = input.replace(templateVarArray[0], data[templateVarArray[1]]);
if(strArray.length==2){
input = input.replace(templateVarArray[0], data[strArray[0]][strArray[1]]);
}
}
// return step;
return input;
}
var input = "<%test.child%><%more%><%name%><%age%>";
document.write(render(input,{
test: { child: "abc"},
more: "MORE",
name:"ivan",
age: 22
}));
My result:
abc<%more%><%name%>22
what I want is: abc MORE ivan 22
Also, the RegExp /<%([^%>]+)?%>/g is referenced online, I did search its meaning, but still quite not sure the meaning. Especially why does it need "+" and "?", thanks a lot!
If you add a console.log() statement it will show where the next search is going to take place:
while((templateVarArray = re.exec(input))!=null){
console.log(re.lastIndex); // <-- insert this
var strArray = templateVarArray[1].split(".");
// step+= templateVarArray[1]+" ";
if(strArray.length==1)
input = input.replace(templateVarArray[0], data[templateVarArray[1]]);
if(strArray.length==2){
input = input.replace(templateVarArray[0], data[strArray[0]][strArray[1]]);
}
}
You will see something like:
14
26
This means that the next time you run re.exec(...) it will start at index 14 and 26 respectively. Consequently, you miss some of the matches after you substitute data in.
As #Alexander points out take the 'g' off the end of the regex. Now you will see something like this:
0
0
This means the search will start each time from the beginning of the string, and you should now get what you were looking for:
abcMOREivan22
Regarding your questions on the RegEx and what it is doing, let's break the pieces apart:
<% - this matches the literal '<' followed immediately by '%'
([^%>]+) - the brackets (...) indicate we want to capture the portion of the string that matches the expression within the brackets
[^...] - indicates to match anything except what follows the '^'; without the '^' would match whatever pattern is within the []
[^%>] - indicates to match and exclude a single character - either a '%' or '>'
[^%>]+ - '+' indicates to match one or more; in other words match one or more series of characters that is not a '%' and not a '>'
? - this indicates we want to do reluctant matching (without it we do what is called 'greedy' matching)
%> - this matches the literal '%' followed immediately by '>'
The trickiest part to understand is the '?'. Used in this context it means that we stop matching with the shortest pattern that will still match the overall regex. In this case, it doesn't make any difference whether you include it though there are times where it will matter depending on the matching patterns.
Suggested Improvement
The current logic is limited to data that nests two levels deep. To make it so it can handle an arbitrary nesting you could do this:
First, add a small function to do the substitution:
var substitute = function (str, data) {
return str.split('.').reduce(function (res, item) {
return res[item];
}, data);
};
Then, change your while loop to look like this:
while ((templateVarArray = re.exec(input)) != null) {
input = input.replace(templateVarArray[0], substitute(templateVarArray[1], data));
}
Not only does it handle any number of levels, you might find other uses for the 'substitute()' function.
The RegExp.prototype.exec() documentation says:
If your regular expression uses the "g" flag, you can use the exec() method multiple times to find successive matches in the same string. When you do so, the search starts at the substring of str specified by the regular expression's lastIndex property (test() will also advance the lastIndex property).
But you are replacing each match in the original string so next re.exec with a lastIndex already set not to zero will continue to search not from beginning and will omit something.
So if you want to search and substitute found results in original string - just omit \g global key:
var render = function(input, data) {
var re = /<%([^%>]+)?%>/;
var templateVarArray;
// var step = "";
while (!!(templateVarArray = re.exec(input))) {
var strArray = templateVarArray[1].split(".");
if (strArray.length == 1)
input = input.replace(templateVarArray[0], data[templateVarArray[1]]);
if (strArray.length == 2) {
input = input.replace(templateVarArray[0], data[strArray[0]][strArray[1]]);
}
}
// return step;
return input;
}
var input = "<%test.child%><%more%><%name%><%age%>";
document.write(render(input, {
test: {
child: "abc"
},
more: "MORE",
name: "ivan",
age: 22
}));

Regular Expressions required format

I want to validate following text using regular expressions
integer(1..any)/'fs' or 'sf'/ + or - /integer(1..any)/(h) or (m) or (d)
samples :
1) 8fs+60h
2) 10sf-30m
3) 2fs+3h
3) 15sf-20m
i tried with this
function checkRegx(str,id){
var arr = strSplit(str);
var regx_FS =/\wFS\w|\d{0,9}\d[hmd]/gi;
for (var i in arr){
var str_ = arr[i];
console.log(str_);
var is_ok = str_.match(regx_FS);
var err_pos = str_.search(regx_FS);
if(is_ok){
console.log(' ID from ok ' + id);
$('#'+id).text('Format Error');
break;
}else{
console.log(' ID from fail ' + id);
$('#'+id).text('');
}
}
}
but it is not working
please can any one help me to make this correct
This should do it:
/^[1-9]\d*(?:fs|sf)[-+][1-9]\d*[hmd]$/i
You were close, but you seem to be missing some basic regex comprehension.
First of all, the ^ and $ just make sure you're matching the entire string. Otherwise any junk before or after will count as valid.
The formation [1-9]\d* allows for any integer from 1 upwards (and any number of digits long).
(?:fs|sf) is an alternation (the ?: is to make the group non-capturing) to allow for both options.
[-+] and [hmd] are character classes allowing to match any one of the characters in there.
That final i allows the letters to be lowercase or uppercase.
I don't see how the expression you tried relates anyhow to the description you gave us. What you want is
/\d+(fs|sf)[+-]\d+[hmd]/
Since you seem to know a bit about regular expressions I won't give a step-by-step explanation :-)
If you need exclude zero from the "integer" matches, use [1-9]\d* instead. Not sure whether by "(1..any)" you meant the number of digits or the number itself.
Looking on the code, you
should not use for in enumerations on arrays
will need string start and end anchors to check whether _str exactly matches the regex (instead of only some part)
don't need the global flag on the regex
rather might use the RegExp test method than match - you don't need a result string but only whether it did match or not
are not using the err_pos variable anywhere, and it hardly will work with search
function checkRegx(str, id) {
var arr = strSplit(str);
var regx_FS = /^\d+(fs|sf)[+-]\d+[hmd]$/i;
for (var i=0; i<arr.length; i++) {
var str = arr[i];
console.log(str);
if (regx_FS.test(str) {
console.log(' ID from ok ' + id);
$('#'+id).text('Format Error');
break;
} else {
console.log(' ID from fail ' + id);
$('#'+id).text('');
}
}
}
Btw, it would be better to separate the validation (regex, array split, iteration) from the output (id, jQuery, logs) into two functions.
Try something like this:
/^\d+(?:fs|sf)[-+]\d+[hmd]$/i

How to tell if a string contains HTML entity (like &)?

I'm trying to write a function that checks a parameter against an array of special HTML entities (like the user entered '&amp' instead of '&'), and then add a span around those entered entities.
How would I search through the string parameter to find this? Would it be a regex?
This is my code thus far:
function ampersandKiller(input) {
var specialCharacters = ['&', ' ']
if($(specialCharacters).contains('&')) {
alert('hey')
} else {
alert('nay')
}
}
Obviously this doesn't work. Does anyone have any ideas?
So if a string like My name is & was passed, it would render My name is <span>&</span>. If a special character was listed twice -- like 'I really like &&& it would just render the span around each element. The user must also be able to use the plain &.
function htmlEntityChecker(input) {
var characterArray = ['&', ' '];
$.each(characterArray, function(idx, ent) {
if (input.indexOf(ent) != -1) {
var re = new RegExp(ent, "g");
input = input.replace(re, '<span>' + ent + '</span>');
}
});
return input;
}
FIDDLE
You could use this regular expression to find and wrap the entities:
input.replace(/&| /g, '<span>$&</span>')
For any kind of entity, you could use this too:
input.replace(/&(?:[a-z]+|#\d+);/g, '<span>$&</span>');
It matches the "word" entities as well as numeric entities. For example:
'test & & <'.replace(/&(?:[a-z]+|#x?\d+);/gi, '<span>$&</span>');
Output:
test & <span>&</span> <span><</span>
Another option would be to make the browser do a decode for you and check if the length is any different... check this question to see how to unescape the entities. You can then compare the length of the original string with the length of the decoded. Example below:
function htmlDecode(input){
var e = document.createElement('div');
e.innerHTML = input;
return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
}
function hasEntities(input) {
if (input.length != htmlDecode(input).length) {
return true;
}
return false;
}
alert(hasEntities('a'))
alert(hasEntities('&'))
The above will show two alerts. First false and then true.

Categories