35

So, I need to know the width of an element with javascript, the problem I have is that the function fires too early and the width changes when the css is tottally applied. As I understood, the $(document).ready() function was fired when the document is completed, but it doesn't seem to work like that.

Anyways, I'm sure that with the code my problem will be understood (this is a simplified example):

<html>
<head>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
    <link href='http://fonts.googleapis.com/css?family=Parisienne' rel='stylesheet' type='text/css'>

    <style type="text/css">
        #target {
            font-family: 'Parisienne', cursive;
            float: left;
        }
    </style>
</head>
<body>
    <div id="target">Element</div>
</body>
</html>

<script type="text/javascript">
    $(document).ready(function(){
        console.debug($('#target').outerWidth());
        alert('hold on');
        console.debug($('#target').outerWidth());
    });
</script>

I want to know the width of the #target div, the problem is that the code that's executed before the alert gives a different output than the one after, presumably because the font is not fully loaded and it's measuring the div with the default font.

It works as I expect in Google Chrome, but it doesn't on IE and Firefox.

Noel De Martin
  • 2,779
  • 4
  • 28
  • 39
  • I can confirm 2013-11-24 that this problem still exists on JQuery 2.x when you hit the browser's refresh button. Go to this [fiddle](http://jsfiddle.net/jLDbX/). When you first load the fiddle, everything should work fine for the most times. It also works if you put your mouse cursor in the URL and hit enter. But things will go to hell if you click on the refresh button or hit *CTRL+R*. – Martin Andersson Nov 24 '13 at 11:48
  • I tried to implement a "count down latch" to measure width and height only when both window-load fired and when dependent fonts has finished fire. See if it helps: [http://jsfiddle.net/jLDbX/1/](http://jsfiddle.net/jLDbX/1/). In this fiddle, everything seems to be working just right. But in my real world application, I kept getting errors. For you though, it might work. – Martin Andersson Nov 24 '13 at 13:10

7 Answers7

72

If you rely on external content to be already loaded (e.g. images, fonts), you need to use the window.load event

$(window).on("load", function() {
    // code here
});

The behaviour of these events is described in this article:

There is [a] ready-state however known as DOM-ready. This is when the browser has actually constructed the page but still may need to grab a few images or flash files.

Edit: changed syntax to also work with jQuery 3.0, as noted by Alex H

Mira Weller
  • 2,406
  • 22
  • 27
  • 5
    Two years later, still helpful! – Stachu Jan 30 '14 at 15:32
  • 6
    In JQuery 3.0, you should use the syntax `$(window).on('load', function() { });` since $(window).load() is deprecated. – Alex Jul 21 '16 at 06:06
  • @AlexH, good point. `$(window).load()` was deprecated in jQuery 1.8. Furthermore, it is confusing because there are two `.load()` methods in jQuery (before the event handling one was removed). – ryanpcmcquen Dec 15 '17 at 12:54
  • As this is the top answer, it's worth mentioning that actually in jQuery 3.0 _on_ methods are depreciated and one should use .ready()/.load() https://api.jquery.com/ready/#ready-handler – darth0s Feb 07 '19 at 14:26
  • 1
    @darth0s no, the $(window).on('load',...) syntax didn't change, and $(window).load() actually was removed. You're right about the .ready() case however. – Mira Weller Feb 07 '19 at 14:36
12

Quote OP:

"As I understood, the $(document).ready() function was fired when the document is completed,"

$(document).ready() fires when the DOM ("document object model") is fully loaded and ready to be manipulated. The DOM is not the same as the "document".

W3C - DOM Frequently Asked Questions

You can try $(window).load() function instead...

$(window).load(function() {
    // your code
});

It will wait for all the page's assets (like images and fonts, etc.) to fully load before firing.

Sparky
  • 98,165
  • 25
  • 199
  • 285
5

The jQuery .ready() function fires as soon as the DOM is complete. That doesn't mean that all assets (like images, CSS etc) have been loaded at that moment and hence the size of elements are subject to change.

Use $(window).load() if you need the size of an element.

Jay
  • 2,141
  • 6
  • 26
  • 37
4

The "ready" event fires when the DOM is loaded which means when it is possible to safely work with the markup.

To wait for all assets to be loaded (css, images, external javascript...), you'd rather use the load event.

$(window).load(function() {
    ...
});
Didier Ghys
  • 30,396
  • 9
  • 75
  • 81
2

You could use $(window).load(), but that will wait for all resources (eg, images, etc). If you only want to wait for the font to be loaded, you could try something like this:

<script type="text/javascript"> 
    var isFontLoaded = false;
    var isDocumentReady = false;
    $("link[href*=fonts.googleapis.com]").load(function () {
        isFontLoaded = true;
        if (isDocumentReady) {
            init();
        }
    });
    $(document).ready(function () {
        isDocumentReady = true;
        if (isFontLoaded) {
            init();
        }
    });
    function init () {
        // do something with $('#target').outerWidth()
    }
</script> 

Disclaimer: I'm not totally sure this will work. The <link> onload event may fire as soon as the stylesheet is parsed, but before its external resources have been downloaded. Maybe you could add a hidden <img src="fontFile.eot" /> and put your onload handler on the image instead.

gilly3
  • 87,962
  • 25
  • 144
  • 176
1

I have absolutely, repeatably seen the same problem in IE9 and IE10. The jquery ready() call fires and one of my <div>'s does not exist. If I detect that and then call again after a brief timeout() it works fine.

My solution, just to be safe, was two-fold:

  1. Append a <script>window.fullyLoaded = true;</script> at the end of the document then check for that variable in the ready() callback, AND

  2. Check if $('#lastElementInTheDocument').length > 0

Yes, I recognize that these are nasty hacks. However, when ready() isn't working as expected some kind of work-around is needed!

As an aside, the "correct" solution probably involves setting $.holdReady in the header, and clearing it at the end of the document. But of course, the really-correct solution is for ready() to work.

jeesty
  • 1,124
  • 1
  • 10
  • 13
  • I think I'm seeing this behavior as well. Maybe the solution is to put the document.ready command at the bottom of the page, which seems to be a more common practice these days. I'm seeing this issue, but I've got my document.ready command in the tag currently. – hairbo Dec 10 '13 at 18:16
1

The problem $(document).ready() fires too early can happen sometimes because you've declared the jQuery onReady function improperly.

If having problems, make sure your function is declared exactly like so:

 $(document).ready(function() 
 {
   // put your code here for what you want to do when the page loads.
 });

For example, if you've forgotten the anonymous function part, the code will still run, but it will run "out of order".

console.log('1');
$(document).ready()
{
  console.log('3');
}
console.log('2');

this will output

1
3
2
Brad Parks
  • 66,836
  • 64
  • 257
  • 336