Onclick function for all members of a class - javascript

I'm making a two-column math page, with theorems and definitions on the left side, and additional explanations on the right side.
The way that the user accesses the explanations is by clicking on text of the class "explainable".
After the page loads, I run a script to give each "explainable" element its own onclick function, which hides the text on the right panel (if any) and refills it with the clicked's elements explanation.
But when I try and run it, it tells me that the explainable element is undefined.
Here is the relevant function and an example of the text with the class:
function
<script type="text/javascript">
window.setTimeout(makeExplanations, 3000);
function makeExplanations(){
var explainables = document.getElementsByClassName("explainable");
var currentlyShowing = "";
for(var i = 0; i < explainables.length; i++){
console.log("id: " + explainables[i].id);
var element = explainables[i];
explainables[i].onclick = function(){
if(currentlyShowing != ""){
document.getElementById(currentlyShowing + 'e').style.display = "none";
}
var explanationDiv = document.getElementById(explainables[i].id + 'e')
explanationDiv.style.display = "inline";
explanationDiv.focus();
currentlyShowing = explainables[i].id;
};
}
}
</script>
I have it waiting 3 seconds to execute because doing body onload=makeExplainables() wasn't sufficient. (Might have something to do with the way that the MathJax loads - I'm not sure.)
example of explainable class
<span class="explainable" id="595858535">so either statement is vacuously true</span>. Thus, $X = Y$ holds,
and hence we declare that there exists only one set with no elements.</p>
and the explanation it reveals
<div id="595858535e" style="display: none;">
<p>An if-then statement (a conditional) is vacuously true when the if portion is false in all cases.
For example, the statement "If 2 + 2 = 5, then pigs fly" is vacuously true, since its
antecedent (if 2 + 2 = 5) is always false.</p>
<p>In this case, the conditional in question is "there exists some $x \in X$ such that $x \notin Y$".
But since $X$ is an empty set, there does not exist any $x \in X$ such that anything. Hence, the
statement is vacuously true in all cases.</p>
<p>Another way to think about it is this: a conditional is only falsified if its antecedent ("if ...") is true
and its consequent ("then ... ") is false. If the antecedent is always false, then this can never happen.
Thus the statement as a whole is always true.</p>
</div>
Error in Firebug when I try to click on the explainable text:
16:23:44.704 "id: 595858535" zfc:89
16:23:44.704 "id: 595858536" zfc:89
16:23:53.045 TypeError: explainables[i] is undefined zfc:95

The real reason for that error is that "i" has already reached a value equal to the array's length, when the click handler is called. Hence explainables[i], i.e. explainables[explainables.length] is undefinded.
JS fiddle with console logs to explain you better: http://jsfiddle.net/wV4Lh/6/
element.onclick = function(){
// length of array is 2 for this example
console.log("explainables length: " + explainables.length);
// value of i is 2 in the click handler
console.log("i: " + i);
// explainables[i] = explainables[2] is undefined
console.log("explainables[i] - ith: " + explainables[i]);
// explainables[0] is valid
console.log("explainables[0] - 0th: " + explainables[0]);
// explainables[1] is valid
console.log("explainables[1] - 1st: " + explainables[1]);
};
This clearly shows that explainables is well in scope of the click handler, but value of i = length of the array as the 'for' loop has already completed it's full execution way before the click event is triggered.
P.S: The difficulty in initially understanding this behavior of a handler is due the counter-intuitive way a handler works, i.e. it is not executed in sequence with other code surrounding it. A handler only get's executed when corresponding event is triggered and not in sequence with in the for loop, in this case.

So, I finally got it work by changing the explanation[i] in the onclick function to this.
I thought that I was making a closure wherein the explanation[i] variable would be accessible from within the onclick function even after makeExplanations() ended, but I guess that is incorrect.
The corrected onclick function looks like this:
explainables[i].onclick = function(){
if(currentlyShowing != ""){
document.getElementById(currentlyShowing + 'e').style.display = "none";
}
var explanationDiv = document.getElementById(this.id + 'e')
explanationDiv.style.display = "inline";
explanationDiv.focus();
currentlyShowing = this.id;
};

Related

Why does this return statement in JavaScript return nothing?

