Javascript - making form inputs appear needed through key input - javascript

I have a form that has five text inputs, a through e. I set it though so only the first two are visible through the css:display, and I set the last three to display:none initially.
I have my javascript so that it sets the last input to 'b', and the next input to 'c', and depending on whether the other texts are empty or not changes the last and next variables.
option-a, option-b, etc. is the id of the text box in the form
answer_answera, answer_answerb, etc. is the class of the form input
<script>
var last = 'b';
var next = 'c';
if (document.getElementById('option-c').value != null) {
last = 'c';
next = 'd';
}
if (document.getElementById('option-d').value != null) {
last = 'd';
next = 'e';
}
if (document.getElementById('option-e').value != null) {
last = '';
next = '';
}
$('#answer_answer'+last).keyup(function() {
console.log('beg');
var elem = document.getElementById('option-'+next);
elem.style.display="block";
console.log('hit this');
})
</script>
This works for the first input. When I type in form input b, form input c appears. However, how do I get this to continually, I suppose, refresh itself, as form input d does not appear when I type in form input c. I thought about putting a while loop around the entire last block/keyup function, but it made the entire app slow and wouldn't load properly.
Thanks for any thoughts! :)

Before we go into solving this problem, let's quickly review some Javascript concepts. When your document first loads, everything inside the <script></script> tags will execute once. This means that the following statements will all be evaluated:
var last = 'b';
var next = 'c';
if (document.getElementById('option-c').value != null) {
last = 'c';
next = 'd';
}
if (document.getElementById('option-d').value != null) {
last = 'd';
next = 'e';
}
if (document.getElementById('option-e').value != null) {
last = '';
next = '';
}
Once they are evaluated, they will never be run again until you refresh the page. Thus, when the page is done loading last = 'b' and next = 'c' and the three if statements all evaluate to false and are not executed (I assume the text fields are empty on initial load). The following code will also be executed once:
$('#answer_answer'+last).keyup(function() {
console.log('beg');
var elem = document.getElementById('option-'+next);
elem.style.display="block";
console.log('hit this');
})
However, this code binds a 'handler' (a future promise) to execute some code given a user action.
The action is a keyup event on the '#answer_answer'+last element.
Because we know that last = 'b' when this promise is made, this
really means '#answer_answerb'
The promise is to execute the following code:
console.log('beg');
var elem = document.getElementById('option-'+next);
elem.style.display="block";
console.log('hit this');
Thus, when you begin typing in #answer_answerb the #answer_answerc field is displayed (remember that next = 'c'). And if you type some more into #answer_answerb the #answer_answerc field remains visible. And now we know everything that your code does.
So, how do we fix it? Can you guess? We can make more promises. The full code:
<script>
$('#answer_answerb).keyup(function() {
var elem = document.getElementById('option-c');
elem.style.display="block";
})
$('#answer_answerc).keyup(function() {
var elem = document.getElementById('option-d');
elem.style.display="block";
})
$('#answer_answerd).keyup(function() {
var elem = document.getElementById('option-e');
elem.style.display="block";
})
</script>
This will have the desired effect, but is hard to maintain. If you decide to add more text fields, you will have to create a promise for each one. You can find a more elegant solution to your problem here http://jsfiddle.net/tppiotrowski/GkT2g/

Related

Multiple DOM Traverse without errors or runtime errors

