Can scripts with async attribute trigger callback functions when loaded? - javascript

Google Pagespeed told me to load my JS files asynchronously, so I wrote this code:
// Add script elements as a children of the body
function downloadJSAtOnload() {
var filesToLoad = [
["/assets/plugins/typeahead.bundle.min.js", "onTypeaheadLoaded"],
["https://apis.google.com/js/client:plusone.js?onload=googlePlusOnloadCallback", ""]
];
filesToLoad.forEach(function(entry) {
var element = document.createElement("script");
element.src = entry[0];
if (entry[1] != "") { // if an onload callback is present (NOTE: DOES NOT SUPPORT NAMESPACES -- http://stackoverflow.com/a/359910/1101095)
element.onload = function() {
if (typeof window[entry[1]] != "undefined") {
window[entry[1]]();
}
};
}
document.body.appendChild(element);
});
}
// Check for browser support of event handling capability
if (window.addEventListener)
window.addEventListener("load", downloadJSAtOnload, false);
else if (window.attachEvent)
window.attachEvent("onload", downloadJSAtOnload);
else window.onload = downloadJSAtOnload;
However, someone just directed me to this article, which says using the async attribute is better than injecting script tags (this is contrary to what I've read most places, but the author show evidence he's right).
So, my question is, if I change to using the async attribute, is there a way to have a function triggered when the script is done loading?

Related

How to defer javascript of an embedded YouTube video on Wordpress?

I want to defer the loading of a particular Javascript file. On GTMetrix, I get a 14 score under the 'Defer Loading Of Javascript' category. I want to defer the loading of this file:
https://www.youtube.com/yts/jsbin/player_ias-vflRCamp0/en_US/base.js
I've tried inserting scripts in my Theme's Footer, right before the tag. Here's what I've tried:
`<script type="text/javascript">
function parseJSAtOnload() {
var element = document.createElement("script");
element.src = "https://www.youtube.com/yts/jsbin/player_ias-vflRCamp0/en_US/base.js";
document.body.appendChild(element);
}
if (window.addEventListener)
window.addEventListener("load", parseJSAtOnload, false);
else if (window.attachEvent)
window.attachEvent("onload", parseJSAtOnload);
else window.onload = parseJSAtOnload;
</script>
`
I've also tried:
`<script type="text/javascript">
function downloadJSAtOnload() {
var element = document.createElement("script");
element.src = "https://www.youtube.com/yts/jsbin/player_ias-vflRCamp0/en_US/base.js";
document.body.appendChild(element);
}
if (window.addEventListener)
window.addEventListener("load", downloadJSAtOnload, false);
else if (window.attachEvent)
window.attachEvent("onload", downloadJSAtOnload);
else window.onload = downloadJSAtOnload;
</script>`
These scripts do nothing to the GTMetrix score.
I've tried this as well:
`<script type="text/javascript">
function parseJSAtOnload() {
var links = ["www.youtube.com/yts/jsbin/player_ias-vflRCamp0/en_US/base.js", "www.youtube.com/yts/jsbin/www-embed-player-vflO7uv3_/www-embed-player.js"],
headElement = document.getElementsByTagName("head")[0],
linkElement, i;
for (i = 0; i < links.length; i++) {
linkElement = document.createElement("script");
linkElement.src = links[i];
headElement.appendChild(linkElement);
}
}
if (window.addEventListener)
window.addEventListener("load", parseJSAtOnload, false);
else if (window.attachEvent)
window.attachEvent("onload", parseJSAtOnload);
else window.onload = parseJSAtOnload;
</script>`
This script will work temporarily. I'll test the page on GTMetrix and it shows that the Javascript is deferred. On the Waterfall chart, it shows that those scripts are canceled (insight here would be appreciated as well?)
When I Retest to make sure it's working, I'm back to the original score of 14. What else can I try, or what am I doing wrong?
Website Here: https://www.fralinpickups.com/product/vintage-hot/
You should create a file with your code and queue it with [wp_enqueue_script][1] hook.
If your file is called my_script.js and it is in the folder /js/ of your theme, the function should look like this:
// You probably already have a function to enqueue scripts in your theme function.php file.
function my_script() {
wp_enqueue_script( 'my_script', get_template_directory_uri() . '/js/my_script.js', array(), '1.0.0', true );
}
add_action( 'wp_enqueue_scripts', 'my_script' );
The key to load it after other scripts is in the fifth parameter of the function, when set to true it loads the script in the footer, before the body tag. If your script requires jQuery and/or other dependencies you can add them in the array using the corresponding handle, as indicated in the documentation (here).
If you want to asign defer to the script tag you could use the following:
function add_defer_attribute($tag, $handle) {
if ( 'my_script' !== $handle )
return $tag;
return str_replace( ' src', 'defer="defer" src', $tag );
}
add_filter('script_loader_tag', 'add_defer_attribute', 10, 2);
More info on script_loader_tag.
Update:
Here is a valid answer that can help you with deferring youtube javascript: How do you Defer Parsing of JavaScript for YouTube video on a WordPress site?

