I know this will be a very obvious answer for must of you guys, but please keep in mind that I recently started learning JS and this is one of the only principle that I don't understand.
I am trying to find a way to make a variable inside a function be global. I will show you my code so maybe you will understand:
$('#inputSubmit').click(function() {
addText_1("Good, your character's name is " + getInput() + ".");
condition1 = true;
});
if(condition1 == true) {
addText_1("Now")
};
I am trying to find a way so that the if statement can access the condition1 variable in order to execute the folowing line of code.
For some reason, the if statement cannot access the condition1 value inside the click event.
Also, maybe you know a better way that this way to do what I am trying to do. Basically, I am trying to write code that will use the addText_1 function if the #inputSubmit was clicked. If you know another way of doing this, I would like to know.
Thank you very much for the help and I am sorry if this question wasn't very clear.
the basic problem is the time axis of your code. althought the code is build with 2 blocks, the last block actually run BEFORE the first block
$('#inputSubmit').click(function() // occurs when the user click (#2)
{
addText_1("Good, your character's name is " + getInput() + ".");
condition1 = true;
});
if(condition1 == true) // occurs when the page load (#1)
{
addText_1("Now")
};
i guess what you are trying to do is:
$('#inputSubmit').click(function()
{
addText_1("Good, your character's name is " + getInput() + ".");
addText_1("Now")
});
Related
I am trying to write a game where on-click innerText of the object changes.
Below is the function from my JS file which is called on-click. In the console I can see the expected sign, but doesn't reflect on the page.
function printx(number){
let isko = document.getElementById("r" + number);
console.log(isko);
if(isko.innerText==""){
isko.innerText = sign;
console.log(isko.innerText);
checksign();
disp.innerHTML= "<center>" + sign + " Turn " + "</center>" ;
winner();
}
}
JS Fiddle link: https://jsfiddle.net/c9ejhox4/
Your problem is that the winner function loops through and resets innerHTML for every tile item for every turn. Make sure that loop is inside the if statement to check if someone actually won.
for loop in winner() override text in td tag
There are few errors that can be corrected.
Loop in winner() function must be inside the if condition.
innerText, innerHTML and some outdated attributes are used.
You must use jquery to avoid such lengthy code.
Update this code to jquery to see if the problem solve.
I am admittedly a super newbie to programming in general. I am trying to design a quick piece of javascript to inject on a website for a class that will both uncheck and simulate a click on a series of checkboxes. This is nothing malicious, the web form we use to download data for use in this class presents way more variables than necessary, and it would be a lot more convenient if we could 'uncheck' all and only check the ones we want. However, simply unchecking the boxes via javascript injection doesn't yield the desired result. A mouse click must be simulated on each box. I have been trying to use the .click() function to no avail. Any help is greatly appreciated. My code below fails with an error of:
"TypeError: Cannot read property 'click' of null"
CODE:
var getInputs = document.getElementsByTagName("input");
for (var i = 0, max = getInputs.length; i < max; i++){
if (getInputs[i].type === 'checkbox')
getInputs[i].checked = false;
document.getElementById('shr_SUBJECT=VC' + i).click();
}
--------EDIT#1--------------
FYI, this is the website that I am trying to use this on:
http://factfinder2.census.gov/faces/nav/jsf/pages/searchresults.xhtml
if you search for and open up any of these tables they are huge. It would be awesome if I could easily pare down the variables by 'unchecking' and 'clicking' them all at once via javascript.
The code at the bottom ALMOST works.
The problem I am running into now is that it throws an error after the first or second run through the for loop:
"TypeError: document.getElementById(...) is null"
I understand that this is because the value it's trying to find doesn't exist? Sometimes on these tables the checkboxes are greyed out/don't exist or are otherwise 'unclickable'. My theory as to why I am getting this error is because in the table/form the 'available' ID's will start around:
shr_SUBJECT=VC03 or sh_SUBJECT=VC04
and it may then skip to:
shr_SUBJECT=VC06 then skip to shr_SUBJECT=VC09 and so on...
So if the for loop hits an ID that isn't available such as 05 or 07, it returns a null error :(
I did some reading and learned that javascript is able to 'catch' errors that are 'thrown' at it? My question now is that I'm wondering if there is an easy way to simply iterate to the next ID in line if this error is thrown.
Again, any and all help is appreciated, you guys are awesome.
OLD DRAFT OF SCRIPT
var getInputs = document.getElementsByTagName("input");
for (var i = 3, max = getInputs.length; i < max; i++){
if (getInputs[i].type === 'checkbox' && i < 10){
var count = i;
var endid = count.toString();
var begid = "shr_SUBJECT=VC0";
var fullid = begid.concat(endid);
document.getElementById(fullid).click();
}
else if(getInputs[i].type === 'checkbox' && i >= 10){
var count = i ;
var endid = count.toString();
var begid = "shr_SUBJECT=VC";
var fullid = begid.concat(endid);
document.getElementById(fullid).click();
}
}
--------EDIT#2----------
An example of a table that I am trying to manipulate can be found at this URL:
http://factfinder2.census.gov/faces/tableservices/jsf/pages/productview.xhtml?pid=ACS_12_5YR_DP02&prodType=table#
If you click on the 'Modify Table' button, you are able to select/deselect specific variables via the checkboxes. If you right-click on a couple of 'active' checkboxes and inspect the elements, and it looks something like this:
<input id="shr_SUBJECT=VC03" checked="" alt="hide SUBJECT=VC03" name="" value="" onclick="javascript:hiderow('SUBJECT=VC03');" type="checkbox">
<input id="shr_SUBJECT=VC25" checked="" alt="hide SUBJECT=VC25" name="" value="" onclick="javascript:hiderow('SUBJECT=VC25');" type="checkbox">
Thank you so much #Jonathan Steinbeck for the tip about the ternary operator, it really cleaned up my code.
The script works properly, but the problem I am running into now is that it doesn't iterate enough times after the try, catch statement. If there is a gap in the id #'s; say it jumps from shr_SUBJECT=VC19 to shr_SUBJECT=VC=24 the script will stop running. Is there a way to make it keep retrying the try/catch until it gets a valid ID # or one that exists/is an active checkbox?
CURRENT DRAFT OF SCRIPT :
var getInputs = document.getElementsByTagName("input");
for (var i = 3, max = getInputs.length; i < max; i += 1) {
try {
if (getInputs[i].type === 'checkbox'){
document.getElementById("shr_SUBJECT=VC" + (i < 10 ? "0" : "") + i).click();
}
}
catch (err) {
i+=1;
if (getInputs[i].type === 'checkbox'){
if (getInputs[i].type === 'checkbox'){
document.getElementById("shr_SUBJECT=VC" + (i < 10 ? "0" : "") + i).click();
}
}
}
}
When you call document.getElementById() with a non-existing ID, null is returned. Therefore this error means that you're trying to call the .click() method on null, which can't work.
So you should check what the correct ID naming scheme for the elements you want is. Maybe the elements' count starts with 1 instead of 0?
Also, the .click() doesn't work for all elements like you would expect as far as I know. So depending on the kind of element you are trying to retrieve you might have to create and dispatch your own event as suggested by RobG's comment.
EDIT in response to your recent edit:
You can wrap code that throws errors in a try-catch like this:
for (var i = 3, max = getInputs.length; i < max; i += 1) {
try {
document.getElementById("the_ID").click();
}
catch (error) {
console.error(error);
// continue stops the current execution of the loop body and continues
// with the next iteration step
continue;
}
// any code here will only be executed if there's not been an error thrown
// in the try block because of the continue in the catch block
}
Also, what are you doing with the 'i' variable? It doesn't make sense to assign it to so many variables. This does the same:
document.getElementById("shr_SUBJECT=VC" + (i < 10 ? "0" : "") + i).click();
The ... ? ... : ... is an operator (called the 'ternary operator') that works like this: evaluate the expression before the "?" - if it results in a truthy value, the expression between "?" and ":" is evaluated and becomes the result of using the operator; if the condition results to false, the part after the ":" is evaluated as the value of the operator instead. So while "if" is a statement in JavaScript (and statements usually don't result in a value), the ternary operator can be used as an expression because it results in a value.
By concatenating a string with something else, you are forcing the 'something else' to be converted to string. So an expression like this will usually result in a string:
"" + someNonStringVar
Also, it doesn't make sense to define variables in a loop body in JavaScript. JavaScript variables have function scope, not block scope. What this means is that any variables defined in the loop body exist inside the whole function as well. Therefore it is recommended to write all of the "var"s at the top of your function to make it clear what their scope is. This behaviour of JavaScript is called 'hoisting', by the way.
I've furthermore taken a look at the URL you've given in your recent edit but I fail to find the kind of naming scheme for IDs you describe. In which table did you find those?
Edit in response to your second edit:
You shouldn't mess with the 'i' variable inside the body of a for loop. It makes your code much harder to reason about and is probably not what you want to do anyway. You don't need to handle the next step of the iteration in the catch block. The 'i' variable is incremented even if there's an error during fetching the element from the DOM. That's why you use catch in the first place.
I'm kind of new to Javascript and I've bee wondering for hours how to solve my problem. I have a litle function associated to a button. It work once but I cannot get it to execute after the first time.
function CheckEmpty1(){return "false";}
function Generate(){
if(CheckEmpty1() == "true"){
alert("Please fill all mandatory field.\n\nAll missing field are black colored.\n\nPlease also make sure to make a choice for all radio button.");
}
else{
document.getElementById('TemplateOutput').style.display = "block";
lol = "lol";
document.getElementById('TemplateOutput').value = lol;
lol = "test2";
}
return;
}
"TemplateOutput" is a simple textarea centered in the browser. The code is originally more complicated than that but while doing the test to ensure the problem was not coming from somewhere else, it reduced to that but still doesn't work.
The second "lol = "test2";" is just to check that if I make a change to the variable, it is suposed to apply the second time I hit the button.
it seems to be basic for me but I can't figure out why... any help?
thanks.
EDIT:
I think I found the source of my error in my original script. My original code look like this:
function Output(){
Output = "CSD Troubleshooting: " + Troubleshoot + "\n";
return Output;
}
function Generate(){
FillVars();
GenerateOutput = Output();
alert(GenerateOutput);
}
function FillVars(){
Troubleshoot = document.getElementById('Troubleshoot').value;
}
I reduced it to the minimum but it still behave the same way.
The problem is coming from the Output() function because it work fine if I do it like this:
GenerateOutput = document.getElementById('Troubleshoot').value;
alert(GenerateOutput);
or
GenerateOutput = Troubleshoot;
alert(GenerateOutput);
BEHAVIOR: I click the button. The alert is filling like it is suposed to be. The second time I click the button, it just do nothing.
regards,
Updated Answer:
Your edit changes things markedly. The central issue is here:
function Output(){
Output = "CSD Troubleshooting: " + Troubleshoot + "\n";
return Output;
}
The first time you run that function, you replace the function with a string. The Output symbol is a reference to the function.
It looks like you might have a Visual Basic background. In JavaScript, you simply do this:
function Output(){
return "CSD Troubleshooting: " + Troubleshoot + "\n";
}
or if you want it in a variable first, declare the variable (with var) and probably to avoid confusion use a different name:
function Output(){
var result = "CSD Troubleshooting: " + Troubleshoot + "\n";
return result;
}
Original Answer:
The second "lol = "test2";" is just to check that if I make a change to the variable, it is suposed to apply the second time I hit the button.
It won't, because your previous
lol = "lol";
...line runs, setting it back to "lol". You'll never see the code put "test2" into the input.
The line
document.getElementById('TemplateOutput').value = lol;
copies the value from lol to the value property. It does not make the value property a reference to the variable lol. Changing the variable later has no effect, because there is no continuing link between it and the value property.
Since the if block in your code will never run, let's just look at the else block. Here, in detail, is what happens:
// 1
document.getElementById('TemplateOutput').style.display = "block";
That looks in the DOM for the element with the id "TemplateOutput", and sets its style object's display property to "block".
// 2
lol = "lol";
That assigns the value "lol" to the lol variable. Unless you've declared lol somewhere you haven't shown, it also creates an implicit global variable. Details: The Horror of Implicit Globals.
// 3
document.getElementById('TemplateOutput').value = lol;
That copies the value "lol" from the lol variable into the value property of the input.
// 4
lol = "test2";
That copies the value "test2" into the lol variable.
I actually just spent a really long time figuring out answers to this question by piecing it together from the spotty documentation and a lot of web console inspection. Since by far the most useful information came from Stack Overflow, I wanted to put this question, and its answer, here for the future benefit of anyone else searching.
The question is, how can you add javascript to a question that will immediately save that question's answer into an embedded data field? Immediately, as in, not waiting until the next Qualtrics.SurveyEngine.AddOnLoad(). This involves the sub-question of how can you add javascript to a survey question and have it run when the Next button is clicked? And the sub-question of, how can you save embedded data fields that are dynamically named based on the question ID?
Caveat Emptor: all this javascript hacking is not explicitly supported by Qualtrics, so the code here may stop working eventually.
Here is how to get the response value of a slider and save it in an embedded data field with a name that is numbered based on the question ID. This way only works if the text input box for the slider is visible. There's probably a way to do it without that, too, maybe someone else can add that to the answers.
Qualtrics.SurveyEngine.addOnload(function()
{
// everything here runs when the page is loaded. So, we can get the Question ID here,
// but we can't get the response value until later.
var currentQuestionID = this.getQuestionInfo().QuestionID
//var resultEmbeddedName = currentQuestionID + "_result" //e.g. QID6_result
var resultEmbeddedName = "result_" + currentQuestionID.substring(3) //e.g. result_6
$('NextButton').onclick = function (event) {
// everything in here will run when you click the next button
// note that it has access to javascript variables from the enclosing function
// however, if you declare something in here with "var" then it will be a LOCAL variable
// the following alert will appear when you click the next button. For me, it appears twice;
// I'm not sure why.
// Save the current question's response value
var responseTextField = document.getElementById(currentQuestionID + '~1~result')
var currentResponse = responseTextField.value
alert("Result: " + currentResponse + "\nwill be available to future questions as: \n$" + "{e://Field/" + resultEmbeddedName + "}")
Qualtrics.SurveyEngine.setEmbeddedData(resultEmbeddedName, currentResponse)
// and now run the event that the normal next button is supposed to do
Qualtrics.SurveyEngine.navClick(event, 'NextButton')
}
});
For multiple choice items, it's a bit more complicated. Here's the code that works, with lots of comments and demos. Note that the variable "currentResponse" ends up holding the NUMBER of the chosen choice, while "currentChoiceText" ends up holding the text label for that choice (e.g. Yes or No.) So you can save whichever one you prefer.
Qualtrics.SurveyEngine.addOnload(function()
{
// everything here runs when the page is loaded. So, we can get the Question ID here,
// but we can't get the response value until later.
var currentQuestionID = this.getQuestionInfo().QuestionID
console.log("Current Question ID is: " + currentQuestionID)
//var resultEmbeddedName = currentQuestionID + "_result" //e.g. QID6_result
var resultEmbeddedName = "result_" + currentQuestionID.substring(3) //e.g. result_6
$('NextButton').onclick = function (event) {
// everything in here will run when you click the next button
// note that it has access to javascript variables from the enclosing function
// however, if you declare something in here with "var" then it will be a LOCAL variable
// the following alerts will appear when you click the next button. For me, it appears twice;
// I'm not sure why.
// Save the current question's response value
var questionObject = Qualtrics.SurveyEngine.getInstance(currentQuestionID)
var currentResponse = questionObject.getSelectedChoices()[0] //in case more than one is selected, it will only work here to take one!
var theQuestionInfo=questionObject.getQuestionInfo()
var choicesObject=theQuestionInfo.Choices
var thisChoiceObject=choicesObject[currentResponse]
var currentChoiceText=thisChoiceObject.Text
console.log("Number of the current choice is " + currentResponse)
console.log("Text of the current choice is " + currentChoiceText)
alert("Result: " + currentChoiceText + "\nwill be available to future questions as: \n$" + "{e://Field/" + resultEmbeddedName + "}")
Qualtrics.SurveyEngine.setEmbeddedData(resultEmbeddedName, currentChoiceText)
// and now run the event that the normal next button is supposed to do
Qualtrics.SurveyEngine.navClick(event, 'NextButton')
}
});
Both of these refer to the question ID dynamically, so they will work individually if you copy the question to a new one. Without needing to change any of the javascript code.
To test these, make a slider question or a multiple choice question and add the respective javascript. Then, make another slide after a page break (the page break is necessary to cause the appearance of the Next button). In that slide, put a piped text field like ${e://Field/result_1} or whatever the alert dialog tells you the saved value will be. When you preview the survey, you should see the value that was entered on the first question, in the piped text field of the second question.
As far as this demo goes, you could certainly achieve the same effect with just piped text. But if you have some reason that you want a question to actually immediately save its response into embedded data, this will do that for you.
Also, if you need to know how to run code when the Next button is clicked, this can be adapted for any general need for that.
I was having problems that calling the below code in the $('NextButton').onclick method was forcing $('NextButton').onclick method to be called twice. Removing it worked for me so the onclick method gets called only once but still moves the survey forward.
// and now run the event that the normal next button is supposed to do
Qualtrics.SurveyEngine.navClick(event, 'NextButton')
This works each time you press the "Next" button. Hope that helps!
Qualtrics.SurveyEngine.addOnload(function(){
var nextButton =document.getElementById("NextButton");
if (nextButton!=null){
$("NextButton").on("click", function(event) {
alert("Hello World!");
});
}
});
I have a script that is taking too long to run and that is causing me This error on ie : a script on this page is causing internet explorer to run slowly.
I have read other threads concerning this error and have learned that there is a way to by pass it by putting a time out after a certain number of iterations.
Can u help me apply a time out on the following function please ?
Basically each time i find a hidden imput of type submit or radio i want to remove and i have a lot of them . Please do not question why do i have a lots of hidden imputs. I did it bc i needed it just help me put a time out please so i wont have the JS error. Thank you
$('input:hidden').each(function(){
var name = $(this).attr('name');
if($("[name='"+name+"']").length >1){
if($(this).attr('type')!=='radio' && $(this).attr('type')!=='submit'){
$(this).remove();
}
}
});
One of the exemples i found : Bypassing IE's long-running script warning using setTimeout
You may want to add input to your jquery selector to filter out only input tags.
if($("input[name='"+name+"']").length >1){
Here's the same code optimised a bit without (yet) using setTimeout():
var $hidden = $('input:hidden'),
el;
for (var i = 0; i < $hidden.length; i++) {
el = $hidden[i];
if(el.type!=='radio' && el.type!=='submit'
&& $("[name='" + el.name + "']").length >1) {
$(el).remove();
}
}
Notice that now there is a maximum of three function calls per iteration, whereas the original code had up to ten function calls per iteration. There's no need for, say, $(this).attr('type') (two function calls) when you can just say this.type (no function calls).
Also, the .remove() only happens if three conditions are true, the two type tests and check for other elements of the same name. Do the type tests first, because they're quick, and only bother doing the slow check for other elements if the type part passes. (JS's && doesn't evaluate the right-hand operand if the left-hand one is falsy.)
Or with setTimeout():
var $hidden = $('input:hidden'),
i = 0,
el;
function doNext() {
if (i < $hidden.length) {
el = $hidden[i];
if(el.type!=='radio' && el.type!=='submit'
&& $("[name='" + el.name + "']").length >1) {
$(el).remove();
}
i++;
setTimeout(doNext, 0);
}
}
doNext();
You could improve either version by changing $("[name='" + el.name + "']") to specify a specific element type, e.g., if you are only doing inputs use $("input[name='" + el.name + "']"). Also you could limit by some container, e.g., if those inputs are all in a form or something.
It looks like the example you cited is exactly what you need. I think if you take your code and replace the while loop in the example (keep the if statement for checking the batch size), you're basically done. You just need the jQuery version of breaking out of a loop.
To risk stating the obvious; traversing through the DOM looking for matches to these CSS selectors is what's making your code slow. You can cut down the amount of work it's doing with a few simple tricks:
Are these fields inside a specific element? If so you can narrow the search by including that element in the selector.
e.g:
$('#container input:hidden').each(function(){
...
You can also narrow the number of fields that are checked for the name attribute
e.g:
if($("#container input[name='"+name+"']").length >1){
I'm also unclear why you're searching again with $("[name='"+name+"']").length >1once you've found the hidden element. You didn't explain that requirement. If you don't need that then you'll speed this up hugely by taking it out.
$('#container input:hidden').each(function(){
var name = $(this).attr('name');
if($(this).attr('type')!=='radio' && $(this).attr('type')!=='submit'){
$(this).remove();
}
});
If you do need it, and I'd be curious to know why, but the best approach might be to restructure the code so that it only checks the number of inputs for a given name once, and removes them all in one go.
Try this:
$("[type=hidden]").remove(); // at the place of each loop
It will take a short time to delete all hidden fields.
I hope it will help.
JSFiddle example