How do I change getElementById 2nd time around? - javascript

I am creating a multiple choice question that requires the user to click an answer (radio button). If the user clicks the check answer button before selecting an answer, he is prompted to select an answer. This works fine. However, if the user then selects a wrong answer and clicks the check answer button, the appropriate response is displayed over the previous prompt. I tried changing the getElementById for the prompt to display "", but it didn't work. Any help would be appreciated.
for (i = 0; i < len; i++) {
if (document.Questions.Q_ans[i].checked == false) {
document.getElementById("reply_b").innerHTML = "Select answer before continuing.";
}
}
if (document.getElementById('answer_b').checked || document.getElementById('answer_c').checked) {
document.getElementById("reply_a").innerHTML = incorrect;
document.getElementById("reply_b").innerHTML = "";
}

This line fails since incorrect should either be defined as a variable or it should be wrapped in quotes:
document.getElementById("reply_a").innerHTML = "incorrect"; // <-- Should be in quotes
So your script never executes the line after.

Use Jquery, replace all the "document.getElementById('yourId')" for "$('#yourid')"
and you must have initialized a var len for your loop.
js:
for (var i = 0; i < len; i++) {
if (document.Questions.Q_ans[i].checked == false) {
$("#reply_b").innerHTML = "Select answer before continuing.";
}
}
if ($('#answer_b').checked || $('#answer_c').checked) {
$("#reply_a").innerHTML = incorrect;
$("#reply_b").innerHTML = "";
}

As #brian buck said, your script doesn't get executed after the incorrect statement which should be a defined variable or wrapped in quotes (to be executed as a string). Your script silently fails and basically does nothing (at least, nothing you can see).
A few things here I would also recommend:
Make sure to use var i in your loop to avoid scope error
Your first loop check seems a bit odd to me: for every (assumed) possible answer not checked, you display the same error. It is not critical, but your script does something each time instead of once. You could have, let's say, a boolean that you toggle when you discover that an answer has been checked somewhere, and assign your error statement at the end depending on the value of this boolean. However, my "naked" JS is a bit rusty for that situation, and I am totally sure that there are better solutions to it!
As it appears you haven't been notified of the script failure, I don't think you use any debugger. If you are doing JS in a web browser, you could use the embedded consoles. Otherwise, there are plenty of tools taht could help you a lot. I remember losing my hair when starting to play with JS, because it got silent everytime it wasn't happy...

Related

How to use JavaScript to get all element from a dynamic scroll list?

Like the title said, how do I get all elements from a scroll div? The elements in the scroll list are loaded and destroyed dynamically.
I tried to crawl all course names from this website:
https://public.enroll.wisc.edu/search?term=1204
The code below only works for one time:
let list = document.getElementsByClassName('md-virtual-repeat-scroller')[0]
let childs = document.getElementsByClassName("result__name")
console.log(childs[0].innerText)
However, if I do this, I will get the same result for 10 times:
let list = document.getElementsByClassName('md-virtual-repeat-scroller')[0]
for(let i = 0; i < 10; i++) {
let childs = document.getElementsByClassName("result__name")
for(let j = 0; j < childs.length; j++) {
console.log(childs[j].innerText)
}
// scroll by 1000px every time
list.scrollBy(0, 1000)
}
I don't know what's the problem. Is it because that scrollBy() works asynchronously? But I tried to use async and await. It still doesn't work.
Give more information in less words as a possible. Many problems could be related to browser and its version, for example. How is this script called? Are you giving commands via browser console? Have you done a copy of the site and performed some modification on it? It's hard to understand the problem in a realistic level.
Tip: Avoiding use innerText. It's slower and is supported in many browsers only for compability to scripts written to old versions of IE. (I don't know why so many examples in internet use it as first option). User textContent instead.
It's always good to test the returned value of a function/methods - specially during the development of the program.
Never ask to the StackOverFlow community (and to any other) to write progams for you!
You question "how do I get all elements from a scroll div?" is so "loose". scroll div? The answer to this, independently to the "type of div" (and tag!) would be found below.
Your code seems to be no sense in order to do what you want. Why iterate from 0 to 10?
Look at this snipet. I think it will help you
const list = document.getElementsByClassName('md-virtual-repeat-scroller')[0];// if there is no intention to reassign it. Use [0] if you are sure it's the first element of this collection
let childs = list.getElementsByClassName("result__name"); // get only elements inside the first variable!
Use the iterator of the variable.
for(item of childs)
{
/*code*/
}
I am sure you will achieve your goals!
And never suggest us (Community) to code for you or even to resolve your problem. This sound very agressive! To you too! I'm sure.
I solved my problem by reading this article:https://intoli.com/blog/scrape-infinite-scroll/
The reason why I kept getting the same elements is that scrollBy() works asynchronously, so I have to wait then evaluate the page again. I am using puppeteer by the way.
please read the article, super helpful.