LaTeX does not get converted to Math Text unless the page is reloaded

I am using mimetex.cgi to convert LaTeX text into Maths Text. For which I have put the following in the head tag
<head>
<script src="../../asciimath/js/ASCIIMathMLwFallback.js" type="text/javascript">
</script>
<script type="text/javascript">
var AScgiloc = '../../includes/svgimg.php';
var AMTcgiloc = "http://www.imathas.com/cgi-bin/mimetex.cgi";
</script>
</head>
In the body tag I have the following div which is refreshed by ajax. This contains math text.
<div id="mathtext"> .... </div>
Problem that I am facing:
On the first page load the LaTeX code in mathtext div is getting converted to Math Text image. However, when the div is loaded with new LaTeX code using ajax, it doesn't get converted to Math Text.
If I click on refresh, the LaTeX gets converted to MathText again.
I am not sure what is it that I am doing wrong here.
Edit 1: Including the onload function that is a part of ASCIIMathMLwFallback.js
if(typeof window.addEventListener != 'undefined')
{
//.. gecko, safari, konqueror and standard
window.addEventListener('load', generic, false);
}
else if(typeof document.addEventListener != 'undefined')
{
//.. opera 7
document.addEventListener('load', generic, false);
}
else if(typeof window.attachEvent != 'undefined')
{
//.. win/ie
window.attachEvent('onload', generic);
}
//** remove this condition to degrade older browsers
else
{
//.. mac/ie5 and anything else that gets this far
//if there's an existing onload function
if(typeof window.onload == 'function')
{
//store it
var existing = onload;
//add new onload handler
window.onload = function()
{
//call existing onload function
existing();
//call generic onload function
generic();
};
}
else
{
//setup onload function
window.onload = generic;
}
}
if (checkForMathML) {
checkForMathML = false;
var nd = AMisMathMLavailable();
AMnoMathML = (nd != null);
}
It calls a function generic() using the above... I guess it would do if I call this function at the end of my ajax query ?
Solved it !
ASCIIMathMLwFallback.js has a function called as generic() which is being called on page load here.This function translates Latex into math functions.
In order to convert latex to math function using a ajax query , simply call the generic function by adding the below to the ajax code.
generic.call();
This will ensure that all Latex is converted to math !!

Get files into the DOM with Javascript

To avoid the Google Pagespeed Messeger "Parsing Javascript later" I've copied this script.
<script type="text/javascript">
function downloadJSAtOnload() {
var element = document.createElement("script");
element.src = "http://example.com/templates/name/javascript/jquery.js";
document.body.appendChild(element);
var element1 = document.createElement("script");
element1.src = "http://example.com/templates/name/javascript/jquery.colorbox-min.js";
document.body.appendChild(element1);
}
if (window.addEventListener)
window.addEventListener("load", downloadJSAtOnload, false);
else if (window.attachEvent)
window.attachEvent("onload", downloadJSAtOnload);
else
window.onload = downloadJSAtOnload;
</script>
How could I solve it with a loop because I need one more javascript file insert into the DOM.
Greets
Ron
You can make a function like this that takes an arbitrary number of script filenames:
function loadScriptFiles(/* pass any number of .js filenames here as arguments */) {
var element;
var head = document.getElementsByTagName("head")[0];
for (var i = 0; i < arguments.length; i++) {
element = document.createElement("script");
element.type = "text/javascript";
element.src = arguments[i];
head.appendChild(element);
}
}
function downloadJSAtOnload() {
loadScriptFiles(
"http://example.com/templates/name/javascript/jquery.js",
"http://example.com/templates/name/javascript/jquery.colorbox-min.js",
"http://example.com/templates/name/javascript/myJS.js"
);
}
if (window.addEventListener)
window.addEventListener("load", downloadJSAtOnload, false);
else if (window.attachEvent)
window.attachEvent("onload", downloadJSAtOnload);
else
window.onload = downloadJSAtOnload;
If your script files have a required load order (I presume colorbox must load after jQuery, for example), you will have to do something more sophisticated than this because this loads them all asynchronously so they have no guaranteed load order. Once you need a particular load order, it's probably best to get code that someone else has written to solve this problem like RequireJS or LABjs or Google.load().
Note: I'm also appending the script files to the <head> tag which is a bit better place to put them.
When using LABjs, you are not putting the .wait() in the right place. .wait() tells the loader to wait until all PRIOR scripts are loaded before loading the next one. I think you need it like this:
$LAB
.script("templates/name/javascript/jquery.js").wait()
.script("templates/name/javascript/jquery.colorbox-min.js");

