Shorten javascript code teaser - javascript

I have five input fields. When you delete the content from, let say first, that first input field is populated with input field 2, and the input field 5 is then empty. Again, if you delete the content from the first input field, the content from second goes into the first, and now you have input field four and five empty. And I need that five input field to disappear, to have left only one empty input field. And then again if the third and fourth element are empty, forth should disappear, and so on till there is only first input field left. So, I solve this with hard coded values:
if ((document.getElementsByClassName('requestRightsTitle4')[0].value == '')
&& (document.getElementsByClassName('requestRightsEmail4')[0].value == ''))
{
displayInputField('none', 5);
}
if (
(document.getElementsByClassName('requestRightsTitle3')[0].value == '')
&& (document.getElementsByClassName('requestRightsEmail3')[0].value == '')
&& (document.getElementsByClassName('requestRightsTitle4')[0].value == '')
&& (document.getElementsByClassName('requestRightsEmail4')[0].value == '')
)
{
displayInputField('none', 4);
}
if (
(document.getElementsByClassName('requestRightsTitle2')[0].value == '')
&& (document.getElementsByClassName('requestRightsEmail2')[0].value == '')
&& (document.getElementsByClassName('requestRightsTitle3')[0].value == '')
&& (document.getElementsByClassName('requestRightsEmail3')[0].value == '')
&& (document.getElementsByClassName('requestRightsTitle4')[0].value == '')
&& (document.getElementsByClassName('requestRightsEmail4')[0].value == '')
)
{
displayInputField('none', 3);
}
And now I would like to shorten this code like a real programmer but I don't have the time (sending me to another project) or skills to do so. But I would really like to learn how would that be possible, something like:
for (let i = 0; i < 5; i++) {
if (document.getElementsByClassName('requestRightsTitle' + i)[0].value == ''
&& document.getElementsByClassName('requestRightsEmail' + i)[0].value == '')
displayInputField('none', (i+1));
}

You can set up a listener on all your input fields. If a field is emptied, shift all remaining values to the left and determine which fields need to be displayed (i.e. all fields that have a value and the first empty field).
Here's a complete standalone snippet:
const action = (inputs) => {
const listener = () => {
const values = inputs.map(input => input.value).filter(v => v);
inputs.forEach((input, i) => {
input.value = values[i] || '';
input.style.display = values[i] || !i || values[i - 1] ? '' : 'none';
});
};
inputs.forEach((input, i) => input.addEventListener('input', listener));
};
action([...document.querySelectorAll('input')]);
<input value="One">
<input value="Two">
<input value="Three">
<input value="Four">
<input value="Five">

Better to use querySelector when you only want to select the first element. I'd make an object with the value of each element, and then check the object values:
const valsAreEmpty = ...vals => vals.every(val => val === '');
const vals = [
'requestRightsEmail2',
'requestRightsEmail3',
'requestRightsEmail4',
'requestRightsTitle2',
'requestRightsTitle3',
'requestRightsTitle4',
].map(classStr => document.querySelector('.' + classStr).value);
if (valsAreEmpty(
vals.requestRightsTitle4,
vals.requestRightsEmail4
)) {
displayInputField('none', 5);
}
if (valsAreEmpty(
vals.requestRightsTitle3,
vals.requestRightsEmail3,
vals.requestRightsTitle4,
vals.requestRightsEmail4
)) {
displayInputField('none', 4);
}
// etc
If there are even more Email# and Title# elements, you might use a loop to create the vals instead.

