2

I'm making a non-jQuery-dependent plugin which, to play nicely with various existing popular jQuery features and plugins, must execute a function after the end of the DOM has been reached, but not after the execution of anything later queued using jQuery .ready().

The standard non-jQuery .ready() alternative is document.addEventListener("DOMContentLoaded",function(){});.

If a page has functions deferred using both jQuery .ready() and DOMContentLoaded:

  • all the jQuery .ready()s execute in the order they were defined
  • ...then all the DOMContentLoadeds execute, in the order they were defined:

...and both occur between the interactive and complete document readyStates:

 document.addEventListener("readystatechange", function () { alert(document.readyState); });

document.addEventListener("DOMContentLoaded", function(event) { alert(1); });
                          
$(document).ready(function(){ alert(2); });

document.addEventListener("DOMContentLoaded", function(event) { alert(3); });

$(document).ready(function(){  alert(4); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

What can I use which is like document.addEventListener("DOMContentLoaded") but is not pre-empted by any and all jQuery .ready()s like this?

To be clear about the exact timing, in my particular case, I can guarantee that this time-crucial code is queued before any .ready()s are queued, so answers which append to the same queue as .ready() will also solve my problem, not just answers which pre-empt all .ready()s. But pre-empting all .ready()s is probably preferable, since it would be applicable for a wider range of cases.


(bonus points for any clear simple explanation as to what it is about jQuery's .ready() that causes it to execute before all document.addEventListener("DOMContentLoaded") regardless of when they were defined, I can't figure it out)

user56reinstatemonica8
  • 32,576
  • 21
  • 101
  • 125
  • I have been! `:-)` But I've not yet cracked the question of how jQuery manages to sneak in ahead of all `"DOMContentLoaded"`s even if they're defined before jQuery itself – user56reinstatemonica8 May 29 '15 at 16:47

3 Answers3

1

It actually has nothing to do with the .ready() call itself, it has to do with the fact that jQuery is loaded before your code example.

jQuery adds an event listener for DOMContentLoaded/load when it is loaded, so by the time your code example runs jQuery already has a listener added. So it will get fired before the listeners in your example.

The browser goes through these steps

  1. Browser loads jquery.js
  2. jQuery initializes and adds listener on DOMContentLoaded/load
  3. Your code runs that adds listener on DOMContentLoaded and adds .ready() callbacks
  4. DOMContentLoaded is triggered
  5. jQuery's listener is in the queue before the others so it gets fired first
  6. jQuery executes each .ready() callback in succession
  7. All other DOMContentLoaded listeners in the queue get fired next

Now you can have yours called first by putting your addEventListener code before your inclusion of jQuery

<script>
    document.addEventListener("DOMContentLoaded", function(event) { alert(1); });
</script>
<script src="jquery.js"></script>
<script>
    document.addEventListener("DOMContentLoaded", function(event) { alert(3); });
    $(document).ready(function(){ alert(2); });
    $(document).ready(function(){  alert(4); });
</script>

Demo

<script>
    document.addEventListener("DOMContentLoaded", function(event) { alert(1); });
</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
    document.addEventListener("DOMContentLoaded", function(event) { alert(3); });
    $(document).ready(function(){ alert(2); });
    $(document).ready(function(){  alert(4); });
</script>

If you do not have control over the placement of your code, you might be out of luck. You could try using a interval timer that is executed quickly (ie every millisecond) and check if dom is ready and when it is execute your code. But there is no guarantee it will get executed first.

Patrick Evans
  • 41,991
  • 6
  • 74
  • 87
  • Okay, so the entire `.ready()` queue is one item in the `DOMContentLoaded` queue, added when jQuery is loaded. My problem is now that I have one case where my `DOMContentLoaded`s occur last even when jQuery appears later in the code... I'll try [inspecting the event listeners](http://stackoverflow.com/questions/446892/how-to-find-event-listeners-on-a-dom-node) – user56reinstatemonica8 May 29 '15 at 17:07
  • There is no native js function that will get you a list of event listeners added through addEventListener. The only way would be to keep track of them yourself, but that would still require you to load your code before all others so that you can track them. – Patrick Evans May 29 '15 at 17:10
0

place your jqueries at the end of your document file.. it will help you resolve the conflicts sometimes..:D

  • Yeah, unfortunately it's a plugin which can't influence how the people who use it structure their HTML. In fact making my plugin robust regardless of the order and placement of JS is the problem I'm trying to solve... – user56reinstatemonica8 May 29 '15 at 16:44
0

If interpret Question correctly, you can use $.holdReady()

$.holdReady(true);

// do stuff

document.addEventListener("DOMContentLoaded"
, function(event) { alert(1); });

document.addEventListener("DOMContentLoaded"
, function(event) { alert(2); });

$.holdReady(false);

$(document).ready(function() {//do stuff when `$.holdReady(false)` is called});
guest271314
  • 1
  • 15
  • 104
  • 177