Before you mark this question as a duplicate please understand that I'm new to JS and always feared asking a question of stackoverflow.
I dont understand why calling this function returns nothing unless I enclose the function call in a console.log.
If I enclose the function call in a console.log I get the expected output "There are 3 elements in this array", however without the console.log, I dont get anything.
var counter = function (arr) {
return 'There are ' + arr.length + ' elements in this array';
};
counter(["shaun", "sara", "jessica"])
What I want to know is how I can get the output of this function without using console,.log and the reason why it does not output anything without the console.log.
console.log() is a function used to print information to the
console. return on the other hand is a call to pass some value back
up to where the call was made.
Via - CodeCademy Forum
return terminates a function and possibly returns a value to the caller of that function (whereas) console.log() will not influence the flow of your code.
Via - Difference between console.log and return in javascript?
Check and run the following Code Snippet for a practical example of how the two works:
var x = document.getElementById("first");
var y = document.getElementById("last");
var z = document.getElementById("output");
function printName(){
z.innerText = fullName();
}
function fullName(){
console.log(x.value + " " + y.value); // this wont push the concatenated name to printName()
return x.value + " " + y.value; // this will push the concatenated name to printName()
alert("y u do dis?"); // this won't run anymore since the return statement above prevents the function from invoking anything else
}
document.getElementById("btn").addEventListener("click", printName)
<input type="text" id ="first" />
<input type="text" id ="last" />
<button id="btn">Click Me</button>
<br/>
<div id="full">Hello <span id="output"></span>!!</div>
If the return statement above is removed, the console.log() alone won't return anything to printName() except display the concatenated name in your console.
var counter = function (arr) {
return 'There are ' + arr.length + ' elements in this array';
};
let functionResult = counter(["shaun", "sara", "jessica"]);
You need to return the result to a variable.
Then you can use it as you want.
So, you are actually returning something, you just have no way to view it without console.log() if you're wanting to use the returns of this function in another function, or somewhere else, you can assign it to a variable like
const myCounter = counter(["shaun", "sara", "jessica"])
console.log(myCounter)
All return is doing is making the results of the function available to be used elsewhere. If you aren't displaying it via console.log, or some other method (putting it into an HTML element or something) then you'll never "see" it anywhere, and it'll looks like the function isn't doing anything, even though it is.

Javascript on click event not reading else statement or variables

I'm trying to make a click handler that calls a function; and that function gets a string and basically slices the last character and adds it to the front, and each time you click again it should add the last letter to the front.
It seem so easy at first that I thought I could just do it using array methods.
function scrollString() {
var defaultString = "Learning to Code Javascript Rocks!";
var clickCount = 0;
if (clickCount === 0) {
var stringArray = defaultString.split("");
var lastChar = stringArray.pop();
stringArray.unshift(lastChar);
var newString = stringArray.join('');
clickCount++;
} else {
var newArray = newString.split("");
var newLastChar = newArray.pop();
newArray.unshift(newLastChar);
var newerString = newArray.join("");
clickCount++;
}
document.getElementById('Result').innerHTML = (clickCount === 1) ? newString : newerString;
}
$('#button').on('click', scrollString);
Right now it only works the first time I click, and developer tools says newArray is undefined; also the clickCount stops incrementing. I do not know if it's an issue of scope, or should I take a whole different approach to the problem?
Every time you click you are actually reseting the string. Check the scope!
var str = "Learning to Code Javascript Rocks!";
var button = document.getElementById("button");
var output = document.getElementById("output");
output.innerHTML = str;
button.addEventListener("click", function(e){
str = str.charAt(str.length - 1) + str.substring(0, str.length - 1);
output.innerHTML = str;
});
button{
display: block;
margin: 25px 0;
}
<button id="button">Click Me!</button>
<label id="output"></label>
It is, in fact, a scoping issue. Your counter in inside the function, so each time the function is called, it gets set to 0. If you want a counter that is outside of the scope, and actually keeps a proper count, you will need to abstract it from the function.
If you want to keep it simple, even just moving clickCount above the function should work.
I do not know if it's an issue of scope
Yes, it is an issue of scope, more than one actually.
How?
As pointed out by #thesublimeobject, the counter is inside the function and hence gets reinitialized every time a click event occurs.
Even if you put the counter outside the function, you will still face another scope issue. In the else part of the function, you are manipulation a variable (newString) you initialized inside the if snippet. Since, the if snippet didn't run this time, it will throw the error undefined. (again a scope issue)
A fine approach would be:
take the counter and the defaultString outside the function. If the defaultString gets a value dynamically rather than what you showed in your code, extract its value on page load or any other event like change, etc. rather than passing it inside the function.
Do not assign a new string the result of your manipulation. Instead, assign it to defaultString. This way you probably won't need an if-else loop and a newLastChar to take care of newer results.
Manipulate the assignment to the element accordingly.
You can use Javascript closure functionality.
var scrollString = (function() {
var defaultString = "Learning to Code Javascript Rocks!";
return function() {
// convert the string into array, so that you can use the splice method
defaultString = defaultString.split('');
// get last element
var lastElm = defaultString.splice(defaultString.length - 1, defaultString.length)[0];
// insert last element at start
defaultString.splice(0, 0, lastElm);
// again join the string to make it string
defaultString = defaultString.join('');
document.getElementById('Result').innerHTML = defaultString;
return defaultString;
}
})();
Using this you don't need to declare any variable globally, or any counter element.
To understand Javascript Closures, please refer this:
http://www.w3schools.com/js/js_function_closures.asp