Opening multiple URLs with window.open

I'm pretty new to javascript, so apologies for the beginner question. Basically, I am having some problems with the window.open() method.
My code essentially takes a user string, adds a couple different variations to it, and those searches those different variations, and then is supposed to open a new window with each result. However, it seems that after my first window.open statement, the code stops executing. This is what I am working with:
var searchStrings = new Array(url1, url2, url3);
var arrayLength = searchStrings.length;
for (var i = 0; i<arrayLength; i++) {
window.open(searchStrings[i]);
}
I have tested the loop with code other than window.open to make sure it iterates through the array correctly, and I have set i to values higher than 0 to test opening the second or third item in the array.
It seems like window.open is only meant to be used once, or am I doing something else incorrectly?
Check this out: https://javascript.info/popup-windows
This documents correct usage of window.open() functionality.
Modern browsers block that kind of execution, due the risk of mis-using the functionality.
Imagine, you enter the page and 10x windows open, for no reason.
Actually, have a look you code works but chrome blocks the window and on the address bar you are notified. However Firefox blocks it completely, until manually disable the option.
windows.open can takes second parameter(name), if you want open multiple URLs you have to set unique name for each one. in your scenario you cant use this:
for (var i = 0; i < arrayLength; i++) {
window.open(searchStrings[i], '_wnd' + i);
}

Using loops with prompt()

I'm trying to get five inputs from the user.
I know that calling functions in a loop (like in the below code) will crash, because all of the prompt()'s are getting called at the same time.
for(var i = 0; i < 5; i++) {
prompt();
}
What solutions are there for this? I saw other snippets using a "timeout" with a set amount of time to wait between each function call, but would the code still work when the user can take as much time as they want to enter their prompt?
I know that calling functions in a loop (like in the below code) will crash, because all of the prompt()'s are getting called at the same time.
That't not the case here because prompt is a blocking function, just like alert. I.e. iteration (or rather code execution) only continues after the prompt windows was closed.
You could add all the values to an array like so:
var inputs = [];
for(var i = 0; i < 5; i++) {
inputs.push(prompt());
}
console.log(inputs);
Whether that's the best user experience is a different question...
No need for a timeout, the prompt will wait for user input (ok, cancel) before the rest of the code executes, meaning that the next prompt will come after clicking ok. The timeout is only usefull so that people do not accidentally press twice ok in a row and see the prompt appear.

Whats happening? One day its OK, the next day its 'undefined'?

