for loop execute slow for large data - javascript

I have a for loop that take too long to execute large amount of data:
for (var itm = 0; itm < itmCount; itm++) {
var curObj = $('[aria-describedby=' + gridName + '_' + columnNames[itm].name + ']');
var thisCell = $('#' + gridName + '_' + columnNames[itm].name + ' div');
$('#widthTest').html(thisCell.text()).css({
'font-family': thisCell.css('font-family'),
'font-size': thisCell.css('font-size'),
'font-weight': thisCell.css('font-weight')
});
var maxWidth = Width = $('#widthTest').elementRealWidth() + 17;
var itm2Count = curObj.length;
// Loop through Rows
for (var itm2 = 0; itm2 < itm2Count; itm2++) {
var thisCell = $(curObj[itm2]);
$('#widthTest').html(thisCell.html()).css({
'font-family': thisCell.css('font-family'),
'font-size': thisCell.css('font-size'),
'font-weight': thisCell.css('font-weight')
});
thisWidth = $('#widthTest').elementRealWidth();
if (thisWidth > maxWidth) {maxWidth = thisWidth+10;}
}
$('#' + gridName + ' .jqgfirstrow td:eq(' + itm + '), #' + gridName + '_' + columnNames[itm].name).width(maxWidth).css('min-width', maxWidth+17);
$('#' + gridName + ' .jqgfirstrow td:eq(' + 0 + '), #' + gridName + '_' + columnNames[0].name).width('30').css('min-width', '30px');
I get this issue from firefox:
A script on this page may be busy, or it may have stopped responding. You can stop the script now, open the script in the debugger, or let the script continue.
and the Chrome kills the page. Any idea?
UPDATE:
Here is my code after doing chunk:
var itmCount = columnNames.length;
var numOfElements = itmCount;
var elementsPerChunk = 50;
var numOfChunks = numOfElements / elementsPerChunk; //divide it into chunks
for (var x = 0; x < numOfChunks; x++) {
setTimeout(function() {
for (var y = 0; y < elementsPerChunk; y++) {
var curObj = $('[aria-describedby=' + gridName + '_' + columnNames[elementsPerChunk].name + ']');
var thisCell = $('#' + gridName + '_' + columnNames[elementsPerChunk].name + ' div');
$('#widthTest').html(thisCell.text()).css({
'font-family': thisCell.css('font-family'),
'font-size': thisCell.css('font-size'),
'font-weight': thisCell.css('font-weight')
});
var maxWidth = Width = $('#widthTest').elementRealWidth() + 17;
var itm2Count = curObj.length;
// Loop through Rows
for (var itm2 = 0; itm2 < itm2Count; itm2++) {
var thisCell = $(curObj[itm2]);
$('#widthTest').html(thisCell.html()).css({
'font-family': thisCell.css('font-family'),
'font-size': thisCell.css('font-size'),
'font-weight': thisCell.css('font-weight')
});
thisWidth = $('#widthTest').elementRealWidth();
if (thisWidth > maxWidth) {maxWidth = thisWidth+10;}
}
$('#' + gridName + ' .jqgfirstrow td:eq(' + elementsPerChunk + '), #' + gridName + '_' + columnNames[elementsPerChunk].name).width(maxWidth).css('min-width', maxWidth+17);
$('#' + gridName + ' .jqgfirstrow td:eq(' + 0 + '), #' + gridName + '_' + columnNames[0].name).width('30').css('min-width', '30px');
//grid.setRowData ( iids[itm], false, {height: 30} );
}
}, 0);
}

Try to keep in mind that JavaScript is entirely browser sided. Each browser will respond differently when it thinks your code times out. Furthermore, you can't bypass these errors. A great example would be chromes option to "stop this website from displaying anymore pop-ups." These features are added for the convenience of the end user and usually fix security holes or inform the user that the website is simply taking a while (which most users don't like)
One idea is to find a way to split up the amount of data you process. it seems like the issue, as you stated, is with large amounts of data. Is there a way to split the data up into "pages" and process, say, 50 items at a time?
If you can create stop points while it's updating that would work as well. The browser locks up while JavaScript runs which is a big part of the problem.
Finally, consider processing data on the server side and sending/receiving it with Ajax. This will let the browser/user work while your code is processed elsewhere and only stops to receive new data.
EDIT:
To address your comment:
Using math you could use nested for-loops to split the processing load into chunks of 50:
var numOfElements = /*how ever you count the records*/;
var elementsPerChunk = 50;
var numOfChunks = numOfElements / elementsPerChunk; //divide it into chunks
for (x = 0; x < numOfChunks; x++) {
//Set Time out
for (y = 0; y < elementsPerChunk; y++) {
//Rest of code
}
}
Note:
The above isn't perfect, for instance, you have to run the loop 1 more time to account for any sets of records that is not evenly divisible by 50 but you do not want to loop again if it is divisible by 50 (probably us mod operator to determine if there is a remainder and then add 1 if there is).

