I have some web pages that load an external JavaScript file like this:
<script src="sorttable.js"></script>
This package comes from here: sorttable
I reference it in an onload function like this:
<script type="text/javascript">
window.onload = function() { sorttable.innerSortFunction.apply(document.getElementById("Symbol-2"), []); }
</script>
This works perfectly on Firefox and Chrome, but on IE version 9.0.2 it fails with these messages:
HTML1113: Document mode restart from IE9 Standards to Quirks
SEC7111: HTTPS security is compromised by javascript:void(0)
SCRIPT5007: Unable to get value of the property 'apply': object is null or undefined
This is an internal website, and 9.0.2 is the version my company deploys, and I cannot upgrade to a newer version.
Can I make this work on IE as well as the other browsers?
It looks like the SortTable library is using some sort of hacky browser detection in an attempt to initialize the library at the earliest possible time:
(excerpt from library source code)
/* 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") {
sorttable.init(); // call the onload handler
}
};
/*#end #*/
It looks like IE is rejecting this because of the attempt to use a script with the URL javascript:void(0) on a page accessed over HTTPS.
The library also has a catchall to use the onload handler if it doesn't have a browser-specific approach for the initialization:
window.onload = sorttable.init;
but you are overwriting the onload handler with your own, so this never executes.
I think the simplest solution is just to modify your onload handler to perform the initialization:
window.onload = function() {
sorttable.init();
sorttable.innerSortFunction.apply(document.getElementById("Symbol-2"), []);
};
and you should be all set. The init() method has an internal check to prevent it from performing the initialization twice, so you don't need to worry about issues from calling it if it has already been called.
You most likely need to set your doctype correctly. If you're using <!DOCTYPE html>, then try adding
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
or
<meta http-equiv="X-UA-Compatible" content="IE=9">
to your <head>.
Also, make sure there is nothing occurring before the doctype. Including whitespace and newlines. Check the output of the html, not the source from your server-side code.
Otherwise fall back to a different doctype and re-test your other browsers.
It is a security issue with IE although this might fix it:
<script type="text/javascript">
sorttable.innerSortFunction.apply(document.getElementById("Symbol-2"), []);
</script>
scripts are already synchronous, but if that doesn't work try moving the script to after the body tag
Related
I'm having some troubles with an iOS Smart App Banner, which I'm trying to add through JavaScript.
The actual smartbanner is as simple as adding this little block to the head of the HTML:
<meta name="apple-itunes-app" content="app-id=375380948">
Unfortunately, I'm quite restricted in the way I can upload my script. I can't change the HTML directly, so I'll do it through our Tag Manager, which basically does it through JavaScript. But it turns out that this doesn't work.
I've tried to simplify the case for testing:
Hardcoded tag in the HTML: works (as expected)
<meta name="apple-itunes-app" content="app-id=375380948">
Inserted with JavaScript directly when the document is ready: works
$(document).ready(function(){
$("head").append('<meta name="apple-itunes-app" content="app-id=375380948">');
});
Inserted with JavaScript, after a setTimeout delay: DOES NOT WORK
$(document).ready(function(){
setTimeout(showBanner, 1); //after 1 millisecond
});
function showBanner(){
$("head").append('<meta name="apple-itunes-app" content="app-id=375380948">');
}
Can anyone confirm or explain why this delayed JavaScript doesn't work?
Important: for testing open the page on an actual iOS device!
Desktop Safari/Chrome or iOS emulator won't work.
Also, don't close the banner, because it won't show up a second time.
UPDATE:
I've added some examples without jQuery, so plain 'ol JavaScript. But the results are the same. As soon as we wait for a setTimeout() the Smart App Banner fails to load.
Vanilla JavaScript - direct execution. works
showBanner();
function showBanner() {
var meta = document.createElement("meta");
meta.name = "apple-itunes-app";
meta.content = "app-id=375380948";
document.head.appendChild(meta);
}
Vanilla JavaScript - delayed execution. DOES NOT WORK
setTimeout(showBanner, 1);
function showBanner() {
var meta = document.createElement("meta");
meta.name = "apple-itunes-app";
meta.content = "app-id=375380948";
document.head.appendChild(meta);
}
UPDATE 2:
The exact same unfortunate behavior can be observed when loading the script asynchronously
async loading. DOES NOT WORK
<script src="async.js" async></script>
Which then calls the same vanilla JavaScript with direct execution
showBanner();
function showBanner() {
var meta = document.createElement("meta");
meta.name = "apple-itunes-app";
meta.content = "app-id=375380948";
document.head.appendChild(meta);
}
CONCLUSION:
I can only conclude that iOS Safari only looks for the smartbanner the HTML in the primary thread. And that makes me sad :-(
Only the directly available HTML, or HTML that is added synchronously through JavaScript.
But no a-sync action is allowed, be it loading the JavaScript asynchronously, or using setTimeout() (or an other construct that uses eval())
Since it is not possible to have safari show the native banner if it is inserted after the page load (As tested above, and confirmed by apple), the only viable workaround is not to use the native smartbanner, but to create your own.
For instance with a javascript plugin like this
As of April 2017, the smart banner will show up if added later via JavaScript.
I'm encountering a strange issue. I am developing a books application and using javascript onload. I read somewhere that its best to include your javascript at the end of the html. This works for most of the html loaded. However some complain that onload init() not found. This gets solved if i include the javascript in the html head. But than other htmls start behaving strangely. onload gets called before the page is fully loaded. i dont get the correct scroll width. Please suggest what could be worng. Whats the best way of including javascripts. Thanks
html is as follows
columizer id use css column-width which i've defined like this.
css style below
#columnizer
{
width:290px;
height:450px;
column-width:290px;
column-gap:10px;
word-wrap:break-word;
}
Javascript onload is defined like this.
function init()
{
docScrollWidth = document.getElementById('columnizer').scrollWidth;
document.getElementsByTagName('body')[0].style.width = docScrollWidth + "px";
window.external.notify(str);
}
Since the actual answer was in my comment, I'll add that to my answer:
My guess is that you're doing something like window.onload = init(); instead of window.onload = init; and the init function will have to be declared before you do that assignment. You assign function references without the parens. Using the parens causes it to get executed immediately.
You say you're using this code:
docScrollWidth = document.getElementsByTagName('body')[0].style.width
The main problem with this is that style.width ONLY reads a style attribute set directly on the body object. It doesn't get the width of the object as calculated by layout or CSS rules.
So, what you should use instead really depends upon what you're trying to do. The body width will nearly always be the same or more than the window width unless your content is entirely fixed width. So, that makes me wonder what you're trying to accomplish here? What you should use instead depends upon what you're really trying to do.
FYI, document.body is a direct reference to the body object so you don't need document.getElementsByTagName('body')[0].
First, let me define the problem. The window.onload event is used by programmers to kick-start their web applications. This could be something trivial like animating a menu or something complex like initialising a mail application. The problem is that the onload event fires after all page content has loaded (including images and other binary content). If your page includes lots of images then you may see a noticeable lag before the page becomes active. What we want is a way to determine when the DOM has fully loaded without waiting for all those pesky images to load also.
Mozilla provides an (undocumented) event tailor-made for this: DOMContentLoaded. The following code will do exactly what we want on Mozilla platforms:
// for Mozilla browsers
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", init, false);
}
So what about Internet Explorer?
IE supports a very handy (but non-standard) attribute for the tag: defer. The presence of this attribute will instruct IE to defer the loading of a script until after the DOM has loaded. This only works for external scripts however. Another important thing to note is that this attribute cannot be set using script. That means you cannot create a script using DOM methods and set the defer attribute – it will be ignored.
Using the handy defer attribute we can create a mini-script that calls our onload handler:
<script defer src="ie_onload.js" type="text/javascript"></script>
The contents of this external script would be a single line of code to call our onload event handler:
init();
There is a small problem with this approach. Other browsers will ignore the defer attribute and load the script immediately. There are several ways round this. My preferred method is to use conditional comments to hide the deferred script from other browsers:
<!--[if IE]><script defer src="ie_onload.js"></script><![endif]-->
IE also supports conditional compilation. The following code is the JavaScript equivalent of the above HTML:
// for Internet Explorer
/*#cc_on #*/
/*#if (#_win32)
document.write("<script defer src=ie_onload.js><\/script>");
/*#end #*/
So far so good? We now need to support the remaining browsers. We have only one choice – the standard window.onload event:
// for other browsers
window.onload = init;
There is one remaining problem (who said this would be easy?). Because we are trapping the onload event for the remaining browsers we will be calling the init function twice for IE and Mozilla. To get around this we should flag the function so that it is executed only once. So our init method will look something like this:
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;
// do stuff
};
I’ve provided a sample page that demonstrates this technique.
This question is so basic, I'm certain in must be a duplicate of something, even though I've looked for something similar.
My question is basically: Where is the best place to initially register event handlers for HTML elements?
The easiest way to register an event handler is obviously to just do it inline:
<div id = "mybutton" onclick = "doSomething()">Click me</div>
But this goes against the overwhelming march towards separation of logic and content in modern web development. So, in 2012, all logic/behavior is supposed to be done in pure Javascript code. That's great, and it leads to more maintainable code. But you still need some initial hook that hooks up your HTML elements with your Javascript code.
Usually, I just do something like:
<body onload = "registerAllEventHandlers()">
But... that's still "cheating", isn't it - because we're still using inline Javascript here. But what other options do we have? We can't do it in a <script> tag in the <head> section, because at that point we can't access the DOM since the page hasn't loaded yet:
<head>
<script type = "text/javascript">
var myButton = document.getElementById("mybutton"); // myButton is null!
</script>
</head>
Do we place a <script> tag at the bottom of the page or something? Like:
<html>
<body>
...
...
<script type = "text/javascript">
registerAllEventHandlers();
</script>
</body>
</html>
What is the best practice here?
You can use window.onload:
<script type = "text/javascript">
window.onload = registerAllEventHandlers;
</script>
Or if you use jquery:
$(registerAllEventHandlers);
Using onload works because it registers onload event immediately but fires it when DOM is ready.
I had a similar answer to this but was about JavaScript in general. But the idea is still the same - load scripts before closing the body.
Take advantage of libraries that abstract the window.onload and the DOM ready event. That way, you can load the scripts as soon as the DOM is ready.
Personally, I have no problems with adding onlclick="doSomething();" to elements. No logic, just a function call.
All logic is where it should be: in the function defined in the HEAD or a separate file.
Tell me what the difference is when you add href="somepage.html" or even href="somepage.html#someanchor" to an A tag.
You should register your event handlers as soon as the DOM is ready. Detecting this across all browsers hasn't always been easy, although with the notable exception of IE 8 (and earlier) most widely used browsers now support the DOMContentLoaded event (thanks to gengkev for pointing that out in the comments).
This is essentially equivalent to calling your registerAllEventHandlers function at the end of your body, but it has the advantage that you don't need to add any JavaScript to your HTML.
It is significantly better than using window.onload because that isn't executed until all of the page's assets (images, CSS etc.) have loaded.
If you're using one of the major JavaScript frameworks, then you can very easily detect when the DOM is ready, even in older versions of IE. With jQuery you would use the ready event:
jQuery(document).ready(function () {
// Your initialisation code here
});
Or the shorthand:
jQuery(function() { … });
With Prototype you would use the dom:loaded event:
document.observe("dom:loaded", function() {
// Your initialisation code here
});
I use this code to load my JS async in the head
<script type='text/javascript'>
// Add a script element as a child of the body
function downloadJSAtOnload() {
var element4= document.createElement("script");
var element5= document.createElement("script");
element4.src="http:///ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"
element5.src="http://yourjavascript.com/301810712121/slidemenu_horiz.js"
element4.async=true;
element5.async=true;
document.body.appendChild(element4);
document.body.appendChild(element5);
}
// 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;
</script>
In IE and Firefox works fine, but in Chrome I have this error:
"Uncaught ReferenceError: jQuery is not defined "
When I refresh the page for second time (or third) the script works fine in Chrome, please I need to know how to resolve this.
Given your needs, and because I've used it successfully in the past I'd suggest using LABjs - http://labjs.com/
As mentioned, there are loads of script loaders to choose from - LABjs is focused on performance more than anything and doesn't include many of the extra features that others such as requirejs (AMD loader), YepNope (feature detection-based conditional loader) have. If all you need is to load your scripts asynchronously and have control over the execution order, LABjs is a very small script that handles this well.
Using LABjs, you'd do the following to replicate your code above:
<script src="js/libs/LAB.js"></script>
<script>
$LAB
.script('http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js').wait()
.script('http://yourjavascript.com/301810712121/slidemenu_horiz.js')
.wait(function () {
// Check jQuery has loaded (could do this for the slider as well)
if (window.jQuery) {
// Do something with your slider
}
});
</script>
In the example above, the .wait() function ensures that jQuery has executed before slidemenu_horiz.js - the last .wait() is passed an anonymous function as a callback - within this you can test that everything has loaded and then do your initialisations.
It's worth checking out all the options as far a script loaders go. There really are loads out there and each has a different feature set that you may find addresses your problem better.
EDIT: Added script reference to LABjs in code example for clarity
Can you confirm my understanding of HTML5's <script async> attribute?
Any libraries referenced by other code in the page should not specify the async attribute.
For example, my script references might appropriately look like:
<script src="jquery..." /> <!-- async not used - ensure that this is loaded before JQuery UI and my code -->
<script src="jquery.ui..." /> <!-- async not used - ensure that this is loaded before my code -->
<script src="my_code1.js" async /> <!-- async used, for page load performance -->
<script src="my_code2.js" async /> <!-- async used, for page load performance -->
For any code in a $(document).ready(function () { } block, I can be assured that all async script have already loaded.
Do I have this right?
As with all new HTML5 features, I think the best way to find answers is to test them on as many current browsers as we can. As a general rule, old browsers should completely ignore the async flag, so code should work as expected, parsed from top to bottom in order.
As long as browsers are inconsistent in handling them, you should avoid using them in production code if you're not sure they will work.
The main question with this feature is, browsers that do support it, in what order do events get fired, for example if you define a jQuery ready function in an async loaded script, is it going to get fired? Does your ready event fire before or after async scripts have loaded?
I have created a couple of test files, you are quite welcome to have a play with them on different browsers to see how they behave.
Short Answer
About #Dave's assumption:
For any code in a $(document).ready(function(){} block, I can be assured that all async script have already loaded.
It doesn't look like it so far, it's pretty inconsistent. In Chrome the main ready event fires before the async file has loaded, but in Firefox it fires after it.
jQuery developers will have to make up their minds about this, if they will (and can) support it in the future or not.
Test Page
My test script spits out a string which shows you in what order were different parts of it executed. It can be built by the following:
D: It means the script block in the main file got executed. It can be
followed by :ok if the function in
the async loaded script if defined,
or :undefined if it's not.
R: It means the jQuery ready event in the main file got executed.
It can be followed by :ok if the
function in the async loaded script
if defined, or :undefined if it's
not.
L: Async loaded script file has been executed.
AR: jQuery ready event in the async loaded script has been
executed.
Test Results
Browsers supporting async:
Google Chrome 11.0.696.68: D:undefined R:undefined L AR
Firefox 4.0.1: D:undefined L R:ok AR
Browsers supporting async but tested without async (expecting same results):
Google Chrome 11.0.696.68: L D:ok AR R:ok
Firefox 4.0.1: L D:ok AR R:ok
Browsers NOT supporting async (expecting same results):
Internet Explorer 9: L D:ok AR R:ok
Opera 11.11: L D:ok AR R:ok
Test Script
test.html
<!DOCTYPE HTML>
<head>
<title>Async Test</title>
<script type="text/javascript">
var result = "";
</script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script type="text/javascript" src="test.js" async></script>
<script type="text/javascript">
try{
myFunc();
result += "D:ok ";
} catch(ex) { result += "D:undefined "; }
$(function(){
try{
myFunc();
result += "R:ok ";
} catch(ex) { result += "R:undefined "; }
$('body').text(result);
});
</script>
</head>
<body>
</body>
</html>
test.js
// Fires straight away when this file is loaded.
result += "L ";
$('body').text(result);
// A test function to see if it's defined in certain parts of the main file.
function myFunc(){
return;
}
// A ready function to see if it fires when loaded async.
$(function(){
result += "AR ";
$('body').text(result);
});
This question has bothered me too for quiet some time now.
So I just finished writing "jqinit.js". The purpose of it is managing the dependencies in a way that you can just put them into your html as you did. And you can load jquery with async, too.
It works mainly by checking if jquery has been loaded and delaying execution of you script until it has. As a bonus you can manage dependencies between your scripts, too. And it can be loaded async itself.
Have a look if it fits your needs (feedback welcome): https://github.com/ScheintodX/jqinit.js