I am writing a greasemonkey script. Recently i had this same problem twice and i have no idea why is this happening.
function colli(){
.....
var oPriorityMass = bynID('massadderPriority');//my own document.getElementById() function
var aPriorities = [];
if (oPriorityMass) {
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
var sCollNumber = oPriorityMass.childNodes[cEntry].getAttribute('coll');
if (bynID('adder' + sCollNumber + '_check').checked)
aPriorities.push(parseInt(sCollNumber));
}
}
.....
}
So the mystery of this is, one day i had oPriorityMass named as oPririoty. It was working fine, but the whole function was not yet complete and i started working on another functions for my script. These functions have no connection with each other.
Few days later i decided to go back to my function in the above example and finish it. I ran a test on it without modifying anything and got an error in the firefox's (4) javascript error console saying that oPriority.chilNodes[cEntry] is undefined. NOTE, few days back i have tested it exactly the same way and there was no such problem at all.
Ok, so, i decided to rename oPriority to oPriorityMass. Magically, problem got solved.
At first i thought, maybe there was some conflict of 2 objects, with the same name being used in different functions, which somehow continued to live even outside of function scope. My script is currently over 6000 lines big, but i did a search and found out that oPriority was not mentioned anywhere else but in this exact function.
Can somebody tell me, how and why is this happening? I mentioned same thing happened twice now and they happened in different functions, but the same problem node.childNodes[c] is undefined yet node is not null and node.childNodes.length show correct child count.
What is going on? How do i avoid such problems?
Thank you
EDIT: The error given by error console is
Error: uncaught exception: TypeError: oPriorityMass.childNodes[cEntry] is undefined
In response to Brocks comment:
GM_log(oPriorityMass.childNodes[cEntry]) returns undefined as a message. So node.childNodes[c] is the thing that is undefined in general.
My script creates a div window. Later, the above function uses elements in this div. Elements do have unique IDs and i am 100% sure the original site don't know about them.
My script has a start/stop button to run one or the other function when i need to.
I have been refreshing the page and running my script function now. I have noticed that sometimes (but not always) script will fail with the described error on the first run, however, if i run it again (without refreshing the page) it starts working.
The page has a javascript that modifies it. It changes some of it's element widths so it changes when the browser is resized. But i know it has no effect on my div as it is left unchanged when i resize browser.
EDIT2:
function bynID(sID) {
return top.document.getElementById(ns(sID));
}
function ns(sText) {
return g_sScriptName + '_' + sText;
}
ns function just adds the script name in front of the ID. I use it when creating HTML element so my elements never have the same id as the web page. So bynID() is simple function that saves some typing time when i need to get element by ID.
I have modified my colli() function to include check
if (oPriorityMass) {
if (!oPriorityMass.childNodes[0]) {
GM_log('Retrying');
setTimeout(loadPage,2000);
return;
}
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
var sCollNumber = oPriorityMass.childNodes[cEntry].getAttribute('coll');
if (bynID('adder' + sCollNumber + '_check').checked)
aPriorities.push(parseInt(sCollNumber));
}
}
The loadPage function does 1 AJAX call, then i run few XPATH queries on it, but the actual contents are never appended/shown on the page, just kept inside document.createElement('div'), then this function calls colli(). So now, as i have modified my function, i checked the error console and saw that it may take up to 5 tries for it to start working correctly. 5 x 2seconds, thats 10 seconds. It is never 5 retries always, may vary There's got to be something else going on?
In Firefox, childNodes can include #text nodes. You should check to make sure that childNodes[cEntry] has nodeType == 1 or has a getAttribute method before trying to call it. e.g.
<div id="d0">
</div>
<div id="d1"></div>
In the above in Firefox and similar browsers (i.e. based on Gecko and WebKit based browsers like Safari), d0 has one child node, a text node, and d1 has no child nodes.
So I would do something like:
var sCollNumber, el0, el1;
if (oPriorityMass) {
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
el0 = oPriorityMass.childNodes[cEntry];
// Make sure have an HTMLElement that will
// have a getAttribute method
if (el0.nodeType == 1) {
sCollNumber = el0.getAttribute('coll');
el1 = bynID('adder' + sCollNumber + '_check');
// Make sure el1 is not falsey before attempting to
// access properties
if (el1 && el1.checked)
// Never call parseInt on strings without a radix
// Or use some other method to convert to Number
aPriorities.push(parseInt(sCollNumber, 10));
}
}
Given that sCollNumber seems like it is a string integer (just guessing but it seems likely), you can also use:
Number(sCollNumber)
or
+sCollNumber
whichever suits and is more maintainable.
So, according to your last edit, it now works, with the delay, right?
But when I suggested the delay it was not meant to do (even more?) ajax calls while waiting!!
NOT:
if (!oPriorityMass.childNodes[0]) {
GM_log('Retrying');
setTimeout(loadPage,2000);
return;
More like:
setTimeout (colli, 2000);
So the ajax and the other stuff that loadPage does could explain the excessive delay.
The random behavior could be caused by:
return top.document.getElementById(ns(sID));
This will cause erratic behavior if any frames or iframes are present, and you do not block operation on frames. (If you do block such operation then top is redundant and unnecessary.)
GM does not operate correctly in such cases -- depending on what the script does -- often seeming to "switch" from top scope to frame scope or vice versa.
So, it's probably best to change that to:
return document.getElementById (ns (sID) );
And make sure you have:
if (window.top != window.self) //-- Don't run on frames or iframes
return;
as the top lines of code.
Beyond that, it's near impossible to see the problem, because of insufficient information.
Either boil the problem into a Complete, Self Contained, Recipe for duplicating the failure.
OR, post or link to the Complete, Unedited, Script.

Remaning checkbox name from JS cause IE to run slow, not FF?

I have a list of check boxes. For the check boxes that are selected, I change it's name before submitting. In FF the function works. In IE I get:
A script on this page is causing Internet Explorer to run slowly. If it
continues to run, your computer may
become unresponsive.
Do you want to abort the script? YES/NO
Not sure why this loop is causing problems in IE and not FF?
function sub()
{
var x=document.getElementsByName("user");
for (i = 0; i < x.length; i++) //for all check boxes
{
if (x[i].checked == true)
{
x[i].name="id"; //change name of data so we know it is for an id
//By renaming the first element of the list, we have reduced the length of the list by one
//and deleted the first element. This is why we need to keep i at it's current position after a name change.
i=i-1;
}
}//end for
document.checks.submit();
}
I would avoid writing a script like that - it is like having an for/i++ loop calling a function that changes the index as a side effect - unpredictable. You run an iterator through NodeList while modifying the list from inside the loop. You cannot be sure it works until you happen to know exactly the way NodeList is implemented.
It's unpleasant, but I would first copy the list into "real" array, and then do the renaming.
This should work with a both a live and a non-live list.
Personally, though, I'd try to find some way to output the original page so that the server can figure out which elements to use, instead of relying on javascript to do it.
function sub()
{
var x=document.getElementsByName("user");
var nodesToChangeIndex=0;
var nodesToChange=new Array();
for (i = 0; i < x.length; i++) //for all check boxes
{
if (x[i].checked == true)
{
nodesToChange[nodesToChangeIndex++] = x[i];
}
}//end for
for(i=0; i < nodesToChangeIndex; i++)
{
nodesToChange[i].name="id";
}
document.checks.submit();
}
Internet Explorer is buggy. It doesn't return a live nodelist but just a snapshot.
Using a framework such as jQuery is your best bet in achieving full compatibility.
Edit: This is wrong, Nick is right. Although I'd look extra hard to make sure IE actually implements the NodeList correctly, since it still sounds like what you're running into is an infinite loop in IE.
Thanks for the heads up, Nick, I learned something new today. Javascript never ceases to surprise me :)
Just renaming an element in the array would not remove it from the array -- getElementsByName is not run constantly, just the initial time that it is called. So IE is stuck in an infinite loop where it keeps checking the same checked element over and over. I'm honestly not sure why Firefox doesn't also get stuck in the same infinite loop.
As far as I understand, the i=i-1 line is entirely unnecessary.

Categories