referenceerror treasureHunter not defined?

so i was programming a javascript program, and for some reason, i got an error saying: ReferenceError: treasureHunter not defined
I searched all over the internet for this info, but although this has been answered many times, their answers don't fit specifically to the problem in the code.
Here's the code:
// Sets a global variable for loot. Global variables exist outside functions or conditional statements. Local variables exist within those things.
global loot = 0;
// alert(loot);
// Creates a function called treasureHunter(). We don't put anything in the parenthesis, but we will later for other functions.
function treasureHunter() {
//Creates a local variable inside treasureHunter called treasureSuccess equal to Math.random(). Math.random is bult into JavaScript, and creates a random number between 0 and 1, e.g. .456 or .99. We use this to create excitement in an otherwise dull and boring existence.
var treasureSuccess = Math.random();
//Here we create a local variable called closedChest equal to the image that we want to pull down from the interwebs when we fail at getting loot.
var closedChest = "<img src='https://s-media-cache-ak0.pinimg.com/736x/eb/c7/84/ebc784ae55857b1470767c4747eb0715.jpg' length='285px' width='285px'>";
//Here we create a local variable called openChest equal to the image that we want to call up when we succeed - YES!
var openChest = "<img src='https://slm-assets1.secondlife.com/assets/1545906/lightbox/e6cfd0355756301c0f6f6d666e75c3de.jpg?1277247329' length='285px' width='285px'>";
var dragon = "<img src='http://orig06.deviantart.net/dd94/f/2012/112/c/b/cb9da88db9660edea4746e95df8fa4ed-d4x7orn.jpg' length='285px' width='285px'>";
//Here we create a conditional statement that asks if the variable treasureSuccess is greater than 5. This is called a boolean operation, it can be answered yes or no. This will of course be random, since treasureSuccess is equal to Math.random() - see above.
if (treasureSuccess > .5) {
//Here we tell the conditional statement what to do if it evaluates true, in other words, if Math.random() happens to be greater than .5. This will change the element using treasureChest, our DIV above in our HTML to the openChest variable, which is set to the image of an open chest. Go figure :)
if (treasureSuccess > .9) {
//dragon time!!
document.getElementById("TreasureChest").innerHTML = dragon;
//dragon image
loot = 0;
//loot reset
document.getElementById("Loot").innerHTML = "You lost all your gold!";
}
}
else {
document.getElementById("TreasureChest").innerHTML = openChest;
//This adds 100 to the global variable loot, each time we are successful, or each time treasureSuccess is greater than .5
loot = loot + 100;
//This tells the loot ID selector to update, so we can see how much gold we've got. THis uses the updated value for loot, which is why it is written below the loot = loot +100 line.
document.getElementById("Loot").innerHTML = "You've got" + " " + loot + " " + " gold";
//waits 500 milliseconds
setTimeout(treasureHunter,500);
document.getElementById("TreasureChest").innerHTML = closedChest;
//reputs the picture
}
//this is the end of the conditional statement
//This tells the statement what to do if the conditional statement evaluates to false, or if treasureSuccess is less than .5. You get a chest slammed shut in your face son.
else document.getElementById("TreasureChest").innerHTML = closedChest;
}
//this is the end of the function
I call it like this:
<div onclick="treasureHunter()" align="center" id="TreasureChest">
<img src="https://s-media-cache-ak0.pinimg.com/736x/eb/c7/84/ebc784ae55857b1470767c4747eb0715.jpg" length="285px" width="285px">
Any help is appreciated!
There are some syntax errors, so treasureHunter() wasn't defined.
global in global loot = 0; doesn't seem valid. Change it to var or something.
else in else document.getElementById("TreasureChest").innerHTML = closedChest; is invalid since there are no corresponding if. Remove it or fix the code properly.

