Ext.onReady() vs $(document).ready() - javascript

Whats the difference? I have on $(document).ready function which should check if extjs is loaded but the main problem is extjs does not load on time and things inside $(document).ready starts to execute, extjs create function which produces the main error 'cannot execute create of undefined' on Ext.create("...", {..}); line. If i put double check like this:
$(document).ready(function() {
Ext.onReady(function() {
Ext.create('Ext.Button', {...});
});
});
Things magically work. Now I'm using ext-all.js which has ~1.3MB minified which is pretty large imho...and things get magically loaded while he does the second check...but I think those 2 functions are not the same as they definitions suggest, because if I put another $(document).ready instead of Ext.onReady() line, things break again. I think Ext.onReady({}); function does some other black magic which $(document).ready() does not, and I'm interested if someone knows what is this kind of magic?
Because it work's and I don't know why which is killing me.
Thanks for reading the post. =)
ps. I'm using ExtJS for about day so I'm pretty new to it.

No they're not the same, the first one will proc when your jQuery library is loaded, the Ext.onReady(.. will proc when your ExtJS library is loaded.
If you want to combine them you could do something like this:
var extReady = false;
var jQueryReady = false;
var librariesReady = function () {
if (jQueryReady && extReady) {
//They're both ready
}
};
$(document).ready(function () {
jQueryReady = true;
librariesReady();
});
Ext.onReady(function () {
extReady = true;
librariesReady();
});

Ext.onReady() and $(document).ready() have nothing to do about either library being loaded as the current accepted answer suggests.
According to the documentation both are about the DOM being loaded and ready.
Documentation
Ext JS: https://docs.sencha.com/extjs/6.7.0/modern/Ext.html#method-onReady
jQuery: https://api.jquery.com/ready/
An Answer to Your Case
It's possible that you're loading the Ext JS resource after your script fires, but jQuery is already loaded above your script. Thus using jQuery to wait until the DOM is loaded guarantees that the DOM has been loaded and thus by then Ext JS has also been loaded.
If you try to invert them and us Ext JS first you'll likely have an error.
According to the documentation they're doing the same thing so you shouldn't need to nest them
A Fix for this Scenario
If you are loading your resources like so:
jQuery
Your Script
Ext JS
It would be best to load them in this order:
jQuery and/or Ext JS
Order shouldn't matter as they can stand by themselves without requiring one or the other
Your Script
Additional Explanation
Due to how the DOM is loaded and parsed by the time it reads your script it guarantees that jQuery and Ext JS are available. This is why you can reference their libraries in your script; you're not waiting for them to load they're already there and available to be used which is why you can call them and use their ready calls.
You need to use the ready event of one of the libraries to guarantee that all elements are loaded into the DOM and available to be accessed. You also shouldn't try to add anything to the DOM until it's ready although you can append to current elements that have been loaded above your element/script tag. It's just best practice to not touch the DOM until it's finished loading.
Additional Explanation Nobody Asked For 🔥
Handling DOM ready is more involved than these libraries make it which is why they both include such an event handler.
The following link explains with vanilla JS how you cannot only add your event listener you also need to check if it has already fired when you go to add your event listener for DOM ready. This is a common case to handle with eventing - where you create a race condition where an event may fire before you start listening for it - then you don't know that it ever happened without another way to check.
https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded#Checking_whether_loading_is_already_complete

They both check for when the DOM is ready.
If you need Ext to be loaded when using jQuery, try to invert the logic (don't know if it will work, haven't tried).
Ext.onReady(function() {
$(document).ready(function() {
Ext.create('Ext.Button', {...});
});
});
Another StackOverflow question on this subject: Extjs + jQuery together

Related

Defer execution of widget js until page is fully loaded

I have a widget with some custom js:
class ImagePreviewWidget(ClearableFileInput):
...
class Media:
css = {
'all': ('css/image-preview-widget.css',)
}
js = ('js/image-preview-widget.js', )
The custom js uses jQuery, which is loaded independently, so I need to wrap my module initialization in:
window.onload = function() {
cusomJsStart();
};
Which is not very clean (one problem is that I am maybe interfering with other window.onload calls). Is there a better way to load the widget javascript?
EDIT
Just to make it clear: the whole point of this question is that jQuery is not yet loaded when the script runs, and that the script loading order is outside my control.
Instead of setting window.onload you should use addEventListener:
window.addEventListener("load", customJsStart);
(If you need to support IE<9 then some fallback code is required - see the MDN link above).
Even nicer would be if you could tell Django to add a defer attribute to the scripts you pass in the Media class, but it doesn't support that yet.
There is nothing better than pure Javascript ! Alternatively you can use JQuery like this :
$(function(){
cusomJsStart();
});
But with this method your page will be heavier because of JQuery source file loading.
Or you can put your cusomJsStart() at the end of your HTML file, but it's not clean at all !
You did the right choice.

