Check if Javascript script exists on page - javascript

I have a bookmarklet that I've made and it loads a script from my server onto the users current page. However I have an if check in my script that if a condition is not met then no action is taken. However if the user then meets that condition then the code is run, but has caused there to be two sets of scripts inserted into their page. Can i prevent this?
<a href="javascript: (function () {
var jsCode = document.createElement('script');
jsCode.setAttribute('src', 'http://xxx.co.uk/xxx/script.js');
document.body.appendChild(jsCode);
}());">Bookmarklet</a>

You can check whether your script is loaded like this:
function isMyScriptLoaded(url) {
if (!url) url = "http://xxx.co.uk/xxx/script.js";
var scripts = document.getElementsByTagName('script');
for (var i = scripts.length; i--;) {
if (scripts[i].src == url) return true;
}
return false;
}
Alternatively, you could do something like this:
<a href="javascript:
if (!jsCode) {
var jsCode = document.createElement('script');
jsCode.setAttribute('src', 'http://xxx.co.uk/xxx/script.js');
document.body.appendChild(jsCode);
}
">Bookmarklet</a>
This "pollutes" the global namespace with the jsCode variable, but that might be a necessary evil. You could rename it to something that is unlikely to appear in the document where the bookmarklet is run.
Please note that while the javascript URI scheme is okay for bookmarklets as in this case, it's not considered to be a good practice for normal use.

Just check the selector length. Here's an example using jQuery:
if ($('script[src="http://xxx.co.uk/xxx/script.js"]').length > 0) {
//script exists
}

You can place id attributes on your script tags and use document.getElementById('your-id') to identify whether the script is on the page before adding.
if (!document.getElementById('your-id')) {
// append your script to the document here, ensure it has its id attribute set to 'your-id'
}

Solution with ES6, no jQuery:
const url = 'http://xxx.co.uk/xxx/script.js';
function scriptExists(url) {
return document.querySelectorAll(`script[src="${url}"]`).length > 0;
}
if(scriptExists(url)) {
...
}
It's not recommended to inline JS into HTML. Instead add event listeners:
function bookmark() {
if(scriptExists(url)) {
...
}
}
const button = document.querySelectorAll('a.bookmark');
button.addEventListener('click', bookmark, false);

In case working with local and live alternatively.
The exact URL may change. I think the ID method is better.
This is a combination of Two StackOverflow answers.
if (!document.getElementById('your-id')) {
addScript("your_script_src"); //adding script dynamically
addCSSFile("your_css_src"); // adding css files
}
function addScript(path) {
var head = document.getElementsByTagName("head")[0];
var s = document.createElement("script");
s.type = "text/javascript";
s.src = path;
s.id = "your-id";
head.appendChild(s);
}
function addCSSFile(path) {
var head = document.getElementsByTagName("head")[0];
var s = document.createElement("style");
s.type = "text/css";
s.src = path;
head.appendChild(s);
}

if you create a variable in the global scope (window.yourVariable) and check if that exists already then you can decide if you want to add your jsCode snippet code or run whatever you are running in script.js

if (document.getElementById('element-id')) {
// if exist must do something
}
hi, this is worked for me, please try it if you still need it

Related

How do I load a new script after the page is done loading?

