I have a nested loop in a function which takes a while to load on IE8 and results in an unresponsive page.
I have a loading bar which 'freezes' when the script is running.
How can I use setInterval() to stop processing JS after each iteration to make it appear the loading bar is still moving and make it appear that the page is responsive?
The function is:
function createDropDown() {
var target = $('#mainList');
for (var i = 0; i < info.books.length; i++) {
var gnrval = info.books[i].genre
var catval = info.books[i].category
for (var j = 0; j < info.books[i].publishers.length; j++) {
var pubval = info.books[i].publishers[j].publisher
if (typeof app.cache.pub[pubval] == 'undefined') {
app.cache.pub[pubval] = {
'ul': $('<li class="publisher" data-value="' + pubval + '">' + pubval + '<ul class="sub-menu" data-title="Publishers"></ul></li>').appendTo(target).children('ul'),
'aut': {}
};
}
var ulauthors = app.cache.pub[pubval].ul;
for (var k = 0; k < info.books[i].publishers[j].authors.length; k++) {
var autval = info.books[i].publishers[j].authors[k].name + ' (' + gnrval + ')'
var aut_val = info.books[i].publishers[j].authors[k].name
if (typeof app.cache.pub[pubval].aut[autval] == 'undefined') {
app.cache.pub[pubval].aut[autval] = $('<li class="author" data-value="' + autval + '">' + autval + '<ul class="sub-menu" data-title="Authors"></ul></li>').appendTo(ulauthors).children('ul')
}
var ulyears = app.cache.pub[pubval].aut[autval]
console.log(ulyears)
var gItems = []
for (var m = 0; m < info.books[i].publishers[j].authors[k].yearsPublished.length; m++) {
var yearval = info.books[i].publishers[j].authors[k].yearsPublished[m]
var year = ulyears.find('.year[data-value="' + yearval + '"]')
if (year.size() == 0) {
var id = ++count
gItems.push('<li class="year" data-value="' + yearval + '"><a id="selyear' + id + '" class="addone" data-id="' + id + '" data-year="' + yearval + '" data-pub="' + pubval + '" data-aut="' + aut_val + '" data-cat="' + catval + '" data-gnr="' + gnrval + '">' + yearval + '</a></li>')
}
}
ulyears.append(gItems.join(''))
};
};
};
I tried adding:
setTimeout(function () {
//last nested loop code here
timeout();
}, 1000);
But obviously it didn't work.
You should start by breaking this gigantic function down. Simple tip: Principle of single responsibility.
Nesting loops squares the number of operations done. I suggest simplifying your data so that it can be done in one loop or a series of loops, and not nested loops. This would mean unnesting the data or structuring it in a way that you can simply do one pass.
A caveat is that the data will multiply in size, so it's a tradeoff between payload size and processing performance. Here's an example, where a case of locating the "geo" book would take several searches on the first data structure, but would only be a simple filter on the second data structure.
// So you loop through the properties of books, then another loop through math
// then another loop through science, then you get your "geo". Loops: 3
{
books : {
math : ['algebra','trigo','solids'],
science : ['bio','geo','psycho']
}
}
// Here, var geoBook = array.filter(function(book){return book.topic === 'geo'})[0];
// Loops: 1 (filter is essentially a loop)
[
{
type : 'book',
subject : 'math',
topic : 'algebra'
},{
type : 'book',
subject : 'math',
topic : 'trigo'
},{
type : 'book',
subject : 'math',
topic : 'solids'
},{
type : 'book',
subject : 'science',
topic : 'bio'
},{
type : 'book',
subject : 'science',
topic : 'geo'
},{
type : 'book',
subject : 'science',
topic : 'psycho'
},
]
To avoid freezing the browser, you need to "defer" operations using timers. You can use setInterval with a counter instead of loops. Here's a simple example:
function each(array,iterator){
var i = 0, length = array.length;
var timer = setInterval(function(){
iterator.call(array,array[i]);
if(++i >= length) clearInterval(timer);
},1000);
}
each([1,2,3,...10000],function(n){
console.log(n);
})
Related
Can anyone help me with this? All I need to do is prevent a duplicate from showing. I am populating an array and randomly generating the recipes. When you refresh the page, sometimes the same item appears twice. I need to prevent this from happening. I included a Fiddle at the bottom thanks.
Below is my code:
var recipe_data = [
{
"id":"11",
"recipeName":"Hummus",
"recipeCategory":"4",
"recipeImageCategoryURL":"http://www.slurrpy.com/wp-content/uploads/2012/08/roasted-eggplant-hummus-800x500.jpg"
},
{
"id":"12",
"recipeName":"Antipasto",
"recipeCategory":"4",
"recipeImageCategoryURL":"http://static.menutabapp.com/img/cache/800x500/2012/10/23/7857b394d50293d29443dc09eac76b3d.jpeg"
},
{
"id":"10",
"recipeName":"Zucchini",
"recipeCategory":"4",
"recipeImageCategoryURL":"https://paleofood.io/wp-content/uploads/2016/05/garlic-shrimp-zucchini-noodle-main-800x500.jpg"
}
]
var categoryItems = [];
$.each(recipe_data, function(i, item){
if (item.recipeCategory == "4") { categoryItems.push(item); }
});
var similarRecipe = '';
var randomRecipe = {};
for(var i = 0; i < categoryItems.length; i ++) {
randomRecipe = categoryItems[Math.floor(Math.random()*categoryItems.length)];
categoryItems.length = 2;
similarRecipe = [ '<div class="col-md-6 col-sm-6 img-margin">' + ' <div class="addthis_inline_share_toolbox" data-url="' + randomRecipe.recipePageURL +'" data-title="' + randomRecipe.recipeName + '"></div>'
+ '' + '<img class="img-responsive" src="' + randomRecipe.recipeImageCategoryURL + '">' + ''
+ '' + '<h3 class="recipeSubCategoryImgCaption">' + randomRecipe.recipeName + '</h3>' + '' + '</div>' ];
$('#recipeSimilar').append(similarRecipe);
}
Here is a fiddle: https://jsfiddle.net/wn4fmm5r/
After picking a random item, just remove it from the array so it's not picked again:
var randomIndex = Math.floor(Math.random()*categoryItems.length);
randomRecipe = categoryItems[randomIndex];
categoryItems.splice(randomIndex, 1);
Updated fiddle: https://jsfiddle.net/bLpqvs4f
May store the last recipe id in local storage, to prevent of showing it again (i suppose refreshing means reloading the page?)?
var showed=localStorage.getItem("stored")||[];//get the recipes already showed
var id;
while(!id||showed.find(el=>el===id)){//retry until generated a new one
id=Math.floor(Math.random()*categoryItems.length);//generate new one
}
showed.push(id);
localStorage.setItem("stored",showed);//store again
randomRecipe = categoryItems[id];//your new & random recipe
Unlike the other answers, this also work with browser refreshes...
I suppose in this case you want to take n different items from recipe_data?
In this case you should write a specified function to get the items you want
function getRandomItems(noOfItems, source){
var samples = source.slice();
var results = [];
for(var i=0; i < noOfItems;i++){
results = results.concat(samples.splice(Math.floor(Math.random() * samples.length), 1));
}
return results;
}
Some things to note here is to use .slice() to shallow copy an array, instead of running a for loop to add items, and when you want to pull items from an array .splice() is the function to choose.
See demo: https://jsfiddle.net/wn4fmm5r/3/
I want to show up the names of the members of an array "path" in my console.
console.log("start: ", path[0].name, "second: ", path[1].name, "third: ", path[2]name, ....)
But the problem is, that my array always changes it's size (clicking algorithm), that means sometimes it has the lenght 4 or sometimes 8 ect.
How can i adjust the console.log code to this dynamic array?
Thanks so much!
Try
path.forEach((each, i)=>{
console.log ("item" + i+ ':' + each.name );
})
Something like this:
var path = ['Prit', 'Bab', 'Nav']
var item = ["first","second", "third"];
for (i = 0; i < path.length;i++){
console.log(item[i] + ":" + path[i])
}
Try something like this for single line result set ...
var result = "";
for (var i = 0, len = path.length; i < len; i++) {
if (i !== 0) {
result += ", ";
}
result += (i + 1) + ": " + path[i].name;
}
console.log(result);
you could use a for loop here , ie,
for (var i=0;i<path.length;i++) {
console.log("item no "+ i +": " + path[i]);
}
/* Console Array */
var consoleArray = new Array;
/* Names */
var path = [
{name: 'bob'},
{name: 'jimmy'},
{name: 'chris'},
{name: 'alexander'},
{name: 'mark'}
];
/* Loop */
for(var i = 0; i < path.length; i++) {
consoleArray.push((i + 1) + ': ' + path[i].name);
}
/* Console Log */
console.log(consoleArray.join("\n"));
With ES6, you could use spread syntax ....
var path = [{ name: 'Jo'}, { name: 'John'}, { name: 'Jane'}];
console.log(...path.map((a, i) => (i ? i + 1 : 'Start') + ': ' + a.name));
The object array is built here in the first script..."order_items". I want to pass it into the second script so I can loop through values and build a pixel to render to my screen. I'm stuck trying to pass the array.
#for (int i = 0; i < OrderItemsReceived.Count; i++)
{
<script type="text/javascript" id="pepper" data-search="order_items">
var order_items = [{
'name':ProductName',
'sku': SKU',
'price': UnitPrice.ToString().Replace(",", "")',
'quantity': Quantity.ToString()'
}]
</script>
}
<script type="text/javascript">
var script_tag = document.getElementById('pepper');
var order_items = script_tag.getAttribute("data-search");
var order_id = #Model.OrderId;
var pixel_html = '';
var integration = 'DYNAMIC';
var program_id = 7302;
if (order_id && order_items) {
jQuery.each( order_items, function (i, order_item) {
pixel_html += '&' + 'ITEM_ID' + i + '=' + order_item.sku +
'&' + 'ITEM_PRICE' + i + '=' + order_item.price +
'&' + 'QUANTITY' + i + '=' + order_item.quantity;
});
if (pixel_html) {
pixel_html = '<iframe src="https://t.pikespeak.com/track?' +
'INT=' + integration +
'&' + 'PROGRAM_ID' + '=' + program_id +
'&' + 'ORDER_ID' + '=' + order_id +
pixel_html +
'" width="1" height="1" frameborder="0"></iframe>';
}
}
$('body').append(pixel_html);
</script>
this is how I solved the problem.
<script type="text/javascript" id="pepper" data-search="order_items">
alert("hello");
var order_items = #Html.Raw(Json.Encode(Model.OrderItemsReceived.OrderItemsReceived));
var order = [];
var order_listItems = [];
for (var i = 0; i < order_items.length; i++){
var orderList = order_items[i];
order = [{
'sku': orderList.SKU,
'price': orderList.UnitPrice,
'quantity': orderList.Quantity
}]
order_listItems.push(order);
}
I chose to go ahead with a json object because my array would be constructed of key, value pairs. Once I realized this, the scope of the variable was no longer the issue.
I'm using lightGallery and I'm using dynamic creation of galleries, this is the code to generate just one image:
$(this).lightGallery({
dynamic:true,
dynamicEl: [{
'src':'css/images/pictures/gal_'+id+'/1.jpg',
'thumb':'css/images/thumbnails/gal_'+id+'/1.jpg'
}]
});
This id variable is always the same, but I want to loop through a number which I take for example from variable x. So, if x=4 the code generated would look like this:
$(this).lightGallery({
dynamic:true,
dynamicEl: [{
'src':'css/images/pictures/gal_'+id+'/1.jpg', //here's 1
'thumb':'css/images/thumbnails/gal_'+id+'/1.jpg'
},{
'src':'css/images/pictures/gal_'+id+'/2.jpg', //here's 2 and so on
'thumb':'css/images/thumbnails/gal_'+id+'/2.jpg'
},{
'src':'css/images/pictures/gal_'+id+'/3.jpg',
'thumb':'css/images/thumbnails/gal_'+id+'/3.jpg'
},{
'src':'css/images/pictures/gal_'+id+'/4.jpg',
'thumb':'css/images/thumbnails/gal_'+id+'/4.jpg'
}]
});
So I guess the question is how to include a for loop inside an object, if that's even possible, thanks in advance!
No. It's not possible to have control structures(like loops) inside an object definition. You need to create your array of images first, like this:
var dynamicEl = [];
for (var i = 1; i <= 4; i++) {
dynamicEl.push({
'src':'css/images/pictures/gal_' + id + '/'+ i + '.jpg',
'thumb':'css/images/thumbnails/gal_' + id + '/' + i + '.jpg'
});
}
And then to pass it onto the object definition:
$(this).lightGallery({
dynamic:true,
dynamicEl: dynamicEl
});
first create a method to dynamically generate thumbs
function genThumbs(count, id)
{
var arr = [];
for ( var counter = 1; counter <= count; counter++)
{
arr.push( {
'src':'css/images/pictures/gal_'+id+'/' + counter + '.jpg',
'thumb':'css/images/thumbnails/gal_'+id+'/' + counter + '.jpg'
} );
}
return arr;
}
then use the same while calling the gallery
$(this).lightGallery({
dynamic:true,
dynamicEl: genThumbs(5, id)
});
Try this
var genEls = function(id, count)
{
var els = [];
for(i = 1; i <= count; i++)
{
els.push({
'src':'css/images/pictures/gal_'+ id + '/' + i + '.jpg',
'thumb':'css/images/thumbnails/gal_' + id + '/' + i + '.jpg',
});
}
return els;
}
var id = 3;
var count = 4;
$(this).lightGallery({
dynamic:true,
dynamicEl: genEls(id,count);
});
This is as inline as it can get ;)
Hope this helps ...
I'm trying to build a simple site that will check and print out "Buy It Now Prices" for cars. I can't get the JavaScript push function to print out anything but strings.
The eBay API says that buyItNowPrice returns an Amount.
I have experimented with the other Item functions, and the only ones that are working for me are ones that return a String.
The question is, how should the line var itemPrice = item.buyItNowPrice; be formatted to output a number?
function _cb_findItemsByKeywords(root) {
var items = root.findItemsByKeywordsResponse[0].searchResult[0].item || [];
var html = [];
html.push('<table width="100%" border="0" cellspacing="0" cellpadding="3"><tbody>');
for (var i = 0; i < items.length; ++i) {
var item = items[i];
var title = item.title;
var pic = item.galleryURL;
var viewitem = item.viewItemURL;
var itemPrice = item.buyItNowPrice;
var timeLeft = item.watchCount;
if (title != null && null != viewitem) {
html.push('<tr><td>' + '<img src="' + pic + '" border="1">' + '</td>' +
'<td><a href="' + viewitem + '" target="_blank">' +
title + '</a>' // end hyperlink
+
'<br>Item Price: ' + itemPrice +
'<br>Time Remaining: ' + timeLeft +
'</td></tr>');
}
}
html.push('</tbody></table>');
document.getElementById("results").innerHTML = html.join("");
}
// Create a JavaScript array of the item filters you want to use in your request
var filterarray = [{
"name": "MaxPrice",
"value": "250000",
"paramName": "Currency",
"paramValue": "USD"
},
{
"name": "MinPrice",
"value": "15000",
"paramName": "Currency",
"paramValue": "USD"
},
//{"name":"FreeShippingOnly", "value":"false", "paramName":"", "paramValue":""},
{
"name": "ListingType",
"value": ["AuctionWithBIN", "FixedPrice", /*"StoreInventory"*/ ],
"paramName": "",
"paramValue": ""
},
];
// Generates an indexed URL snippet from the array of item filters
var urlfilter = "";
function buildURLArray() {
for (var i = 0; i < filterarray.length; i++) {
var itemfilter = filterarray[i];
for (var index in itemfilter) {
// Check to see if the paramter has a value (some don't)
if (itemfilter[index] !== "") {
if (itemfilter[index] instanceof Array) {
for (var r = 0; r < itemfilter[index].length; r++) {
var value = itemfilter[index][r];
urlfilter += "&itemFilter\(" + i + "\)." + index + "\(" + r + "\)=" + value;
}
} else {
urlfilter += "&itemFilter\(" + i + "\)." + index + "=" + itemfilter[index];
}
}
}
}
}
buildURLArray(filterarray);
// Construct the request
var url = "http://svcs.ebay.com/services/search/FindingService/v1";
url += "?OPERATION-NAME=findItemsByKeywords";
url += "&SERVICE-VERSION=1.0.0";
url += "&SECURITY-APPNAME=REDACTED";
url += "&GLOBAL-ID=EBAY-MOTOR";
url += "&RESPONSE-DATA-FORMAT=JSON";
url += "&callback=_cb_findItemsByKeywords";
url += "&REST-PAYLOAD";
//url += "&categoryId=6001";
url += "&keywords=Ferrari 575";
url += "&paginationInput.entriesPerPage=12";
url += urlfilter;
// Submit the request
s = document.createElement('script'); // create script element
s.src = url;
document.body.appendChild(s);
You are reading the wrong eBay documentation. FindItemsByKeywords is part of the Finding API service. The buyItNowPrice field is found in the item.listingInfo field. Changing the code to the following will output the price.
var itemPrice = '---';
// buyItNowPrice may not be returned for all results.
if(item.listingInfo[0].buyItNowPrice) {
itemPrice = item.listingInfo[0].buyItNowPrice[0]['#currencyId'] + ' ' + item.listingInfo[0].buyItNowPrice[0].__value__;
} else if(item.sellingStatus[0].currentPrice) {
itemPrice = item.sellingStatus[0].currentPrice[0]['#currencyId'] + ' ' + item.sellingStatus[0].currentPrice[0].__value__;
}