Can't assign querySelectorAll() to a variable - weird behaviour - javascript

I was trying to crawl a very old website for a specific tag, I need to get it by it's for= attribute. So I used this piece of code.
var character = document.querySelectorAll("label[for=char_1]");
For some reason it returns an undefined, but I was using this script for a few days now and it worked like a charm. Here's the fun part. Typing that command in browsers console will result in undefined. But typing this alone:
document.querySelectorAll("label[for=char_1]");
Will return a proper NodeList. Why it won't assign to a variable...?
edit: It seems that deleting var and typing character without it will make it work. It's resolved but I would still love to get an answer to "why is this happening"?
edit2:
for (var i = 0; i < 15; i++) {
var character = document.querySelectorAll("label[for=char_" + i +"]");
console.log(character); // this will return [] from the script.
var color = character[0].children[0].style.color;
}
A simple for loop. All I get is Cannot read property 'children' of undefined. But I can type in the very same command document.querySelectorAll... and it will work in the browser and will return NodeList.
I had it working like this in a very hacky script. It worked.
var character1 = document.querySelectorAll("label[for=char_1]");
var characterColor1 = character1[0].children[0].style.color;
edit3:
var character1 = document.querySelectorAll("label[for=char_1]");
var characterColor1 = character1[0].children[0].style.color;
var character2 = document.querySelectorAll("label[for=char_2]");
var characterColor2 = character2[0].children[0].style.color;
// ...
The above code works without a single problem though. I don't think it's DOM not being ready as this code is also run from Greasemonkey script and it works. The only difference is the for loop.

var x = ""; // returns undefined, because it's a var assignment.
var elements = document.querySelectorAll('div');
That's expected behavior when pasted into the console.
edit: It seems that deleting var and typing character without it will make it work. It's resolved
I'm afraid you're creating a global scope variable now. or perhaps characters is an already defined variable in that scope.

Buhah, as I said in edit 3 "the only difference is the for loop". I was so busy trying to find an answer in the DOM-related things that I made the simplest mistake ever done in programming.
See?
char_1
With...
for(var i = 0...)
0! And I was testing char_1 in the browser instead of char_0. Which - truly - returns [] instead of something useful. Time to go on a holiday break I guess, my brain seems to be there already. :)

Related

Unusual JavaScript for loop with xml in Blockly

I'm working on a project with Google's Blockly, but parts of the documentation are incomprehensible. Can someone help me understand the end condition of the following for loop (xml = allXml[i])?
var allXml = Blockly.Xml.workspaceToDom(workspace);
var allCode = [];
for (var i = 0, xml; xml = allXml[i]; i++) {
var headless = new Blockly.Workspace();
Blockly.Xml.domToWorkspace(headless, xml);
allCode.push(Blockly.JavaScript.workspaceToCode(headless));
headless.dispose();
}
I imagine the loop will exit when allXml[i] is undefined, but how can you iterate through an XML object like this? It seems to always be returning undefined and skipping the loop entirely.
Thanks for you help
Definitions for most of the function can be found at https://code.google.com/p/blockly/source/browse/trunk/core/xml.js?r=1614
And the doc page I pulled this from is https://developers.google.com/blockly/custom-blocks/code-structure?hl=en
I could not find this code in the repo on github, so I guess it is a bit older example in the docs.
But if you will have a look at the Blockly.Xml.workspaceToDom() function's implementation, you will see a very similar thing there.
var blocks = workspace.getTopBlocks(true);
for (var i = 0, block; block = blocks[i]; i++) {
var element = Blockly.Xml.blockToDom_(block);
//...
xml.appendChild(element);
}
The idea here is to iterate over all branches of code. The top block has no top connection (it starts a new branch). The getTopBlocks() returns an array {!Array.<!Blockly.Block>}.
Considering that the documentation is showing it in a section about parallel execution, I think it will be related to the fact, that you can have more unconnected branches of code... and the exact implementation just changed over time.
As per the code, return type of Blockly.Xml.workspaceToDom(workspace) is {!Element}.
It basically returns a DOM node created using goog.dom.createDom('xml');. And for each top level block it is appending a DOM node to it.
So basically the code snippet in question is looping through all the DOM nodes corresponding to the top level blocks.

Dynamically define variable name and assigning value in Google Apps Script