Related

Can't change css style with JS

<script>
var animate = function (element, target, callback) {
clearInterval(element.timer);
element.timer = setInterval(function () {
var step = (target - element.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
element.style.left = element.offsetLeft + step + 'px';
// console.log(element.offsetLeft);
if (element.offsetLeft == target) {
clearInterval(element.timer);
callback && callback();
}
}, 20)
};
window.addEventListener('load', function () {
var ul = document.querySelector('.local-nav');
console.log(ul);
for (var i = 0; i < ul.children.length; i++) {
ul.children[i].children[0].children[0].style.backgroundPosition = '0 ' + -i * 32 + 'px';
}
var ul2 = document.querySelector('.subnav-entry ul');
console.log(ul2);
for (var i = 0; i < ul2.children.length; i++) {
console.log(ul2.children[i].children[0].children[0]);
ul2.children[i].children[0].children[0].style.backgroundPosition = '0 ' + -i * 32 + 'px';
}
var focus_ul = document.querySelector('.focus ul');
var ol = document.querySelector('.focus ol');
var count = 0;
var focus_timer = setInterval(function () {
if (count == 2) {
animate(focus_ul, focus_ul.offsetLeft - 375);
// console.log('i run');
// console.log(focus_ul.offsetLeft + ' ' + count);
// focus_ul.style.left = focus_ul.offsetLeft + 375 * 2 + 'px';
focus_ul.style.left = 0 + 'px'; // Here is my problem
console.log(focus_ul);
console.log(focus_ul.offsetLeft + ' ' + count);
}
else {
console.log('before animation ' + focus_ul.offsetLeft + ' ' + count);
animate(focus_ul, focus_ul.offsetLeft - 375);
console.log(focus_ul.offsetLeft + ' ' + count);
}
// focus_ul.style.left = focus_ul.offsetLeft + 375 + 'px';
count++;
count = count % 3;
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
ol.children[count].className = 'current-choice';
console.log('after a round ' + focus_ul.offsetLeft + ' ' + count);
}, 2500)
})
</script>
I meant to do a photo show that can play automaticly, like a Carousel or swiper, The 375px means that the screen is 375px wide, according to the code, when comming to the last photo, as the animation end, it should change back to the initial one which style.left == 0px, but 0px only occur a small while, next time it would be -1125px, -1500px and so on, I'm confused why i set style.left to 0 but fruitless. I'm new in front end ,thanks helping
You can use more of CSS (and less JavaScript) for this animation.
Declare two separate CSS class definitions for focus_ul. Use JavaScript to change the className of focus_ul.
Use CSS transition for animation of CSS offset-left property.
See: https://www.w3schools.com/css/tryit.asp?filename=trycss3_transition1
Write that custom className instead :hover in above example.

"for" in wrong loop .js

I'm having some problem with this function. I ask sorry if the code looks a bit confused.
Basically what i'm trying to do is to divide some elements between the top and bottom of the documents having between them the same space circa.
i wrote this code but seems like the first "for" cycle goes loop ever and ever again.
** update**
so i improved the code and now the problem seems to be the variable "ifc"(if-counter). It suppose to increase by one every time that one of the if or else if is completed , but as you can see on the console it always remain 0 .
anyone can help?
thanks
var sizewidth = window.innerWidth;
var sizeheight = 3000;
function initialPosition(){
var els = document.querySelectorAll('.ico');
var tb = Math.round(els.length * 0.4);
console.log(tb);
var lr = els.length - tb;
var distPixEachTopBottom = sizewidth / (tb/2);
console.log(distPixEachTopBottom);
var distPixEachLefRight = sizeheight / (lr/2);
var nextTop = 0;
var nextLeft = 0;
var nextBottom = 0;
var nextRight = 0 ;
for (i = 0; i < els.length; i++) {
var ifc = 0;
console.log('i' + i);
if(i < tb){
var sf = 0;
for(;;){
console.log('sf' + sf + 'tb ' + tb);
if(ifc > 1){
console.log('ifc'+ ifc);
ifc = 0;
break
}else if(ifc == 0){
els[i].style.top = 0 +'px';
els[i].style.left = nextTop + 'px';
console.log('ifc ' + ifc + 'i ' + i + 'nexttop' + nextTop)
nextTop = nextTop + distPixEachTopBottom;
ifc++;
break;
}else if(ifc == 1){
els[i].style.bottom = 0 +'px';
els[i].style.right = nextBottom + 'px';
console.log('ifc ' + ifc + 'i ' + i + 'nextbottom' + nextBottom)
nextBottom = nextBottom + distPixEachTopBottom;
ifc++;
break;
}else{
console.log('else if problem')
break;
}
}
}else{
}
}
initialPosition();
update
i created a pen as you asked
codepen
So basically of all the boxes the 40% of them (6 in this case) should be divided between top and bottom but they go just on top.
So I solved by myself .
I put the var ifc before then any function .
Anyway thanks everyone

Javascript event not defined in Firefox

This has been asked before but I'm still struggling to wrap my head around how to fix the error in my case. I'm new to learning Javascript/jQuery. Firefox gives an error "ReferenceError: getFirstArr is not defined". I have a simplified script of what I'm trying to do here JSFiddle (to make it work, select a year button first before a month button).
The culprit seems to be the getFirstArr(videos[i]) line 28. I really don't even know what to try since my code seems correct. It works in Safari, Chrome and IE. Firefox is the odd man out. Here's a snippet of the on click event where the problem is.
$('.campbutton').on('click', function () {
camp = $(this).attr('id');
$('.campbutton').removeClass('green');
$(this).addClass('green');
$('#searcharea').html('<table></table>');
var campyear = camp + year;
var count = 1;
var noResultCount = 0;
for (i = 0; i < videos.length; i++) {
for (j = 0; j < 5; j++) {
getFirstArr(videos[i]); // Firefox doesn't like this line
function getFirstArr(video) { // prints the the array where a match is found
The JSFiddle will have the whole code. So my question is, why is Firefox not accepting the function call, and what needs to be changed? Any help or hints are appreciated (btw, I'm still working on getting the correct table tags to format the output correctly so the videos don't just stack on top of themselves).
Edit: The specific problem Firefox has is when the camp button is clicked, no videos load in the div. The other button events are fine.
Here's the entire code in question:
var videos = [ ["string1A", "string1B", "string1C"], ["string2A", "String2B", String2C"] ];
var camp = "";
var year = "";
$('#searcharea').html('select a year button first');
$('.yearbutton').on('click', function () {
year = $(this).attr('id');
$('.yearbutton').removeClass('green');
$(this).addClass('green');
});
$('.campbutton').on('click', function () {
camp = $(this).attr('id');
$('.campbutton').removeClass('green');
$(this).addClass('green');
$('#searcharea').html('<table></table>');
var campyear = camp + year;
var count = 1;
var noResultCount = 0;
for (i = 0; i < videos.length; i++) {
for (j = 0; j < 5; j++) {
getFirstArr(videos[i]);
function getFirstArr(video) {
if (campyear === video[j]) {
var pos = video.indexOf(video[j]);
$('#searcharea').append('<tr><td>' + video[(pos - pos)] + '</td>' + '<td>' + 'Composer: ' + video[(pos -pos) + 1] + '<br>' + 'Player: ' + video[(pos - pos) + 2] + '<br>' + 'Piece: ' + video[(pos - pos) + 3] + '</td>');
}
else noResultCount++;
if (campyear === video[j] && count % 3 === 0 && j === 4)
$('#searcharea').append('</tr><tr>');
if (i === videos.lenght && j === 4)
$('#searcharea').append('</table>');
}
}
count++;
}
if (noResultCount === videos.length * 5)
$('#searcharea').html("No results found");
});
http://jsfiddle.net/3ncc5xdx/123/
Here i have moved your function to outside the loop like so, I think it works, unless I misunderstood what the issue is:
$('.campbutton').on('click', function () {
camp = $(this).attr('id');
$('.campbutton').removeClass('green');
$(this).addClass('green');
$('#searcharea').html('<table></table>');
var campyear = camp + year;
var count = 1;
var noResultCount = 0;
function getFirstArr(video) {
if (campyear === video[j]) {
var pos = video.indexOf(video[j]);
$('#searcharea').append('<tr><td>' + video[(pos - pos)] + '</td>' + '<td>' + 'Composer: ' + video[(pos -pos) + 1] + '<br>' + 'Player: ' + video[(pos - pos) + 2] + '<br>' + 'Piece: ' + video[(pos - pos) + 3] + '</td>');
}
else noResultCount++;
if (campyear === video[j] && count % 3 === 0 && j === 4)
$('#searcharea').append('</tr><tr>');
if (i === videos.lenght && j === 4)
$('#searcharea').append('</table>');
}
for (i = 0; i < videos.length; i++) {
for (j = 0; j < 5; j++) {
getFirstArr(videos[i]);
}
count++;
}
if (noResultCount === videos.length * 5)
$('#searcharea').html("No results found");
});
So the reason that it works is that the function is now declared before it is used once. Also, its now only declared once rather than again and again in your loop. It probably works in Chrome because Chrome is pretty smart and figuring our what you were implying – but Firefox will need a slightly more strict approach.

In bootstrap modal width is always 0

This is a function in the jqgrid in the load complete.
I have a bootstrap modal that is opened by clicking a button. In that modal I have a jqgrid. When I call a function in load complete, in that function, width of appending span is always 0. You can see the function below:
function columnresize(id) {
$(this).parent().append('<span id="widthTest" />');
gridName = this.id;
$('#gbox_' + gridName + ' .ui-jqgrid-htable' + gridName).css('width', 'inherit');
$('#' + gridName).parent().css('width', 'inherit');
var columnNames = $("#" + gridName).jqGrid('getGridParam', 'colModel');
var thisWidth;
var itmCount = columnNames.length;
/*var grid = $('#' + gridName);
var iids = grid.getDataIDs();*/
// Loop through Cols
for (var itm = 0; itm < itmCount; itm++) {
var curObj = $('[aria-describedby=' + gridName + '_' + columnNames[itm].name + ']');
var thisCell = $('#' + gridName + '_' + columnNames[itm].name + ' div');
$('#widthTest').html(thisCell.text()).css({
'font-family': thisCell.css('font-family'),
'font-size': thisCell.css('font-size'),
'width': thisCell.css('width'),
'font-weight': thisCell.css('font-weight')
});
var maxWidth = Width = $('#widthTest').width() + 17;
//var maxWidth = 0;
var itm2Count = curObj.length;
// Loop through Rows
for (var itm2 = 0; itm2 < itm2Count; itm2++) {
var thisCell = $(curObj[itm2]);
$('#widthTest').html(thisCell.html()).css({
'font-family': thisCell.css('font-family'),
'font-size': thisCell.css('font-size'),
'font-weight': thisCell.css('font-weight')
});
thisWidth = $('#widthTest').width();
if (thisWidth > maxWidth) {maxWidth = thisWidth+10;}
}
$('#' + gridName + ' .jqgfirstrow td:eq(' + itm + '), #' + gridName + '_' + columnNames[itm].name).width(maxWidth).css('min-width', maxWidth+17);
$('#' + gridName + ' .jqgfirstrow td:eq(' + 0 + '), #' + gridName + '_' + columnNames[0].name).width('30').css('min-width', '30px');
//grid.setRowData ( iids[itm], false, {height: 30} );
}
$('#widthTest').remove();
}
I call the above function in the load complete of jqgrid like this:
loadComplete: function() {
columnresize.call(this, 'Table');
}
The width $('#widthTest').width() in line:
var maxWidth = Width = $('#widthTest').width() + 17;
is always 0!
Any idea?
after a lot of research I found that because it is a hidden dom element, I have to use this
jquery to get the actual size of hidden dom element.
Thanks to #Justinas.

adding two variables together

I have been trying for... about 4 hours now lmao.
currentCalc returns 50
currentSum returns 0 when i alert them. Yet I cannot add them together with parseInt????
what am i doing wrong :'(
var identRow = $('tr.identRow');
identRow.each(function () {
var getIdentClass = $(this).attr('class').split(' ').slice(1);
$('tr.ohp' + getIdentClass + ' td.EURm').each(function (index) {
var currentCalc = parseInt($(this).text().replace('.', ''), 10);
var currentSum = $('tr.' + getIdentClass + ' td.totalEURm', this).text().replace('.', '');
total = parseInt(currentCalc, 10) + parseInt(currentSum, 10);
$('tr.' + getIdentClass + ' td.totalEURm').text(total);
if (index == 6) {
alert(total);
}
});
});
EDIT:
Oh goodness. Im completely confused now. I putr the break there. It says total = 50.
I want each iteration to add itself to the total. That is why I add currentCalc to the text of the field im plopping the currentCalc into.
$('tr.' + getIdentClass + ' td.totalEURm').text(total);
with my code now like this:
var identRow = $('tr.identRow');
identRow.each(function () {
var getIdentClass = $(this).attr('class').split(' ').slice(1);
$('tr.ohp' + getIdentClass + ' td.EURm').each(
function (index) {
var currentCalc = parseInt($(this).text().replace('.', ''), 10) || 0;
var currentSum = parseInt($('tr.' + getIdentClass + ' td.totalEURm', this).text().replace('.', ''), 10) || 0;
var total = currentCalc + currentSum;
$('tr.' + getIdentClass + ' td.totalEURm').text(total);
if (index === 6) {
alert(total);
}
});
});
it alerts: 50, then 0, then 50, then 0.
EDIT:
How do I add currentCalc to its last value?
So first iteration its 10, seconds its 20. How do i make it so on the 2nd iteration it equals 30. currentCalc++ is just adding 1 to it.
Now you understand how crap i am :)
I am no expert in JS, but I saw that currentCalc is already an int:
var currentCalc = parseInt($(this).text().replace('.',''), 10);
//...
total = parseInt(currentCalc, 10) + parseInt(currentSum, 10);
so probably the parseInt on an int instead that on a string fails (?)
If you get two alerts, that likely means either your outer or inner .each statements is matching two entries.
If you're using firebug, use console.debug(total); instead of alert(). I recommend using console.debug(this) at some point to make sure it has what you think it has, too. Put it above the alert(). That information would be useful to see.
I do some code formatting and cleanup, try this:
var identRow = $('tr.identRow');
identRow.each(function () {
var getIdentClass = $(this).attr('class').split(' ').slice(1);
$('tr.ohp' + getIdentClass + ' td.EURm').each(
function (index) {
var currentCalc = parseInt($(this).text().replace('.', ''), 10) || 0;
var currentSum = parseInt($('tr.' + getIdentClass + ' td.totalEURm', this).text().replace('.', ''), 10) || 0;
var total = currentCalc + currentSum;
$('tr.' + getIdentClass + ' td.totalEURm').text(total);
if (index === 6) {
alert(total);
}
});
});
I added condition if parseInt fails the vars currentCalc and currentSum will be 0.
Also, like in answer above i'm avoiding double parseInt
Can you give an example html page to try out?
//SUM OF COLUMNS
var total = 0;
var identRow = $('tr.identRow');
identRow.each(function () {
var getIdentClass = $(this).attr('class').split(' ').slice(1);
$('tr.ohp' + getIdentClass + ' td.EURm').each(
function (index) {
var currentCalc = $(this).text().replace('.', '');
total = parseInt(currentCalc)+total;
$('tr.' + getIdentClass + ' td.totalEURm').text(total);
});
});
did the trick.
Now i just gotta set the total to 0 when it gets to the second category because at the moment it keeps adding from where it left off. Progress though. Thanks for everything

Categories