Make IE7 load javascript first then call function - javascript

IE7 gives the following error: 'myapp' is undefined
//home.html
<script type="application/x-javascript" src="//www.mysite.com/myjs.js"></script>
<script type="text/javascript" charset="utf-8">
new myapp.myfunc();
</script>
javascript file:
//myjs.js
myapp = {
myfunc : function(){
alert('hello world');
}
};
*I understand there are many ways to rewrite the code that is used on home.html, but I want to make this work without changing that. I found a working example with similar structure and no JS errors (IE7, IE6). https://google-developers.appspot.com/custom-search-ads/docs/sample
EDIT:
The < script > code will be given to external clients, so I want to keep it as simple as possible. See example link.

Occam's razor suggests that either IE/MSHTML does not support script elements with type="application/x-javascript". That might have to do with the fact that application/x-javascript is not a registered MIME media type, nor was it ever necessary.
Or it has to do with the fact that //www.mysite.example.com/myjs.js is not a supported URI-reference in that environment. Use fully-qualified URIs like http://www.mysite.example.com/myjs.js instead. (And please use the registered example domains for examples.)
You should also declare identifiers that you intend to use as variables:
var myapp = {
…
};
If you do not do this, problems can occur if there is an element named myapp in the document. In JScript/MSHTML, identifier resolution will find a host object in the scope chain that has myapp as its property. The value of that property will be a reference to the corresponding element object, and attempting to overwrite that property value will cause a runtime error.

Add an event handler to the body's Load event. In that event handler, make your myapp.myfunc() call.

Whenever you're making your code available for consumption, you always want to make sure you're being a good citizen on the page. That means you don't want to create unnecessary global variables, and make sure ones you do create are unique enough. Thats why it's a good idea to wrap your code in an immediately-invoked function expression.
Also, it's generally just easier to do the whole thing with javascript. This is how Google analytics and Facebook plugins load their code.
<script type="text/javascript">
(function() {
var loadScript = function(src, callback) {
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = src;
// modern browsers
script.onload = callback;
// IE 6 & 7
script.onreadystatechange = function() {
if (this.readyState == 'complete') {
callback();
}
}
head.appendChild(script);
};
loadScript('path/to/myscript.js', function() {
//script loaded
});
})();
</script>

Related

How to use 'let' (and supported ECMAScript 6 features) in both Firefox and Chrome

ES6 script using let runs as expected in latest Chrome stable if it's inside a "use strict" definition. And it runs fine in Firefox if it is loaded using a script tag with the special type:
<script type="application/javascript;version=1.7" src=""></script>
But files with that special type now won't run in Chrome! In Chrome no script runs: silent failure, no console messages. What is the cross-browser solution? (I want to know if this can be done without transpiling.)
The naïve solution, assuming you have control over the script itself, is to set some global within the script and check if it exists later. Iff it doesn't, replace this script element with one without the special MIME type so it can run on other browsers. The global can safely be ignored after that.
<script type="application/javascript;version=1.7">
'use strict';
window.fx = true;
let foo = 'bar';
console.log(foo);
</script>
<script>
if (typeof window.fx === 'undefined') {
var oldScript = document.querySelector('script[type="application/javascript;version=1.7"]');
var text = oldScript.text;
document.body.removeChild(oldScript);
var newScript = document.createElement('script');
newScript.text = text;
document.body.appendChild(newScript);
}
</script>
The main disadvantage here is the extra HTTP request that may be incurred when referencing an external script, especially if the script is not cached. Since Firefox is the only browser on which you can guarantee that the script will never be requested more than once per page load, this has the potential of being incredibly wasteful on other browsers.
As an alternative, you can use feature detection as mentioned in the comments to insert the script element either with or without the type attribute rather than including it in the HTML source to start with. According to MDN's compatibility table, basic support is available starting from Firefox 2.0 (in JavaScript 1.7), so you can pick pretty much any Firefox-specific feature from whichever version you wish to extend your support to.
var script = document.createElement('script');
script.text = '"use strict"; let foo = "bar"; console.log(foo);';
try {
document.querySelector('::-moz-selection');
script.type = 'application/javascript;version=1.7';
} catch (e) {
// Not Firefox, leave MIME type unchanged
}
document.body.appendChild(script);

