Solved

Can't show processing icon when displayed.

Posted on 2013-06-25
13
367 Views
Last Modified: 2013-07-05
I'm confused. I have the following javascript function, and the absolute DIV show beneath it. The lines of concern are 5, 8 and 19, and 25-27. The rest of the logic is OK, but included for context:

function setState(nme)
{
    var frm = document.zipForm;
    var state = nme.substring(3,nme.length);
    var img = document.getElementById("processing");
    var el = document.getElementsByTagName("input");

    img.style.display = "block";

    for (var i = 0; i < el.length; i++)
    {
        if ((el[i].type == "checkbox") && (el[i].name.indexOf("CT_" + state + "_") == 0))
        {
            el[i].checked = frm.elements[nme].checked;
            setCity(el[i].name);
        }
    }

    img.style.display = "none";
}

:
:

<div id="processing" style="z-index: 1; position: absolute; top: 600px; left: 500px; display: none">
<img style="width: 50px" src="images/processing.gif">
</div>

Open in new window


The image is just one of those animated wheels. You can see it here: https://www.ohprs.org/ohprs/images/processing.gif. When an onclick calls the setState function, I do not see the animation. However, if I put 'alert(img.style.display);' after line 8, it does show the image. But even then, as soon as I click 'OK' the image freezes and ceases to animate. It does disappear after line 19.

Even if I have just some text like "Please Wait" instead of the image, It doesn't show unless I have an alert() after setting the display style.

This must be a relatively common need. What am I doing wrong?
0
Comment
Question by:jmarkfoley
  • 6
  • 3
  • 2
  • +1
13 Comments
 
LVL 21

Expert Comment

by:Kim Walker
ID: 39275471
It appears that the image div is only displayed while lines 10-17 are processed. Since the input element stored in var "el" doesn't have a length property, the image would appear and disappear in an instant. Certainly faster than your eye can see. If you're trying to use the value of that input element, it would still need to have several million characters in its value in order for the image to remain visible long enough to see.

What is your objective?
0
 
LVL 21

Expert Comment

by:Kim Walker
ID: 39275490
OK. I see why "el" would have a length, it holds all input elements. I still believe the cycle would be completed faster than you would be able to see the image. Are you seeing the checkboxes being checked?
0
 
LVL 17

Expert Comment

by:jrm213jrm213
ID: 39275504
@xmediaman: "el" does have a length because it is an array. When "el" is populated it is an array of all the input elements on the page. So he is looping through the input elements to find a checkbox with a specific name? And he wants that processing image to display while that happens.

Since it does probably happen really fast anyway, I would use setTimeout to hide the image and put at least 1000ms as the time for the timeout.

I would try replacing line 19 with
setTimeout('document.getElementById("processing").style.display = "none"',1000);

Open in new window

0
 
LVL 1

Author Comment

by:jmarkfoley
ID: 39275526
I'll check out the timeout thing, but cycling through the el does not happen fast. There are over 1000 checkboxes to set/unset. It takes about 15 seconds.
0
 
LVL 17

Expert Comment

by:jrm213jrm213
ID: 39275556
well if the form is that big, when you run that function, is the image in the viewable area? 600 top might not be enough. Absolute positioning is not relative to the visible area.

Try making the image display: block; from the outset and make sure you can see it where you expect to see it.
0
 
LVL 38

Assisted Solution

by:Tom Beck
Tom Beck earned 500 total points
ID: 39275819
That set up is NEVER going to get you the results you want. Most browsers (Chrome is the exception) cannot do multi-threading. You cannot do a loop and display an animated gif at the same time. If the loop indeed takes 15 seconds, the gif animation will freeze while the loop is iterating. You're not seeing the gif at all because the img.style.display = "none" line runs before the loop is finished.

Your best bet is to meter in the check box checking using a timer thus allowing the gif to animate between each check box test. It will appear continuous because each checkbox check takes so little time.
function setState(nme)
{
    var frm = document.zipForm;
    var state = nme.substring(3,nme.length);
    var img = document.getElementById("processing");
    var el = document.getElementsByTagName("input");

	img.style.display = "block";

	var i = 0, busy = false;

	var process = setInterval(function() {
		if (!busy) {
			busy = true;
			checkInput(i, nme);
			if (i++ == el.length){
				clearInterval(process);
				img.style.display = "none";
			}
			busy = false;
		}
	},50);

}

