I am creating a property in a DOM element using javascript in a function which also uses setInterval to read back these values.
However I’m finding that the same elements which have had this property set from before, no longer have the value defined. This is rather peculiar and one possible explanation is that the interpreter thinks that no references to this property exist anymore (even though the anonymous function given to setInterval accesses it).
It is very difficult to explain so I will paste the entire code here along with some line numbers.
294 function toCamelCase(variable) {
295 return variable.replace(/-([a-z])/g,function(str,letter){return letter.toUpperCase();});
296 }
297 function parseRGBStr(rgb) {
298 return rgb.match(/rgba?\(([0-9]+),\s?([0-9]+),\s?([0-9]+)/).slice(-3);
299 }
300
301 // the goal for this function is to create a self contained timer-driven
302 // method for animating a fading color onto an arbitrary set of elements
303 // and some particular style property belonging to them.
304 // Furthermore, when multiple colors are to be set to the same property,
305 // their colors are to be blended. Colors specified in a [r,g,b] format
306 function createFadeEffect(elems, styleprop, colorBegin, duration) {
307 var startTime = (new Date).getTime(); // start the effect immediately
308 var endTime = startTime + duration;
309 function lerpColors(colA, colB, alpha) {
310 //alert("a: "+colA+" b: "+colB+' al: '+alpha);
311 if (alpha < 0) alpha = 0;
312 if (alpha > 1) alpha = 1;
313 return [(1.0-alpha)*colA[0] + alpha*colB[0],
314 (1.0-alpha)*colA[1] + alpha*colB[1],
315 (1.0-alpha)*colA[2] + alpha*colB[2]];
316 }
317 var fade_anim_values = [startTime,endTime,colorBegin];
318 for(var i=0;i<elems.length;i++) {
319 var f_a = elems[i].fade_anim;
320 if (f_a == undefined) elems[i].fade_anim = {};
321 f_a = elems[i].fade_anim;
322
323 if (f_a[styleprop] == undefined) {
324 f_a[styleprop] = {arr:[], def:parseRGBStr(document.defaultView.getComputedStyle(elems[i],'').getPropertyValue(styleprop ))};
325 //buffer.push([startTime,elems[i],f_a[styleprop].def]);
326 }
327 f_a[styleprop].arr.push(fade_anim_values); // store reference rather than making copies
328 }
329 var handle = setInterval(function () {
330 var nowTime = (new Date).getTime();
331 if (nowTime > endTime) {
332 // i'm expired. Clean myself up entirely from everything I've touched.
333 clearInterval(handle); // don't run me anymore
334 for (var i=0;i<elems.length;i++) {
335 var f_a = elems[i].fade_anim;
336 for (var j=0;j<f_a[styleprop].arr.length;j++) {
337 if (f_a[styleprop].arr[j] == fade_anim_values) { // that's me!
338 f_a[styleprop].arr.splice(j,1);
339 break;
340 }
341 }
342 if (f_a[styleprop].arr.length == 0) {
343 elems[i].style[toCamelCase(styleprop)] = '';
344 }
345 }
346 return;
347 }
348 for (var i=0;i<elems.length;i++) {
349 var f_a = elems[i].fade_anim;
350 if (f_a[styleprop].arr.length == 0) { alert('encountered empty styleprop array in handler'); continue; } // nothing to do here
351 // only require one interval-closure to process a particular element-styleprop combination.
352 if (f_a[styleprop].arr.length == 1 // i am the only interval attached to this element for this prop
353 || f_a[styleprop].arr[0] == fade_anim_values) // I am the zero-indexed handler
354 {
355 var colorValue = f_a[styleprop].def;
356 for (var j=0;j<f_a[styleprop].arr.length;j++) {
357 var data = f_a[styleprop].arr[j];
358 var alpha = (nowTime - data[0]) / (data[1]-data[0]);
359 var f_c = lerpColors(data[2],f_a[styleprop].def,alpha);
360 colorValue[0] += f_c[0];
361 colorValue[1] += f_c[1];
362 colorValue[2] += f_c[2];
363 }
364 // clamp-norm
365 var max = Math.max(colorValue[0],colorValue[1],colorValue[2]);
366 if (max > 255) {
367 colorValue[0] *= 1.0/max * 255;
368 colorValue[1] *= 1.0/max * 255;
369 colorValue[2] *= 1.0/max * 255;
370 }
371 var propstr = 'rgb('+Math.floor(colorValue[0])+','+Math.floor(colorValue[1])+','+Math.floor(colorValue[2])+')';
372 elems[i].style[toCamelCase(styleprop)] = propstr;
373 }
374 }
375 },30);
376 }
What’s going on here is that I pass an array of elements to the main function createFadeEffect. This function will create and save values to each specified element’s .fade_anim property. It appends to an array contained within. Then it spawns an interval with an anonymous function, which checks .fade_anim in each element. When multiple calls to createFadeEffect are called, and both specify the same elements and the same style properties, the intention is for the element’s .fade_anim.arr to accumulate the color values which are specified. The interval closures loop through this array to take all the colors into account when modifying the elements’ appropriate styles.
What is happening is that the block starting at line 323 is entered multiple times for certain elements. I have been unable to come up with an easy way to display for me which particular elements they are, when it happens, but I do know that the .def value is being set to intermediate values which are a result of the process itself. This should never happen because whenever the value is set, I never delete it myself.
Is this the GC screwing with me? I am looking for suggestions for ways to debug it better to track what’s going on with the .fade_anims.
I made a bit of progress. On line 355, I need to be making a deep-copy. What I did was screw with the stored value when I only meant to modify a temporary variable.
Also.. in response to negative feedback. Yes I did blame the system for misbehaving when it probably wasn’t. Can any programmer say that they have never done this? I guess I should rename the title.