I have two separate script files (script1.js, script2.js). Each of the files has its own functions/variables defined in it. For the sake of simplicity, I will assume each file holds a separate variable. So the files will look like:
script1.js
var x = 2;
script2.js
var y = 2;
I am using the scripts in index.html:
index.html
<button onclick="change()">Change script</button>
<script id="file" src="script1.js"></script>
<script>
function change() {
var file = document.getElementById("file");
if(file.src.slice(-10) == "script1.js") {
file.src = "script2.js";
} else {
file.src = "script1.js";
}
}
</script>
But when I change the src attribute for the script, the loaded script does not change. So even after switching scripts, x has the value 2 while y is undefined.
How do I switch the script after the page has finished loading?
Not sure what you want to accomplish, but as far as loading of javascript is concern, you can use:
$("#id_of_button").click(function(){
$.getScript('helloworld.js', function() {
//do whatever you want to accomplish here....
});
});
More detail here
A better way may be to keep the related code in separate functions in same js file and calling the specific function to override the logic based upon your condition check. Though I'm still not clear what you are trying to achieve. Could I get some scenario based idea to get it clear?
You have to create a new script in order to loaded it, the problem is that you also want to maintain the position of the script.
So here I wrote an example that will replace the old script and insert the new one at the same position.
Read the comment to understand how this work.
function change() {
var file = document.getElementById("file"); // get the script you want to change
var newscript = document.createElement("script"); // create new script
newscript.src = "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" // set the new script src
newscript.setAttribute("id","file"); // set the id to the same id as the old script
newscript.type = 'text/javascript';
file.parentNode.insertBefore(newscript, file); // insert the new script before the old one
file.remove() // remove the old script
var callback= function(){ // when the script has been loded then test and see if jQuery is working now
$("body").append("<p>Jq loaded</p>"); // no error then jQuery has been loaded
}
newscript.onreadystatechange = callback;
newscript.onload = callback;
}
<script id="file" src="script1.js"></script>
<button onclick="change()">Change script</button>
You can try this: https://codepen.io/anon/pen/mvMZOR
HTML
<button type="button">Change script</button>
<script id="file" src="script1.js"></script>
Javascript
var index = 1;
var scriptId = 'file';
var button = document.querySelector('button');
button.addEventListener('click', function() {
// Remove the old script
document.getElementById(scriptId).remove();
// Create the new one
var s = document.createElement('script');
// Add the id you want, in this case "file"
s.id = scriptId;
// It will return "script1.js" or "script2.js" alternatively
s.src = 'script' + (index++ % 2 + 1) + '.js';
// Append your new script at the end of your body
document.querySelector('body').append(s);
});

only add script to head if doesn't exist