I want to traverse rather complicated structure
First I tried
var textFromCLick = $0.closest('span').parentElement.querySelector('label').innerText
This works only if the user clicks on elements that has the exact structure.
If the structure is different I get 'Cannot read property 'querySelector' of null'
So I came up with this dirty code
if(0$.closest('span') !== null) {
var el2 = 0$.closest('span');
if (el2.parentElement.querySelector('label') !== null) {
textFromCLick = el2.parentElement.querySelector('label').innerText;
} else {
textFromCLick = "";
}
} else {
textFromCLick = "";
}
This works but.. as you see, the code is too long and inefficient.
How can I make it graceful?
Chaining method calls is fragile if any call might fail, as in
$0.closest('span').parentElement.querySelector('label').innerText;
If any part fails, the next part will likely throw an error. So test each part at the point that it might return a value such that the next part throws an error. In this case, various expressions might return either null or something else, so to test each bit:
let span = $0.closest('span');
let parent = span && span.parent;
let label = parent && parent.querySelector('label');
let text = label? label.textContent : '';
You can probably combine the second and third lines as:
let label = span && span.parent.querySelector('label');
on the basis that if there is a span (or any element that below the root HTML element), it will have a parent node hence span.parent is very unlikely to fail.
An alternative is to wrap the call in try..catch, but that's just lazy:
let text = '';
try {
text = $0.closest('span').parentElement.querySelector('label').textContent;
catch (e) {
text = '';
}
A better strategy is to associate elements by some property such as id or class so the relationship isn't structural, e.g.
function findLabel() {
let label = document.querySelector('label.' + this.className);
console.log(label && label.textContent);
}
window.onload = ()=>{
document.querySelectorAll('button').forEach(button =>
button.addEventListener('click', findLabel)
);
}
<p>
<span>
<button class="fooBar0">Find my label</button>
</span>
</p>
<label class="fooBar0">Here I am</label>

How do I return the result (an array) of a function once for each button click rather than continuously pushing to another array?

The problem I have is that on every button click, the result of my function, which is an array pushes continuously to a primary array. The most important elements in my HTML page are:
Two input element of the type:number with ids of #firstinput and
#secondinput;
A button with id #submit;
A div to show result with id #result.
I had an idea of refreshing my browser on every click with the use of window.location.reload(), but this does not solve the problem as the result of my work will disappear instantly.
Here is my JavaScript Code:
let scale = [],
submitButton = document.querySelector("#submit");
submitButton.addEventListener("click", weightBalancer);
function weightBalancer() {
let weightsForLeftAndRightSides = document.querySelector("#firstinput").value,
weightsForBalancing = document.querySelector("#secondinput").value;
inputValidator(weightsForLeftAndRightSides, weightsForBalancing);
inputValuesToJson(weightsForLeftAndRightSides, weightsForBalancing);
}
function inputValidator(firstInput, SecondInput) {
if (firstInput == "" || SecondInput == ""
|| isNaN(firstInput) || isNaN(SecondInput)) {
return alert("Enter the require number of digits");
}
if (firstInput < 10 || SecondInput > 99) {
return alert("Enter the require number of digits");
}
if (firstInput < 1000 || SecondInput > 9999) {
return alert("Enter the require number of digits");
}
}
function inputValuesToJson(firstInput, secondinput) {
let firstInputArray = Array.from(firstInput.toString()).map(Number),
secondInputArray = Array.from(secondinput.toString()).map(Number);
scale.push(JSON.stringify(firstInputArray));
scale.push(JSON.stringify(secondInputArray));
//I will remove this later
console.log(scale);
document.getElementById("result").innerHTML = firstInput;
}
I expect to the function to return:
nothing instead of ["[]","[]"] if input elements are empty on
button click;
e.g. ["[]","[]"] and ["[1,2]","[1,4,2,3]"] on two separate button clicks respectively instead of ["[]","[]","[1,2]","[1,4,2,3]"] on the second click.
Thank you.
Having a hard time understanding exactly what you are trying to accomplish but how about doing this instead of pushing to a global variable
function inputValuesToJson(firstInput, secondinput) {
let firstInputArray = Array.from(firstInput.toString()).map(Number),
secondInputArray = Array.from(secondinput.toString()).map(Number);
// scale.push(JSON.stringify(firstInputArray));
// scale.push(JSON.stringify(secondInputArray));
// I will remove this later
const myArray = [ ...JSON.stringify(firstInputArray), ...JSON.stringify(secondInputArray) ]
console.log(myArray);
// return myArray & consume it wherever you want. The array will be reset
// at every call to this function and therefor you will have the expected
// result
document.getElementById("result").innerHTML = firstInput;
return myArray;
}
nothing instead of ["[]","[]"] if input elements are empty on button click;
To Achieve this, all you need to do is run a null check inside your inputValuesToJson function. Add your entire block into a simple if condition.
if (firstInput, secondinput)
{
your content goes here... }
e.g. ["[ ]","[ ]"] and ["[1,2]","[1,4,2,3]"] on two separate button clicks respectively instead of ["[ ]","[ ]","[1,2]","[1,4,2,3]"] on the second click.
You want your array to capture each event click separately, create an array property in the function which is called each time the click event is triggered. Try moving the let scale = [ ] inside your inputValuesToJson() function.

Filter options by reading character length inside for loop

I have a widget (the widget code in the pen linked below is not the actual code, please just pay attention to the filtering function jQuery.fn.doFilterOptions(){..}).
Use case:
I have a non-native selectbox. I need to extend its functionality to accept an onclick event which allows the user to type data into the selectbox (not targeting a traditional <select>), it should filter the .options available by simply showing or hiding them based on its inner HTML value, if no match is found at any point during the loop through the string being entered by the user, I need the options to continue not being displayed.
Issue:
Right now it works 95% of the way, the only issue is that if an invalid char is found, the loop keeps checking the rest of the users entries char by char, and if the next char is a match to any of the options in the same index, it re-display's this as a valid .option.
$('.selectbox .selected').on('keyup', function(){
var theseOptions = $(this).parent('.selectbox').find('.option');
var defaultPlaceholder = $(this).data('placeholder');
var filterOptions = (function(curSelectedVal){
if (curSelectedVal === ' ' || curSelectedVal.length === 0 || curSelectedVal === defaultPlaceholder){
theseOptions.show();
}
var optionsVal;
var doInputOptionsComparison = (function(){
var invalidOption = false;
for (var letterPos = 0; letterPos < curSelectedVal.length; letterPos++){
theseOptions.each(function(optionIteration){
var thisOption = $(this);
thisOptionsVal = thisOption.html();
if (curSelectedVal.length > thisOptionsVal.length ){ // If a longer string has been input by the user than exists in the option being iterated over, hide this option
thisOption.hide();
invalidOption = true;
}
else if ((thisOptionsVal[letterPos].toLowerCase().trim() === curSelectedVal[letterPos].toLowerCase().trim()) && invalidOption === false){ // If the input string matches this option and no invalid options have been found in the letterPos prior to it, show this option
thisOption.show();
}
else { // If the string does not match any option
invalidOptionFound = true;
thisOption.hide();
}
});
}
})();
})($(this).html());
});
Here is the demo, try selecting then typing abz you will see the filter working properly.
Now erase that input data, and now type azc. You will see the abc option comes available again because the c matches in that same index (user input[i] = optionsHtml[i] = show();), resulting the the above described undesirable effect.
http://codepen.io/nicholasabrams/pen/KwwMPG?editors=001
BONUS:
Would this be easier by using regEx to do the filtering?
I managed to use a dynamic regEx filter function it it cut the code down big time! Wow what a better solution.
$.fn.filterOptionsByUserInput = function(optionSelector){
var curInput = $(this).html().trim().replace(/ /g, '').toLowerCase();
$(optionSelector).each(function(optionIndex){
var userInputRegEx = new RegExp('^'+curInput+'.*');
if ($(this).html().toLowerCase().trim().match(userInputRegEx)){
$(this).fadeIn('slow');
}
else {
$(this).fadeOut('slow');
}
});
};
http://codepen.io/nicholasabrams/pen/LEEwrm?editors=001

What does unescape() function returns?

I have to use unescape() function in an if-else statement. In my website I have two pages, one with a form that the user fills and the second page have to get the information from the filled form by unescape function. I need the if-else statement because in the form I put two radio buttons that each one adds different text areas with different ids' and names so I want to check in the second page what text fields are are filled. (all the fields created by clicking on a radio button which starts a javascript function so in the next page I must check if the field was created and not just to check if it is an unfilled text field).
It is a little bit hard for me to explain so just check the code. In the code you will see params["placeName"] and so on, so placeName for example like all the others is a text field name from the previous page.
So the question is - what does unescape function returns if the component name I insert as a paramater does exist in the previous page?
<script type="text/javascript">
function getParams() {
var idx = document.URL.indexOf('?');
var params = new Array();
if (idx != -1) {
var pairs = document.URL.substring(idx + 1, document.URL.length).split('&');
for (var i = 0; i < pairs.length; i++) {
nameVal = pairs[i].split('=');
params[nameVal[0]] = nameVal[1];
}
}
return params;
}
params = getParams();
//from here it is what I want to do (I don't know if this condition in the if statement is correct, this is what I ask)
// if (unescape(params["placeName"]) == false) {
// }
// else {
var place = unescape(params["placeName"]);
var country = unescape(params["country"]);
var city = unescape(params["city"]);
var address = unescape(params["address"]);
var type = unescape(params["type"]);
var rate = unescape(params["rate"]);
// }
</script>
It can also work if I could check what radio button is checked
You are asking what will unescape(params["something"]) return if params["something"] is not present. The answer is undefined. So you need to check equivalence to "undefined". Meaning: if (unescape(params["placeName"]) == "undefined") (in this case params["placeName"] is not present (was not created).
The undecode function returns -as it's name indicated- a decoded string. If you enter a value that's not defined it will probably cause an error because of the indefinition. If what you want is to check whether the element was or not created or not you could use
if (params.placeName !== undefined) {
// It was created
}
instead.
Aditionally, if you want to check which radio button was checked use
if (document.getElementById('foo').checked) {
// Radio button with id 'foo' was checked
} else if (document.getElementById('bar').checked) {
// Radio button with id 'bar' was checked
} else if
...

Javascript function skips statements

I have a simple function as follows:
function FUNCTION1() {
document.getElementById('Preview1').innerHTML = ''){
if (document.UserData.input1.value.length !== 0;
var input1 = document.UserData.input1.value;
document.getElementById('Preview1').innerHTML = '<div>Hello ' + input1 + '</div>';}
}
I run the above script and everything is just fine.
Then I run another function to clear the div which has the form "UserData".
document.getElementById('UserDataDiv').innerHTML = '';
And then I run the FUNCTION1 again, It brings up old value This value should not be there as div was cleared.
Is there a way to avoid this behaviour or am i doing something wrong?
I think you should just check that the string is empty instead of the length of the value: document.UserData.input1.value.length:
if (document.UserData.input1.value == '')
Oh, as someone else has pointed out, it also looks like there's an extra semi-colon at the end of your first line.

Categories