So, I wanted to automate some of my coding process. I figured maybe I don't have to manually name all the constants that refer to classes, and have a function do that. Here's my code, which, I feel, should work. Of course it doesn't. Should it? If not, why not?
function definitions() {
var allElem = document.querySelectorAll("*"); //Get every HTML element.
for(a = 0; a < allElem.length; a++) { //Iterate through every HTML element.
if (allElem[a].classList.length == 0) { //If an element has no class...
console.log("no var declared, " + a); //...ignore it.
} else { //If it has one or more classes...
for(b = 0; b < allElem[a].classList.length; b++) { //...iterate through the classList.
eval("const " + allElem[a].classList[b] + " = " + "document.getElementsByClassName('" + allElem[a].classList[b] + "');"); //Declare a constant named for the class if refers to
console.log("const " + allElem[a].classList[b] + " = " + "document.getElementsByClassName('" + allElem[a].classList[b] + "');");
}
}
}
console.log(document.getElementsByClassName('clone').length); //This logs 10, which is correct.
console.log(clone.length); // 'Uncaught ReferenceError: clone is not defined'. But... but... I thought I did... :'(
}
Related
I am trying to figure out how I can clear the <p> elements that are generated from a for loop before the for loop starts.
Essentially. I have a webpage where someone searches something and a list of results are shown. However if I search for something else, the new results get appended instead of clearing the old results first.
Here is the code:
async function parseitglinkquery() {
var queriedresults = await getitglinkquery();
console.log(queriedresults);
const output = document.querySelector('span.ms-font-mitglue');
output.removeChild("createditginfo"); \\tried to clear the <pre> here and it failed
for (let i = 0; i < queriedresults.length; i++) {
let text = "Company: " + JSON.stringify(queriedresults[i]["data"]["attributes"].organization-name) + "<br>"+
"Name: " + JSON.stringify(queriedresults[i]["data"]["attributes"].name) + "<br>" +
"Username: " + JSON.stringify(queriedresults[i]["data"]["attributes"].username).replace("\\\\","\\") + "<br>" +
"Password: " + JSON.stringify(queriedresults[i]["data"]["attributes"].password);
let pre = document.createElement('p');
pre.setAttribute("id", "createditginfo")
pre.innerHTML = text;
pre.style.cssText += 'font-size:24px;font-weight:bold;';
output.appendChild(pre);
console.log(typeof pre)
}
}
I tried to create a try and catch block where it would try to clear the <p> using removeChild() but that didn't seem to work either.
async function parseitglinkquery() {
var queriedresults = await getitglinkquery();
console.log(queriedresults);
const output = document.querySelector('span.ms-font-mitglue');
try {
output.removeChild("createditginfo");
}
catch(err){
console.log(err)
}
for (let i = 0; i < queriedresults.length; i++) {
let text = "Company: " + JSON.stringify(queriedresults[i]["data"]["attributes"].organization-name) + "<br>"+
"Name: " + JSON.stringify(queriedresults[i]["data"]["attributes"].name) + "<br>" +
"Username: " + JSON.stringify(queriedresults[i]["data"]["attributes"].username).replace("\\\\","\\") + "<br>" +
"Password: " + JSON.stringify(queriedresults[i]["data"]["attributes"].password);
let pre = document.createElement('p');
pre.setAttribute("id", "createditginfo")
pre.innerHTML = text;
pre.style.cssText += 'font-size:24px;font-weight:bold;';
output.appendChild(pre);
console.log(typeof pre)
}
}
You only have to clear the output-node right before the loop using the innerHTML-property.
output.innerHTML = '';
https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML
There are other ways too, if you want to remove only specific childs. You can make use of Node.childNodes together with a loop. With this, you have the opportunity to remove only specific children.
[...output.childNodes].forEach(childNode => {
output.removeChild(childNode)
});
// or specific
[...output.childNodes].forEach(childNode => {
// remove only <div>-nodes
if (childNode.nodeName == 'DIV') {
childNode.remove();
}
});
https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes
https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild
https://developer.mozilla.org/en-US/docs/Web/API/Element/remove
The answer above is correct, but I believe that the original code and the answer can be further improved:
for variables that do not change, use const instead of let - this helps explaining the intention.
there seems to be a bug - if you have an attribute called "organization-name", you cannot access it as a property (...["attributes"].organization-name), but you can use array access instead: ...["attributes"]["organization-name"]. Otherwise, the code end up effectively as ...["attributes"].organization - name
when you have long property paths that are repeated a lot (like queriedresults[i]["data"]["attributes"] in your case), consider assigning them to a local variable.
This makes the code more readable and is actually better for performance (because of less array lookups):
// old code
let text = "Company: " + JSON.stringify(queriedresults[i]["data"]["attributes"].organization-name) + "<br>"+
"Name: " + JSON.stringify(queriedresults[i]["data"]["attributes"].name) + "<br>";
// new code
const attrs = queriedresults[i]["data"]["attributes"];
let text = "Company: " + JSON.stringify(attrs['organization-name']) + "<br>"+
"Name: " + JSON.stringify(attrs.name) + "<br>";
you are creating several pre's in a loop, this is ok, however the element id must be different! Each id must be unique in the whole page:
// wrong
for (let i = 0; i < queriedresults.length; i++) {
...
pre.setAttribute("id", "createditginfo")
...
}
// right/better
for (let i = 0; i < queriedresults.length; i++) {
...
pre.setAttribute("id", "createditginfo" + i) // "i" for uniqueness
...
}
you can use innerText, in which case you do not need to encode the attributes, plus it simplifies the code:
const pre = document.createElement('p');
pre.innerText = [
"Company: " + attrs["organization-name"],
"Name: " + attrs.name,
"Username: " + attrs.username, // presumably you don't need to decode "/" anymore :)
"Password: " + attrs.password
].join("\n") // so we declared a bunch of lines as an array, then we join them with a newline character
Finally, regarding the original question, I see three main ways:
simply clearing the contents of the parent with output.innerHtml = ''
iterating over each child and removing it with output.removeChild(childPre)
you can keep references to the generated pre's (eg, store each element in an array) and remove the later with point 2, but this is less automatic but in some cases it might be more efficient if you have a tone of elements at that same level.
I've got this code and it's returning that reference error, but I have no idea why, I've seen lots of Q&As about reference errors, but I still can't find it. Any help is appreciated.
Thanks in advance and may the force be with you.
My code:
var data = '[{"name":"node1","id":1,"is_open":true,"children":[{"name":"node2","id":6,"children":[{"name":"child3","id":7}]},{"name":"child1","id":2}]}]';
var dadSon = [];
(function printDadSon(data, parent) {
if (!data) return;
for (var i = 0; i < (data.length); i++) {
if (parent && parent != 'undefined') {
console.log('Dad: ' + parent + ' & Son: ' + data[i].id);
dadSon += ('Dad: ' + parent + ' & Son: ' + data[i].id);
}
printDadSon(data[i].children, data[i].id);
}
})(JSON.parse(data));
printDadSon(data);
You're only declaring the function in the scope of your immediately called named function expression.
So it's not visible on the line after, when you call it.
You could choose to declare the function as in
var data = '[{"name":"node1","id":1,"is_open":true,"children":[{"name":"node2","id":6,"children":[{"name":"child3","id":7}]},{"name":"child1","id":2}]}]';
var dadSon = [];
function printDadSon(data, parent) {
if (!data) return;
for (var i = 0; i < (data.length); i++) {
if (parent && parent != 'undefined') {
console.log('Dad: ' + parent + ' & Son: ' + data[i].id);
dadSon += ('Dad: ' + parent + ' & Son: ' + data[i].id);
}
printDadSon(data[i].children, data[i].id);
}
}
printDadSon(JSON.parse(data));
But you don't need to call it after, the line after is useless (and wrong in your code as you don't parse the JSON), just use
var data = '[{"name":"node1","id":1,"is_open":true,"children":[{"name":"node2","id":6,"children":[{"name":"child3","id":7}]},{"name":"child1","id":2}]}]';
var dadSon = [];
(function printDadSon(data, parent) {
if (!data) return;
for (var i = 0; i < (data.length); i++) {
if (parent && parent != 'undefined') {
console.log('Dad: ' + parent + ' & Son: ' + data[i].id);
dadSon += ('Dad: ' + parent + ' & Son: ' + data[i].id);
}
printDadSon(data[i].children, data[i].id);
}
})(JSON.parse(data));
The advantage of the second version is that it doesn't pollute the external scope with a function declaration that you don't need.
printDadSon is created using a named function expression.
Named function expressions create a variable (matching their name) in their own scope.
printDadSon(data[i].children, data[i].id);
The above line is in the scope of the function, so it can access that variable.
printDadSon(data);
The above line is not.
However, it is passing a string and the earlier line ((JSON.parse(data)) is already calling it with an actual array, so just remove that line because it isn't doing (or trying to do) anything useful.
I have trouble accessing object' property in the example below. On the third line I'd like to have the number 42 replaced with the value of the variable devNumTemp, but so far I'm not successfull.
What is the proper way to do this? I've tried several options, but never getting any further than getting undefined.
function getTempDev(devNumTemp, devNumHum, id, description){
$.getJSON("http://someurl.com/DeviceNum=" + devNumTemp,function(result){
var array = result.Device_Num_42.states;
function objectFindByKey(array, key, value) {
for (var i = 0; i < array.length; i++) {
if (array[i][key] === value) {
$("#id").html(description + "<div class='right'>" + array[i].value + "°C" + " (" + someVariable + "%" + ")" + "<br></div>");
}
}
};
objectFindByKey(array, 'service', 'something');
});
};
You can acces to object's properties like this
var array = result["Device_Num_" + devNumTemp].states;
It's considered a good practice to test for field's existance before trying to access it:
var array = [];
if (result && result["Device_Num_" + devNumTemp]){
array = result["Device_Num_" + devNumTemp].states;
}
This way we prevent Null Pointer Exception type errors.
I've been working on a script which collates the scores for a list of user from a website. One problem is though, I'm trying to load the next page in the while loop, but the function is not being loaded...
casper.then(function () {
var fs = require('fs');
json = require('usernames.json');
var length = json.username.length;
leaderboard = {};
for (var ii = 0; ii < length; ii++) {
var currentName = json.username[ii];
this.thenOpen("http://www.url.com?ul=" + currentName + "&sortdir=desc&sort=lastfound", function (id) {
return function () {
this.capture("Screenshots/" + json.username[id] + ".png");
if (!casper.exists(x("//*[contains(text(), 'That username does not exist in the system')]"))) {
if (casper.exists(x('//*[#id="ctl00_ContentBody_ResultsPanel"]/table[2]'))) {
this.thenEvaluate(tgsagc.tagNextLink);
tgsagc.cacheCount = 0;
tgsagc.
continue = true;
this.echo("------------ " + json.username[id] + " ------------");
while (tgsagc.
continue) {
this.then(function () {
this.evaluate(tgsagc.tagNextLink);
var findDates, pageNumber;
pageNumber = this.evaluate(tgsagc.pageNumber);
findDates = this.evaluate(tgsagc.getFindDates);
this.echo("Found " + findDates.length + " on page " + pageNumber);
tgsagc.checkFinds(findDates);
this.echo(tgsagc.cacheCount + " Caches for " + json.username[id]);
this.echo("Continue? " + tgsagc["continue"]);
this.click("#tgsagc-link-next");
});
}
leaderboard[json.username[id]] = tgsagc.cacheCount;
console.log("Final Count: " + leaderboard[json.username[id]]);
console.log(JSON.stringify(leaderboard));
} else {
this.echo("------------ " + json.username[id] + " ------------");
this.echo("0 Caches Found");
leaderboard[json.username[id]] = 0;
console.log(JSON.stringify(leaderboard));
}
} else {
this.echo("------------ " + json.username[id] + " ------------");
this.echo("No User found with that Username");
leaderboard[json.username[id]] = null;
console.log(JSON.stringify(leaderboard));
}
});
while (tgsagc.continue) {
this.then(function(){
this.evaluate(tgsagc.tagNextLink);
var findDates, pageNumber;
pageNumber = this.evaluate(tgsagc.pageNumber);
findDates = this.evaluate(tgsagc.getFindDates);
this.echo("Found " + findDates.length + " on page " + pageNumber);
tgsagc.checkFinds(findDates);
this.echo(tgsagc.cacheCount + " Caches for " + json.username[id]);
this.echo("Continue? " + tgsagc["continue"]);
return this.click("#tgsagc-link-next");
});
}
Ok, looking at this code I can suggest a couple of changes you should make:
I don't think you should be calling return from within your function within then(). This maybe terminating the function prematurely. Looking at the casperjs documentation, the examples don't return anything either.
Within your while loop, what sets "tgsagc.continue" to false?
Don't use "continue" as a variable name. It is a reserved word in Javascript used for terminating an iteration of a loop. In your case this shouldn't be a problem, but its bad practice anyhow.
Don't continually re-define the method within your call to the then() function. Refactor your code so that it is defined once elsewhere.
We ended up having to scope the function, so it loads the next page in the loop.
This is mainly because CasperJS is not designed to calculate scores, and it tries to asynchronously do the calculation, missing the required functions
This is the code that I currently have, one problem that is happening is I cannot use test() because presets[index].name and value are not visible outside of their function scope, how should I declare my array of objects in the global scope in order for me to be able to access these two variables in other functions?
var presets = [];
var index;
function CreatePresetArray(AMib, AVar) {
var parentpresetStringOID = snmp.getOID(AMib, AVar);
var presetStringOID = parentpresetStringOID;
parentpresetStringOID = parentpresetStringOID.substring(0, parentpresetStringOID.length - 2);
log.error("parentpresetStringOID is " + parentpresetStringOID);
var presetswitches = {};
for (var i = 1; i < 41; i++) {
presets.push(presetswitches);
try {
log.error("presetStringOID before getNextVB= " + presetStringOID);
vb = snmp.getNextVB(presetStringOID);
presetStringOID = vb.oid;
log.error("presetStringOID after getnextVB= " + presetStringOID);
var presetStringVal = snmp.get(presetStringOID);
log.error("presetStringVal= " + presetStringVal);
index = i - 1;
presets[index].name = presetStringOID;
presets[index].value = presetStringVal;
log.error("preset array's OID at position [" + index + "] is" + presets[index].name + " and the value stored is " + presets[index].value);
//log.error("presets Array value ["+index+"] = "+presets[index].configs);
if (presetStringOID.indexOf(parentpresetStringOID) != 0) {
break;
}
} catch (ie) {
log.error("couldn't load preset array " + index);
};
};
}
CreatePresetArray(presetMib, "presetString");
function test() {
for (i = 1; i < 41; i++) {
log.error("test" + presets[index].name + " " + presets[index].value);
};
}
test();
The for loop in your function test iterates over i but uses index inside the loop. Perhaps you meant to use
for (i = 0; i < 40; i++) { // 1 lower as you were using `index = i - 1` before
log.error("test" + presets[i].name + " " + presets[i].value);
}
Re-wrote your code. I don't think I made that much by way of change. If this doesn't clear up your problem, consider: Is the catch happening each iteration? Is the problem actually coming from a different method which is only visible here? Also, consider logging the whole presets Array when debugging to see what it looks like.
var presets = [];
function CreatePresetArray(AMib, AVar) {
var parentPresetOID, presetOID, presetValue, preset, vb, i;
parentPresetOID = snmp.getOID(AMib, AVar);
presetOID = parentPresetOID; // initial
parentPresetOID = parentPresetOID.substring(0, parentPresetOID.length - 2);
log.error("parentPresetOID is " + parentPresetOID);
presets = []; // empty array in case not already empty
for (i = 0; i < 40; ++i) {
try {
preset = {}; // new object
// new presetOID
vb = snmp.getNextVB(presetOID);
presetOID = vb.oid;
log.error("presetOID after getnextVB= " + presetOID);
// new value
presetValue = snmp.get(presetOID);
log.error("presetValue= " + presetValue);
// append data to object
preset.name = presetOID;
preset.value = presetValue;
// append object to array
presets.push(preset);
// more logging
log.error(
"preset array's OID at position [" + i + "]" +
" is" + presets[i].name + " and " +
"the value stored is " + presets[i].value
);
if (presetOID.indexOf(parentPresetOID) !== 0) {
break;
}
} catch (ie) {
log.error("couldn't load preset array " + i);
if (presets.length !== i + 1) { // enter dummy for failed item
presets.push(null);
}
}
}
}
Two options come to mind immediately:
you could pass the preset array as a argument to test().
You could put both CreatePresetArray() and test() inside a wrapper function and declare preset array at the top of your wrapper. That would give them both access to the variable.
It's generally considered Bad Form to declare globals if it can be avoided. Pollutes the namespace.