Some remarks :
it looks like you are using html classes to identify single elements in your page
you can use html classes more generically :
<input class="requestRightsTitle" />
<input class="requestRightsEmail" />
<input class="requestRightsTitle" />
<input class="requestRightsEmail" />
<input class="requestRightsTitle" />
<input class="requestRightsEmail" />
<input class="requestRightsTitle" />
<input class="requestRightsEmail" />
On the javascript side : document.getElementsByClassName('requestRightsEmail') will give you a useful array :
let emailInputs = document.getElementsByClassName('requestRightsEmail');
emailInputs[0].value = ...
emailInputs[1].value = ...
emailInputs[2].value = ...
...
// note : this array will be 0-indexed.
// emailInputs[0] will match your 'requestRightsEmail1'
This will probably help you on the css side too.
then you can more easily build and scan arrays of values
e.g :
let titles = document.getElementsByClassName('requestRightsTitle')
.map( e => e.value )
let emails = document.getElementsByClassName('requestRightsEmail')
.map( e => e.value )
// as said above : '3' matches 'requestRights4'
if (titles[3] == '' && emails[3] == '') {
displayInputField('none', 5);
}
if ( titles[2] == '' && emails[2] == ''
&& titles[3] == '' && emails[3] == '') {
displayInputField('none', 4);
}
you can then more easily put this in a loop
a final note, on what your sample does, and how you can rewrite it :
if requestRightsEmail3 is not empty, you already know that all the tests which include requestRightsEmail3 will return false.
So another way to look at your loop is :
if i is not empty, stop here, otherwise, show those extra fields :
for (let i = 3; i >= 1; i--) { // <- decreasing loop
// if one field contains something, stop here :
if (emails[i] != '' || titles[i] != '') {
break;
}
// otherwise : call your displayInputFields
displayInputField('none', i);
}

Related

set maxlength value dynamically in html

I have input field, with maxlength = 6 and ng-pattern="/^[0-9]{1,6}$/" accepting only numeric values.
On on-blur event I'm formatting the 6 digit code ("123456") to "12-34-56" and browser remembers the formatted value i.e. 12-34-56
next time when I try to auto fill the field using remembered data by browser,
it only fill 4 digits.
How can I fill all the 6 digits. I tried to set the maxlength = 8 when text contains -, but it didn't help.
I prefer solving this kind of issue by rethinking task.
Instead of changing maxLength attribute, change input format. It would be better if user can enter data in "40-15-15" format.
var part1 = document.querySelector("#part1")
var part2 = document.querySelector("#part2")
var part3 = document.querySelector("#part3")
part1.onkeyup = function(el) {
if(isNaN(el.key) && el.key !== "Backspace") {
part1.value = part1.value.slice(0, -1)
}
if (part1.value.length === 2) {
part2.focus()
}
console.log(part1.value + part2.value + part3.value)
}
part2.onkeyup = function(el) {
if(isNaN(el.key) && el.key !== "Backspace") {
part2.value = part2.value.slice(0, -1)
}
if (part2.value.length === 0 && el.key === "Backspace") {
part1.focus()
}
if (part2.value.length === 2) {
part3.focus()
}
console.log(part1.value + part2.value + part3.value)
}
part3.onkeyup = function(el) {
if(isNaN(el.key) && el.key !== "Backspace") {
part3.value = part3.value.slice(0, -1)
}
if (part3.value.length === 0 && el.key === "Backspace") {
part2.focus()
}
console.log(part1.value + part2.value + part3.value)
}
input {
width: 3em;
}
<input type="text" id="part1" maxlength="2" /> -
<input type="text" id="part2" maxlength="2" /> -
<input type="text" id="part3" maxlength="2" />

Delete Button Won't Work Within SSN Input Field