function checkInput(num, nme) {
	if ((el[num].type == "checkbox") && (el[num].name.indexOf("CT_" + state + "_") == 0))
        {
            el[num].checked = frm.elements[nme].checked;
            setCity(el[num].name);
        }
}

Open in new window

Adjust the interval to a lower number to speed up the checkbox checking.

This is not thoroughly tested using your code, but works in my testing with hard numbers in place of your el.length value.
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 1

Author Comment

by:jmarkfoley
ID: 39283476
tommyBoy: Hmmm, I guess I thought animated gifs just "played" on their own. I didn't think the browser needed to run them. Anyway, I used your example as a base and got it to work as shown below. I didn't need the separate checkInput() function. Note that I don't need to show the spinning image if I am collapsing the tree. It visually collapses immediately, even if it is running through the branches and leaves behind the scene for another 15 seconds or so.

jrm213jrm213: > if the form is that big, when you run that function, is the image in the viewable area?

I'm working on that. What I'm trying to do is move the page down as the tree expands using the stateDiv.scrollTop = stateDiv.scrollHeight; statement, but it seems to do nothing at all. All web examples I've seen show this statement. I've verified that my <DIV></DIV> is properly formed and the stateDiv.scrollHeight does have a value -- but the page does not scroll. Any ideas? If this is not a simple question I'll post separately.

function setState(nme)  // nme of the form: ST_OH
{
    var frm = document.zipForm;
    var state = nme.substring(3,nme.length);  // gives: "OH"

    // Show if selected and hidden, or hide if unselected and visible

    if ((frm.elements[nme].checked && document.getElementById("I_" + state).src.indexOf("plus") > 0) ||
        (!frm.elements[nme].checked && document.getElementById("I_" + state).src.indexOf("plus") < 0))
        showByCity(state);

    var el = document.getElementsByTagName("input");

    var stateDiv = document.getElementById(state);     // get <DIV> of target state
    var img = document.getElementById("processing");  // spinning .gif
    var i = 0;

    if (frm.elements[nme].checked) img.style.display = "block";  // don't reveal if hiding cities

    var process = setInterval(function()
    {
        if ((el[i].type == "checkbox") && (el[i].name.indexOf("CT_" + state + "_") == 0)) // if city is in state ...
        {
            el[i].checked = frm.elements[nme].checked;  // set city checkbox to same as state's
            setCity(el[i].name);   // set city's ZIP code checkboxes to same as city's
            stateDiv.scrollTop = stateDiv.scrollHeight;  // attempt to scroll as page expands -- doesn't work
        }

        if (++i >= el.length)
        {
            clearInterval(process);
            img.style.display = "none";
        }
    }, 1);
}

Open in new window

0
 
LVL 38

Expert Comment

by:Tom Beck
ID: 39284120
An animated gif is a series of images that the browser displays one after another like a flip book. It requires processing from the browser's rendering engine.

If you want to move the window's scroll position, try scrollTo.

window.scrollTo(0, stateDiv.offsetTop);

I doubt you will see it scroll however. With a 1 millisecond interval, it will never have time to scroll the window before it moves to the next interval. As a test, try changing the interval to 1000 and you may see it scroll. Obviously this will make the process of testing the checkboxes take longer than necessary.

We really have not addressed the basic problem yet. We have managed to get the animated gif to display by using a timer to test the check boxes instead of a for loop. The three fold requirement now is insuring that the gif has time to animate, the current test is complete and the window has time to scroll before moving on to the next test. It's not happening with a 1 millisecond interval. But arbitrarily increasing it is just as bad from a programming standpoint as arbitrarily decreasing it. The whole script needs more work.
0
 
LVL 1

Accepted Solution

by:
jmarkfoley earned 0 total points
ID: 39286195
Actually, this all works just great! I had a feeling that "stateDiv.scrollTop = stateDiv.scrollHeight;" wouldn't work, even though all the google postings I found showed it that way. The DOM documentation, however, showed scrollTop as not being settable.

