I'm loading a few YUI scripts dynamically in my code in response to an Ajax request. The DOM and the page is fully loaded when the request is made - it's a response for an user event.
I add the <scripts> tag to head as children, but I stumbled in a few problems:
I add two YUI scripts hosted at the Yahoo! CDN and an inlined script of my own responsible for creating object, adding event listeners and rendering the YUI widgets. But I when my script run the YUI scripts are not loaded yet giving me errors and not running as I expect.
There's a way to only run my script (or define a function to be run) when YUI scripts are fully loaded?
Have you tried an onload event?
Edited:(thanks Jamie)
var script = document.createElement("script");
script.type = "text/javascript";
script.src = src;
//IE:
if(window.attachEvent && document.all) {
script.onreadystatechange = function () {
if(this.readyState === "complete") {
callback_function(); //execute
}
};
}
//other browsers:
else {
script.onload = callback_function; //execute
}
document.getElementsByTagName("head")[0].appendChild(script);
If you're using YUI 2.x I highly recommend using the YUI Get utility, as it's designed to handle just this sort of a problem.
If you are loading multiple individual script files from the Yahoo! CDN, you'll need to makes sure both are loaded before executing your dependent code. You can avoid this using the combo handler. See the Configurator to get what the script url should be to load both/all needed YUI files from one url.
http://developer.yahoo.com/yui/articles/hosting/
With that in mind, assuming you must load the YUI files asynchronously, you should use an onload/onreadystatechange handler as noted by digitalFresh.
I would recommend the following pattern, however:
(function (d) {
var s = d.createElement('script'),
onEvent = ('onreadystatechange' in s) ? 'onreadystatechange' : 'onload';
s[onEvent] = function () {
if (("loaded,complete").indexOf(this.readyState || "loaded") > -1) {
s[onEvent] = null;
// Call your code here
YAHOO.util.Dom.get('x').innerHTML = "Loaded";
}
};
// Set the src to the combo script url, e.g.
s.src = "http://yui.yahooapis.com/combo?2.8.1/...";
d.getElementsByTagName('head')[0].appendChild(s);
})(document);
You could use a setTimeout() to run some function that just checks if it's loaded - check something like
if (typeof YUI_NAMESPACED_THING !== "undefined") runCode()
EDIT Thanks, CMS
If I understand this correctly, your ajax response with this:
<script href="yui-combo?1"></script>
<script href="yui-combo?2"></script>
<p>some text here</a>
<script>
// using some of the components included in the previous combos
// YAHOO.whatever here...
</script>
If this is the case, this is a clear case in which you should use dispatcher plugin. Dispatcher will emulate the browser loading process for AJAX responses. Basically it will load and execute every script in the exact order.
Best Regards,
Caridy
Related
This question already has answers here:
'onload' handler for 'script' tag in internet explorer
(2 answers)
Closed 7 years ago.
I know my subject is quite tricky but i dont know how to much more ellaborate it on the subject alone.
so here how it goes.
i have a button
Load IT!
on the script tag:
function loadTheFile() {
var script = $("<script><\/script>");
script.attr("type", "text/javascript");
script.attr('src','http://www.thisismyexternalloadingjsfile"');
$('body').append(script);
alert("done! the file has been loaded");
}
the script well, when loaded will automatically have a modal box.
but the problem is, my alert seems to fire first than what is one the script
so how will i know if i have finished to load the script?
update for the first attempt to answer:
function loadTheFile() {
var script = $("<script><\/script>");
script.attr("type", "text/javascript");
script.attr('src','http://www.thisismyexternalloadingjsfile"');
$('body').append(script);
$(document).ready(function() {
alert("done! the file has been loaded")};
}
same problem
alert does indeed run before the script has been loaded. All that appending the script tag to the page does is append the script tag to the page. Then the browser has to download the script and, once received, run it. That will be after your loadTheFile function has exited.
So you need to get a callback when the script has actually be loaded and run. This is more standard than it used to be, but still has some cross-browser hassles. Fortunately for you, jQuery's already solved this problem for you (since you're using jQuery already):
function loadTheFile() {
$.getScript('http://www.thisismyexternalloadingjsfile"')
.then(function() {
alert("done! the file has been loaded");
});
}
Re your comment:
but my script file has data-* attributes
Assuming you're talking about data-* attributes on the script tag, then you'll have to do a bit more work, but it's still fairly straightfoward:
function loadTheFile() {
var load = $.Deferred();
var script = document.createElement('script');
script.src = 'http://www.thisismyexternalloadingjsfile"';
// No need for `type`, JavaScript is the default
script.setAttribute("data-foo", "bar");
script.onreadystatechange = function() {
if (script.readyState === "loaded") {
load.resolve();
}
};
script.onload = function() {
load.resolve();
};
load.then(function() {
alert("done! the file has been loaded");
});
document.body.appendChild(script); ;// Or wherever you want to put it
}
The onreadystatechange bit is to handle older versions of IE.
Rather than forge the script with text and jQuery, just use native Javascript:
var s = document.createElement('script');
s.onload = scriptLoaded;
s.src = '/path/to/my.js';
document.body.appendChild(s);
function scriptLoaded() {
console.log('Script is loaded');
}
Try something along these lines:
Your main page:
function whenScriptIsReady(){
alert('This function is called when the other script is loaded in!')
}
function loadTheFile() {
var script = $("<script><\/script>");
script.attr("type", "text/javascript");
script.attr('src','myotherjs.js');
$('body').append(script);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Load IT!
myotherjs.js:
alert('This will automatically run when the JS is loaded in!');
whenScriptIsReady();
JavaScript is executed asynchronously, so you alert will be executed before the browser can load the new script. If you want to execute logic after the script has been loaded, you could add an event listener to your script that will call the function 'loadFunc` once the script load is completed:
var loadFunc = function() {
alert("External Javascript File has been loaded");
};
//Other browsers trigger this one
if (script.addEventListener)
script.addEventListener('load', loadFunc, false);
I want to develop a Chrome extension, just imagine when Facebook loads you are allowed to add extra JS on it.
But my problem is I can't modify the DOM of the later content, which means the newly loaded content that appear when the user scrolled down.
So I want to detect XHR using JavaScript.
I tried
send = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
/* Wrap onreadystaechange callback */
var callback = this.onreadystatechange;
this.onreadystatechange = function() {
if (this.readyState == 4) {
/* We are in response; do something, like logging or anything you want */
alert('test');
}
callback.apply(this, arguments);
}
_send.apply(this, arguments);
}
But this is not working.. any ideas?
Besides Arun's correct remark that you should use _send for both, your approach doesn't work because of how Content Scripts work.
The code running in the content script works in an isolated environment, to prevent it from conflicting with page's own code. So it's not like you described - you're not simply adding JS to the page, you have it run isolated. As a result, your XHR replacement only affects XHR calls from your extension's content scripts and not the page.
It's possible to inject the code into the page itself. This will affect XHR's from the page, but might not work on all pages, if the Content Security Policy of the page in question disallows inline code. It seems like Facebook's CSP would allow this. Page's CSP should not be a problem according to the docs. So, this approach should work, see the question I linked.
That said, you're not specifically looking for AJAX calls, you're looking for new elements being inserted in the DOM. You can detect that without modifying the page's code, using DOM MutationObservers.
See this answer for more information.
to detect AJAX calls on a webpage you have to inject the code directly in that page and then call the .ajaxStart or .ajaxSuccess
Example:
// To Successfully Intercept AJAX calls, we had to embed the script directly in the Notifications page
var injectedCode = '(' + function() {
$('body').ajaxSuccess(function(evt, request, settings) {
if (evt.delegateTarget.baseURI == 'URL to check against if you want') {
// do your stuff
}
});
} + ')();';
// Inserting the script into the page
var script = document.createElement('script');
script.textContent = injectedCode;
(document.head || document.documentElement).appendChild(script);
script.parentNode.removeChild(script);
I am building a single page javascript app and when the application starts I use a single javascript file to load every other file I need on the fly. When I hit refresh, according to firebug, my HTML page as well as javascript pages will load with a 304 Not Modified Error and my javascript stops working.
I understand this is due to browser caching, but how can I avoid this? I load the initial HTML page with a single script call
<script src="js/config.js" type="text/javascript"></script>
and then continue to load the rest dynamically from within that script
window.onload = function () {
var scripts = ['http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js', 'js/core.js', 'js/sandbox.js']; //Application scripts
var loaded = 0;
//Callback is executed after all scripts have been loaded.
var callback = function () {
if (loaded + 1 == scripts.length) {
//Create Modules
CORE.loader("js/modules/Login.js", function () {
CORE.createModule('loginForm', Login);
});
//Create HTML bindings.
CORE.createBinding('appContainer', '#Login', 'login.html');
CORE.bindHTML(window.location.hash); //Loads hash based page on startup
} else {
loaded++;
loadScript(scripts[loaded], callback);
}
};
loadScript(scripts[0], callback);
function loadScript(scriptSrc, callback) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.src = scripts[loaded];
if (script.readyState) {
script.onreadystatechange = function () {
if (script.readyState == 'loaded' || script.readyState == 'complete') {
script.onreadystatechange = null;
callback();
}
};
} else {
script.onload = function () {
callback();
};
}
document.getElementsByTagName('head')[0].appendChild(script);
}
};
I know that Gmail uses cookies to prevent this. Does anyone have any idea how to take that approach? Should I set the cookie on the server and then check it with JS on each page load/refresh and use something like window.location.refresh() if the cookie tells me the page is loaded from cache?
To expand on #Ramesh's answer:
to force a reload of the js file, instead of the cache, use this html:
<script src="js/config.js?v=42" type="text/javascript"></script>
The next time you make changes to that file just +1 the v. This also works with css files by the way.
Caching is an important for performance reasons. I would recommend you pass a version number in your query string and with every update, increment the version number. This will force the browser to resend the request and it will load from cache if it already has the same version.
I agree with all the other answers. 304 is not an error and there are many reasons why this behavior is correct.
That being said, there is a simple "hack" you can use. Simply attach a unique URL parameter to the JS call.
var timestamp = +new Date;
var url = "http://mysite.com/myfile.js?t=" + timestamp;
Again, this is a hack. Performance wise, this is horrible.
<META HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE">
Add this into your HTML HEAD.
<?php
$xn = date("YmdHis");
echo " <script src='root.js?$xn'></script>";
?>
I would suggest to use Javascript to generate a random number using Math.random multiply it and then Math.floor to return the integer of it. Then, I would add this number to the URL as a variable. Since the number changes during every page load, the file should never be cached.
<script>
var url="yourscript.js";
var extra="?t=";
var randomNum = String((Math.floor(Math.random() * 200000000000000)));
document.getElementById('myScript').src = url+extra+randomNum;
</script>
<script id="myScript"></script>
You need to set script.src = scripts[loaded]; after adding the onreadystatechange/onload handlers. Otherwise the event is going to fire before the handlers are added, since the cached version loads instantly.
After struggling with the cache issue for months, trying just about anything (including a script which changes the URLs using a parameter) I found a post which explains how to do that using the IIS.
Start IIS Manager (INETMGR from the start menu works for me)
Navigate to desired site in the Connections tree
Open Output Caching
Edit Feature Settings
Uncheck Enable cache and Enable kernel cache
If you cannot save changes, make sure your web.config file is not marked read only
IISRESET is required for the changes to take place
This is the original post, which mentions that it is relevant for IIS 7.5 (in Windows 7)
https://forums.iis.net/t/959070.aspx?How+do+you+disable+caching+in+IIS+
One of the things I tried before that was adding .html and .js in Output Caching, and checking "Prevent All Caching". So if it doesn't work after the IISRESET, try that, though I'm not sure it is actually required.
EDIT:
If it does not work, still in the IIS go to HTTP Response Headers and add new actions:
Name: cache-control, Value: no-cache
Name: expires, Value: 0
This question already has answers here:
JQuery to load Javascript file dynamically
(2 answers)
Closed 7 years ago.
I have a web page and a canvas with Google Maps embedded in it. I am using jQuery on this site.
I want to load Google Maps API only if the user clicks on "Show me the map". Further, I want to take away the whole loading of the Google Maps from the header in order to improve my page performance.
So I need to load JavaScript dynamically. What JavaScript function I can use?
You may want to use jQuery.getScript which will help you load the Google Maps API javascript file when needed.
Example:
$.getScript('http://maps.googleapis.com/maps/api/js?libraries=geometry&sensor=true', function(data, textStatus){
console.log(textStatus, data);
// do whatever you want
});
Use the Loading on Demand Loading Strategy
Loading on Demand
The previous pattern loaded additional JavaScript unconditionally after page load, assuming
that the code will likely be needed. But can we do better and load only parts of
the code and only the parts that are really needed?
Imagine you have a sidebar on the page with different tabs. Clicking on a tab makes an
XHR request to get content, updates the tab content, and animates the update fading
the color.
And what if this is the only place on the page you need your XHR and animation
libraries, and what if the user never clicks on a tab?
Enter the load-on-demand pattern. You can create a require() function or method that
takes a filename of a script to be loaded and a callback function to be executed when
the additional script is loaded.
The require() function can be used like so:
require("extra.js", function () {
functionDefinedInExtraJS();
});
Let’s see how you can implement such a function. Requesting the additional script is
straightforward. You just follow the dynamic element pattern. Figuring out
when the script is loaded is a little trickier due to the browser differences:
function require(file, callback) {
var script = document.getElementsByTagName('script')[0],
newjs = document.createElement('script');
// IE
newjs.onreadystatechange = function () {
if (newjs.readyState === 'loaded' || newjs.readyState === 'complete') {
newjs.onreadystatechange = null;
callback();
}
};
// others
newjs.onload = function () {
callback();
};
newjs.src = file;
script.parentNode.insertBefore(newjs, script);
}
“JavaScript Patterns, by Stoyan Stefanov
(O’Reilly). Copyright 2010 Yahoo!, Inc., 9780596806750.”
you would just generate the script tag via javascript and add it to the doc.
function AddScriptTag(src) {
var node = document.getElementsByTagName("head")[0] || document.body;
if(node){
var script = document.createElement("script");
script.type="text/javascript";
script.src=src
node.appendChild(script);
} else {
document.write("<script src='"+src+"' type='text/javascript'></script>");
}
}
I think you're loking for this http://unixpapa.com/js/dyna.html
<input type="button" onclick="helper()" value="Helper">
<script language="JavaScript">
function helper()
{
var head= document.getElementsByTagName('head')[0];
var script= document.createElement('script');
script.type= 'text/javascript';
script.src= 'your_script_url';
head.appendChild(script);
}
</script>
I used the Tangim response, but after found that is more easy use jquery html() function. When we make ajax request to html file that have html+javascript we do the follow:
$.ajax({
url:'file.html',
success:function(data){
$("#id_div").html(data); //Here automatically load script if html contain
}
});
I'd like to inject jQuery into a page using the Google AJAX Libraries API, I've come up with the following solution:
http://my-domain.com/inject-jquery.js:
;((function(){
// Call this function once jQuery is available
var func = function() {
jQuery("body").prepend('<div>jQuery Rocks!</div>');
};
// Detect if page is already using jQuery
if (!window.jQuery) {
var done = false;
var head = document.getElementsByTagName('head')[0];
var script = document.createElement("script");
script.src = "http://www.google.com/jsapi";
script.onload = script.onreadystatechange = function(){
// Once Google AJAX Libraries API is loaded ...
if (!done && (!this.readyState || this.readyState == "loaded" || this.readyState == "complete")) {
done = true;
// ... load jQuery ...
window.google.load("jquery", "1", {callback:function(){
jQuery.noConflict();
// ... jQuery available, fire function.
func();
}});
// Prevent IE memory leaking
script.onload = script.onreadystatechange = null;
head.removeChild(script);
}
}
// Load Google AJAX Libraries API
head.appendChild(script);
// Page already using jQuery, fire function
} else {
func();
}
})());
The script would then be included in a page on a separate domain:
http://some-other-domain.com/page.html:
<html>
<head>
<title>This is my page</title>
</head>
<body>
<h1>This is my page.</h1>
<script src="http://my-domain.com/inject-jquery.js"></script>
</body>
</html>
In Firefox 3 I get the following error:
Module: 'jquery' must be loaded before DOM onLoad! jsapi (line 16)
The error appears to be specific to the Google AJAX Libraries API, as I've seen others use a jQuery bookmarklet to inject jQuery into the current page. My question:
Is there a method for injecting the Google AJAX Libraries API / jQuery into a page regardless of the onload/onready state?
If you're injecting, it's probably easier to request the script without using the google loader:
(function() {
var script = document.createElement("script");
script.src = "http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js";
script.onload = script.onreadystatechange = function(){ /* your callback here */ };
document.body.appendChild( script );
})()
I found this post after we figured out a different solution. So for some reason, if you can't use the accepted solution, this one seem to work fine:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
if (typeof jQuery == 'undefined') {
// jQuery hasn't been loaded... so let's write it into the head immediately.
document.write('<script type="text/javascript" src="/jquery-1.3.2.min.js"><\/script>')
}
</script>
One issue with the accepted solution is that you're forced to put all your code that you want to run on the page into your callback function. So anything that needs jQuery (like plugins) need to be called from that function. AND, all your other included JS files that require jQuery are dependent upon jQuery being loaded BEFORE all the other scripts fire.
I Got It Working!!!!! I figured it out by looking in the application playground....
Here is the wrapper to start using jquery.... Put an alert in the function to see it work
google.load("jquery", "1.4.2");
function OnLoad(){
$(function(){
});
}
google.setOnLoadCallback(OnLoad);
You can use less painful solution to inject jquery (the lastest stable version available) to any page.
jQuerify - Chrome extension used to inject jQuery (the latest stable version available) into any web page (even HTTPS)"