When loading JavaScript libraries (like jQuery), they tend to create an object on the Window element.
I'm trying to work out how I can detect when the library has just loaded so I've gone down the path of trying to detect a property change on the window object...
window.addEventListener("DOMAttrModified", invalidate, false);
function invalidate(evt)
{
console.log('attrChange = ' + evt.attrChange);
}
... This doesn't work.
Perhaps someone knows of another way to solve both this solution and a mechanism to detect when an external library has loaded.
p.s. I have looked at the onload tag for the script tag but I'm concerned as it's not in the W3C.
p.p.s. Ultimately, I'm trying to implement a function where it is safe to use jQuery code.
If your js code is lower then script tag then don't worry, html rendering stops while script tag is not loaded, so if your js is lower then library is already loaded.
Also you may check for existence of some variable (like $).
if (typeof jQuery != 'undefined') {
// is loaded
} else {
// is not loaded
}
Or in loop though interval:
var interval = setInterval(function () {
if (typeof jQuery != 'undefined') {
clearInterval(interval);
// do what you want
}
});
Related
I wanted to build a simple Chrome extension that would search the HTML/DOM of the current active tab and print out in a popup the number of elements that contained javascript matching a certain source.
I read in the Chrome extension guides that the Content Scripts are unable to either interact with or even see other javascript on the page, leading me to believe this is not possible. Does anyone know for sure if creating this type of extension is feasible?
I did something similar not long ago; I needed to see elements' onclick and other attributes, which is not normally possible:
It's worth noting what happens with JavaScript objects that are shared by the page and the extension - for example, the window.onload event. Each isolated world sees its own version of the object.
There is a technique of injecting code into the page's context. Such code can reach the window's JS context and then pass it to your content script. In my case, I just added an extra attribute to nodes with JS attached.
// Fill inline handler copies
function fillClickHandlers(callback) {
var injected = function() {
// Note: This executes in another context!
// Note: This assumes jQuery in the other context!
$("[onclick]").each(function() {
this.dataset["onclick"] = this.attributes["onclick"].value;
});
$("[onsubmit]").each(function() {
this.dataset["onsubmit"] = this.attributes["onsubmit"].value;
});
$("[onload]").each(function() {
this.dataset["onload"] = this.attributes["onload"].value;
});
}
var s = document.createElement('script');
s.textContent = "(" + injected + ")();";
(document.head||document.documentElement).appendChild(s);
// Script is synchronously executed here
s.parentNode.removeChild(s);
callback();
}
// Erase inline handlers copies
function eraseClickHandlers(callback) {
$("[data-onclick], [data-onsubmit], [data-onload]").each(function() {
delete this.dataset.onclick;
delete this.dataset.onsubmit;
delete this.dataset.onload;
});
callback();
}
// Usage:
fillClickHandlers(function() {
doActualWork(function() {
eraseClickHandlers(doSomethingElse)
});
});
Note that for actual <script> tags, you can freely inspect src or textContent attribute.
I'm currently loading a custom.js file on my site and it calls various functions. However, to keep the size down I only load the libraries needed on certain pages.
Because of this, since the custom.js file is loaded on every page and it calls functions that the particular page may not have, I get undefined is not a function errors on my site on certain pages.
What I would like to be able to do is determine if something is defined before executing the code to keep the errors from popping up.
For an example, I'm using Jarallax (http://www.jarallax.com/) on my front page only with the following:
var jarallax = new Jarallax();
jarallax.addAnimation('div#bigSlider',[{progress:'0%',marginTop:'0px'},{progress:'100%', marginTop:'-200px'}]);
Since Jarallax is only loaded on the homepage and no others I get the undefined function error on all pages but the hompeage. How could I first confirm Jarallax is loaded before attempting to execute the code?
Since referring to undefined variables raises a ReferenceError exception, you could use a try/catch block to handle the exception.
try {
var jarallax = new Jarallax();
}
catch (e) {
// desired behavior for this situation.
}
More on try/catch blocks.
However, to keep the size down I only load the libraries needed on
certain pages. Because of this I get "undefined is not a function"
errors on my site on certain pages.
So this means you're not doing it properly on every page?
You could solve this by using a wrapper object or class:
(function($){
var wrapper = {
init: function(){
var jarallax;
if (typeof Jarallax == 'function'){
jarallax = new Jarallax();
jarallax.addAnimation('div#bigSlider',[{progress:'0%',marginTop:'0px'},{progress:'100%', marginTop:'-200px'}]);
}
}
};
// once the DOM is read
$(function(){
wrapper.init();
});
}(window.jQuery));
By stalling the init function on the DOM ready, you can be certain the script is loaded if you make sure the script tag for Jarallax is added before the wrapper in the HTML. In any other case the init function won't do a thing.
if (typeof jarallax === "undefined") {
var jarallax = {
obj: {},
return {
obj;
};
I have a js file that contains my closure, this file is loaded before jQuery, let's say it can't be moved. How can I pass in or check for jQuery with a view to use it in the closure?
This is what I've got so far:
(function MyClosure() {
var interval = setInterval(function() {
if (typeof jQuery !== 'undefined') {
doJqueryStuff();
clearInterval(interval);
}
}, 500);
function doJqueryStuff() {
// Some stuff with jQuery.
}
})();
It actually works, but is there a "better" way? I always think I'm doing something wrong whenever I use setInterval() for things like this, also the fact I am losing time in that 500ms.
You could wait and attach your execution to the window.onload event, assuming jQuery is loaded once the window is loaded...
window.onload = function() {
// do stuff with jQuery
};
Don't worry - while it does look hackish (at least to me and you) it isn't bad. Often times you need to wait until a complex object is initialized and you need to do the same thing. The best thing is to just ensure the order that your scripts load to solve any dependency issues - but as you requested let's assume the order can't be adjusted.
The only improvement I would suggest: adding an escape hatch to anonymous setInterval function. That way if jQuery never becomes available for some reason, the script can notify the user and stop checking.
var checkCount = 0;
var interval = setInterval(function() {
if (checkCount++ > 20) {
alert("jQuery could not be loaded - degrading user experience");
clearInterval(interval);
}
if (typeof jQuery !== 'undefined') {
doJqueryStuff();
clearInterval(interval);
}
}, 500);
Wait for the onload event on the script tag. In this case, the doJqueryStuff should be a global function.
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" onload='doJqueryStuff()'></script>
I'm working on a do-dad that can be embedded in a page like a youtube video. The particular effect I want needs jQuery to work.
I want to load jQuery on the condition that something on the page hasn't already added jQuery.
I though of testing
if (typeof($)=='function'){...
but that only works if jQuery is loaded & running by the time the page gets to my script. Since best practices these days is to embed you scripts in the footer, my embed code probably will never see jQuery most of the time anyway.
I thought of doing the test onready instead of onload, but the onready function is inside of jQuery. (I suppose I could use a standalone script? is there a good one?)
Lastly, I though of testing for jQuery after a timeout delay, but this seems inelegant at best and unreliable at worst.
Any thoughts?
Given your constraints, I see only two options:
Use window.load event:
(function() {
if (window.addEventListener) {
// Standard
window.addEventListener('load', jQueryCheck, false);
}
else if (window.attachEvent) {
// Microsoft
window.attachEvent('onload', jQueryCheck);
}
function jQueryCheck() {
if (typeof jQuery === "undefined") {
// No one's loaded it; either load it or do without
}
}
})();
window.load happens very late in the loading cycle, though, after all images are and such loaded.
Use a timeout. The good news is that the timeout can probably be quite short.
(function() {
var counter = 0;
doDetect();
function doDetect() {
if (typeof jQuery !== "undefined") {
// ...jQuery has been loaded
}
else if (++counter < 5) { // 5 or whatever
setTimeout(doDetect, 10);
}
else {
// Time out (and either load it or don't)
}
}
})();
You'll have to tune to decide the best values for the counter and the interval. But if jQuery isn't loaded even on the first or second loop, my guess (untested) is that it isn't going to be loaded...unless someone else is doing what you're doing. :-)
You can use window.onload. This fires after domReady, so jQuery would surely be loaded by this point.
And check for jQuery, not $. Sometimes people use jQuery with other libraries and use $ for something different.
However, IMHO, I don't think it's a big deal if jQuery gets loaded twice.
I've been using this code for to do this very thing for a while now. It also checks for a minimum version of jQuery (in our case, we're still using 1.4.2) before loading:
/* Checks if JQuery is loaded... if not, load it. */
/* Remember to update minimum version number when updating the main jquery.min.js file. */
if (typeof jQuery != 'undefined') {
/* jQuery is already loaded... verify minimum version number of 1.4.2 and reload newer if needed */
if (/1\.(0|1|2|3|4)\.(0|1)/.test(jQuery.fn.jquery) || /^1.1/.test(jQuery.fn.jquery) || /^1.2/.test(jQuery.fn.jquery)|| /^1.3/.test(jQuery.fn.jquery)) {
loadJQ();
}
} else {
loadJQ();
}
/* loads jQuery if not already loaded, or if not a recent enough version */
function loadJQ() {
/* adds a link to jQuery to the head, instead of inline, so it validates */
var headElement = document.getElementsByTagName("head")[0];
linkElement=document.createElement("script");
linkElement.src="../scripts/lib/jquery.min.js";
linkElement.type="text/javascript";
headElement.appendChild(linkElement);
}
How do I find out if CKEditor is loaded? I've looked through the API docs, but could only find the loaded event. I want to check if CKEditor is loaded, because if I load it a second time, my textareas disapears.
The loaded event didn't work for me. instanceReady worked:
CKEDitor_loaded = false;
CKEDITOR.on('instanceReady', function(){ CKEditor_loaded = true; });
var waitCKEDITOR = setInterval(function() {
if (window.CKEDITOR) {
clearInterval(waitCKEDITOR);
//CKEDITOR.replace(...);
}
}, 100/*milli*/);
I've looked through the API docs, but could only find the loaded event.
I don't know whether there exists a specific property for this - there might! - but you could use the loaded event to set a global flag. It's not really nice but would do the job.
// At the top of the script
CKEDitor_loaded = false;
// then later
CKEDITOR.on('loaded', function(){ CKEditor_loaded = true; });
Instead of a global variable, you could also consider setting something inside CKEDITOR:
CKEDITOR.flag_loaded = true;
This would be a bit cleaner.
If instance is not ready, the text set would be discarded
On initialization of the CkEditor (version 4 here), you should never set any data before the editor is ready to handle it.
// Initialize this._editor with replace
if (this._editor.status !== "ready") {
this._editor.on("instanceReady",
event => {
event.editor.setData(data);
});
} else {
this._editor.setData(data);
}
Hope this helps someone.
I also load a page snippet with CKEDITOR functionality via AJAX and as such I have experienced many of the problems outlined in this question. This is my solution:
function setCk(id){
if(window.CKEDITOR){
var _instId = CKEDITOR.instances[id];
if(_instId == undefined){
CKEDITOR.inline(id);
}else{
CKEDITOR.instances[id].destroy();
CKEDITOR.inline(id);
}
}
}
On each AJAX response for this snippet I inject a script element into the head with a call to setCk(textareaId). The trick being to destroy any previous CKEDITOR instances for the target ID & re-initialise CKEDITOR after each AJAX snippet load.
I know this is a very old post, but in my research it kept coming up. I am dynamically loading the CKEditor through jQuery. I didn't want to load it multiple times since things start happening, as you found out.
Simple solution:
if (!window.CKEDITOR) {
// (not loaded yet, your code to load it)
}
//creating instance of ck-editor
var yourInstance = CKEDITOR.instances.yourContainer;
//check instance of your ck-editor
if(yourInstance){
//destroy instance
yourInstance .destroy(true);
}
// create instance again
CKEDITOR.replace( 'yourContainer' );