Waiting for a script object to load [duplicate] - javascript

This question already has answers here:
dynamic script loading synchronization
(6 answers)
Closed 8 years ago.
I'm trying to create a jquery script that will be run from the console of google chrome and will analyze a page.
My problem is that when I run the following code:
var jq = document.createElement('script');
jq.src = "http://code.jquery.com/jquery-latest.min.js";
document.getElementsByTagName('head')[0].appendChild(jq);
alert($('a'));
I get a message box with null (bad result)
But if I separate it to to executions like this:
step1:
var jq = document.createElement('script');
jq.src = "http://code.jquery.com/jquery-latest.min.js";
document.getElementsByTagName('head')[0].appendChild(jq);
step2:
alert($('a'));
It works great and I get [object] that is my desired result.
The question is what can I do to run this in a single batch?

In general, you need to attach a listener on the script nodes onload event. jQuery will not be available until its fully transfered and executed. Like
var jq = document.createElement('script'),
head = document.head || document.getElementsByTagName('head')[0] || document.documentElement;
jq.src = "http://code.jquery.com/jquery-latest.min.js";
jq.onload = jq.onreadystatechange = function() {
if(!jq.readyState || /loaded|complete/.test( jq.readyState ) ) {
jq.onload = jq.onreadystatechange = null;
jq = undefined;
}
}
head.insertBefore(jq, head.firstChild);
The above code is pretty much rocksolid and works in IE6+ aswell.

You could try something like this and check the ready state of the script:
jq.onreadystatechange = function() {
if (this.readyState == 'complete') {
alert($('a'));
}
}

If you want a solid way and tested listeners for a complete load of script, try third party libraries like:
http://yepnopejs.com/
Start a script loader from scratch is not recommended, not Today.

Related

Remove/Prevent .js file in JavaScript

