How do I include external javascript files in bookmarklets? - javascript

I'm making a cdn for a bunch of scripts I've written for work. I used to have to distribute them and each person get the file and install it on their computer, but I'm moving them to an amazon cloudfront/s3 instance.
I'll be using this to inject the bookmarklet: http://allben.net/post/2010/01/30/CSS-JavaScript-Injection-Bookmarklets
However, the old way I was doing this I used the jquery bookmarklet generator. This is different than that and I am unaware how to include jquery should I want to use it.
Here is an example script:
javascript:(function(){var%20s=document.createElement('script');s.setAttribute('src','cdn.domain.com/scripts/somescript.js');document.getElementsByTagName('body')[0].appendChild(s);})();
That goes in a bookmark.
The script:
alert($(somecontainer).size());
Obviously this doesn't work because the bookmarklet is no longer injecting jquery. So, what is the best way to go about this?

The problem you are facing, I guess, is that the jQuery bookmarklet generator does not make $ available within the page. It keeps the jQuery variable isolated within a function and then removes jQuery entirely from the page after running.
Below is a modified version of code from here: http://benalman.com/projects/run-jquery-code-bookmarklet/ which should work.
function (e, a, g, h, f, c, b, d) {
if (!(f = e.jQuery) || g > f.fn.jquery || h(f)) {
c = a.createElement("script");
c.type = "text/javascript";
c.src = "http://ajax.googleapis.com/ajax/libs/jquery/" + g + "/jquery.min.js";
c.onload = c.onreadystatechange = function () {
if (!b && (!(d = this.readyState) || d == "loaded" || d == "complete")) {
var s = document.createElement('script');
s.setAttribute('src', 'cdn.domain.com/scripts/somescript.js');
document.getElementsByTagName('body')[0].appendChild(s)
}
};
a.documentElement.childNodes[0].appendChild(c)
}
})(window, document, "1.3.2")
Keep in mind that this will replace any jQuery and $ variable on the page. If you need to run the bookmarklet on pages that use those variables already, use jQuery.noConflict(1). For example _jq = e.jQuery.noConflict(1) will let you use _jq instead of $ and will return the original $ and jQuery to their original values. Example:
alert(_jq(somecontainer).size());
If you want to use noConflict but also use $ in your .js code, wrap your code in a function and create a locally scoped $. Example:
(function(){
var $ = _jq; // this $ will not affect any $ that exists outside this function.
alert($(somecontainer).size());
})();

Related

Check if javascript is already attached to a page?

Is there any way to check if javascript file is already attached to the page by its file name.
For eg :
if ("path-to-script/scriptname.js") already embeded
{
call related function
}
else
{
Append '<script src="path-to-script/scriptname.js" type="text/javascript"> </script> '
call related function
}
Basically I dont want 1 script to be attached twice on the same page.
You might not always know what objects or functions a script contains in advance, in such cases you can search for script tags containing the desired src.
With jquery:
$("script[src*='"+scriptName+"']");
Without jquery:
document.querySelector("script[src*='"+scriptName+"']");
You'd need to test whether the actual function from the script file exists, like this:
if (window.function_name) {
// script loaded
} else {
// script not loaded
}
I agree with #zathrus though I think you should be using requirejs for things like this. The idea is that dependencies must be fetched before executing the code. The above method you are using may work but you can not guarantee anything.
RequireJS will beautifully maintain all the dependency loading. It is very easy to learn as well.
Simply check if the library is defined, otherwise import it:
if ( !jQuery ) {
var s = document.createElement('script');
s.type = 'text/javascript';
document.body.appendChild(s);
s.src = "path-to-script/scriptname.js";
void(0);
}
// call function
you really need a script loader.. because as you said you want it specific with the javascript resource filename and this is sort of your javascript files are depending to each other
www.headjs.com
www.modernizr.com
www.yepnopejs.com
I thought this will help you.
if (typeof scriptname== "undefined") {
var e = document.createElement("script");
e.src = "scriptname.js";
e.type = "text/javascript";
document.getElementsByTagName("head")[0].appendChild(e);
}

Why doesn't jQuery UI see jQuery?

