I have a javascript function which creates an image dynamically, then shows that image either in a div or on a separate page.
Since the image creation takes a few seconds I'd like to display a simple 'please wait' message so that the user knows something is happening, and I am doing this by replacing the contents of an initially empty div with some text.
$('#chartImage').html('Please wait...');
This works fine on it's own - the div contents are displayed immediately when the triggering link is clicked. The problem is when I add the actual image creation code after it, the 'please wait' does not show up until after the image creation is complete, and only for a split second, so it's kinda pointless.
Is there something like a flush method to make sure all actions have taken effect before continuing?
Alternatively if there is a better way to do this I'm happy to hear it...
-------------EDIT-----------------
Here is a more complete description of what I'm doing, using open flash chart to make an image:
function createChartImage(chartid)
{
$('#chartImageDiv'+chartid).html('Please wait...');
ofc = findSWF("ofc_chart_"+chartid);
x = ofc.post_image( getUploadChartURL(chartid), 'doneChartUpload', false );
setTimeout("",1000); //allow some time for upload to complete
window.location.href = getViewChartImageURL(chartid);
}
The 'please wait' appears to take effect immediately before the final line. If I change the div's contents again at that point, the 'please wait' never shows at all.
(According to the docs for the charts I'm using, the function 'doneChartUpload' should be called when the upload is complete which would remove the need for the setTimeout, but I spent a long time trying to get it working, even copying code verbatim from the page below, to no avail.)
http://teethgrinder.co.uk/open-flash-chart-2/adv-upload-image.php
From assessing your code i would definitely split it up:
function generateChartImageProcess(chartid){
displayLoader();
createChart(chartid);
}
function createChart(chartID){
ofc = findSWF("ofc_chart_"+chartid);
x = ofc.post_image( getUploadChartURL(chartid), 'doneChartUpload', false );
setTimeout("",1000); //these two lines here to be replaced by your chart generations complete process
window.location.href = getViewChartImageURL(chartid);
}
function displayLoader(){
$('#chartImageDiv'+chartid).html('Please wait...');
}
function chartGeneratedComplete(arguments){
//TODO
}
This happens because the image isn't loaded yet, and it takes some time for the browser to load it before it can be displayed.
You could try using the jquery.load() method to add a callback for the event of the image being loaded like this, (supposing the div containing the image has the id #imageContainer):
$('#imageContainer img').load(function() {
// Remove the Please wait
});
BlockUI is a good tool to use for these situations: http://jquery.malsup.com/block/. In both cases (using BlockUI or rolling your own), trigger the "waiting" code first, run your image generator, and then remove the waiting effect after the image is loaded.
Related
I have a Bootstrap modal on my page. Basically, what happens is the user picks some options on the page, clicks a go button and that modal pops up and gets populated with the live output of the job they started.
After the job runs, I'd like for the user to be able to close the modal, choose more options, and run the job again. The problem is, I can't seem to get rid of the output from the previous job.
I tried this answer, which was to clone the div and use replaceWith() to restore the content to it's original state. This works for the first two times (job runs once, then when you start another the modal is back to it's original state), but for any time after that, the modal pops up with the content of the previous run until it's text gets overridden.
I have this at the beginning, to capture the contents before anything is done:
$(document).ready(function() {
modalHold = $("#postModal").clone();
});
And, this runs when the modal closes:
$('#postModal').on('hidden.bs.modal', function (){
$("#postModal").replaceWith(modalHold.clone());
})
I would've expected the replaceWith(modalHold.clone()) to replace it with a new clone of the original element, however it seems that I'm still modifying the original. Any help would be appreciated, or if there's a better way of doing this I'd be glad to hear it.
Bootstrap does some javascript magic with the Modal, so I guess you can't just clone whole the Modal's HTML. As a workaround you may try to play with class="modal-body" node only, clone and replace it.
But the truth is on another way. You need to implement a function which would reset your inputs and call it each time the Modal is being hidden.
var modalDefault = {
input1: '',
input2: 'Hello!'
};
var resetModal = function() {
$('#modalInput1').val(modalDefault.input1);
$('#modalInput2').val(modalDefault.input2);
}
// ...
$('#postModal').on('hidden.bs.modal', resetModal);
Not sure why I didn't think of this to begin with, but dhilt's answer helped point me in the right direction. The idea of creating defaults and just switching back to those could be helpful in some cases, but I had some content (including job info and a loading bar) inside the modal that I'd really like to be displayed each time a job starts, until it is done and the output can be displayed.
Instead of doing any fancy cloning, I placed that content into a div and just grabbed its innerHTML:
$(document).ready(function() {
modalHold = $('#jobOutputHolder').html();
});
When the .load () runs, it will update #jobOutputHolder with the output of the job. Then, on hide of the modal:
$('#postModal').on('hidden.bs.modal', function (){
$('#jobOutputHolder').html(modalHold);
})
With this method, I can run a job, see the loading screen, see the job output, close the modal, and repeat as many times as I need without ever seeing the output of previous jobs.
I am loading an image from the client side using createObjectURL. Sometimes, the image is big, so I wanted to put a "loading" animated gif in there.
I have a container div (fileDisplay) and an img (imgDisplay).
fileDisplay has the background set to the animated gif. So, as imgDisplay is loading a big file, you see the loading gif. When imgDisplay is done loading, it covers the gif. So, its still there, but you dont see. I figured that should be fine. All is well--as long as the img is a square, which distorts the img proportions.
But, if the image loaded into imgDisplay is resized with correct proportions and not square (fileDisplay dimensions are 45x45 pixles) you still see the gif in the background.
So what I need is to be able to turn off the animated gif (background url=none on fileDisplay), or cover it with another div, or something. But only when the image has fully loaded.
I can see by now, after trying everything I can think of, that there is no way to change backgrounds, turn divs on/off, etc and do this. It seems everything is processed and then the final results are all painted at once as opposed to incrementally as if it were, say, a VB app.
Can anyone help me with working out how to do this--I'm assuming it CAN be done. And I think the solution is "promise." I have looked for samples, read what I can find, but just can't seem to grasp it.
The "uploadButton on change" event fires this code:
imgSrc = window.URL.createObjectURL(this.files[0]);
//document.getElementById("imgDisplay" + justNumber).src = imgSrc;
getImgSize(imgSrc, useImgSize);
Where the commented line just displays the image as a 45x45. But the getImgSize function calculates the size and displays the image resized--which is when the problem occurs. Just try to load an image that is not square and you will see the issue.
A complete fiddle is here: https://jsfiddle.net/msith718/xfuv79b3/334/
So to do this you want to remove the gif/img element from the parent div when you load the new image into the div. Which you would do with the element.removeChild() function.
//Put this code as soon as you want to remove the loading gif
//This gets the node or the element you want to remove
var node = document.getElementById("gif");
//This removes the node from the parent div
document.getElementById("parentDiv").removeChild(node);
Here is one way of solving this problem https://jsfiddle.net/xfuv79b3/353/. Because you are wanting to change something after the image has loaded we need to add the code somewhere after the onload event has been called. I chose to pass in an id to my imgLoader that gets passed on to the callback that processes the image once loaded. Then finally use that id to modify the background of the div once the img element has loaded the new image. What you ne
getImgSize(imgSrc, useImgSize, justNumber);
//...
img.onload = function() {
fn({
width: img.width,
height: img.height
},id);
}
//...
document.getElementById("imgDisplay" + id).onload=function(){
document.getElementById('fileDisplay'+ id).style.background='none';
}
//Also to reset the background if upload is canceled
document.getElementById("fileDisplay" + justNumber).style.background = '';
Extra Note
From your reference to vb and painted I'm guessing your background is more geared towards a main loop that handles all of the interactions and holds all the data or state of your application. Javascript is very different from this in that user interactions happen through events which are asynchronous. This means data has to be explicitly passed around if need inside a certain callback.
Think about it like this. Your program starts in a resting state, but is listening for events that signal some change has happened. If an event happens (such as a user clicks a button) your program calls an event handler. This happens asynchronously meaning two things. One the only data available to it is the data passed into the handler and the data available from its parent scope. And two it means any subsequent actions or data processing have to be called from that event handler. So each event handler must perform all the actions needed and any state change updates to the program before returning to the resting state. Then your program waits for the next event.
This is very simplified (e.g. multiple events can be fired at the same time) and hopefully helpful, but once you get the hang of it javascript makes a whole lot more sense. I'd suggest reading up on event driven programming with javascript since I'm sure there are much better tutorials and explanations out there, but hopefully this gets you headed in the right direction.
I've got code aimed at visualizing some complex data. The user first selects a data file to load; after clicking the load button, the page generates a "plot" button with an onclick event handler with the function renderPlots().
When the user clicks the "plot" button, then all the plots are properly generated and I'm happy. However, in the case of a larger data set, the plotting process may take a few seconds so I'd like to change the button text to something like "rendering..." while the plots are being generated so that the user knows their plots are on the way.
As per the bootstrap documentation for dynamically changing button text, I've tried something like
function renderPlots() {
$("#render").button("loading");
rest of code that does all the D3 plot generating...;
$("#render").button("reset");
}
The problem with the above code is that it doesn't actually change the button text first, it's doing things asynchronously and I'm at a loss of how I can force the button text to change on screen before any plotting actually starts.
Any thoughts?
Actually, if I understood the issue, stuff is running synchronously — not asynchronously — which is why the browser doesn't get a chance to refresh the DOM with the new button text prior to starting the big processing task. If you were to console.log the button text somewhere between the two calls to .button() you'd see that the text is set to "loading"; it just doesn't appear yet.
If so, then using setTimeout to delay the start of processing until the next "frame" will fix the issue:
function renderPlots() {
$("#render").button("loading");
setTimeout(function() {
// rest of code that does all the D3 plot generating...;
$("#render").button("reset");
}, 100); // even 0 will do the trick
}
I have a piece of code in jQuery that I use to get the contents of an iFrame after you click a link and once the content is completed loading. It works, but I have a problem with it repeating - at least I think that is what it is doing, but I can't figure out why or how.
jQuery JS:
$(".pageSaveButton").bind("click",function(){
var theID = $(this).attr("rel");
$("#fileuploadframe").load(function(){
var response = $("#fileuploadframe").contents().find("html").html();
$.post("siteCreator.script.php",
{action:"savePage",html:response, id: theID},
function(data){
alert(data);
});
});
});
HTML Links ( one of many ):
<a href="templates/1000/files/index.php?pg=0&preview=false"
target="fileuploadframe" class="pageSaveButton" rel="0">Home</a>
So when you click the link, the page that is linked to is opened into the iframe, then the JS fires and waits for the content to finish loading and then grabs the iframe's content and sends it to a PHP script to save to a file. I have a problem where when you click multiple links in a row to save multiple files, the content of all the previous files are overwritten with the current file you have clicked on. I have checked my PHP and am pretty positive the fault is with the JS.
I have noticed that - since I have the PHP's return value alerted - that I get multiple alert boxes. If it is the first link you have clicked on since the main page loaded - then it is fine, but when you click on a second link you get the alert for each of the previous pages you clicked on in addition to the expected alert for the current page.
I hope I have explained well, please let me know if I need to explain better - I really need help resolving this. :) (and if you think the php script is relevant, I can post it - but it only prints out the $_POST variables to let me know what page info is being sent for debugging purposes.)
Thanks ahead of time,
Key
From jQuery .load() documentation I think you need to change your script to:
$(".pageSaveButton").bind("click",function(){
var theID = $(this).attr("rel");
var lnk = $(this).attr("href");//LINK TO LOAD
$("#fileuploadframe").load(lnk,
function(){
//EXECUTE AFTER LOAD IS COMPLETE
var response = $("#fileuploadframe").contents().find("html").html();
$.post("siteCreator.script.php",
{
action:"savePage",
html:response,
id: theID
},
function(data){alert(data);}
);
});
});
As for the multiple responses, you can use something like blockui to disable any further clicks till the .post call returns.
This is because the line
$("#fileuploadframe").load(function(){
Gets executed every time you press a link. Only add the loadhandler to the iframe on document.ready.
If a user has the ability via your UI to click multiple links that trigger this function, then you are going to run into this problem no matter what since you use the single iframe. I would suggest creating an iframe per save process, that why the rendering of one will not affect the other.
I'm changing the image src of an image node.
I want to be able to make sure that it's changed before executing somecode. How would i do that?
right now i have
function changePic(imgNode, newPic, desc){
var descNode = $("#description");
$(imgnode).fadeTo(500, 0, function(){
$(imgnode).attr("src", newPic);
$(imgnode).attr("alt", desc)
descNode.text(desc);
$(imgnode).fadeTo(500, 1);
});
}
Works great if the server's fast/ a local server. Works terribly if the server's slow, where the image will fade back in before changing...
any idea?
Edit: I'm loading the image when changePic is called. Any better ways to do it?
More: Also why is it not a good idea to put the last line,
$(imgnode).fadeTo(500, 1);
, outside of the callback function?
Preload the image, but to be sure it's completely loaded, use the .load() event.
Quote:
The load event is sent to an element
when it and all sub-elements have been
completely loaded. This event can be
sent to any element associated with a
URL: images, scripts, frames, iframes,
and the window object.
And don't miss this line:
It is possible that the load event
will not be triggered if the image is
loaded from the browser cache. To
account for this possibility, we can
use a special load event that fires
immediately if the image is ready.
event.special.load is currently
available as a plugin.
I put together an example of how I think you want it to work. I switch between three images I found through Google Images. I bind the load event before I change the src of the image to be sure it's triggered.
http://jsfiddle.net/xdjjR/1/
I guess, you can preload image in hidden elements, so that it's loaded with other html. When the source changed such image should be shown immediately.
Use the callback param
doc
ex from doc:
$('#clickme').click(function() {
$('#book').fadeOut('slow', function() {
// Animation complete.
});
});