JavaScript "if" statement wont run

(Using Javascript)
If I enter text in the textbox or not, the alert will not come up.
I know this is a simple fix but I can not figure it out!
(This is for learning purposes.)
Workout Log Test
<script type="text/javascript">
function myResults() {
myExercise();
myWeight();
mySets();
myReps();
myFunction();
}
function myExercise() {
var txtExercise = document.getElementById("txtExercise");
var txtOutput = document.getElementById("txtOutput1");
var name = txtExercise.value;
txtOutput1.value = "You destroyed, " + name + "!"
if (txtExercise.length === 0) {
alert ('Do you even lift?');
return;
First off, you're checking the "length" property of the element rather than the value of the input.
Second of all, you're checking against an integer value. If you were to simply read the value of the element, you're going to get text.
I'm guessing, what you want is something like:
var exercise = parseInt(txtExercise.value, 10);
if(exercise === 0) {
alert('Do you even lift?');
return;
}
But that's assuming txtExercise is an input element. Without seeing your markup, it's hard to be sure that any given answer will work.
Here you go, all fixed. You need an event handler, and this is a better if/else use case.
http://codepen.io/davidwickman/pen/vOKjqV
// Check the .value.length instead of just the .length
if (txtExercise.value.length === 0) {
alert('Bro, do you even lift?');
} else {
txtOutput1.value = "You destroyed, " + name + "!";
}
Assuming you have closed the function and if statement properly, you are missing a semi colon just before the if statement.
txtOutput1.value = "You destroyed, " + name + "!"; <---- here

JavaScript not recognizing variable

I'm new to Javascript and struggling to figure out why this piece of code isn't working for me.
Essentially I'm defining a variable, yet when I go to use that variable in an IF or Switch statement, it doesn't seem to be able to match the contents of the variable. No errors, the IF statement just doesn't get satisfied. While with the Switch, it always falls through to the default setting, as it can't match the contents.
I have a Print statement in place after the variable is defined, and it does display the contents of the variable correctly.
I'm really at a loss as to why the print can return the value of the variable, yet the IF and Switch can't find it.
Below is the snippet I'm working from. The variable is "strWilma", which doesn't get reflected properly in the Print second value statement, but not in the IF.
for (var i=0; i < Flinstones.length; i++)
{
if (Flinstones[i].startsWith("?"))
{
// Convert the Secondary field map to a Properties item, for easier navigation
var objSecondaryFieldMap = PropertiesFromString(strSecondaryFieldMap);
// Map all of the Secondary values
var arraySecondaryFields = objSecondaryFieldMap.keys();
while (arraySecondaryFields.hasMoreElements())
{
strFred = arraySecondaryFields.nextElement();
strWilma = objSecondaryFieldMap.get(strFred);
print("TargetType:" + Object.prototype.toString.call(strFred));
print("SourceType:" + Object.prototype.toString.call(strWilma));
print("Text Type:" + Object.prototype.toString.call("hardcoded value"));
print("First Value:" + objItem.getNewFieldValue(Flinstones[i].substring(1)) );
print("Second Value:" + strWilma );
if (objItem.getNewFieldValue(Flinstones[i].substring(1)) == strWilma)
//if (objItem.getNewFieldValue(Flinstones[i].substring(1)) == "hardcoded value") // WORKS
{
print("It Worked!!!");
}
}
}
}
}
Thanks

Categories

Resources