In a nutshell I want a callback to fire after the html has been injected and the content of that html has rendered. Reason being I need to know the height of the new content right away. Something like this:
$('div').html(tonsofstuff);
console.log( $('div').height() ); //works in Firefox, but returns 0 in Chrome
setTimeout(function(){
console.log( $('div').height() ); //works everywhere, but takes too long
},3000);
The issue is occasionally in some browsers (and always in chrome) $('div').height() fires before the new content has a height.
My fantasy:
$('div').html(tonsofstuff, function(){
console.log( $('div').height() );
});
or even
$('div').html(tonsofstuff).load(function(){
console.log( $('div').height() );
});
This is what I do:
var height = $('div').html(tonsofstuff).height();
I never had any problems with waiting for it to render.
If your "tonsofstuff" has images, the setTimeout attack is the wrong approach. You need to know when each of the images are are loaded, then do your DOM height calculations. And for that, well, you may have to introspect the html to load, find the images, and use a call to somethig like the Image Preloader Plugin. I'm not necessarily recommending it; it's fairly trivial to write your own (although there may be some gotchas with certain methods). I just know there are things like this out there that should work pretty well.
If issue occurs in chrome I assume that content you are inserting contains come images. Chrome does not calculate dimentions of the images until they are completely loaded (DOM ready is not enough). So what can usualy fix the problem is executing your rendering on window.onload event. This way you make sure images loaded and .height() / .width() will work fine. Although I realize that it's not always possible because window.onload event is fired much after then DOMContentLoaded.
Another approach is to specify image dimentions explicitly via width and height attributes of the corresponding image tag. This can work if you know the image sizes. In this case it's not necessary to perform your logic on window.onload.
Related
Is there an way (maybe an event?) to delay querying the height of element until all of the styles for it have been fully calculated, so that I know that it's height is calculated properly?
In my app I need to get the height of an element as soon as possible. I run that calculation soon after it's been inserted into DOM (using MutationObservers for detecing that). Still some (not all) of the results are incorrect - the height is different (higher) than what I see in the end in my browser. If I delay the calculation by 200ms using setTimeout, the results are corrent though.
Is there some cleaner solution to that, without using setTimeout? That one is a bit hacky (including the fact 200ms works in my browser doesn't mean that it'll work in all others and I can't increase it - I need to do that calculation as soon as possible).
You can use the onload event which fires after everything in the document has been completely loaded.
You can use it in your markup like this:
<body onload="doStuffAfterEverythingIsLoaded()">...</body>
or in your script:
window.addEventListener("load", doStuffAfterEverythingIsLoaded);
Hope this will help.
I need to set the height of an element based on the height of another element. Let's say that when that element is finished rendering, it's 490px tall.
If I query that other element's height at the beginning of my function inside $(document).ready(), it returns 432px.
If I query that other element's height inside a setTimeout(function, 100), it returns about 475px.
If I bump the setTimeout up to 150 or so, the element is finished rendering and I get 190. But obviously I can't count on this.
I don't want to wait for the entire page to finish loading, if possible; I want to wait only on this element.
It seems like the load event is supposed to work on arbitrary elements, but as far as I can tell that's unimplemented in browsers, or at least in Chrome.
EDIT: The element is not an image and (almost certainly) no images will ever be inside the element. It's just a <div> with some other <div>s and <p>s and things in it. The height is apparently getting queried in the middle of the CSS being applied or something. Code samples forthcoming.
EDIT 2: Here's a fiddle hacked out of my site. (Please forgive the horrid markup; I haven't cleaned up Drupal's output yet.) The fiddle doesn't exhibit the bad behavior I'm trying to correct, though, because the whole snippet gets rendered before the relevant part of the Javascript is run.
Is there a way to do this, or am I stuck with window.onload?
You can do something like this:
<img src="myImage.gif" onload="loadImage()">
You will want to use at least $(window).load() since you need to wait on additional resources like images and CSS sheets that can affect the dimensions of elements on the page.
load() can work on images as well. You should be able to get it right without needing setTimeout.
http://api.jquery.com/load-event/ contains a notice that it doesn't work reliably for images.
Might seem like a trivial question but I've run into an issue while using jQuery. When I try and get the height for an element within a .ready I am always given zero.
$(function() {
$('#my-elem').height() // Always gives me zero.
});
If I put code in a delay with setTimeout() around the height check (say of .5s) then the height is correctly reported back to me. I'm guessing that this is because the styles haven't had a chance to be applied yet?
The only solution I've found to this problem is to use an interval to poll the height of the element until it becomes non-zero but this seems overkill to me. Is there a better way?
The document.ready event signals that the HTML DOM is ready for accessing via Javascript, but that doesn't mean that the elements have already rendered.
In fact, that's the whole shebang behind ready: it's a means for you to start manipulating your document's HTML DOM without having to wait for the page to finish loading. It's safe to assume that at document.ready, your elements are not yet displayed on the page.
Now, that comes with a caveat: if the elements haven't rendered yet, how can the browser / Javascript know what its resolving height is? .height() may give zero at document.ready because of this. It's probably best to wait until load instead of ready when it comes to pulling box dimensions from the layout.
I want to call some JS after the page-load, this may involve a delay and as such I want the page loaded first so content is shown... but it seems that the code in onLoad handler is called before the rendering is complete. Is there a better event I can use, which is triggered when the page is 'finished'?
To clarify, I want to run some JS after the page is rendered on-screen, so a 'post-everything event' really.
There are several points of interest along the time sequence. This generic sequence is a good overview, even though different browsers and versions implement the details a little differently. (This assumes you're using raw Javascript and need to minimize cross-browser issues; with it's comprehensive internal handling of cross-browser issues JQuery is a little different):
T0] page-begun-- The browser has started working on the page, but otherwise the environment is in flux. Your JS operations may occur in the wrong context, and simply be flushed away when the right context stabilizes. You probably don't want to try to execute any JS at all.
T1] "onLoad" event-- [however you get events: addEventListener("Load"..., window.onload=..., etc.] All parts of the page have been identified and downloaded from the server and are in the local system's memory. In order for all parts to be identified, some parsing has already occurred. (Note that "load" is a cognate of "download", not "parse" nor "render".)
You now have the right environment and can begin to execute JS code without fear of losing anything. HOWEVER, operations that try to read or manipulate the HTML [getElementById(..., appendChild(..., etc.] may fail in strange ways, or may appear to work but then disappear, or may do something different than you expected.
T2] DOM-almost-ready-- This hack is very simple and fully cross browser. Just put your JS <script>...</script> at the very end of your HTML, just before the </body> tag. Most things will work right, although attempts to append to or modify the DOM at the very end of the <body> may produce surprising results. This isn't fully correct, but it works 99% of the time. Given its simplicity and the very high probability of correct operation, this may be the way to go (at least if you don't use JQuery).
T3] DOM-ready-- [however you get events: addEventListener("DOMContentLoaded"..., window.ondomcontentloaded=..., etc.] At this point the HTML has been completely parsed and JS is 100% available, including all functions that read or manipulate the HTML [getElementById(..., appendChild(..., etc.].
T4] Render-done-- The browser is finished displaying the content on the screen. There is NOT any such event or any reasonable cross-browser version-agnostic way to detect this situation. That's just as well, as you probably don't really want this anyway. If the browser has already displayed the page on the screen and then you manipulate the DOM, you'll get a "flash", where both the before and the after are visible on the screen at least briefly. What you probably really want is the point where you can execute arbitrary JS code; that's the previous (T3] DOM-ready) point in time.
Either attach a callback to window.onload
window.onload = function(){
// your code here
};
this will fire when all resources are loaded (which might be not what you want).
Or put all of your code at the bottom the page (before the closing body tag). The code will be run when the HTML is parsed.
FWIW, here is the jQuery code. You see, the use custom event handlers for IE and the other browsers, but use window.onload as fallback:
// Mozilla, Opera and webkit nightlies currently support this event
if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
// A fallback to window.onload, that will always work
window.addEventListener( "load", jQuery.ready, false );
// If IE event model is used
} else if ( document.attachEvent ) {
// ensure firing before onload,
// maybe late but safe also for iframes
document.attachEvent("onreadystatechange", DOMContentLoaded);
// A fallback to window.onload, that will always work
window.attachEvent( "onload", jQuery.ready );
// If IE and not a frame
// continually check to see if the document is ready
var toplevel = false;
try {
toplevel = window.frameElement == null;
} catch(e) {}
if ( document.documentElement.doScroll && toplevel ) {
doScrollCheck();
}
}
As with a lot of JavaScript this will depend on which browser you are using.
As #Avitus' answer, have you looked at the execution point of JQuery's document ready event? This has been generalised across all browsers.
If you plan on using a javascript library (like jQuery) I would rather go with the $(document).ready() statement which is called once the DOM is ready to be manipulated.
The other option I see would be to include your function call at the end of your HTML page so that all the HTML content would be loaded so you can afterward execute your code safely
"The onload event waits for all binary content to download before firing. No kitty-tickilng until then."
As this post says, it is called after all binary content is downloaded, you need to listen for a ready event either using jQuery's ready, or your own function. This project looks interesting.
There are many cross browser implementations so use either jQuery or that project I linked to.
I have written my own function for my library, it uses internal methods so will not work on its own but might give you a feel for what you have to do. You can find that function here.
Since placing javascript DOM methods in the bottom of the html page (after <body>) is a lot faster than using the jQuery 'ready' event, shouldnt we be forcing it by doing:
$('document').trigger('ready');
...after the body tag? I havent really tried this, but it should speed up things. Or did I miss something?
jQuery.ready();
The ready event means the document has now been parsed and the DOM is available to be manipulated. That happens when the browser has completed its parsing, and you can't make it happen sooner.
How do you think such a thing would work? Would it flip a magic switch in the browser's HTML parser that makes it run faster than it normally does? Would it cause the computer's processor to run faster, so the browser would finish parsing the document sooner?
You can't force the browser to parse the document any faster than it's going to anyway. Not even with jQuery ;-)
I had a closely related question, I ended up finding the answer myself right before resorting to posting to SO. As people who have my question will likely land here (number one google result for "jquery force document ready"), allow me to give some extra info.
My problem is that I am dynamically generating some HTML (using XSLT) that will sometimes be saved for later, but other times I just want to open a new window with the new HTML so the user can preview it. Like so:
var html = UseXSLTToGenerateSomeHTML();
var myWindow = window.open('', '', 'width=805,height=493');
myWindow.document.write(html);
myWindow.focus();
Problem is, the generated HTML uses jQuery, and the domready event was never getting invoked. It should have been obvious to me immediately from David's answer how to do it, but the tweak escaped me momentarily. It is:
var html = UseXSLTToGenerateSomeHTML();
var myWindow = window.open('', '', 'width=805,height=493');
myWindow.document.write(html);
myWindow.focus();
mywindow.jQuery.ready();
Note that in this case, the page doing this doesn't even use jQuery... only the generated HTML does. Doesn't matter, you are generating the jQuery event on the other document.