Hi I am working with a webservice and I have to manipulate the design to make it look better. Recently I had to make it work on IPad.
So my Problem is my edit's don't work in Iphone because the service adds if its a mobile device a viewport and an extra .js file. This is causing unwanted changes. So is there a way to prevent the system from loading / opening? I can use Javascript to do this. My recent trys was to get the "IPhone.js" and make it empty
var scriptElements = document.getElementsByTagName("script");
var patt = /iPhone.js/g;
var sourceOfElement = "";
for (var i = 0; i < scriptElements.length; i++) {
sourceOfElement = scriptElements[i].src;
if (patt.test(sourceOfElement)) {
scriptElements[i].src = "";
};
This didn't really worked because the IPhone.js is loaded before I can "make it empty".
Another try was to remove the viewport, this also did't worked.
So anybody have any idea how to prevent the service from loading/executing the IPhone.js?
can you add iphone.js via javascript not script tag?
It will be something like this:
if (shouldILoad) {
(function() {
var myscript = document.createElement('script');
myscript.type = 'text/javascript';
myscript.src = ('iphone.js');
var s = document.getElementById('myscript');
s.parentNode.insertBefore(myscript, s);
})();
}
If you don't want iphone.js to execute just add a return statement at the start of the iphone.js file if condition is matched
iPhone.js
if(YOUR_CONDITION_NOT_TO_EXECUTE_IPHONE_JS) return; //check for your condition and return
//Other iphone.js code

Loading jQuery and jQuery UI in Bookmarklet if they're not already loaded

I'm working on a bookmarklet which needs access to jquery-ui as well as jquery min. The concern of course being that the page may already have jQuery loaded and conflicts should be avoided.
Using Ben Alman's code at found at http://benalman.com/code/javascript/jquery/jquery.ba-run-code-bookmarklet.js I've been able to gracefully introduce jQuery and hacked in UI to load as well but there seems to be an issue with the delay and jQuery UI is not ready to go right away...
Is there a better way to handle loading both scripts in sequence before executing the actual code?
(function( window, document, jQuery, req_version, callback, callback_orig, parent, script ){
if ( !window[jQuery] || req_version > window[jQuery].fn.jquery ) {
parent = document.documentElement.childNodes[0];
script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js';
parent.appendChild(script);
callback_orig = callback;
callback = function($, L) {
'$:nomunge, L:nomunge';
$(script).remove();
callback_orig( $, L );
};
}
if (typeof jQuery.ui == 'undefined'){
parent = document.documentElement.childNodes[0];
scriptui = document.createElement('script');
scriptui.type = 'text/javascript';
scriptui.src = 'https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.7/jquery-ui.js';
parent.appendChild(scriptui);
alert('Loading your matches...');
}
(function loopy($){
'$:nomunge'; // Used by YUI compressor.
( $ = window[jQuery] ) && req_version <= $.fn.jquery
? callback( parent ? $.noConflict(1) : $, !!parent ) : setTimeout( loopy, 50 );
})();
})( window, document, 'jQuery', '1.3.2',
function($,L) {
'$:nomunge, L:nomunge';
<all the jquery stuff goes here>
There's a similar question at Using jQuery UI in a Bookmarklet with in-depth answers but I have been unable to translate this from CoffeeMarklet to "standard" js.
See my gist here - This is a bookmarklet template for loading jQuery, but you can specify additional scripts and css to load before execution.
https://gist.github.com/2897748
You initialise it like this.
Edit: If you need to load dependencies, you can create an array and push things into it depending on different conditions.
var jsDependencies = [];
// This will only load jQuery UI if it does not exist already.
// Of course if you rely on the page's copy, you have to do some
// more advanced things to check for versions and whatnot.
if(!window.jQuery || !window.jQuery.ui){
jsDependencies.push('http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js');
}
var MyBookmarklet = MyBookmarklet || new Bookmarklet({
// debug: true, // use debug to bust the cache on your resources
css: ['/my/style.css'],
js: jsDependencies,
// jqpath: '/my/jquery.js', // defaults to google cdn-hosted jquery
ready: function(base) { // use base to expose a public method
base.init = function(){
// doStuff();
}
base.init();
}
});

How to inject a JavaScript function to all web page using Firefox extension

I am developing a Firefox addon. What I want to do is to inject a custom JavaScript function.
i.e.
function foo() {..}
So all the pages can call the foo without define it first.
I have look from other answer such as: http://groups.google.com/group/greasemonkey-users/browse_thread/thread/3d82a2e7322c3fce
But it requires modification on the web page. What if perhaps I want to inject the function foo into Google.com? Is it possible to do so?
I can do it with a userscript, but I want to use the extension approach if possible.
The first thing I thought when reading your question was "this looks like a scam". What are you trying to achieve?
Anyway, here's a Jetpack (Add-on builder) add-on that injects a script in every page loaded:
main.js:
const self = require("self"),
page_mod = require("page-mod");
exports.main = function() {
page_mod.PageMod({
include: "*",
contentScriptWhen: "ready",
contentScriptFile: self.data.url("inject.js")
});
};
inject.js:
unsafeWindow.foo = function() {
alert('hi');
}
unsafeWindow.foo();
What if you make a simple href with javascript function on the page.
Like bookmarklets work.
Here is a sample code :
function(scriptUrl) {
var newScript = document.createElement('script');
// the Math.random() part is for avoiding the cache
newScript.src = scriptUrl + '?dummy=' + Math.random();
// append the new script to the dom
document.body.appendChild(newScript);
// execute your newly available function
window.foo();
}('[url of your online script]')
To use it, put your script's url.
It must be only one line of code, url formated, but for code readability I've formated it.
I've never developed a Firefox extension, but for javascript injection that's how I would roll.
Hope it helped.
You can use Sandbox
// Define DOMContentLoaded event listener in the overlay.js
document.getElementById("appcontent").addEventListener("DOMContentLoaded", function(evt) {
if (!evt.originalTarget instanceof HTMLDocument) {
return;
}
var view = evt.originalTarget.defaultView;
if (!view) {
return;
}
var sandbox = new Components.utils.Sandbox(view);
sandbox.unsafeWindow = view.window.wrappedJSObject;
sandbox.window = view.window;
sandbox.document = sandbox.window.document;
sandbox.__proto__ = sandbox.window;
// Eval your JS in the sandbox
Components.utils.evalInSandbox("function foo() {..}", sandbox);
}, false);

Loading external Javascript Sequentially

I am working on a javascript that sequentially loads a list of other external javascript.
The code I have so far:
function loadJavascript(url){
var js = document.createElement("script");
js.setAttribute("type", "text/javascript");
js.setAttribute("src", url);
if(typeof js!="undefined"){
document.getElementsByTagName("head")[0].appendChild(js)
}
}
loadJavascript("Jquery.js");
loadJavascript("second.js");
loadJavascript("third.js");
The problem I ran into is that sometimes the other js files loads before the Jquery file completes its loading. This gives me some errors.
Is it possible to make it so that the next JS file is only initiated when the previous file is finished loading.
Thanks in advance
Sure there is, but there's entire libraries written around doing this. Stop reinventing the wheel and use something that already works. Try out yepnope.js or if you're using Modernizr it's already available as Modernizr.load
loadJavascript("Jquery.js");
$(function(){
$.getScript('second.js', function(data, textStatus){
$.getScript('third.js', function(data, textStatus){
console.log("loaded");
});
});
}
Also, consider using the Google or Microsoft CDN for the jQuery, it will save you bandwidth and hopefully your visitors will already have it cached.
Actually, it's not necessary to load jquery within a js function. But if you insist, you can callback to make sure other js loaded after jquery.
Still, I recommend you load jquery just before </body> then use $.getScript to load other .js
You could do a check to see if jQuery is loaded, not the best way to do it, but if you really have to wait until jQuery is loaded before loading the other scripts, this is how I would do it, by checking for $ :
loadJavascript("Jquery.js");
T=0;
CheckIfLoaded();
function CheckIfLoaded() {
if (typeof $ == 'undefined') {
if (T <= 3000) {
alert("jQuery not loaded within 3 sec");
} else {
T=T+200;
setTimeout(CheckIfLoaded, 200);
} else {
loadJavascript("second.js");
loadJavascript("third.js");
}
}
In technical terms: Browsers have a funny way of deciding I which order to execute/eval dynamically loaded JS, so after suffering the same pain and checking a lot of posts, libraries, plugins, etc. I came up with this solution, self contained, small, no jquery needed, IE friendly, etc. The code is extensively commented:
lazyLoader = {
load: function (scripts) {
// The queue for the scripts to be loaded
lazyLoader.queue = scripts;
lazyLoader.pendingScripts = [];
// There will always be a script in the document, at least this very same script...
// ...this script will be used to identify available properties, thus assess correct way to proceed
var firstScript = document.scripts[0];
// We will loop thru the scripts on the queue
for (i = 0; i < lazyLoader.queue.length; ++i) {
// Evaluates if the async property is used by the browser
if ('async' in firstScript ) {
// Since src has to be defined after onreadystate change for IE, we organize all "element" steps together...
var element = document.createElement("script");
element.type = "text/javascript"
//... two more line of code than necessary but we add order and clarity
// Define async as false, thus the scripts order will be respected
element.async = false;
element.src = lazyLoader.queue[i];
document.head.appendChild(element);
}
// Somebody who hates developers invented IE, so we deal with it as follows:
// ... In IE<11 script objects (and other objects) have a property called readyState...
// ... check the script object has said property (readyState) ...
// ... if true, Bingo! We have and IE!
else if (firstScript.readyState) {
// How it works: IE will load the script even if not injected to the DOM...
// ... we create an event listener, we then inject the scripts in sequential order
// Create an script element
var element = document.createElement("script");
element.type = "text/javascript"
// Add the scripts from the queue to the pending list in order
lazyLoader.pendingScripts.push(element)
// Set an event listener for the script element
element.onreadystatechange = function() {
var pending;
// When the next script on the pending list has loaded proceed
if (lazyLoader.pendingScripts[0].readyState == "loaded" || lazyLoader.pendingScripts[0].readyState == "complete" ) {
// Remove the script we just loaded from the pending list
pending = lazyLoader.pendingScripts.shift()
// Clear the listener
element.onreadystatechange = null;
// Inject the script to the DOM, we don't use appendChild as it might break on IE
firstScript.parentNode.insertBefore(pending, firstScript);
}
}
// Once we have set the listener we set the script object's src
element.src = lazyLoader.queue[i];
}
}
}
}
Of course you can also use the minified version:
smallLoader={load:function(d){smallLoader.b=d;smallLoader.a=[];var b=document.scripts[0];for(i=0;i<smallLoader.b.length;++i)if("async"in b){var a=document.createElement("script");a.type="text/javascript";a.async=!1;a.src=smallLoader.b[i];document.head.appendChild(a)}else b.readyState&&(a=document.createElement("script"),a.type="text/javascript",smallLoader.a.push(a),a.onreadystatechange=function(){var c;if("loaded"==smallLoader.a[0].readyState||"complete"==smallLoader.a[0].readyState)c=smallLoader.a.shift(),
a.onreadystatechange=null,b.parentNode.insertBefore(c,b)},a.src=smallLoader.b[i])}};

Find the iframe that called a function in its parent

I have several iframes on a page that display ads unicorns/bacon to users. Because its not possible to detect an iframe's domready event via the parent (please let me know if this isn't true) I have some initialization-code in each iframe like this:
<body data-controller="unicorn">
<!-- content -->
<script>
var $ = parent.jQuery;
if($ && $.frameReady){
$(document).ready(function(){
$.frameReady(document);
});
}
</script>
</body>
The parent document has code very similar to the following (about this technique via #Paul Irish):
var frames = {
// the following is irrelevant to my question but awesome.
"unicorn": function (document) {
var script = document.createElement("script"),
element = document.getElementsByTagName("script")[0];
script.src = "http://www.cornify.com/js/cornify.js";
script.onload = function () {
// defaultView is the DOMWindow.
document.defaultView.cornify_add();
$(document).click(document.defaultView.cornify_add);
script.parentNode.removeChild(script);
};
element.parentNode.appendChild(script, element);
},
"bacon" : function(document) { /** mmm, bacon **/ }
};
// relevant but boring...
$.frameReady = function(document){
var controller = $(document.body).data("controller");
controller && frames[controller] && frames[controller](document);
};
Here is an example in jsfiddle (you can edit it here). It works great (at least it does in Chrome dev).
Now what I would LIKE to do is get rid of the data-controller bit in the iframe and instead use the id (or data-* or whatever) of the actual iframe element that is in the parent document to initialize the code.
If I could query the DOM via DOMWindow it would look like this:
$.frameReady = function(document){
var iframe = $("body").find(document.defaultView),
controller = iframe.data("controller");
controller && frames[controller] && frames[controller](document);
};
Luckily, I only need this to run on webkit based browsers, Adobe Air 2.5 actually (but I'm testing in Chrome ATM).
Because S.O. answerers like it when a Question has a question here it is:
Is there any (efficient) ways to query the DOM via document or window in webkit-based browsers - including Adobe Air 2.5?
I have now found that one iframe containing the unicorn for you
console.log(
$("iframe").contents().filter( function(){
return this == document
}).length
);

Categories