I am working on a javascript code that looks like the following. I am only showing the basic skeleton of the code first.
var array = [];
function mainFun()
{
A();
}
function A()
{
//some code
B();
//code to print all values in "array"
}
function B()
{
C();
}
function C()
{
//some code
//push elements one by one in "array"
for (var i=0; i<10; i++)
{
array.push(i); //something on these lines
}
}
I know this code seems bizarre but this is exactly the situation I am working on. Thanks to Javascript's function level scoping (as against the regular block-level scoping), I am unable to access and print all the elements in A() that have been pushed in the array in C(). So how can I make my array variable work like a true global variable that has knowledge of what elements were pushed into it?
Ok, here is my original source code (I don't know how the dummy code worked though!)
var allLinks = {}; //set of all internal and external links
var Queued = [];
var crawlingURL;
var Crawled = [];
var xmlHttp = null, originURL, maxHops = 0, currentHop = 0;
function changeText(){
var tabID, sourceURL;
chrome.tabs.query({currentWindow: true, active: true}, function(tabs){
console.log(tabs[0].url);
document.getElementById('URL').innerHTML="URL of Current Page : "+tabs[0].url;
tabID = tabs[0].id;
sourceURL = tabs[0].url;
Queued.push(sourceURL); //push the origin link the Queued array to begin crawling
beginCrawl(sourceURL);
});
}
document.addEventListener('DOMContentLoaded', function () {
changeText();
});
function beginCrawl(url)
{
originURL = url;
maxHops = 2;
currentHop = 1;
var queueIndex = 0;
//httpGet(originURL);
while(queueIndex<1) //(currentHop <= maxHops)
{
crawlingURL = Queued[queueIndex];
//allPages[crawlingURL] = {url:url, state:"crawling", level:0, host:startingHost};
httpGet(crawlingURL);
Crawled.push(crawlingURL);
queueIndex++;
for(var j = 0; j < Queued.length; j++)
{
console.log(j+". "+Queued[j]+"\n");
}
}
}
function httpGet(theUrl)
{
xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", theUrl, true );
xmlHttp.send( null );
xmlHttp.onreadystatechange = ProcessRequest;
}
function ProcessRequest()
{
if ( xmlHttp.readyState == 4 && xmlHttp.status == 200 ) // xmlHTTP success
{
var container = document.createElement("p");
container.innerHTML = xmlHttp.responseText;
var anchors = container.getElementsByTagName("a");
var list = [];
for (var i = 0; i < anchors.length; i++)
{
var href = anchors[i].href;
var exists = 0;
// to check for duplicate entries in the list
for(var j = 0; j < Queued.length; j++) // remove duplicates
if(Queued[j] == href)
exists = 1;
if (exists == 0)
{
Queued.push(href);
document.getElementById('printORGLinks').innerHTML += href + "<br />";
}
}
}
}
I am unable to get print the values in my Queued Array ! (As you may understand, this is a preliminary code for a web crawler of some sort. I need to get the list of all URLs pushed into the Queued array).
This works as you've entered it - fiddle here http://jsfiddle.net/LmFqq/1/
Output
some code executing in A()
some code executing in B()
adding elements in C()
printing in A()
[0,1,2,3,4,5,6,7,8,9]
Code
var array = [];
var el = document.getElementById('output');
mainFun();
function log(msg) {
el.innerHTML += msg + "<br />";
}
function mainFun()
{
A();
}
function A()
{
log("some code executing in A()");
B();
log("printing in A()");
log(JSON.stringify(array));
}
function B()
{
log("some code executing in B()");
C();
}
function C()
{
log("adding elements in C()");
for (var i=0; i<10; i++)
{
array.push(i); //something on these lines
}
}
Related
The following is the full JavaScript code for a quiz. It works perfectly on desktop browsers. However, when I went to test it on my phone, it doesn't work.
Mobile loads the JSON quiz, but after all the answers are selected, it does not freeze all the selected answers and display the results. Instead, nothing happens, no result is shown/calculated, and the user can continue to select answers.
I'm not experienced with JavaScript on Mobile and don't know what is causing the problem.
I've deleted a few parts of the code that simply appended html in order to cut down on size.
You can view it here: plnkr.co/edit/tkCQVxoIq9oOiApeUY66?p=preview
// Adds the functionality to check if an element has a class
HTMLElement.prototype.hasClass = function (className) {
"use strict";
if (this.classList) {
return this.classList.contains(className);
}
return (-1 < this.className.indexOf(className));
};
// Adds the ability to remove classes from elements
HTMLElement.prototype.removeClass = function (className) {
"use strict";
if (this.classList) {
this.classList.remove(className);
}
return this;
};
var BF_QUIZ = {};
BF_QUIZ.quiz = function () {
"use strict";
// Sets variables
var highest_score, quiz_div, quiz_title, quiz_image, questions = [],
results = [], inputs = [], answers = [], userAnswers = [],
// Gets the Quiz "canvas"
getQuizCanvas = function getQuizCanvas() {
quiz_div = document.getElementById("bf-quiz");
},
// Parses the JSON data passed from the Loader
getJSONData = function getJSONData(json_data) {
//Main Quiz Title
quiz_title = json_data[0].quiz_title;
//Main Quiz Image
quiz_image = json_data[0].quiz_image;
//Populates questions arrary with questions from JSON file
for (var i = 0; i < json_data[0].quiz_questions.length; i++) {
questions.push(json_data[0].quiz_questions[i]);
}
//Populates results array with results from JSON file
for (var j = 0; j < json_data[0].quiz_results.length; j++) {
results.push(json_data[0].quiz_results[j]);
}
},
// Writes the Quiz into the document
writeQuiz = function writeQuiz() {
var newQuizWrapper, newTitle, newQuestionTextWrapper, newQuestionText,
newAnswerForm, newAnswer, newAnswerImage, newAnswerTextWrapper, newAnswerInput,
newAnswerText, newQuestion;
newQuizWrapper = document.createElement("div");
newQuizWrapper.className = "quiz-wrapper";
newTitle = document.createElement("h1");
newTitle.innerHTML = quiz_title;
newQuizWrapper.appendChild(newTitle);
for (var i = 0; i < questions.length; i++) {
newQuestionTextWrapper = document.createElement("div");
newQuestionTextWrapper.className = "quiz-question-text-wrapper";
newQuestionText = document.createElement("h2");
newQuestionText.innerHTML = questions[i].question.text;
newQuestionTextWrapper.appendChild(newQuestionText);
newAnswerForm = document.createElement("form");
for (var j = 0; j < questions[i].question.question_answers.length; j++) {
newAnswer = document.createElement("div");
newAnswer.className = "quiz-answer";
newAnswer.setAttribute("data-quizValue",
questions[i].question.question_answers[j].answer.value);
if (questions[i].question.question_answers[j].answer.image) {
newAnswerImage = document.createElement("img");
newAnswerImage.src = questions[i].question.question_answers[j].answer.image;
newAnswer.appendChild(newAnswerImage);
}
else{
//no image
}
newAnswerTextWrapper = document.createElement("div");
newAnswerTextWrapper.className = "quiz-answer-text-wrapper";
newAnswerTextWrapper.id = "quiz-answer-text-wrapper";
newAnswerInput = document.createElement("input");
newAnswerInput.type = "radio";
newAnswerInput.name = "answer";
inputs.push(newAnswerInput);
newAnswerText = document.createElement("label");
newAnswerText.htmlFor = "quizzer";
newAnswerText.innerHTML = questions[i].question.question_answers[j].answer.text;
newAnswerTextWrapper.appendChild(newAnswerInput);
newAnswerTextWrapper.appendChild(newAnswerText);
newAnswer.appendChild(newAnswerTextWrapper);
answers.push(newAnswer);
newAnswerForm.appendChild(newAnswer);
}
newQuestion = document.createElement("div");
newQuestion.className = "quiz-question";
newQuestion.appendChild(newQuestionTextWrapper);
newQuestion.appendChild(newAnswerForm);
newQuizWrapper.appendChild(newQuestion);
}
quiz_div.appendChild(newQuizWrapper);
},
//Checks all of the inputs to see if the
checkInputs = function checkInputs() {
var c = 0;
for (var i = 0; i < inputs.length; i++) {
if (inputs[i].checked) {
userAnswers.push(inputs[i].parentNode.parentNode.dataset.quizvalue);
c++;
}
}
if (c==questions.length) {
calcResult();
}
},
calcResult = function calcResult() {
var highest = 0;
for (var i = 0; i < results.length; i++) {
results[i].countof = 0;
for (var j = 0; j < userAnswers.length; j++) {
if (userAnswers[j] == results[i].result.id) {
results[i].countof++;
}
}
if (results[i].countof > highest) {
highest = results[i].countof;
highest_score = results[i];
}
}
//disable the inputs after the quiz is finished
writeResult();
disableAnswers();
},
writeResult = function writeResult() {
newResult = document.createElement("div");
//append html to render (quiz result)
...;
quiz_div.appendChild(newResult);
},
updateSelectedAnswer = function updateSelectedAnswer(element) {
element.children.namedItem("quiz-answer-text-wrapper").firstChild.checked = true;
for (var i = 0; i < element.parentNode.children.length; i++) {
if (element.parentNode.children.item(i).hasClass("selected")) {
element.parentNode.children.item(i).removeClass("selected");
}
}
element.className = element.className + " selected";
},
addClickEvents = function addClickEvents() {
var onAnswerClick = function onAnswerClick() {
if (!this.hasAttribute("disabled")) {
updateSelectedAnswer(this);
checkInputs();
}
};
for (var i = 0; i < answers.length; i++) {
answers[i].addEventListener("click", onAnswerClick);
}
},
disableAnswers = function disableAnswers() {
for (var q = 0; q < answers.length; q++) {
answers[q].disabled = true;
answers[q].setAttribute("disabled", true);
answers[q].className = answers[q].className + " disabled";
}
};
return {
init: function (json_data) {
getQuizCanvas();
getJSONData(json_data);
writeQuiz();
addClickEvents();
}
};
}();
BF_QUIZ.quizLoader = function () {
"use strict";
var json_data, request,
loadQuizJSON = function loadQuizJSON(json_url) {
request = new XMLHttpRequest();
request.open("GET", json_url, false);
request.onload = function() {
if (request.status >= 200 && request.status < 400) {
// Success!
json_data = JSON.parse(request.responseText);
} else {
// We reached our target server, but it returned an error
}
};
request.onerror = function() {
// There was a connection error of some sort
};
request.send();
};
return {
init: function(json_url) {
loadQuizJSON(json_url);
BF_QUIZ.quiz.init(json_data);
}
};
}();
If you ever try to see console log on a mobile device, you might notice that there is a JavaScript error because iOS Safari is less forgiving than whatever desktop browser you use. Particularly it is illegal to set style property of HTMLElement as string in strict mode. You may see examples of the ways to set it properly at https://developer.mozilla.org/en/docs/Web/API/HTMLElement/style If you fix this issue, code seems to be working on Mobile Safari as well.
P.S. note that the offending code is missing in your question and is only visible in the full code of writeResult on plunker. This is why it is so important to provide a Minimal, Complete, and Verifiable example
From your plunker, if you update your code on line 152 from newResultTitle.style = "color:rgba(238,62,52,.99);"; to newResultTitle.style.color = "rgba(238,62,52,.99)"; this would make it work on mobile browsers.
HTMLElement.style reruns a read only property, desktop browser ignoring it when you are trying to assign a value to it, and mobile browser throwing an error.
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style
Working plunker http://plnkr.co/edit/aVxTP5YCbL94v2GI0IKh?p=preview
I'm creating an object literal and I want to use the reserved word "this". The problem I'm having is that the "this" points to the window object in an object literal. I know the this points to the current object when used in a constructor function. Is there a way to override it so that "this" points to my object literal?
main = {
run: function()
{
var elements = [];
var allElements = document.querySelectorAll("*");
for(var i = 0; i < allElements.length; i++)
{
if(allElements[i].nodeType != 3)
{
elements.push(allElements[i]);
}
}
for(var i = 0; i < elements.length; i++)
{
// Doesn't work
// this.parseElement(elements[i]);
// Works
main.parseElement(elements[i]);
}
},
parseElement: function(e)
{
// Unimportant code
}
}
(function()
{
main.run();
})();
The thing you claim works in your question doesn't work:
var main = {
run: (function()
{
var elements = [];
var allElements = document.querySelectorAll("*");
for(var i = 0; i < allElements.length; i++)
{
if(allElements[i].nodeType != 3)
{
elements.push(allElements[i]);
}
}
for(var i = 0; i < elements.length; i++)
{
// Doesn't work
// this.parseElement(elements[i]);
// Works
main.parseElement(elements[i]);
}
})(),
parseElement: function(e)
{
// Unimportant code
}
};
<div></div>
Fundamentally, you cannot refer to the object being constructed from within the object initializer. You have to create the object first, because during the processing of the initializer, while the object does exist no reference to it is available to your code yet.
From the name run, it seems like you want run to be a method, which it isn't in your code (you've edited the question now to make it one). Just remove the ()() around the function:
var main = {
run: function() {
var elements = [];
var allElements = document.querySelectorAll("*");
for (var i = 0; i < allElements.length; i++) {
if (allElements[i].nodeType != 3) {
elements.push(allElements[i]);
}
}
for (var i = 0; i < elements.length; i++) {
this.parseElement(elements[i]);
}
},
parseElement: function(e) {
console.log("Parsing " + e.tagName);
}
};
main.run();
<div></div>
Since this is set by how the function is called for normal functions, if you want run to be bound to main so that it doesn't matter how it's called, using main instead of this is the simplest way to do that in that code.
But if you don't want to use main, you could create a bound function:
var main = {
run: function() {
var elements = [];
var allElements = document.querySelectorAll("*");
for (var i = 0; i < allElements.length; i++) {
if (allElements[i].nodeType != 3) {
elements.push(allElements[i]);
}
}
for (var i = 0; i < elements.length; i++) {
this.parseElement(elements[i]);
}
},
parseElement: function(e) {
console.log("Parsing " + e.tagName);
}
};
// Bind run
main.run = main.run.bind(main);
// Use it such that `this` would have been wrong
// if we hadn't bound it:
var f = main.run;
f();
<div></div>
Just as a side note, we can use Array.prototype.filter and Array.prototype.forEach to make that code a bit more concise:
var main = {
run: function() {
var allElements = document.querySelectorAll("*");
var elements = Array.prototype.filter.call(allElements, function(e) {
return e.nodeType != 3;
});
elements.forEach(this.parseElement, this);
},
parseElement: function(e) {
console.log("Parsing " + e.tagName);
}
};
// Use it
main.run();
<div></div>
That assumes that parseElement only ever looks at the first argument it's given (since forEach will call it with three: the entry we're visiting, its index, and the object we're looping through).
I have a function which is called when a file is needed to be read in a folder. In this case since i have 3 files in that folder, it is called 3 times consecutively. I need to save all files info into array mapped_data2 like that:
mapped_data2[0] = inner_data1 //first file info,
mapped_data2[1] = inner_data2 //second file info etc.
However using my code i am having just the first files information 3 times. I am a bit confused with global variables, if you can point out the problem, i would appreciate it.
here is the code:
var mapped_data = [];
var mapped_data2 = [];
function o(msg) {
if (msg.data) {
var inner_data = [];
var lines = msg.data.split('\n'); //read lines of a file
for (var i = 2; i < lines.length; i++) {
if (lines[i].length > 0) {
.. //do same data format here
inner_data.push([son, vactual, voutput]);
}
}
mapped_data = inner_data;
}
else {
if (msg.topic == "/somefolder/somefolder") {
for (var i = 0; i < msg.args.length; i++) {
var filename = msg.args[i];
aw.get(filename);
}
}
}
}
function de() { //where i wanted to use these files info
for (var i = 0; i < 3; i++) {
mapped_data2[i] = { key: "Group" + (i + 1), values: mapped_data };
}
var datam = mapped_data2;
var den = JSON.stringify(datam);
document.write(den);
};
function init() {
..//create new client called aw for the server application and start it;
..//if the connection is established:
aw.onmessage = o;
aw.get("/somefolder/somefolder"); // request list of files in a folder
};
//when html page is being onload, call the functions init() and de()
var mapped_data2 = [];
function o(msg) {
var mapped_data = []; // this doesn't need to be global
if (msg.data) {
var inner_data = [];
...
mapped_data = inner_data;
} else {
...
}
mapped_data2.push({ key: "Group" + mapped_data2.length + 1, values: mapped_data };)
// do more stuff as in den()
}
I've encountered this before and I have a fair understanding of scope though not enough. I'm trying to pass a string as a parameter to an anonymous function, inside of a for loop. From my understanding by default this is recursive so no matter how many times the variable's string changes it still sends the same string (1,2,3,4 is passed on (maybe like?) 1,1,1,1 when we want 1,2,3,4).
I want to make this non-recursive so in the example below the file names from the multiple-file-input-element are different (since none of them can be the same).
How can we make the event listener receive the non-recursive parameter?
function images_upload(e)
{
for (var i = 0; i < document.getElementById('post_files').files.length; i++)
{
var file = document.getElementById('post_files').files[i];
var n = images_name(file.name);
var xhr = new XMLHttpRequest();
if (typeof xhr.upload=='object')
{
var upload = xhr.upload;
upload.addEventListener('progress', function(e,n)
{
alert('n = '+n);
}, false);
xhr.open('POST','upload.php');
xhr.setRequestHeader('Cache-Control','no-cache');
xhr.setRequestHeader('X-Requested-With','XMLHttpRequest');
xhr.setRequestHeader('X-File-Name',file.name);
xhr.send(file);
}
else {var ns = true;}
}
if (ns) {alert('Error: your browser does not support AJAX file uploads.');}
}
Yes, I know the context of what I'm testing (testing in Firefox 10, Chrome 17(?) and Opera 12).
Consider the following two snippets:
var i;
for (i = 0; i < 5; i += 1) {
setTimeout(function () {
// here, i will be the loop variable, and thus you'll get
// an ouput of 5, 5 times, because by the time this function will be
// called, the loop will be long finished
console.log(i);
}, 250);
}
for (i = 0; i < 5; i += 1) {
setTimeout(function (i) {
return function () {
// here, i will be the function parameter of the outer function
// which has been called immediately passing in a 'copy' of
// the loop variable.
// output will be: 0, 1, 2, 3, 4
console.log(i);
};
}(i), 250);
}
demo: http://jsfiddle.net/Hpxqq/
Also, this has nothing to do with recursion.
Regarding your code, you should change:
upload.addEventListener('progress', function(e,n)
{
alert('n = '+n);
}, false);
to:
upload.addEventListener('progress', function (n) {
return function(e) {
alert('n = ' + n);
};
}(n), false);
Javascript doesn't have block scope...
Try this :
function images_upload(e) {
var ns = false;
for (var i = 0; i < document.getElementById('post_files').files.length; i++) {
(function(i) {
var file = document.getElementById('post_files').files[i];
var n = images_name(file.name);
var xhr = new XMLHttpRequest();
if (typeof xhr.upload == 'object') {
var upload = xhr.upload;
upload.addEventListener('progress', function(e, n) {
alert('n = ' + n);
}, false);
xhr.open('POST', 'upload.php');
xhr.setRequestHeader('Cache-Control', 'no-cache');
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.setRequestHeader('X-File-Name', file.name);
xhr.send(file);
}
else {
ns = true;
}
})(i);
}
if (ns) {
alert('Error: your browser does not support AJAX file uploads.');
}
}
I want to make a function, like this.
For example:
function Logger() {
this.log = function(msg) {
console.log(msg);
}
}
And I want to use it in functions/modules etc, and that all works fine.
But the default console in my browser normally give the fileName + lineNumber.
Now when I abstract this functionality, the fileName and lineNumber is not where I put my instance.log(). Because it will say from where the console.log is being called, not the function itself.
So my question:
How can I get the correct information from where I want to use my logger?
Or give me, please, any tips to improve this functionality.
function Logger() {
this.log = console.log.bind(console);
}
I asked about this some time ago: Create shortcut to console.log() in Chrome.
Try using backtrace function like this one :
function printStackTrace() {
var callstack = [];
var isCallstackPopulated = false;
try {
i.dont.exist += 0; //doesn't exist- that's the point
} catch (e) {
if (e.stack) { //Firefox
var lines = e.stack.split('\n');
for (var i = 0, len = lines.length; i & lt; len; i++) {
if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
callstack.push(lines[i]);
}
}
//Remove call to printStackTrace()
callstack.shift();
isCallstackPopulated = true;
}
else if (window.opera & amp; & amp; e.message) { //Opera
var lines = e.message.split('\n');
for (var i = 0, len = lines.length; i & lt; len; i++) {
if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
var entry = lines[i];
//Append next line also since it has the file info
if (lines[i + 1]) {
entry += ' at ' + lines[i + 1];
i++;
}
callstack.push(entry);
}
}
//Remove call to printStackTrace()
callstack.shift();
isCallstackPopulated = true;
}
}
if (!isCallstackPopulated) { //IE and Safari
var currentFunction = arguments.callee.caller;
while (currentFunction) {
var fn = currentFunction.toString();
var fname = fn.substring(fn.indexOf( & amp; quot;
function & amp; quot;) + 8, fn.indexOf('')) || 'anonymous';
callstack.push(fname);
currentFunction = currentFunction.caller;
}
}
output(callstack);
}
function output(arr) {
//Optput however you want
alert(arr.join('\n\n'));
}
Try assigning the function:
(function () {
window.log = (console && console.log
? console.log
: function () {
// Alternative log
});
})();
Later just call log('Message') in your code.