I've built a JavaScript widget that must be embeddable on any third-party site, in any environment. The widget relies on jQuery and jQuery UI. I followed the steps in How to embed Javascript widget that depends on jQuery into an unknown environment to add jQuery in a responsible manner -- works great for embedding jQuery. But when I try to add jQuery UI, it fails. Here's the code:
(function(window, document, version, callback) {
var j, d;
var loaded = false;
if (!(j = window.jQuery) || version > j.fn.jquery || callback(j, loaded)) {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js";
script.onload = script.onreadystatechange = function() {
if (!loaded && (!(d = this.readyState) || d == "loaded" || d == "complete")) {
callback((j = window.jQuery).noConflict(1), loaded = true);
j(script).remove();
}
};
document.documentElement.childNodes[0].appendChild(script)
}
})(window, document, "1.3.2", function($, jquery_loaded) {
$.getScript('http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.js', function(){
console.log('loaded');
});
});
When I run this, I get the 'loaded' mesage, followed by an error saying that "$ is undefined" on line 15 of jquery-ui.js. But how can $ be undefined when I'm using $.getScript() to load jQuery UI? And why do I see the message 'loaded' before I get the error? According to the jQuery documentation, getScript shouldn't execute the callback until the script has been loaded and executed.
Is there any way I can use this framework to include jQuery UI, or do I need to use a script loader like RequireJS in order to load everything, enforce dependencies, etc.?
By calling .noConflict(1), the same as .noConflict(true), you're deleting jQuery, just remove the 1. The true argument to .noConflict() tells jQuery to remove not only $, but window.jQuery, which jQuery UI is trying to use afterwards, when it loads.
You can test it here, see there are no errors in the console.

Can't access variables from dynamically loaded javascript

I'm using a fairly simple system to load javascript dynamically:
include = function (url) {
var e = document.createElement("script");
e.src = url;
e.type="text/javascript";
document.getElementsByTagName("head")[0].appendChild(e);
};
Let's say I have a file test.js which has the following contents:
var foo = 4;
Now, in my original script, I want to use
include(test.js);
console.log(foo);
However, I get a 'foo has not been defined' error on this. I'm guessing it has to do with the dynamic script being included as the last child of the <head> tag. How can I get this to work?
It is because you have to wait for the script to load. It is not synchronous like you would think. This may work for you:
include = function (url, fn) {
var e = document.createElement("script");
e.onload = fn;
e.src = url;
e.async=true;
document.getElementsByTagName("head")[0].appendChild(e);
};
include("test.js",function(){
console.log(foo);
});
That is one problem, but it also takes time for the browser to download and parse the remote JS file — and it hasn't done that before you call console.log.
You need to delay things until the script has loaded.
This is easiest done by letting a library do the heavy lifting, jQuery has a getScript method that lets you pass a callback to run when the script has loaded.

Can javascript file get its own name?

I am trying to build some sort of logger functionality in javascript. Is there any API for a script to get its own filename?
This should work:
(new Error).fileName
Or you can try this:
var filepath;
(function(){
var scripts = document.getElementsByTagName('script');
filepath = scripts[ scripts.length-1 ].src;
}());
The second option gives you the path of your script file.
I see two ways:
put into every JS file a variable var filename = 'script.js';
get the filename using <script> tag name
JS can not get filename like bash/perl/c scripts.
If we can get the current script's tag, then we can read its src attribute. Excerpt from https://stackoverflow.com/a/22745553/4396817 below:
document.currentScript will return the element whose script is currently being processed.
<script>
var me = document.currentScript;
</script>
Benefits
Simple and explicit. Reliable.
Don't need to modify the script tag
Works with asynchronous scripts (defer & async)
Works with scripts inserted dynamically
Problems
Will not work in older browsers and IE.
...So from there, we can simply read the src attribute!
<script src="http://website.com/js/script.js">
alert(document.currentScript.src);
</script>
// Alerts "http://website.com/js/script.js"
Unfortunately this is not possible.
If you change your approach, getting function names may help you which is sometimes possible. Your best chance would be extracting function name from "arguments.callee". This only works if function is defined like
function FN() { ... }
And does not work when
var FN = function() { ... }
this is my modification that fixes a few possible issues, but adds a requirement.
It needs you to name the file in a certain way, so for example if you have a .js file, but you want to know which version is loaded (for example to tell a php server). so your js file would be "zoom_v34.js".
var version;
(function(){
var scripts = document.getElementsByTagName('script');
for (var i=0; i<scripts.length; i++) {
var start = scripts[i].src.indexOf('zoom_');
if (start != -1) { var end = scripts[i].src.indexOf('.',start); version = scripts[i].src.substr(start+6,end-start-6); break; }
}
}());
post='login{JS:'+version+'}';
You can try putting this at the top of your JavaScript file:
window.myJSFilename = "";
window.onerror = function(message, url, line) {
if (window.myJSFilename != "") return;
window.myJSFilename = url;
}
throw 1;
Make sure you have only functions below this. The myJSFilename global variable will contain the full path of the JavaScript file, and the filename can be parsed from that. Tested in IE11, but it should work elsewhere.

