This is to populate a table with the amount of results that are returned from the MediaWiki API query /api.php?action=query&list=querypage&qppage=BrokenRedirects. The number of results is then added to the id, for example:
// BrokenRedirects
$.getJSON('/api.php?action=query&list=querypage&qppage=BrokenRedirects&format=json', function (data) {
$('#BrokenRedirects').text(data.query.querypage.results.length);
});
But as it's being repeated another 7 times I made the arguments for qppage into an array and used a for loop to shorten overall code.
var array = ['BrokenRedirects',
'DoubleRedirects',
'Unusedcategories',
'Unusedimages',
'Wantedcategories',
'Wantedfiles',
'Wantedpages',
'Wantedtemplates'];
for (var i = 0; i < array.length; i++) {
$.getJSON('/api.php?action=query&list=querypage&qppage=' + array[i] + '&format=json', function (data) {
$('#' + array[i]).text(data.query.querypage.results.length);
});
}
The first, unlooped, version works. But when I added a loop it didn't. The $getJSON part executes, but it then fails to add the resultant data to the id. I ran it through JSLint which apart from complaining about functions in a loop and declaring var i with var array returned little help. I'm relatively inexperienced with javascript so thought perhaps a variable can't be used twice within a loop? Other than that, maybe something to do with using an id within a loop?
That's a classical problem : i has the value of end of loop when the callback is called.
You can fix it like this :
for (var i = 0; i < array.length; i++) {
(function(i) { // protects i in an immediately called function
$.getJSON('/api.php?action=query&list=querypage&qppage=' + array[i] + '&format=json', function (data) {
$('#' + array[i]).text(data.query.querypage.results.length);
});
})(i);
}
2018 addendum:
There's now another cleaner solution in today's browsers: use let instead of var:
for (let i = 0; i < array.length; i++) {
$.getJSON('/api.php?action=query&list=querypage&qppage=' + array[i] + '&format=json', function (data) {
$('#' + array[i]).text(data.query.querypage.results.length);
});
}
getJSON is an asynchronous ajax call
REFER: use synchronous ajax calls
Use Jquery $.each() to iterate over the array instead of a for loop.
For example:
$.each(array, function(_, value) {
var url = '/api.php?action=query&list=querypage&qppage=' + value + '&format=json';
$.getJSON(url, function (data) {
$('#' + value).text(data.query.querypage.results.length);
});
});
You should write a function like -
function callUrl(value)
{
$.getJSON('/api.php?action=query&list=querypage&qppage=' + value + '&format=json', function (data) {
$('#' + value).text(data.query.querypage.results.length);
});
}
and then call it with some timeout option like -
setTimeout('callUrl(+ array[i] +)',500); within the loop -
i.e.
for (var i = 0; i < array.length; i++) {
setTimeout('callUrl(+ array[i] +)',500);
}
Some delay for each call will be required here.
Related
I am newbie in javascript, I want to do looping normally use "for". I would like to duplicate this script about 10 ...
how loop this script?
function getComboMotif1() {
$.get("file.php?opt1=" + $("#id1"), function (data) {
$("#asd1").html(data);
});
}
The manual Loop script like this !!
function getww1() {
$.get("file.php?opt1=" + $("#id1"), function (data) {
$("#asd1").html(data);
});
}
function getww2() {
$.get("file.php?opt1=" + $("#id2"), function (data) {
$("#asd1").html(data);
});
}
function getww3() {
$.get("file.php?opt1=" + $("#id3"), function (data) {
$("#asd1").html(data);
});
} //and further
Something like that :
function getResource(which) {
$.get('file.php?opt1=' + $('#id' + which), function (data) {
$('#asd' + which).html(data);
}
}
for (var i = 0, max = 3; i < max; i += 1) {
getResource(i);
}
But your code contains a few oddities.
$('#id1') is a jquery object, so it can't be sent to the server as a string.
If you always replace the $('#asd1').html(data) in each callback, it will get overwritten each time you get an answer from the server. That's why I made it dynamic also.
If you need it to just run through a forloop, then start at 1 in order to accommodate your name and id.
for(var i = 1; i <= 10; ++i)
$.get("file.php?opt1=" + $("#id" + i), function (data) {
$("#asd1").html(data);
});
Now since get is asynchronous, each one will not wait for the other to complete.
If you need each function to be created, getww1 and such, then I recommend using eval to create those functions for you. But, that is very inefficient to do and should be avoided unless there is a specific requirement. Ex:
...
eval("(function () { return function "
+fname+" (){"+
codeAsString
+"};})()"));
...
That will return the newly created function.
I hope this helps to some degree.
I am using this piece of code to retrieve some JSONs from twitch:
for (var i = 0; i < streamList.length; i++) {
$.get(baseURL + streamList[i], getStreamInfo, "json");
}
where getStreamInfo is the callback function. I would like to know if it is possible to pass the value of "i" to the callback function somehow, along with the JSON.
Yes, you can pass the default parameter that receive the data of the ajax query and then add the i var.
for (var i = 0; i < streamList.length; i++) {
$.get(baseURL + streamList[i],
function(data) { getStreamInfo(data, i) },
"json");
}
Note that you need to receive it in getStreamInfo function
Hope it helps.
You can add any variables you want to the anonymous object. Be sure those variables are not used by the get function.
For exemple, I added the variable foo to the anonymous object and used it with this.foo in the callback function :
for (var i = 0; i < streamList.length; i++) {
$.get({
url: baseURL + streamList[i],
success: getStreamInfo,
dataType: "json",
foo:i
});
}
function getStreamInfo()
{
var i = this.foo;
}
You can use Closures.
for (var i = 0; i < streamList.length; i++) {
(function(index){
$.get(baseURL + streamList[i], function(data){
getStreamInfo(data, index);
}, "json");
})(i);
}
Note: Modify your function getStreamInfo to accept index.
Read How do JavaScript closures work?
browser.findElements(protractor.By.repeater('cat in cats')).then(function(rows) {
for (i = 0; i < rows.length; i++) { //which is always 3
var tmp = element(by.repeater('cat in cats').row(i)).element(by.binding('cat.name')).getText();
tmp.then(function(text) {
console.log('text is : ' + text);
console.log('iteration number is: ' + i);
if (text == 'someText') {
element(by.repeater('cat in cats').row(i)).element(by.binding('cat.action')).click();
}
});
}
In this case the value of 'i' inside the function is always returning 3.
I have get text and then check if the text is what I want and click on an element.
The value of 'i' in the 'if' statement is always returned as 3. Something to do with promises, but I am not sure.
Any help with modified code is much appreciated.
Thanks!
Don't call by.repeater() multiple times; instead, use map() and "chain" the promises like this:
element.all(By.repeater('cat in cats')).map(function(elm) {
return {
text: elm.element(by.binding('cat.name')).getText(),
action: elm.element(by.binding('cat.action'))
};
}).then(function(arr) {
for (var i = 0; i < arr.length; i++) {
if (arr[i].text == 'someText') {
return arr[i].action;
}
}
throw new Error('Text not found');
}).then(function(elm) {
elm.click();
});
All of the credits go to #Andres for the solution provided here:
Passing Protractor ElementFinder to deferred.fulfill() results in a promise containing a null value
Also see:
Protractor find element inside a repeater
I have a jquery/javascript function that creates an array to be placed in a form's hidden field. However, this is a nested form and so I need to invoke this function many times to populate the hidden field for all the children: test_suite_run[test_runs_attributes][//id][packages_id]. This means that I need to run this function with a different child id each time.
I have added //id to indicate the only differences between the many function calls. I do not know how to duplicate this function without copying it many times manually and replacing //id with the indexes 0...n, for each nested child instance. Could this somehow be done by passing parameters to the javascript function?
Sorry if this a little confusing, I will be happy to explain in more detail if needed.
JQuery Function
$(document).ready(function () {
arr = new Array();
$(document).on('change', 'select[id ^="s_package//id"]', function () {
var arr = $('select[id ^="s_package//id"]').map(function () {
return this.value
})
result = ""
for (j = 0; j < arr.length - 1; j++) {
result += (arr[j] + ", ");
}
result += (arr[arr.length - 1])
$("input[name='test_suite_run[test_runs_attributes][//id][packages_id]']").val(result);
});
});
You can pass an array of ids to use in your function and iterate them:
function somethingMeaningful(ids) {
for (var i = 0, l = ids.length; i < l; i++) {
var id = ids[i];
// do something with this id
}
}
$(function() {
somethingMeaningful(['id1', 'id2', 'idn']);
});
It might also be possible to simplify your selector and calculate the id at runtime, depending on their actual format:
$(document).on('change', 'select[id^="s_package"]', function () {
var id = $(this).attr('id').slice('s_package'.length);
// Do stuff with real id
});
I was reading this website for notes on closure: blog.morrisjohns.com/javascript_closures_for_dummies.html
It had the following example:
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
var item = 'item' + list[i];
result.push( function() {alert(item + ' ' + list[i])} );
}
return result;
}
function testList() {
var fnlist = buildList([1,2,3]);
// using j only to help prevent confusion - could use i
for (var j = 0; j < fnlist.length; j++) {
fnlist[j]();
}
}
Their notes (correctly) point out:
The line result.push( function()
{alert(item + ' ' + list[i])} adds a
reference to an anonymous function
three times to the result array. If
you are not so familiar with anonymous
functions think of it like:
pointer = function() {alert(item + ' ' + list[i])};
result.push(pointer);
Note that when you run the example,
"item3 undefined" is alerted three
times! This is because just like
previous examples, there is only one
closure for the local variables for
buildList. When the anonymous
functions are called on the line
fnlistj; they all use the same
single closure, and they use the
current value for i and item within
that one closure (where i has a value
of 3 because the loop had completed,
and item has a value of 'item3').
Is there a "correct" way to get the "expected" behavior in javascript?
Thanks!
result.push((function (jtem, j) {
return function() { alert(jtem + ' ' + list[j]); }
})(item, i));
You create an anonymous function which introduces a new variable scope. You immediately execute that function, passing your variables, which become the new variables jtem and j in a new scope, hence the "connection" to item and i is "broken". You then return the actual function, which now uses different variables (you could keep the names i and item, I just changed them for illustrative purposes).
Yes, you assign the value of i to a another variable so that variable can be part of the closure.
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
var item = 'item' + list[i];
result.push(function(cItem, index){
return function(){ alert(cItem + ' ' + list[index]); }
}(item, i));
}
return result;
}
The reason for this is that JavaScript does not have block scope, but a lexical scope that is capture by functions. To get around this problem, you create another function, as seen above, thereby capturing the variables into a new scope that will not be affected by the loop.