I want to add additional scripts and styles to my site when a specific div is loaded.
I start out by defining a path to either a script or stylesheet and then create an element. Hereafter I append the element to the head tag in HTML.
But I would like a way to see if the script or stylesheet already has been append before I append it again. It would be stupid to append an already existing script or stylesheet.
Q: How do I use javascript to check wether or not a script already exists in the head tag, and then append the element if not?
EDIT
I have made a function based on the answer from #KernelPanik. It doesn't work yet but hopefully it will. The function is currently in question: my script appending function doesn't work
If you can use jquery, this code is good
function appendScript(filepath) {
if ($('head script[src="' + filepath + '"]').length > 0)
return;
var ele = document.createElement('script');
ele.setAttribute("type", "text/javascript");
ele.setAttribute("src", filepath);
$('head').append(ele);
}
function appendStyle(filepath) {
if ($('head link[href="' + filepath + '"]').length > 0)
return;
var ele = document.createElement('link');
ele.setAttribute("type", "text/css");
ele.setAttribute("rel", "Stylesheet");
ele.setAttribute("href", filepath);
$('head').append(ele);
}
In your code write
appendScript('/Scripts/myScript.js');
appendStyle('/Content/myStyle.css');
var lib = '/public/js/lib.js';
if (!isLoadedScript(lib)) {
loadScript(lib);
}
// Detect if library loaded
function isLoadedScript(lib) {
return document.querySelectorAll('[src="' + lib + '"]').length > 0
}
// Load library
function loadScript(lib) {
var script = document.createElement('script');
script.setAttribute('src', lib);
document.getElementsByTagName('head')[0].appendChild(script);
return script;
}
You can use the DOM getElementsByTagName("script") to get all of the <script> tags in the document. Then you can check the src urls of each script tag returned, for the url of the script(s) that you have added to the head section. Likewise, you can do something similar for the style sheets by replacing the search of "script" with "style".
For example, if the url of the script appended to the <head> section is header_url.html
var x = document.getElementsByTagName("script");
var header_already_added = false;
for (var i=0; i< x.length; i++){
if (x[i].src == "header_url.html"){
// ... do not add header again
header_already_added = true;
}
}
if (header_already_added == false){
// add header if not already added
}
Likewise, if the url of the style appended to the <head> section is header_style.css
var x = document.getElementsByTagName("style");
var header_already_added = false;
for (var i=0; i< x.length; i++){
if (x[i].src == "header_style.css"){
// ... do not add header again
header_already_added = true;
}
}
if (header_already_added == false){
// add header if not already added
}
A similar question was also asked here: Check if Javascript script exists on page
I used Jack Lee's solution. It was easy to implement and quickly versitile with just about any type file.... I didn't expand on anything ...I actually probably stupefied it a bit... just wanted to list what I did in case it helps someone else...
var lib_jq = '//pathtofile/jquery.js';
var lib_bs = '//pathtofile/bootstrap.min.3.5.js';
var lib_cs = '//pathtofile.css';
///checks files with the SRC attribute
function isLoadedScript(lib) {
return document.querySelectorAll('[src="' + lib + '"]').length > 0
}
///checks files with the HREF attribute
function isLoadedCss(lib) {
return document.querySelectorAll('[href="' + lib + '"]').length > 0
}
///loads the script.js files
function loadScript(link) {
document.write('<scr' + 'ipt type="text/javascript" src="'+link+'"></scr' + 'ipt>');
}
///loads the style.css files
function loadCss(link) {
document.write('<link rel="stylesheet" href="'+link+'">');
}
/// run funtion; if no file is listed, then it runs the function to grab the URL listed. ///Run a seperate one for each file you wish to check.
if (!isLoadedScript(lib_jq)) { loadScript(lib_jq); }
if (!isLoadedScript(lib_bs)) { loadScript(lib_bs); }
if (!isLoadedCss(lib_cs)) { loadCss(lib_cs); }
I know there is always a "better" and more "elegant" solution, but for us beginiers, we got to get it working before we can start to understand it...
Another way with a function helper like below
function isScriptAlreadyPresent(url) {
var scripts = Array.prototype.slice.call(document.scripts);
return scripts.some(function(el) {
return el.src && el.src != undefined && el.src == url;
});
}
isScriptAlreadyPresent('http://your_script_url.tld/your_lib.js');
It uses Array.prototype.some function. You may need a es5-shim if your are in browsers not supporting ES5 (IE7 and IE8...)
maybe headjs can help you.
or maybe you can add onload attribute in the script tag.
my english is a little poor,so maybe i'm misunderstand your question.
if(ie){
js.onreadystatechange = function(){
if(js.readyState == 'complete'){
callback(js);
}
}
else{
js.onload = function(){
callback(js);
}
You can try calling some function, object, variable from that js script file, if it finds it then it exists, if not, you need to insert that js script file.

Get file path of currently executing JavaScript code for dynamically loaded cross domain JavaScript file

I need to load cross-domain JavaScript
files dynamically for bookmarklets in my site http://jsbookmarklets.com/
The solution should satisfy:
Fetch the path of current file
The domain of current web-page and JS file in execution are different
The solution should be cross-browser
Multiple scripts might be loaded at once asynchronously (that's why the related questions mentioned below are not a fit)
I want to get the file path of currently executing JavaScript code for dynamically loading few more resources (more CSS files and JS files like custom code and jQuery, jQuery UI and Ext JS libraries) which are stored in the same/relative folder as the JavaScript Bookmarklet.
The following approach does not fit my problem:
var scripts = document.getElementsByTagName("script");
var src = scripts[scripts.length-1].src;
alert("THIS IS: "+src);
Related questions which do not fit my problem:
Get the url of currently executing js file when dynamically loaded
Get script path
The current solution that I'm using, which works, but is very lengthy:
var fnFullFilePathToFileParentPath = function(JSFullFilePath){
var JSFileParentPath = '';
if(JSFullFilePath) {
JSFileParentPath = JSFullFilePath.substring(0,JSFullFilePath.lastIndexOf('/')+1);
} else {
JSFileParentPath = null;
}
return JSFileParentPath;
};
var fnExceptionToFullFilePath = function(e){
var JSFullFilePath = '';
if(e.fileName) { // firefox
JSFullFilePath = e.fileName;
} else if (e.stacktrace) { // opera
var tempStackTrace = e.stacktrace;
tempStackTrace = tempStackTrace.substr(tempStackTrace.indexOf('http'));
tempStackTrace = tempStackTrace.substr(0,tempStackTrace.indexOf('Dummy Exception'));
tempStackTrace = tempStackTrace.substr(0,tempStackTrace.lastIndexOf(':'));
JSFullFilePath = tempStackTrace;
} else if (e.stack) { // firefox, opera, chrome
(function(){
var str = e.stack;
var tempStr = str;
var strProtocolSeparator = '://';
var idxProtocolSeparator = tempStr.indexOf(strProtocolSeparator)+strProtocolSeparator.length;
var tempStr = tempStr.substr(idxProtocolSeparator);
if(tempStr.charAt(0)=='/') {
tempStr = tempStr.substr(1);
idxProtocolSeparator++;
}
var idxHostSeparator = tempStr.indexOf('/');
tempStr = tempStr.substr(tempStr.indexOf('/'));
var idxFileNameEndSeparator = tempStr.indexOf(':');
var finalStr = (str.substr(0,idxProtocolSeparator + idxHostSeparator + idxFileNameEndSeparator));
finalStr = finalStr.substr(finalStr.indexOf('http'));
JSFullFilePath = finalStr;
}());
} else { // internet explorer
JSFullFilePath = null;
}
return JSFullFilePath;
};
var fnExceptionToFileParentPath = function(e){
return fnFullFilePathToFileParentPath(fnExceptionToFullFilePath(e));
};
var fnGetJSFileParentPath = function() {
try {
throw new Error('Dummy Exception');
} catch (e) {
return fnExceptionToFileParentPath(e);
}
};
var JSFileParentPath = fnGetJSFileParentPath();
alert('File parent path: ' + JSFileParentPath);
var s = document.createElement('script');
s.setAttribute('src', 'code.js');
document.body.appendChild(s);
Can you not simply do this?
var myScriptDir = 'http://somesite.tld/path-to-stuff/';
var s = document.createElement('script');
s.setAttribute('src', myScriptDir + 'code.js');
document.body.appendChild(s);
// code inside http://somesite.tld/path-to-stuff/code.js will use myScriptDir to load futher resources from the same directory.
If you don't want to have code inside the script to be responsible for loading further resources you can use the onload attribute of the script tag, like s.onload=function(){...}. For cross browser compatibility you might first load jQuery and then use the getScript function. Relevant links are http://www.learningjquery.com/2009/04/better-stronger-safer-jquerify-bookmarklet and http://api.jquery.com/jQuery.getScript/
Some of the comments have already mentioned this, but I'll try to elaborate a bit more.
The simplest, most cross-browser, cross-domain way of figuring out the path of the current script is to hard-code the script's path into the script itself.
In general, you may be loading third-party script files, so this would not be possible. But in your case, all the script files are under your control. You're already adding code to load resources (CSS, JS, etc.), you might as well include the script path as well.

Serve alternate external Javascript file depending on if elements are defined or not in the page

Ok, here goes my first question on here.
Setup: We use a javascript based tool to A/B test our landing page designs. I need version A (control) to link to one external javascript file, and version B (variation) to link to an alternate javascript file.
Goal: to have an internal js script at the bottom of the control that looks to see if the tool is in fact serving A or B, and if true, which one was served. The result indicates which external script should be linked.
Issue: regardless of if the tool is in fact serving A or B, the original script is linked first, then if the tool is detected, the appropriate script is linked after that.
Here is my code (I apologize in advance for any newbie mistakes):
//script at bottom of original or tool-served control html page template
<script type="text/javascript">
valForTool = function () {
var variationId = _tool_exp[_tool_exp_ids[0]].combination_chosen;
if (variationId == 1) {
var newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.src = 'js/scripts.js';
document.body.appendChild(newScript);
};
}
originalValidation = function () {
var newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.src = 'js/scripts.js';
document.body.appendChild(newScript);
}
$(function(){
if (typeof _tool_exp_ids !== 'undefined' && typeof _tool_exp_ids[0] !== 'undefined') {
valForTool();
} else {
originalValidation();
};
});
</script>
//end script on control or original template
//script on tool-served variation html template - will run after the above script
<script type="text/javascript">
$(function() {
$('#project_info input[type=submit]').removeAttr('disabled');
$('#project_info').unbind();
var newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.src = 'js/scripts2.js';
document.body.appendChild(newScript);
$('.input_text').parent().addClass('contact_field');
});
</script>
// end script on variation template
Any ideas as to what I'm doing wrong? Did I provide enough information? Thanks! I love this site as a reference for my questions, but this is my first time actually posting one.
Shortening it down a bit, it seems like your just doing this:
<script type="text/javascript">
$(function(){
if (typeof _tool_exp_ids !== 'undefined' && typeof _tool_exp_ids[0] !== 'undefined') {
var variationId = _tool_exp[_tool_exp_ids[0]].combination_chosen;
if (variationId == 1) {
$.getScript('js/scripts.js', function() {runSecond();});
}
}else{
$.getScript('js/scripts.js', function() {runSecond();});
}
function runSecond() {
$('#project_info input[type=submit]').removeAttr('disabled').unbind();
$.getScript('js/scripts2.js');
$('.input_text').parent().addClass('contact_field');
}
});
</script>
Now looking at that, it's obvious that both scripts are running no matter what conditions are met in those if/else statements, and I don't really get what it is your trying to do, but the first thing i would do, is to add some console.logs to see if those if/else statements are working like they are supposed to, and then figure what scripts should be loaded under which conditions etc ?

innerHTML to insert script in body (for ZeroClipboard) not working

I'm building <div> elements using AJAX, and I want to add ZeroClipboard functionality. Firebug shows the code is building correctly, and when I copy it into a raw HTML test page it works too. The builds are not happening at onload, but down the track.
The code is as follows, calling some functions that create the new elements:
dom_append_child_with_onclick ("img",export_id,"icon_active",report_heading_id, "event.cancelBubble = true;");
dom_append_child ("div",export_script_id,"",report_heading_id);
text = "<script language='JavaScript'>var clip" +rnum +"=new ZeroClipboard.Client();clip"+rnum+".setText('');clip"+rnum+".addEventListener('mouseDown',function(client){alert('firing');clip"+rnum+".setText(document.getElementById('SL40').value);});clip"+rnum+".glue('XR"+rnum+"','RH"+rnum+"');</script>";
document.getElementById(export_script_id).innerHTML=text;
My question: when you insert a script into the <body>, do you have to do something to get it to fire? The script appears not to be doing its thing, and I can't get the alert 'firing' to display.
Note: the cancelBubble is to stop the onClick function of the underlying element. It may be unnecessary if I can get the flash working.
Thanks.
You can just inject your script into the page as a DOM object, but this does not work in all browsers:
var s = document.createElement("script");
s.type = "text/javascript";
s.innerText = "var clip" +rnum +"=new ZeroClipboard.Client();clip"+rnum+".setText('');clip"+rnum+".addEventListener('mouseDown',function(client){alert('firing');clip"+rnum+".setText(document.getElementById('SL40').value);});clip"+rnum+".glue('XR"+rnum+"','RH"+rnum+"');";
document.getElementsByTagName("head")[0].appendChild(s);
Or, for better compatibility, you probably want to just declare a function which sets this up in your page, and then just call the function with the rnum as the parameter.
e.g.
function useZeroClipboard(rnum) {
window["clip" + rnum] = new ZeroClipboard.Client();
cwindow["clip" + rnum].setText('');
window["clip" + rnum].addEventListener('mouseDown', function(client){
alert('firing');
window["clip" + rnum].setText(document.getElementById('SL40').value);
});
window["clip" + rnum].glue('XR"+rnum+"','RH"+rnum+"');
}
Then you can just call that in your code:
useZeroClipboard(rnum);
Instead of writing the script block.
Here is a method that recursively replaces all scripts with executable ones:
function replaceScriptsRecurse(node) {
if ( nodeScriptIs(node) ) {
var script = document.createElement("script");
script.text = node.innerHTML;
node.parentNode.replaceChild(script, node);
}
else {
var i = 0;
var children = node.childNodes;
while ( i < children.length) {
replaceScriptsRecurse( children[i] );
i++;
}
}
return node;
}
function nodeScriptIs(node) {
return node.getAttribute && node.getAttribute("type") == "text/javascript";
}

Categories