Design a header script to run after the document is finished loading

I have a javascript function, "loadFramework()" that modifies an HTML document. Specifically, it repeatedly runs the jQuery command $("#element-id").load("document/name.html"), which injects the HTML in document/name.html directly into the element with #element-id.
Originally, I ran loadFramework() in a script in the document's header. However, since then I've realized that the function fails if the page has not loaded yet, since it relies on there being an element with #element-id.
I can't figure out how to get this function to run when it should. A simple solution seemed to be setting it to be the document.onload function:
document.onload = function() {
loadFramework();
}
But in this case it never seems to run at all.
How do I make sure a header function runs only after the document has loaded?
You should use window.onload if you are looking for a vanilla JS option
window.onload = function() {
loadFramework();
}
Jquery load takes additional argument "complete". You can run the javascript there. So the code would be:
$("#element-id").load("document/name.html", function(){
loadFramework();
});
You can also use $(document).ready(function{loadFramework()}) inside the html you are loading.
If you want to execute the loadFramework() method after "document/name.html" is loaded, I would suggest the following code.
$(function() {
$("#element-id").load("document/name.html", function(){
loadFramework();
});
});

Reload? Javascript after Jquery .live/.load

I wanted to load some fragments of external content inside a div, through a menu.
Found "load" and "live", found a tutorial used it = success!
Except, like what's explicit in the documentation, it doesn't load JavaScript.
The thing is, the destination page already loads, inside the header, that same JavaScript, 'cause Wordpress loads it in every page. In this particular page, I'm only using the plugin (nextgen gallery) through the jQuery AJAX call.
So, what I believe is my problem is that I somehow need to alert/reload the JavaScript, right?
And how can I do this?
<script type="text/javascript" charset="utf-8">
jQuery(document).ready(function(){
// ajax pagination
jQuery('#naveg a').live('click', function(){ // if not using wp-page-numbers, change this to correct ID
var link = jQuery(this).attr('href');
// #main is the ID of the outer div wrapping your posts
jQuery('#fora').html('<div class="loading"><h2>Loading...</h2></div>');
// #entries is the ID of the inner div wrapping your posts
jQuery('#fora').load(link+' #dentro')
return false;
});
}); // end ready function
</script>
PS: I've substituted "live" with "on" but didn't work either.
I'm not sure if I understand... your load() command is puling in some Javascript that you want executed? I'm not sure if you can do that. But if you just need to call some JS upon load() completion, you can pass it a function like so:
jQuery('#fora').load(link+' #dentro', function() {
console.log("load completed");
// JS code to be executed...
});
If you want to execute Javascript code included in the loaded page (the page you retrieve via .load()), than you have to use the url-parameter without the "suffixed selector expression". See jQuery documentation for (.load()):
Note: When calling .load() using a URL without a suffixed selector expression, the content is passed to .html() prior to scripts being
removed. This executes the script blocks before they are discarded. If
.load() is however called with a selector expression appended to the
URL, the scripts are stripped out prior to the DOM being updated,
which is why they are never executed. An example of both cases can be
seen below:
Here, any JavaScript loaded into #a as a part of the document will
successfully execute.
$('#a').load('article.html');
However in this case, script blocks in the document being loaded into
#b are stripped out prior to being executed:
$('#b').load('article.html #target');
I think that's your problem (although I have no solution for you, sorry).
Proposal: Maybe you can load the whole page (including the Scripts) and remove (or hide) the parts you don't need?
Cheers.

Event handling issues