Callback function from URL is not working [duplicate]

Any idea why the piece of code below does not add the script element to the DOM?
var code = "<script></script>";
$("#someElement").append(code);
The Good News is:
It's 100% working.
Just add something inside the script tag such as alert('voila!');. The right question you might want to ask perhaps, "Why didn't I see it in the DOM?".
Karl Swedberg has made a nice explanation to visitor's comment in jQuery API site. I don't want to repeat all his words, you can read directly there here (I found it hard to navigate through the comments there).
All of jQuery's insertion methods use
a domManip function internally to
clean/process elements before and
after they are inserted into the DOM.
One of the things the domManip
function does is pull out any script
elements about to be inserted and run
them through an "evalScript routine"
rather than inject them with the rest
of the DOM fragment. It inserts the
scripts separately, evaluates them,
and then removes them from the DOM.
I believe that one of the reasons jQuery
does this is to avoid "Permission
Denied" errors that can occur in
Internet Explorer when inserting
scripts under certain circumstances.
It also avoids repeatedly
inserting/evaluating the same script
(which could potentially cause
problems) if it is within a containing
element that you are inserting and
then moving around the DOM.
The next thing is, I'll summarize what's the bad news by using .append() function to add a script.
And The Bad News is..
You can't debug your code.
I'm not joking, even if you add debugger; keyword between the line you want to set as breakpoint, you'll be end up getting only the call stack of the object without seeing the breakpoint on the source code, (not to mention that this keyword only works in webkit browser, all other major browsers seems to omit this keyword).
If you fully understand what your code does, than this will be a minor drawback. But if you don't, you will end up adding a debugger; keyword all over the place just to find out what's wrong with your (or my) code. Anyway, there's an alternative, don't forget that javascript can natively manipulate HTML DOM.
Workaround.
Use javascript (not jQuery) to manipulate HTML DOM
If you don't want to lose debugging capability, than you can use javascript native HTML DOM manipulation. Consider this example:
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "path/to/your/javascript.js"; // use this for linked script
script.text = "alert('voila!');" // use this for inline script
document.body.appendChild(script);
There it is, just like the old days isn't it. And don't forget to clean things up whether in the DOM or in the memory for all object that's referenced and not needed anymore to prevent memory leaks. You can consider this code to clean things up:
document.body.removechild(document.body.lastChild);
delete UnusedReferencedObjects; // replace UnusedReferencedObject with any object you created in the script you load.
The drawback from this workaround is that you may accidentally add a duplicate script, and that's bad. From here you can slightly mimic .append() function by adding an object verification before adding, and removing the script from the DOM right after it was added. Consider this example:
function AddScript(url, object){
if (object != null){
// add script
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "path/to/your/javascript.js";
document.body.appendChild(script);
// remove from the dom
document.body.removeChild(document.body.lastChild);
return true;
} else {
return false;
};
};
function DeleteObject(UnusedReferencedObjects) {
delete UnusedReferencedObjects;
}
This way, you can add script with debugging capability while safe from script duplicity. This is just a prototype, you can expand for whatever you want it to be. I have been using this approach and quite satisfied with this. Sure enough I will never use jQuery .append() to add a script.
I've seen issues where some browsers don't respect some changes when you do them directly (by which I mean creating the HTML from text like you're trying with the script tag), but when you do them with built-in commands things go better. Try this:
var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.src = url;
$("#someElement").append( script );
From: JSON for jQuery
It is possible to dynamically load a JavaScript file using the jQuery function getScript
$.getScript('http://www.whatever.com/shareprice/shareprice.js', function() {
Display.sharePrice();
});
Now the external script will be called, and if it cannot be loaded it will gracefully degrade.
What do you mean "not working"?
jQuery detects that you're trying to create a SCRIPT element and will automatically run the contents of the element within the global context. Are you telling me that this doesn't work for you? -
$('#someElement').append('<script>alert("WORKING");</script>');
Edit: If you're not seeing the SCRIPT element in the DOM (in Firebug for example) after you run the command that's because jQuery, like I said, will run the code and then will delete the SCRIPT element - I believe that SCRIPT elements are always appended to the body... but anyway - placement has absolutely no bearing on code execution in this situation.
This works:
$('body').append($("<script>alert('Hi!');<\/script>")[0]);
It seems like jQuery is doing something clever with scripts so you need to append the html element rather than jQuery object.
Try this may be helpful:
var fileref=document.createElement('script');
fileref.setAttribute("type","text/javascript");
fileref.setAttribute("src","scriptAnalytics.js");
document.getElementsByTagName("head")[0].appendChild(fileref);
I want to do the same thing but to append a script tag in other frame!
var url = 'library.js';
var script = window.parent.frames[1].document.createElement('script' );
script.type = 'text/javascript';
script.src = url;
$('head',window.parent.frames[1].document).append(script);
<script>
...
...jQuery("<script></script>")...
...
</script>
The </script> within the string literal terminates the entire script, to avoid that "</scr" + "ipt>" can be used instead.
Adding the sourceURL in the script file helped as mentioned in this page:
https://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/
In the script file, add a statement with sourceURL like "//# sourceURL=foo.js"
Load the script using jQuery $.getScript() and the script will be available in "sources" tab in chrome dev tools
Your script is executing , you just can't use document.write from it. Use an alert to test it and avoid using document.write. The statements of your js file with document.write will not be executed and the rest of the function will be executed.
This is what I think is the best solution. Google Analytics is injected this way.
var (function(){
var p="https:" == document.location.protocol ? "https://" : "http://";
d=document,
g=d.createElement('script'),
s=d.getElementsByTagName('script')[0];
g.type='text/javascript';
g.src=p+'url-to-your-script.js';
s.parentNode.insertBefore(g,s); })();
You don't need jQuery to create a Script DOM Element. It can be done with vanilla ES6 like so:
const script = "console.log('Did it work?')"
new Promise((resolve, reject) => {
(function(i,s,o,g,r,a,m){
a=s.createElement(o),m=s.getElementsByTagName(o)[0];
a.innerText=g;
a.onload=r;m.parentNode.insertBefore(a,m)}
)(window,document,'script',script, resolve())
}).then(() => console.log('Sure did!'))
It doesn't need to be wrapped in a Promise, but doing so allows you to resolve the promise when the script loads, helping prevent race conditions for long-running scripts.
Append script to body:
$(document).ready(function() {
$("<script>", { src : "bootstrap.min.js", type : "text/javascript" }).appendTo("body");
});
Another way you can do it if you want to append code is using the document.createElement method but then using .innerHTML instead of .src.
var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.innerHTML = 'alert("Hey there... you just appended this script to the body");';
$("body").append( script );
I tried this one and works fine. Just replace the < symbol with that \x3C.
// With Variable
var code = "\x3Cscript>SomeCode\x3C/script>";
$("#someElement").append(code);
or
//Without Variable
$("#someElement").append("\x3Cscript>SomeCode\x3C/script>");
You can test the code here.
Can try like this
var code = "<script></" + "script>";
$("#someElement").append(code);
The only reason you can't do "<script></script>" is because the string isn't allowed inside javascript because the DOM layer can't parse what's js and what's HTML.
I wrote an npm package that lets you take an HTML string, including script tags and append it to a container while executing the scripts
Example:
import appendHtml from 'appendhtml';
const html = '<p>Hello</p><script src="some_js_file.js"></script>';
const container = document.getElementById('some-div');
await appendHtml(html, container);
// appendHtml returns a Promise, some_js_file.js is now loaded and executed (note the await)
Find it here: https://www.npmjs.com/package/appendhtml
Just create an element by parsing it with jQuery.
<div id="someElement"></div>
<script>
var code = "<script>alert(123);<\/script>";
$("#someElement").append($(code));
</script>
Working example: https://plnkr.co/edit/V2FE28Q2eBrJoJ6PUEBz

Avoiding jQuery conflicts in a dynamically loaded JavaScript application

I am currently trying to develop a JavaScript application that can be embedded in an existing webpage (which I cannot modify). The application needs a specific version of jQuery.
The script used for loading the application is doing the following:
// loading JavaScript needed by my application
(function () {
document.write('<script src="../jquery-1.10.2.min.js"></script>');
document.write(...); // load other scripts (using jQuery 1.10.2)
// storing the application's jQuery in a new namespace
// to avoid conflicts with other jQuery versions
document.write('<script type="text/javascript">' +
'appJQ = jQuery.noConflict(true);</script>'); // error: jQuery is undefined in IE<10
})();
// initializing the application itself
document.onload = function() {
// ...
};
This works fine in every browser I've tested, except IE < 10. In IE 9 and lower I am getting the error that jQuery is undefined.
Moving jQuery to a new namespace in the document.onload function would work for my application but causes conflicts with other scripts on the webpage that includes my application if they need a different version of jQuery.
Do you have any suggestions how to solve this problem?
Thanks for your help!
Instead of using document.write, try creating a <script> element and defining an onload handler for that element:
(function () {
var script = document.createElement('script');
script.src = '//cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js';
script.onload = function() {
var appJQ = jQuery.noConflict(true);
// app initialization code
};
var head = document.head || document.getElementsByTagName('head')[0];
head.appendChild(script);
})();
If you have multiple scripts that depend on one another, you might want to try using a script loader such as HeadJS or LABjs.
If you want even greater flexibility in managing dependencies, you can try using a module loader such as RequireJS, Browserify, or webpack.
Following Hamza's answer, you can use this method of loading in a different way:
var s = document.createElement("script");
s.src = "../jquery.min.js" // insert your own jQuery link
s.onload = function() {
var appJS = jQuery.noConflict(true);
}
Hope this helps.
The document.write calls would also happen after the document is considered loaded, in which case your onload function might fire before the contents of that first function. You want to load the javascript files in a better way, such that their onload initiates the application itself.

JS files out of order

I'm having a very frustrating problem with my javascript files. I have one global namespace that contains a page_data, utilities, modules etc namespace.
My directories look like this:
/utilities/single_utility.js
/modules/module.js etc tec
Anyways, I load the utilities before the modules, (which use the utilities) but keep getting problems.
My loader script looks like this (its wrapped in an SEAF):
for (var index in file_list) {
var url = file_list[index];
var new_script = document.createElement('script');
new_script.setAttribute("type", "text/javascript");
new_script.setAttribute("src", url);
element.appendChild(new_script);
}
Project is my global namespace, that holds all of these other namespaces. But when I try to reference a utility in one of my modules, with Project.utilities.session.exist() etc, it will sometimes throw an error that Project can't be found?
How can I fix this to make sure my files are loading properly or am I doing something else wrong?
Using async = false should protect your load order.
This is a quick snippet I use to load and maintain order.
var loadScript = function( url ) {
if (url) {
var doc = document,
t = doc.createElement("script"),
s = doc.getElementsByTagName("script")[0];
t.type = "text/javascript";
// Keep script order!
t.async = false;
t.src = url;
s.parentNode.insertBefore(t, s);
}
};
Some references backing this logic (and regarding browser support) from MDN and Microsoft
When you add the script tag to the page, the browser has to go out and download those scripts. There's no guarantee that they will download and execute in the same order that you created them.
You will need to add an event listener to new_script for when it loads, either onload or onreadystatechange, depending on the browser. But truly I'd recommend using a script loading library (such as RequireJS) that handles all the nasty details for you.
As an aside, why not just add all your script tags directly to your page? In that case they load sequentially.
It's better if you resolve this problem using a script loader.
As Matt said RequireJS is a good option if you also want to handle script dependencies.
For script loading only, you can take a look into LAB.js, is very small and straightforward:
for (var index in file_list) {
$LAB.script(file_list[index]);
}
If the scripts have dependencies and must be loaded in order, you can use .wait(), to do that keep the chain object returned by script. You can re-write the loop to keep the chain:
var chain;
for (var index in file_list) {
if (!chain) {
chain = $LAB.script(file_list[index]).wait();
} else {
chain.script(file_list[index]).wait();
}
}
Or just forget the file_list array and use $LAB directly:
$LAB.script('file1.js').wait()
.script('file2.js').wait()
/* ... */

Injecting JS functions into the page from a Greasemonkey script on Chrome

I have a Greasemonkey script that works just fine in Firefox and Opera. I struggle with getting it to work in Chrome, however. The problem is injecting a function into the page that can be invoked by code from the page. Here's what I'm doing so far:
First, I get a helper reference to the unsafeWindow for Firefox. This allows me to have the same code for FF and Opera (and Chrome, I thought).
var uw = (this.unsafeWindow) ? this.unsafeWindow : window;
Next, I inject a function into the page. It's really just a very thin wrapper that does nothing but invoking the corresponding function in the context of my GM script:
uw.setConfigOption = function(newValue) {
setTimeout(setConfigOption, 0, newValue);
}
Then, there's the corresponding function right in my script:
setConfigOption = function(newValue) {
// do something with it, e.g. store in localStorage
}
Last, I inject some HTML into the page with a link to invoke the function.
var p = document.createElement('p');
p.innerHTML = 'set config option to 1';
document.getElementById('injection-point').appendChild(p);
To summarize:
In Firefox, when the user clicks that injected link, it will execute the function call on the unsafeWindow, which then triggers a timeout that invokes the corresponding function in the context of my GM script, which then does the actual processing. (Correct me if I'm wrong here.)
In Chrome, I just get a "Uncaught ReferenceError: setConfigOption is not defined" error. And indeed, entering "window.setConfigOption" into the console yields an "undefined". In Firebug and the Opera developer console, the function is there.
Maybe there's another way to do this, but a few of my functions are invoked by a Flash object on the page, which I believe makes it necessary that I have functions in the page context.
I took a quick look at the alternatives to unsafeWindow on the Greasemonkey wiki, but they all look pretty ugly. Am I completely on the wrong track here or should I look more closely into these?
RESOLUTION: I followed Max S.' advice and it works in both Firefox and Chrome now. Because the functions I needed to be available to the page had to call back into the regular ones, I moved my whole script to the page, i.e. it is completely wrapped into the function he called 'main()'.
To make the extra uglyness of that hack a little bit more bearable, I could at least drop the usage of unsafeWindow and wrappedJSObject now.
I still haven't managed to get the content scope runner from the Greasemonkey wiki working. It should do the same and it seems to execute just fine, but my functions are never accessible to <a> elements from the page, for example. I haven't yet figured out why that is.
The only way to communicate with the code running on the page in Chrome is through the DOM, so you'll have to use a hack like inserting a <script> tag with your code. Note that this may prove buggy if your script needs to run before everything else on the page.
EDIT: Here's how the Nice Alert extension does this:
function main () {
// ...
window.alert = function() {/* ... */};
// ...
}
var script = document.createElement('script');
script.appendChild(document.createTextNode('('+ main +')();'));
(document.body || document.head || document.documentElement).appendChild(script);
I have this :
contentscript.js :
function injectJs(link) {
var scr = document.createElement('script');
scr.type="text/javascript";
scr.src=link;
document.getElementsByTagName('head')[0].appendChild(scr)
//document.body.appendChild(scr);
}
injectJs(chrome.extension.getURL('injected.js'));
injected.js :
function main() {
alert('Hello World!');
}
main();
The other answers either force you to use function expressions, import an external additional file or use a long patched hack.
This answer will add the javascript into the page directly from your source code. It will use ECMAScript 6 (ES6) template literals to get the multi-line javascript string effortlessly onto the page.
var script = document.createElement('script');
script.type = "text/javascript";
script.innerHTML = `
function test() {
alert(1);
}
`;
document.getElementsByTagName('head')[0].appendChild(script);
Please note the backticks `` that define the beginning and the end of a multi-line string.
I took a quick look at the alternatives to unsafeWindow on the Greasemonkey wiki, but they all look pretty ugly. Am I completely on the wrong track here or should I look more closely into these?
You should look, because it's only available option. I'd prefer to use location hack.
myscript.user.js:
function myFunc(){
alert('Hello World!');
}
location.href="javascript:(function(){" + myFunc + "})()"
example.com/mypage.html
<script>
myFunc() // Hello World!
</script>
Sure, it's ugly. But it's working well.
Content Scope Runner method, mentioned by Max S. is better than location hack, because its easier to debug.

Categories

Resources