addLoadEvent is not helping with onload conflict

I'm using the popular addLoadEvent as follows for all my JS loading:
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent( locationToggle );
addLoadEvent( step1 );
addLoadEvent( step2 );
addLoadEvent( step3 );
addLoadEvent( getCounties );
addLoadEvent( mapSelection);
Everything I've read suggests this is a fairly bullet proof way of avoiding onload conflicts. And yet this method doesn't appear to working any better than wrapping the functions in an anonymous window.onload function. Both methods are causing identical onload conflicts with this set of functions.
I am loading these functions from within the same file as the addLoadEvent function itself. I'm also using calender.js which is a third party file which uses mootools 1.2.4 in an additional file. My files are otherwise free of Javascript.
First, could someone verify I've not damaged the code and I'm using it right. Second could someone suggest why the above is not resolving the conflicts?
edit
The problem persists with all other Javascript files disabled.
Your code is fine. The problem is that setting event handlers in the DOM 0 way doesn't ensure that they won't replaced by other code.
You may try the new W3C standard addEventListener and the IE version attachEvent, because the handlers you attach by them cannot be replaced by 3rd party code.
// window.onload W3C cross-browser with a fallback
function addLoadEvent(func) {
if (window.addEventListener)
window.addEventListener("load", func, false);
else if (window.attachEvent)
window.attachEvent("onload", func);
else { // fallback
var old = window.onload;
window.onload = function() {
if (old) old();
func();
};
}
}
Note, that IE will execute the function in reversed order not in the order you added them (if this is a concern).
Finally, I don't know when you want to run your code, but if you don't want to wait for images to load you can execute your functions earlier then window.onload.
Dean Edwards has a nice script which will let you to do that.
With this you can attach your functions for an earlier event: document.ready (DOMContentLoaded)
// document.ready
function addLoadEvent(func) {
if (typeof func == "function") {
addLoadEvent.queue.push(func);
}
}
addLoadEvent.queue = [];
//////////////////////////////////////////////////////////////////////////////
// Dean Edwards/Matthias Miller/John Resig
function init() {
// quit if this function has already been called
if (arguments.callee.done) return;
// flag this function so we don't do the same thing twice
arguments.callee.done = true;
// kill the timer
if (_timer) clearInterval(_timer);
// do stuff: execute the queue
var que = addLoadEvent.queue;
var len = que.length;
for(var i = 0; i < len; i++) {
if (typeof que[i] == "function") {
que[i]();
}
}
};
/* for Mozilla/Opera9 */
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", init, false);
}
/* for Internet Explorer */
/*#cc_on #*/
/*#if (#_win32)
document.write("<script id=__ie_onload defer src=javascript:void(0)>"
+"<\/script>");
var script = document.getElementById("__ie_onload");
script.onreadystatechange = function() {
if (this.readyState == "complete") {
init(); // call the onload handler
}
};
/*#end #*/
/* for Safari */
if (/WebKit/i.test(navigator.userAgent)) { // sniff
var _timer = setInterval(function() {
if (/loaded|complete/.test(document.readyState)) {
init(); // call the onload handler
}
}, 10);
}
/* for other browsers */
window.onload = init;
Note: the usage is the same for both methods as it was for your version.
​

Javascript - How to detect if document has loaded (IE 7/Firefox 3)