I`m writing a script to generate a form in Google Apps Script.
The idea is, that the user should select his/her name from a dropdown list, and then be transferred to the question block for him/her.
Although the questions are the same, there are slight changes in the choices if the dropdowns for some of the questions.
I have an array with the names of the users, and I've defined the questions for every single user.
This is not ideal, as if there is any change in the questions I have to rewrite every block oone by one.
I want to use a loop which generates the question blocks by creating the variables names using the array of the usernames.
I tried the following (this is not the actual code, but throws the same error)
for (a=0; a < 10; a++)
{
eval('var beginning'+a);
}
for (b=0;b<10; b++)
{
eval('beginning' + b) = 1;
}
The first for loop runs fine, but when I try to assign any value it throws an error. (I use here two for loops for debugging only.)
E.g.:
eval('beginning' + b) = 1; //Throws: We're sorry, a server error occurred. Please wait a bit and try again.
eval('beginning' + b + '= 1;'); //Throws: We're sorry, a server error occurred. Please wait a bit and try again.
eval('beginning' + b = 1); //Throws: Invalid assignment left hand side. (line 1, file "Code")
Using eval like this is also fine: choices = eval('lCountries' + Names[i]).getChoices();.
How can I assign values to these variables in a for loop?
Thank you very much in advance.
As far as I read up so far, eval() is almost always a bad choice, should be handled with caution and mostly never used. Using dynamic variables in such way is also a bad programming logic. I've never seen a case where it is a must, and your case obviously isn't. You can easily get around it with an object, just define a generic object var myVariables = {}, and then start assigning its properties dynamically for your variables.
var myVariables = {};
myVariables[ "beginning" ] = 1;
for( i = 0; i < 10; i++){
myVariables[ ("beginning" + i) ] = i;
}
Logger.log( myVariable[ "beginning5" ] ); //Loggs 5

Password Strength Meter

I'm trying to create my own JS Password Strength Meter.
It was working before but i didn't like how it worked so I tried using
{score +=10;}
Instead of just:
score++
This is my code:
http://jsfiddle.net/RSq4L/
Best Regards,
Shawn,
Hope someone can help
Multiple issues:
Your passwordStrength() function was not defined in the global scope in the jsFiddle so it wasn't getting called. This is probably an artifact of how you set up the jsFiddle, perhaps not an issue in your real code.
The method of getting the appropriate ratingMsg will not work because you don't have array values for every possible score so many scores will generate an "undefined" ratingMsg.
Your CSS classes are also sparse so there are many score values that they will not match for either and no appropriate CSS class/style will be in effect. If you want a specific class for each rating value, then perhaps you should put the classname in the ratings array so it can be fetched from there along with the ratingsMsg.
For the first issue, in your jsFiddle, you also have to make sure the password processing function is defined in the global scope. The way your jsFiddle is set up, it is not (it's in the onload handler). You can fix this in the jsFiddle by just setting the first drop-down in the upper left to "no wrap (head)".
For the second issue, you are using:
ratingMsg[score]
but, your array is a sparse array not guaranteed to have an entry for most possible scores. You simply can't do it that way because many elements you access will have undefined values which won't give you a meaningful message. For example, if score was 15, you would be accessing ratingMsg[15], but there is no value in that space in the array so you won't get a meaningful rating message.
The solution is to find a different way to select the right message. The simplest way would just be an if/else if/else if statement that would check which range the score is in and set the appropriate msg. There are more elegant table driven ways, but all will involve searching through a data structure to find which two values the current score is between and using that msg.
If you look at this jsFiddle http://jsfiddle.net/jfriend00/dA7XC/, you'll see that your code is getting called, but it only hits values in the array sometimes.
And, here's a rewritten algorithm that finds the appropriate msg no matter what the score show in this fiddle: http://jsfiddle.net/jfriend00/jYcBT/.
It uses a data structure like this:
var ratingMsg = [
0, "Unclassified",
10, "Weak",
20, "Fair",
50, "Better",
60, "Medium",
70, "Good",
90, "Strong"
];
and a for loop like this to get the appropraite ratingMsg:
for (var i = ratingMsg.length - 2 ; i >= 0; i-=2) {
if (score >= ratingMsg[i]) {
msg = ratingMsg[i+1];
break;
}
}
Here you go: http://jsfiddle.net/RSq4L/11/
The first problem is that in your fiddle you have the onLoad option set, so your passwordStrength function is not actually being declared in the global scope. It is being declared inside of the onLoad block that jsFiddle wraps your code with. This causes the page to error out when the keypress handler tries to invoke the function.
You can fix this problem in several different ways:
By explicitly declaring the function as global as per my example above.
By choosing one of jsFiddle's "no wrap" options instead of onLoad.
By dynamically binding your event-handler instead of setting it through the element's onkeydown attribute in the markup.
The second problem is how you are keying your score messages. You have:
var ratingMsg = new Array(0);
ratingMsg[0] = "Unclassified";
ratingMsg[10] = "Weak";
ratingMsg[30] = "Fair";
ratingMsg[50] = "Better";
ratingMsg[60] = "Medium";
ratingMsg[70] = "Good";
ratingMsg[90] = "Strong";
...and you lookup the message by doing ratingMsg[score]. This will only work if the score exactly matches one of your indices. And based upon your math this will not always be the case.
I would suggest doing something like:
ratingMsg = {};
ratingMsg[0] = "Unclassified";
ratingMsg[10] = "Weak";
ratingMsg[30] = "Fair";
ratingMsg[50] = "Better";
ratingMsg[60] = "Medium";
ratingMsg[70] = "Good";
ratingMsg[90] = "Strong";
function closestRating(score) {
var bestKey = 0;
var bestMatch = 100;
for (var key in ratingMsg) {
if (key <= score && score - key < bestMatch) {
bestMatch = score - key;
bestKey = key;
}
}
return ratingMsg[bestKey];
}
On an unrelated note, are you sure you want to be using onkeydown? I think onkeyup would work better.
Your fiddler script had several errors. Here's the corrected one: new script.
You were missing a semicolon here: document.getElementById("passwordDescription").innerHTML = "" + ratingMsg[score] + ""
You forgot to escape '^' on your regular expression
I just wrote this for it:
Jquery Plugin for password strength forcing

Mind boggling javascript failure at handling some basic addition!

This is killing me! I'm trying to add the values of four fields together, and I get allllll kinds of wierd results!
The code I have so far:
$('input.percent').change(function() {
var totalup = 1;
var totalup = totalup*1;
$('input.percent').each(function(){
var current = $(this).val();
var curvalue = current * 1;
console.log(curvalue);
console.log(totalup);
var totalup = curvalue + totalup;
});
});
This should be ungodly simply. Start with a value of zero, get the value of each input, add it to that totaling value. The console log always shows UNDECLARED or NaN for totalup, but if I remove the last decleration of totalup (where it adds more to totalup) it suddenly doesn't become undefined or Nan.
Why is this not ungodly simply!!! I must be missing something dumb, or Javascript just STINKS!
Thanks in advance for your help!
var percentInputs = $('input.percent');
percentInputs.change(function() {
var total = 0;
percentInputs.each(function(){
total += Number($(this).val());
});
});
Update
Caching those selectors would be a good idea too.
the main problem is the declaration of already declared fields. Leaf the var keyword for the second and third assignment of totalup and it'll work.
add the parseInt() while the calculation for an example
var totalup = parseInt(curvalue) + parseInt(totalup);
Okay! Here is where the issue was arising!!!!
When you write:
var FOO = 'whatever';
...Inside of a function, it is a LOCAL VARIABLE! If however you simply go:
FOO = 'whatever';
You hit the global variable (variable declared outside of the function).
So while the code above is the solution, this is where the explained solution to the problem exists!

Javascript debugging - script works with hard coded variable, not with getElementById('id').value

I'm trying to debug some javascript I wrote and can't figure out why it's not working. If I hard code the variables it works fine, but if I use document.getElementById('id').value to get the variable it fails.
The example below works fine but as soon as I un-comment the commented lines it doesn't. Printing the variables before and after the second section they seem to be identical.
Really don't get what's going on. Maybe I just need to sleep on it, but if anyone's got suggestions that would be great!
roof_width = 5;
roof_depth = 3;
panel_width = 2;
panel_depth = 1;
panel_power = 200;
roof_margin = 0.100;
panel_gap = 0.05;
roof_width = document.getElementById('roof_width').value;
roof_depth = document.getElementById('roof_depth').value;
// panel_width = document.getElementById('panel_width').value;
// panel_depth = document.getElementById('panel_depth').value;
panel_power = document.getElementById('panel_power').value;
// roof_margin = document.getElementById('roof_margin').value;
panel_gap = document.getElementById('panel_gap').value;
Are you trying to add numbers that are in text boxes? Because of the way JavaScript's variable typing system works (combined with the overloading of the + operator), 2 + 2 === 4 (adding numbers) but '2' + '2' === '22' (string concatenation). Try changing the lines to, for example:
panel_width = parseFloat(document.getElementById('panel_width').value);
or alternatively:
panel_width = Number(document.getElementById('panel_width').value);
This will ensure that JavaScript treats the numbers as numbers rather than as strings.
JavaScript parameters can't be called in the same way that you're calling HTML elements. In order to call
document.getElementById('roof_margin').value;
you need to assign 'roof_margin' to an HTML form element.
Pherhaps you have multiple dom elements with the same id? Remember the dom element ID must be unique. I suggest you to use jquery for interacting javascript with html.
Make sure your code is in an onload function. Otherwise the elements may not have been loaded into the DOM yet.
window.onload = funciton(){/* code here */};

Categories