I have a couple of, what may end up being for this forum, overly-novice questions regarding unobtrusive event handling.
As I understand it, a properly set-up document would look something like this:
<html>
<head>
<title>Title</title>
<script src="jsfile.js" type="text/javascript></script>
</head>
<body>
//Body content, like some form elements in my case
</body>
</html>
Jsfile.js would look something like this:
function a() {
//code;
}
function b()...
window.addEventListener('load', a, false);
document.getElementById("id").addEventListener('click', b, false);
document.myForm.typeSel.addEventListener('change', c, false);
//or to use better browser-compatible code...
function addEvent(obj,evt,fn) {
if (obj.addEventListener)
obj.addEventListener(evt,fn,false);
else if (obj.attachEvent)
obj.attachEvent('on'+evt,fn);
}
addEvent(window, 'load', a);
addEvent(document.getElementById('id'), 'click', b);
addEvent(document.myForm.typeSel, 'change', c);
As I understand it, while in the head the browser will load this JavaScript code, adding each of those event handlers to their respective elements. HOWEVER... While the window handler is added properly, none of the others are. But if placed within a function, the (for instance) getElementById method of accessing an element works just fine, and the event handler is added. So I could conceivably make a loadEvents() function which is called via window onload, which contains all of the addEvent() functions for the other document elements for which I need event handlers. But as I understand the whole thing, I shouldn't have to do this.
In addition, if I were to stick the addEvent code within the body along with the element it addresses, such as:
<input type="checkbox" id="test" />
<script type="text/javascript>
document.getElementById("test").onclick = func;
</script>
...then it works fine. But of course it also violates the whole reason for removing inline event handlers!
So question being: In order to use *element*.addEventListener('click',func,false), addEvent(*element*,'click',func), or even *element*.onclick = func - how can I successfully reference an element at the end of a script file in the head, without having to stick it in another function? Why does getElementById and other such methods not work outside of a function in the head?
Or, is there some flaw in my underlying understanding?
Putting <script> in the <head> used to be the wisdom. But nowadays, with heavy ajax pages, <script> is more and more often but in the body, as far down below as possible. The idea is that the loading and parsing of the <script> keeps the rest of the page from loading, so the user will be looking at a blank page. By making sure the body is loaded as fast as possible, you give the user something to look at. See YAHOO best practices for a great explanation on that issue: http://developer.yahoo.com/performance/rules.html
Now, regardless of that issue, the code as you set it up now, can't work - at least, not when the elements you attempt to attach the handlers to aren't created yet. For example, in this line:
document.getElementById("id").addEventListener('click', b, false);
you will get a runtime error if the element with id="id" is inside the body. Now, if you put the <script> in the body, way below, after the content (including the lement with id="id", it will just work, since the script is executed after the html code for those elements is parsed and added to the DOM.
If you do want to have the script in the head, then you can do so, but you'll need to synchronize the adding of the event handlers with the rendering of the page content. You could do this by adding them all inside the document or window load handler. So, if you'd write:
//cross browser add event handler
function addEventHandler(obj,evt,fn) {
if (obj.addEventListener) {
obj.addEventListener(evt,fn,false);
} else if (obj.attachEvent) {
obj.attachEvent('on'+evt,fn);
}
}
addEventHandler(document, 'load', function(){
//set up all handlers after loading the document
addEventHandler(document.getElementById('id'), 'click', b);
addEventHandler(document.myForm.typeSel, 'change', c);
});
it does work.
The reason why window.addEventListener works while document.getEle...().addEventListener does not is simple: window object exists when you're executing that code while element with id="abc" is still not loaded.
When your browser downloads page's sources the source code is parsed and executed as soon as possible. So if you place script in head element - on the very beginning of the source - it's executed before some <div id="abc">...</div> is even downloaded.
So I think now you know why
<div id="test">Blah</div>
<script type="text/javascript">document.getElementById("test").style.color = "red";</script>
works, while this:
<script type="text/javascript">document.getElementById("test").style.color = "red";</script>
<div id="test">Blah</div>
doesn't.
You can handle that problem in many ways. The most popular are:
putting scripts at the end of document (right before </body>)
using events to delay execution of scripts
The first way should be clear right now, but personally I prefer last one (even if it's a little bit worse).
So how to deal with events? When browser finally download and parse whole source the DOMContentLoaded event is executed. This event means that the source is ready, and you can manipulate DOM using JavaScript.
window.addEventListener("DOMContentLoaded", function() {
//here you can safely use document.getElementById("...") etc.
}, false);
Unfortunately not every browser support DOMContentLoaded event, but as always... Google is the anwser. But it's not the end of bad news. As you noticed addEventListener isn't well supported by IE. Well... this browser really makes life difficult and you'll have to hack one more thing... Yes... once again - Google. But it's IE so it's not all. Normal browsers (like Opera or Firefox) supports W3C Event Model while IE supports its own - so once again - Google for cross-browser solution.
addEventListener might seems now the worst way to attach events but in fact it's the best one. It let you easly add or remove many listeners for single event on single element.
PS. I noticed that you consider of using Load event to execute your scripts. Don't do that. Load event is execute too late. You have to wait till every image or file is loaded. You should use DOMContentLoaded event. ;)
EDIT:
I've forgotten... dealing with cross-browser event model is much easier when you're using some framework like very popular jQuery. But it's good to know how the browsers work.
are you familiar with jQuery?
its a javascript library featuring some really awesome tools.
for instance if you want to have some js action done just after your page if fully loaded and all DOM elements are created (to avoid those annoying exceptions) you can simply use the ready() method.
also i see you want to attach click \ change events jQuery takes care of this too :) and you don't have to worry about all those cross-browser issues.
take a look at jQuery selectors to make your life easier when attempting to fetch an element.
well thats it, just give it a shot, its has a very intuitive API and a good documentation.

jQuery load() method memory leak?

I have hunted around for answer to this one, and though have found related quesions, I couldn't quite find an exact match for this.
I have a fairly large app which is supposed to load pages into divs in another page using the jQuery.load() method. The problem I have is that when loading the same page over and over again into the same div, I see the memory of the browser increase substantially (memory leak). If I call $("*").unbind, I of course do not see a leak, but then everything has been reset, so this isn't reallya fix. The following code example reproduces this problem:
Test1.htm
<head>
<title></title>
<script type="text/javascript" language="javascript" src="Scripts/jquery-1.3.2.js"></script>
<script type="text/javascript" language="javascript">
<!--
var i = 0;
$(document).ready(function() {
$("#btn").click(
function() {
i++;
$("#Test1").load("Test2.htm", null, function() {
//$(document).trigger("test");
})
$("#count").html(i);
});
});
//-->
</script>
</head>
<body>
<img id="btn" src="someimage.png" />
<h3>We are loading Test2.htm into below div</h3>
<div>
Count loads =<span id="count">0</span>
</div>
<div id="Test1" style="border-style:solid">EMPTY</div>
</body>
Test2.htm = any old html page..
If you load Test1.htm and click the button mutliple times, you'll notice the browser memory steadily increasing. I believe the problem is that the loaded js and DOM elements are never set for garbage collection. In my real world system I have tried removing (elem.remove() or .empty()) the loaded elements, but this doens't really fix the problem. I also have many js files loaded using "src", which I replaced with $.getScript, this seems to have had made a small improvement. These are all workarounds thought, and I wanted to find a real solution for this problem. Any ideas?
Edit: update due to more info provided about test2.htm (the page being loaded)
Original answer (for historical purposes): I don't actually see any leaks in the code/markup you have provided - is it possible the leak is in Test2.htm (which you haven't provided the code/markup for)?
New answer:
I would suggest that it it probably due to either multiple loads of jQuery, or other scripts you have in test2.htm.
Assuming jQuery does not leak by simply instantiating and then nullifying jQuery and $, loading multiple times will keep at least 2 copies of jQuery in memory. When loaded, jQuery keeps a backup of any previous versions of $ and jQuery in _$ and _jQuery - so you are going to have at least 2 copies of jQuery loaded when you use load() multiple times.
The above assumption is most likely not correct however - there is every chance that jQuery has leaks even if you "unload" it by setting $,jQuery,_$ and _jQuery to null - it's not really intended to be loaded multiple times like that (however I'm sure that they allow it intentionally, so you can use noConflict() to load and use two different versions of jQuery if necessary).
You can add a "selector" to a load URL. For example:
$("#Test1").load("Test2.htm body", null, function() {
//callback does nothing
});
//or
$("#Test1").load("Test2.htm div#the_Div_I_Want", null, function() {
//callback does nothing
});
I would suggest doing this if you are not interested in any scripts in the ajax result, or alternatively if you do want scripts, you'd need to choose a selector to disable only certain elements/scripts, e.g.
/* load with selector "all elements except scripts whose
src attribute ends in 'jquery.js'" */
$("#Test1").load("Test2.htm :not(script[src$='jquery.js'])", null, function() {
//callback does nothing
});
Also of note is that if you leave out the "data" argument (you have it as null), and provide a function as the second argument, jQuery will correctly determine that the second argument is the callback, so
$("#Test1").load("Test2.htm :not(script[src$='jquery.js'])", function() {
//callback does nothing
});
is acceptible
Hmm perhaps it's just something really basic, but if i set $.ajaxSetup({ cache: false }); before the load calls, I don't seem to get the problem. Now, of course my "real" code has this call, so why might I see a problem? I believe the Tabs UI extension is causing caching to be switched on (I don't actually believe this, but invoking the false cache call before each load seems to fix it!!)
Ok so I finally found the problem and it's not a leak at all (which I suspected), it's simply the result of attaching multiple very complex handlers to the same trigger/event. I raised this question relating to that:
JQuery event model and preventing duplicate handlers

Categories

Resources