I want to call a function after a document loads, but the document may or may not have finished loading yet. If it did load, then I can just call the function. If it did NOT load, then I can attach an event listener. I can't add an eventlistener after onload has already fired since it won't get called. So how can I check if the document has loaded? I tried the code below but it doesn't entirely work. Any ideas?
var body = document.getElementsByTagName('BODY')[0];
// CONDITION DOES NOT WORK
if (body && body.readyState == 'loaded') {
DoStuffFunction();
} else {
// CODE BELOW WORKS
if (window.addEventListener) {
window.addEventListener('load', DoStuffFunction, false);
} else {
window.attachEvent('onload', DoStuffFunction);
}
}
There's no need for all the code mentioned by galambalazs. The cross-browser way to do it in pure JavaScript is simply to test document.readyState:
if (document.readyState === "complete") { init(); }
This is also how jQuery does it.
Depending on where the JavaScript is loaded, this can be done inside an interval:
var readyStateCheckInterval = setInterval(function() {
if (document.readyState === "complete") {
clearInterval(readyStateCheckInterval);
init();
}
}, 10);
In fact, document.readyState can have three states:
Returns "loading" while the document is loading, "interactive" once it is finished parsing but still loading sub-resources, and "complete" once it has loaded.
-- document.readyState at Mozilla Developer Network
So if you only need the DOM to be ready, check for document.readyState === "interactive". If you need the whole page to be ready, including images, check for document.readyState === "complete".
No need for a library. jQuery used this script for a while, btw.
http://dean.edwards.name/weblog/2006/06/again/
// Dean Edwards/Matthias Miller/John Resig
function init() {
// quit if this function has already been called
if (arguments.callee.done) return;
// flag this function so we don't do the same thing twice
arguments.callee.done = true;
// kill the timer
if (_timer) clearInterval(_timer);
// do stuff
};
/* for Mozilla/Opera9 */
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", init, false);
}
/* for Internet Explorer */
/*#cc_on #*/
/*#if (#_win32)
document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
var script = document.getElementById("__ie_onload");
script.onreadystatechange = function() {
if (this.readyState == "complete") {
init(); // call the onload handler
}
};
/*#end #*/
/* for Safari */
if (/WebKit/i.test(navigator.userAgent)) { // sniff
var _timer = setInterval(function() {
if (/loaded|complete/.test(document.readyState)) {
init(); // call the onload handler
}
}, 10);
}
/* for other browsers */
window.onload = init;
You probably want to use something like jQuery, which makes JS programming easier.
Something like:
$(document).ready(function(){
// Your code here
});
Would seem to do what you are after.
if(document.readyState === 'complete') {
DoStuffFunction();
} else {
if (window.addEventListener) {
window.addEventListener('load', DoStuffFunction, false);
} else {
window.attachEvent('onload', DoStuffFunction);
}
}
If you actually want this code to run at load, not at domready (ie you need the images to be loaded as well), then unfortunately the ready function doesn't do it for you. I generally just do something like this:
Include in document javascript (ie always called before onload fired):
var pageisloaded=0;
window.addEvent('load',function(){
pageisloaded=1;
});
Then your code:
if (pageisloaded) {
DoStuffFunction();
} else {
window.addEvent('load',DoStuffFunction);
}
(Or the equivalent in your framework of preference.) I use this code to do precaching of javascript and images for future pages. Since the stuff I'm getting isn't used for this page at all, I don't want it to take precedence over the speedy download of images.
There may be a better way, but I've yet to find it.
Mozila Firefox says that onreadystatechange is an alternative to DOMContentLoaded.
// alternative to DOMContentLoaded
document.onreadystatechange = function () {
if (document.readyState == "complete") {
initApplication();
}
}
In DOMContentLoaded the Mozila's doc says:
The DOMContentLoaded event is fired when the document has been
completely loaded and parsed, without waiting for stylesheets, images,
and subframes to finish loading (the load event can be used to detect
a fully-loaded page).
I think load event should be used for a full document+resources loading.
The above one with JQuery is the easiest and mostly used way. However you can use pure javascript but try to define this script in the head so that it is read at the beginning. What you are looking for is window.onload event.
Below is a simple script that I created to run a counter. The counter then stops after 10 iterations
window.onload=function()
{
var counter = 0;
var interval1 = setInterval(function()
{
document.getElementById("div1").textContent=counter;
counter++;
if(counter==10)
{
clearInterval(interval1);
}
},1000);
}
Try this:
var body = document.getElementsByTagName('BODY')[0];
// CONDITION DOES NOT WORK
if ((body && body.readyState == 'loaded') || (body && body.readyState == 'complete') ) {
DoStuffFunction();
} else {
// CODE BELOW WORKS
if (window.addEventListener) {
window.addEventListener('load', DoStuffFunction, false);
} else {
window.attachEvent('onload',DoStuffFunction);
}
}
I have other solution, my application need to be started when new object of MyApp is created, so it looks like:
function MyApp(objId){
this.init=function(){
//.........
}
this.run=function(){
if(!document || !document.body || !window[objId]){
window.setTimeout(objId+".run();",100);
return;
}
this.init();
};
this.run();
}
//and i am starting it
var app=new MyApp('app');
it is working on all browsers, that i know.

Categories

Resources