I am implementing a react component that formats a 9-digit number as a social security number in the format: xxx-xx-xxxx.
Unfortunately, I'm not able to select all (ctrl + a) and delete the ssn, probably due to my "keyup" function in the handleSSN() click handler. The "value" attribute of the Input component takes the nine digit number that the user inputs, and the formatSSN() function formats the number to appear as "xxx-xx-xxxx" in the input field. When a user tries to select all and delete what they have input in the field, the highlighted content will simply become un-highlighted when the user lifts the key up. Is there a way to accommodate for the "ctrl + a" behavior in this handler?
This functionality is heavily based on: http://jsfiddle.net/hughdidit/Y5SK6/
, which has the same issue of not being able to "ctrl+ a" the input box and delete the content. Any insight is greatly appreciated
<FormLabel clear="both" float="left">
Social Security Number:
</FormLabel>
<Input
data-testId="ssn-input"
border="1px solid #E2E8F0"
background="#FFFFFF"
borderRadius="4px"
placeholder="Text here"
color="grey.900"
value={formatSSN(userResponse)}
onChange={(e) => handleChangeSSN(e.target)}
onBlur={(e) => handleChangeSSN(e.target)}}
/>
formatSSN function (this doesn't have to do with the problem at hand, but is how the ssn is displayed in the input when the component loads, if an ssn is already in the database):
export const formatSocialSecurity = (val) => {
val = val && val.replace(/\D/g, '');
val = val && val.replace(/^(\d{3})/, '$1-');
val = val && val.replace(/-(\d{2})/, '-$1-');
val = val && val.replace(/(\d)-(\d{4}).*/, '$1-$2');
return val;
};
The handleSSN() click handler function saves the value to the state (not shown below) after doing some validations itself:
const handleChangeSsn = ({ value }) => {
var keyCode = value.which || value.keyCode || 0;
console.log(keyCode);
document
.getElementById('ssn-input')
.addEventListener('keyup', function (event) {
let val = this.value.replace(/\D/g, '');
let newVal = '';
if (val.length > 4) {
this.value = val;
}
if (val.length > 3 && val.length < 6) {
newVal += val.substr(0, 3) + '-';
val = val.substr(3);
}
if (val.length > 5) {
newVal += val.substr(0, 3) + '-';
newVal += val.substr(3, 2) + '-';
val = val.substr(5);
}
newVal += val;
this.value = newVal;
});

How to recreate the same effect of highlighting with the reference attached?

I am working on a typing game and I am trying to imitate the same effect of highlighting of the characters to be typed like in https://www.ratatype.com/typing-test/test/. However, it was harder than what I imagined.
I created code which uses replace function to give style on the character. However, it only replaces the first instance of the character and the other instances, it won't do what it was supposed to do.
const originTextDiv = document.getElementById('origin-text'); //the div that was supposed to be highlighted.
function spellCheck() {
let textEntered = textArea.value;
//let textCharEntered = textEntered.split("");
let originTextMatch = originText.substring(0, textEntered.length);
// console.log(textEntered);
var key = event.keyCode || event.charCode;
originChar = originText.split("");
//console.log(originTextDiv);
char = originTextDiv.textContent;
//console.log(char);
console.log(originChar[index]);
//console.log(textCharEntered);
if (textEntered == originText) {
textWrapper.style.borderColor = 'orange';
$("#myModal").modal();
stop();
} else {
if (textEntered == originTextMatch) {
textWrapper.style.borderColor = 'green';
textArea.style.backgroundColor = 'white';
// if (!(key == 20 || key == 16 || key == 18 || key == 8 || key == 46 || key ==17 )){
originTextDiv.innerHTML = char.replace(originChar[index], '<span style="background-color:green;">' +originChar[index]+ '</span>'); //here is the code where it is highlighting
++index;
// }
// if (key == 8 || key == 46){
// --index;
// }
} else {
textWrapper.style.borderColor = 'red';
textArea.style.backgroundColor='f23a49';
originTextDiv.innerHTML = char.replace(originChar[index], '<span style="background-color:red;">' +originChar[index]+ '</span>');
if (!(key == 8 || key == 46)){
error++;
}
}
}
}
I expected to have it work as intended but I didn't knew replace only replaces the first instance. I tried to look at on replaceAt functions for javascript but when I tried it the replaceAt also replaces the span tag attached. Could someone give me some pointers on how to recreate the same effect as the reference above?

JavaScript - functions not working as expected

I have these two functions to test if, on my form, a section of 4 rows of elements, each with 4 text boxes and two radio buttons, is "complete."
Complete being 1-3 rows are fully filled, and the rest are empty, or all 4 rows are fully filled and none are empty.
If row 1 is not fully filled and the rest are empty, it is not complete.
If row 1 is fully filled and row 2 is not, it is not complete, and so on.
Right now these functions are saying the form is complete when at least 1 row is fully filled, regardless if the rest are not fully filled.
Are there any errors in this code? I've tried many different fixes and none seem to work - I'm wondering if my logic is off.
function lineIsComplete(i) {
let lastName = document.getElementById("txtChildsPlacementChild" + i + "LastName");
let firstName = document.getElementById("txtChildsPlacementChild" + i + "FirstName");
let caseNumber = document.getElementById("txtChildsPlacementChild" + i + "CaseNumber");
let birthDate = document.getElementById("txtChildsPlacementChild" + i + "BirthDate");
let apprehended = document.getElementById("rdoChildsPlacementChild" + i + "StatusApprehended");
let remainHome = document.getElementById("rdoChildsPlacementChild" + i + "StatusWillRemainInTheHome");
if (lastName != '' && firstName != '' && caseNumber != '' && birthDate != '' &&
(apprehended.checked || remainHome.checked)) {
return true;
} else if (i != 1 && lastName == '' && firstName == '' && caseNumber == '' && birthDate == '' &&
(!apprehended.checked || !remainHome.checked)) {
return true;
}
return false;
}
function section4Complete() {
for (let i=1; i++; i<=4) {
if (!lineIsComplete(i)) {
return false;
} else {
continue;
}
}
return true;
}
Thanks.

Remove Html tags and formatting but keep anchor tag in string

I have following string variable and want to replace all html tags and formatting but wants to keep anchor tag without formatting so it remain clickable.
content = "<div>I was going here and then that happened.<p>Some Text here</p></div>";
It should be look like
content = "I was going here and then that happened. Some Text here"
Try this,
content = "<div>I was going here and then that happened.<p>Some Text here</p></div>";
var output = content.replace(/(<\/?(?:a)[^>]*>)|<[^>]+>/ig, '$1');
console.log(output);
It is not recommended to use RegEx to parse HTML
We could look into unwrap - here is one P with nothing inside
If there is something inside the P we would need to loop each tag
const content = "<div>I was going here and then that happened.<p>Some Text here</p></div>";
let $html = angular.element(content);
console.log($html.html());
const $p = $html.find("p");
$p.replaceWith($p.text());
console.log($html.html());
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
Any tag but A - still not handling tags wrapped in other tags - for that we would need to recursively loop out:
How do I select the innermost element?
const content = "<div>I was going here and then that happened.<p>Some Text here</p>. Here is a <span>span element1</span> some text <span>span element2</span></div>";
let $html = angular.element(content);
console.log($html.html());
const tags = $html.find("*");
for (let i=0;i<tags.length;i++) {
const $tag = angular.element(tags[i]);
const tagName = $tag[0].tagName;
if (tagName==="A") continue; // ignore A
$html.find(tagName).eq(0).replaceWith($tag.text()); // one at a time
};
console.log($html.html());
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
I have used following way to remove all tags except anchor tag.
value = value.replace(/<script[^>]*>([\S\s]*?)<\/script>/gmi, '');
let html = '', addToMax = false, extraLimit = 0;
if(value && typeof value === 'string' && value.length) {
let inDelete = false;
for(let i = 0; i < value.length; i++) {
if(value.charAt(i) == '<' && value.charAt(i + 1) && value.charAt(i + 1) != 'a') {
inDelete = true;
if(value.charAt(i + 1) == '/' && ((value.charAt(i + 2) && value.charAt(i + 2) == 'a') || (value.charAt(i + 2) == ' ') && value.charAt(i + 3) && value.charAt(i + 3) == 'a')) {
inDelete = false;
}
}
if(!inDelete) {
html += value.charAt(i);
}
if(inDelete && value.charAt(i) == '>') {
inDelete = false;
}
}
}
value = angular.copy(html);

Categories

Resources