Pass vars to JavaScript via the SRC attribute

In my HTML file I have linked to the JS with:
src="myscript.js?config=true"
Can my JS directly read the value of this var like this?
alert (config);
This does not work, and the FireFox Error Console says "config is not defined". How do I read the vars passed via the src attribute in the JS file? Is it this simple?
<script>
var config=true;
</script>
<script src="myscript.js"></script>
You can't pass variables to JS the way you tried. SCRIPT tag does not create a Window object (which has a query string), and it is not server side code.
Yes, you can, but you need to know the exact script file name in the script :
var libFileName = 'myscript.js',
scripts = document.head.getElementsByTagName("script"),
i, j, src, parts, basePath, options = {};
for (i = 0; i < scripts.length; i++) {
src = scripts[i].src;
if (src.indexOf(libFileName) != -1) {
parts = src.split('?');
basePath = parts[0].replace(libFileName, '');
if (parts[1]) {
var opt = parts[1].split('&');
for (j = opt.length-1; j >= 0; --j) {
var pair = opt[j].split('=');
options[pair[0]] = pair[1];
}
}
break;
}
}
You have now an 'options' variable which has the arguments passed. I didn't test it, I changed it a little from http://code.google.com/p/canvas-text/source/browse/trunk/canvas.text.js where it works.
You might have seen this done, but really the JS file is being preprocessed server side using PHP or some other language first. The server side code will print/echo the javascript with the variables set. I've seen a scripted ad service do this before, and it made me look into seeing if it can be done with plain ol' js, but it can't.
You need to use Javascript to find the src attribute of the script and parse the variables after the '?'. Using the Prototype.js framework, it looks something like this:
var js = /myscript\.js(\?.*)?$/; // regex to match .js
var jsfile = $$('head script[src]').findAll(function(s) {
return s.src.match(js);
}).each(function(s) {
var path = s.src.replace(js, ''),
includes = s.src.match(/\?.*([a-z,]*)/);
config = (includes ? includes[1].split('=');
alert(config[1]); // should alert "true" ??
});
My Javascript/RegEx skills are rusty, but that's the general idea. Ripped straight from the scriptaculous.js file!
Your script can however locate its own script node and examine the src attribute and extract whatever information you like.
var scripts = document.getElementsByTagName ('script');
for (var s, i = scripts.length; i && (s = scripts[--i]);) {
if ((s = s.getAttribute ('src')) && (s = s.match (/^(.*)myscript.js(\?\s*(.+))?\s*/))) {
alert ("Parameter string : '" + s[3] + "'");
break;
}
}
Whether or not this SHOULD be done, is a fair question, but if you want to do it, http://feather.elektrum.org/book/src.html really shows how. Assuming your browser blocks when rendering script tags (currently true, but may not be future proof), the script in question is always the last script on the page up to that point.
Then using some framework and plugin like jQuery and http://plugins.jquery.com/project/parseQuery this becomes pretty trivial. Surprised there's not a plugin for it yet.
Somewhat related is John Resig's degrading script tags, but that runs code AFTER the external script, not as part of the initialization: http://ejohn.org/blog/degrading-script-tags/
Credits: Passing parameters to JavaScript files , Passing parameters to JavaScript files
Using global variables is not a so clean or safe solution, instead you can use the data-X attributes, it is cleaner and safer:
<script type="text/javascript" data-parameter_1="value_1" ... src="/js/myfile.js"></script>
From myfile.js you can access the data parameters, for instance with jQuery:
var parameter1 = $('script[src*="myfile.js"]').data('parameter_1');
Obviously "myfile.is" and "parameter_1" have to match in the 2 sources ;)
You can do that with a single line code:
new URL($('script').filter((a, b, c) => b.src.includes('myScript.js'))[0].src).searchParams.get("config")
It's simpler if you pass arguments without names, just like function calls.
In HTML:
<script src="abc.js" data-args="a,b"></script>
Then, in JavaScript:
const args=document.currentScript.dataset.args.split(',');
Now args contains the array ['a','b'].

Categories