Your example of "window.scrollTo(0, stateDiv.offsetTop);" didn't quite work because it always scrolled to the top of the <div>. I changed it to:

window.scrollTo(0, (stateDiv.scrollHeight - initHeight) + stateDiv.offsetTop);

Where initHeight is initialized to stateDiv.scrollHeight. It needed the initHeight value because each State <div> contains one or more City <div>'s, so there is a significant initial height to the State <div>. If I don't subtract that bit from the scrollHeight the window is positioned to below the action and the user never really sees the div's expand. If I don't add in the offsetTop the user can see the div's expand, but the actual expansion point is well "below" the bottom of the window. When it is all done doing its reveals, the <Div> initial height is no longer vaild, so after my line 35 (below) I put:

window.scrollTo(0, stateDiv.scrollHeight);

to bring it to the bottom of the div. It works just find with 1ms time intervals. In fact, I don't really need the .gif animation since the expansion of the hidden div's provides plenty of visual feedback (although it's helpful at the beginning to let the user know to be patient).

I'm tempted to revert back to not using the setInterval() function to see if the div's expand OK without a thread interrupt, but I don't think I'll do that -- too much work at this point, plus this provides a nice example of how all this works. Final script results:

function setState(nme)  // nme is of the form: ST_OH
{
    var frm = document.zipForm;
    var state = nme.substring(3,nme.length);  // gives: "OH"

    // Show if selected and hidden, or hide if unselected and visible

    if ((frm.elements[nme].checked && document.getElementById("I_" + state).src.indexOf("plus") > 0) ||
        (!frm.elements[nme].checked && document.getElementById("I_" + state).src.indexOf("plus") < 0))
        showByCity(state);

    var el = document.getElementsByTagName("input");

    var stateDiv = document.getElementById(state);     // get <DIV> of target state
    var initHeight = stateDiv.scrollHeight;          // initial height of state div 
    var img = document.getElementById("processing");  // spinning .gif
    var i = 0;

    if (frm.elements[nme].checked) img.style.display = "block";  // don't reveal if hiding cities

    var process = setInterval(function()
    {
        if ((el[i].type == "checkbox") && (el[i].name.indexOf("CT_" + state + "_") == 0)) // if city is in state ...
        {
            el[i].checked = frm.elements[nme].checked;  // set city checkbox to same as state's
            setCity(el[i].name);   // set city's ZIP code checkboxes to same as city's
            window.scrollTo(0, (stateDiv.scrollHeight - initHeight) + stateDiv.offsetTop);
        }
        else if (stateFound && (el[i].type == "checkbox") && (el[i].name.indexOf("CT_") == 0))
            i = el.length + 1;  // we have progressed to then next state, terminate

        if (++i >= el.length)
        {
            clearInterval(process);
            img.style.display = "none";
            window.scrollTo(0, stateDiv.scrollHeight);
        }
    }, 1);
}

Open in new window


Last thoughts before I stick a fork in this one?
0
 
LVL 38

Expert Comment

by:Tom Beck
ID: 39286612
Last thoughts:

1.) If you revert back to a regular for loop, the browser's rendering engine will freeze while the loop is iterating. No scrolling, no animation, no images appearing one by one. Then it will suddenly jump to the bottom with all images showing up at once. The interval is there to make it appear that multiple processes are executing simultaneously. That's my prediction.

2.) It's great that it's working for you in your testing. The way I look at it however, the 1 millisecond is an arbitrary number with no bearing in time on the actual processes you are calling for each interval. Think about it. You say the checkbox checking takes 15 seconds to do over a thousand in a regular for loop. Let's say you have 1,200 checkboxes. That means 12.5 milliseconds to process each. You have the interval set to 1 millisecond so the looping is done and the interval cleared after 1,200 milliseconds, 1.2 seconds. See the discrepancy? 15 seconds is 15,000 milliseconds. What is happening for the other 13,800 milliseconds? Is the browser engine playing catch up? Does it have all those images and scrollTo commands backed up in memory waiting to barf them up...or CRASH!? There has to be a better way to write this so the interval keeps chugging away until each checkbox is actually processed instead of just stopping when the interval count equals the checkbox count. I guess I'm concerned about unpredictable results in a real world setting. What happens when it's run on a different browser or an older browser or a slower computer?
0
 
LVL 1

Author Comment

by:jmarkfoley
ID: 39286976
> If you revert back to a regular for loop, the browser's rendering engine will freeze while the loop is iterating. ... That's my prediction.

You're probably right. I'm not in a hurry to test this, but I probably will, just to satisfy my own curiosity. I'll post back the results if/when I do that.

> The way I look at it however, the 1 millisecond is an arbitrary number with no bearing in time on the actual processes you are calling for each interval. ...

Well, there are a little over 1,000 checkboxes to process in the largest state (OH). It sets a checkbox on each interval, so that's 1 millisecond per checkbox, not 1,000ms for *each* checkbox. In fact, the whole thing takes 50 seconds versus the original 15 seconds. That's acceptable because I know the user got impatient when nothing happened in the 15 seconds, but seeing boxes expand on the screen in live time is more reassuring, even though the total time is longer. At optimum, the time should be the original 15 seconds, plus the 1000ms for the intervals. The actual time reflects the browsers time to expand the divs, so I think even if having no interval worked, it probably wouldn't make much visual difference.

Probably the best way to handle this is using AJAX and show a progress bar zipping across in 15 seconds.
0
 
LVL 1

Author Comment

by:jmarkfoley
ID: 39288377
Well, I felt the need to try w/o the 1ms interval. I replaced the "var process = setInterval(function()" by the following:

   for (var i = 0; i < el.length; i++)
    {
        if ((el[i].type == "checkbox") && (el[i].name.indexOf("CT_" + state + "_") == 0))
        {
            stateFound = true;
            el[i].checked = frm.elements[nme].checked;
            setCity(el[i].name);
            window.scrollTo(0, (stateDiv.scrollHeight - initHeight) + stateDiv.offsetTop);
        }
        else if (stateFound && (el[i].type == "checkbox") && (el[i].name.indexOf("CT_") == 0))
            break;   // moved to next state, finished
    }

    img.style.display = "none";

Open in new window

This does appear to expand the hidden div's faster than the 1ms interval, but expansion visually stopped after 3-4 seconds (as you predicted). Any attempt to scroll or otherwise manipulate the page gave the status message, "mydom.org is not responding due to a long-running script." After a total of 43 seconds, control returned and the rest of the div's appeared in expanded form.

As I thought, actual exansion of the div's takes the most time: 43 seconds. This means the the 1ms interval only adds an additional 7 seconds to the 50 second total div expansion time. This is acceptable. The user will wait patiently for 50 seconds as long as something is happening on-screen. Without expanding the city div's on-screen, the user will not be patient for the actual 15 seconds it takes to check all boxes with no visual feedback.

I could probably save lots of time by just showing the spinning "processing" image and not expand the div's, which should only take 7s (interval) + 15s (checkbox time) = 22 secs. However it is unlikely in actual practice using this application that anyone will select and entire state at a time and it is otherwise nice for the user to see the expanded selection at the city/zip level.

Thanks for the setInterval() idea. That did the trick. I'm calling this one done.
0
 
LVL 1

Author Closing Comment

by:jmarkfoley
ID: 39301482
I list the complete solution in my post based on tommyboy's idea
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Suggested Solutions

This is a PowerShell web interface I use to manage some task as a network administrator. Clicking an action button on the left frame will display a form in the middle frame to input some data in textboxes, process this data in PowerShell and display…
Nothing in an HTTP request can be trusted, including HTTP headers and form data.  A form token is a tool that can be used to guard against request forgeries (CSRF).  This article shows an improved approach to form tokens, making it more difficult to…
In this Micro Tutorial viewers will learn how to create navigation buttons that change on rollover, using CSS (Continuation of the CSS Image Sprite tutorial) Create a parent ID for all the list items       - Specify position: absolute and display: block…
In this tutorial viewers will learn how to style a corner ribbon overlay for an image using CSS Create a new class by typing ".Ribbon":  Define the class' "display:" as "inline-block": Define its "position:" as "relative": Define its "overflow:" as …

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now