I have to load some script source dynamically. Since I can not use jQuery and did not know about the XmlHttpRequest+eval method, I tried to do it this way:
API.prototype.initCallback = null;
API.prototype.sourceLoadCnt = 0;
API.prototype.sourceReady = function () {
this.sourceLoadCnt--;
if(this.sourceLoadCnt===0){
this.initCallback(); //if all sources loaded
}
}
API.prototype.init = function (callback) {
this.initCallback = callback;
var _this = this;
var js = "../../js/";
var script1 = document.createElement('script');
script1.type = 'text/javascript';
script1.src = js+'script1.js';
this.sourceLoadCnt++;
script1.onload = function(){ _this.sourceReady() };
var script2 = document.createElement('script');
script2.type = 'text/javascript';
script2.src = js+'script2.js';
this.sourceLoadCnt++;
script2.onload = function(){ _this.sourceReady() };
var css1 = document.createElement('link');
css1.type = 'text/css';
css1.rel = 'stylesheet';
css1.href = 'style.css';
css1.media = 'screen';
this.sourceLoadCnt++;
css1.onload = function(){ _this.sourceReady() };
head.appendChild(script1);
head.appendChild(script2);
head.appendChild(css1);
};
My problem is, that the sourceReady-function is called only once.
I still could change everything to load it via XmlHttpRequest but I am curious why my way isn't working. Does anyone have an idea?
It might be because API.prototype.sourceLoadCnt should not exist, it should be an instance variable that lives on this.
The way you have coded it now will only work if you only have a single instance, and if you only have a single instance, going the oob/prototype way seems like a design failure.
Related
I am creating a new element and trying to get the response back which should be a url but I am not sure how to get the response back from the new element. Any suggestions would be great.
public void Setup()
{
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
driver = new ChromeDriver("C:\\SeleniumDrivers\\");
driver.Navigate().GoToUrl("https://www.target.com");
driver.Manage().Window.Maximize();
Thread.Sleep(3000);
string execu = "var document = window.document;"+
"var head = window.getElementByTagName(\"head\");"+
"var script = window.createElement(\"script\");" +
"script.setAttribute(\"type\", \"text/javascript\");" +
"script.setAttribute(\"src\", \"https://go.playerzero.ai/record/6274691b00fbad01561df689\");"+
"head.appendChild(script);";
try
{
js.ExecuteScript(execu);
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e);
}
}
Modify your script to return the src:
This is a working example using .NET6, Selenium.WebDriver 4.3.0, Selenium.WebDriver.ChromeDriver 103.0.5060.5300
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
const string Url = #"https://www.target.com";
using var driver = new ChromeDriver();
driver.Navigate().GoToUrl(Url);
var script = #"let script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'https://go.playerzero.ai/record/6274691b00fbad01561df689';
document.head.appendChild(script);
return script.src;";
var js = (IJavaScriptExecutor)driver;
var src = js.ExecuteScript(script).ToString();
Console.WriteLine(src);
So, if I have to include a Javascript file in a .js file, I use to below script. It works fine.
var script = document.createElement("SCRIPT");
script.src = 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js';
script.type = 'text/javascript';
script.onload = function() {
//Some code
};
document.getElementsByTagName("head")[0].appendChild(script);
What should I do If I need to include more than 1 files.
You can make a function and pass the js files you want to include like so:
function scriptLoader(path, callback)
{
var script = document.createElement('script');
script.type = "text/javascript";
script.async = true;
script.src = path;
script.onload = function(){
if(typeof(callback) == "function")
{
callback();
}
}
try
{
var scriptOne = document.getElementsByTagName('script')[0];
scriptOne.parentNode.insertBefore(script, scriptOne);
}
catch(e)
{
document.getElementsByTagName("head")[0].appendChild(script);
}
}
And call it like so:
scriptLoader('/path/to/file.js');
in the similar manner you can call as many JS file you like this:
scriptLoader('/path/to/file2.js');
scriptLoader('/path/to/file3.js');
and even with onload callback functions like so:
scriptLoader('/path/to/file6.js',function(){
alert('file6 loaded');
});
I would imagine you'd do the same as you've got there but just change the variable name from var script to something like var scriptA and change the code that follows to match like script.src = to scriptA.src =
This function will load one script or many, pass a single file or an array of many:
function include(src, cb) {
arr = (src instanceof Array) ? src : [{
'src': src,
'cb': cb
}];
arr.forEach(function(item) {
_include(item.src, item.cb);
})
function _include(src, cb) {
var script = document.createElement("SCRIPT");
script.src = src;
script.async = true;
script.type = 'text/javascript';
script.onload = function() {
if (cb) cb()
}
document.getElementsByTagName("head")[0].appendChild(script);
}
}
include("/js/file1.js");
include("/js/file1.js", function(){console.log("file1 loaded")});
include([{src:"/js/file1.js"},{src:"/js/file2.js"},{src:"/js/file3.js"}]);
I have a server side script that dynamically creates a js file in an s3 bucket after a given url has 1 page view. The server side script runs every 1 minute.
On any given URL on the site, I have a script which tries to call this dynamically created file.
Such as:
var host = window.location.host;
var pathName = window.location.pathname;
var script = document.createElement('script');
script.async = true;
script.type = 'text/javascript';
script.src="https://s3.amazonaws.com/some_bucket/"+host+pathName+".js;
var node = document.getElementsByTagName('script')[0];
node.parentNode.insertBefore(script, node);
})();
The problem is that if a new URL is created on the site and the first visitor goes to that page then the cron job has had a chance to run, and therefore the js file does not exist in the bucket yet. This causes a 404 error but also causes an error in the on-page javascript above crashing the js library that contains it.
Is there a way to check the existence of the file on the fly and default to a different file if the dynamically created one has not been built yet?
For example (pseudo code):
try{
script.src="https://s3.amazonaws.com/some_bucket/"+host+pathName+".js;
}
catch(e){
404error = true;
}
finally{
if(404error == true){
script.src="https://s3.amazonaws.com/some_bucket/DEFAULT.js";
}
}
Updated code with calling a different file on error. This successfully calls the other file, however it also still causes a 404 error in Chrome.
var host = window.location.host;
var pathName = window.location.pathname;
var script = document.createElement('script');
script.async = true;
script.type = 'text/javascript';
script.onerror = function(){
var host = window.location.host;
var pathName = window.location.pathname;
var script = document.createElement('script');
script.async = true;
script.type = 'text/javascript';
script.src="https://s3.amazonaws.com/some_bucket/some_default_file.js";
var node = document.getElementsByTagName('script')[0];
node.parentNode.insertBefore(script, node);
}
script.src="https://s3.amazonaws.com/some_bucket/"+host+pathName+".js;
var node = document.getElementsByTagName('script')[0];
node.parentNode.insertBefore(script, node);
})();
Use script.onerror:
var pathName = window.location.pathname;
var script = document.createElement('script');
script.async = true;
script.type = 'text/javascript';
script.onerror = function() {
console.log('script failed to load');
};
script.src = "https://s3.amazonaws.com/some_bucket/" + host + pathName + ".js";
var node = document.getElementsByTagName('script')[0];
node.parentNode.insertBefore(script, node);
http://jsfiddle.net/JamesKyle/HQDu6/
I've created a short function based on Mathias Bynens Optimization of the Google Analytics asynchronous script that goes as following:
function async(src) {
var d = document, t = 'script',
o = d.createElement(t),
s = d.getElementsByTagName(t)[0];
o.src = '//' + src;
s.parentNode.insertBefore(o, s);
}
This works great and I've already started using it for several different scripts
// Crazy Egg
async('dnn506yrbagrg.cloudfront.net/pages/scripts/XXXXX/XXXXX.js?' + Math.floor(new Date().getTime() / 3600000));
// User Voice
var uvOptions = {};
async('widget.uservoice.com/XXXXX.js');
// Google Analytics
var _gaq = [['_setAccount', 'UA-XXXXX-XX'], ['_setDomainName', 'coachup.com'], ['_trackPageview']];
async('google-analytics.com/ga.js');
// Stripe
async('js.stripe.com/v1');
The problem comes when I encounter a script that needs to be called after it's loaded:
// Snap Engage
async('snapabug.appspot.com/snapabug.js');
SnapABug.init('XXXXX-XXXXX-XXXXX-XXXXX-XXXXX');
So I figured I'd turn this into a callback function that would be used as so:
async('snapabug.appspot.com/snapabug.js', function() {
SnapABug.init('XXXXX-XXXXX-XXXXX-XXXXX-XXXXX');
});
I did not expect that this would be difficult for me to do but it has turned out that way.
My question is what is the most efficient way to add a callback without overcomplicating the code.
See the jsfiddle: http://jsfiddle.net/JamesKyle/HQDu6/
Thanks RASG for https://stackoverflow.com/a/3211647/982924
Async function with callback:
function async(u, c) {
var d = document, t = 'script',
o = d.createElement(t),
s = d.getElementsByTagName(t)[0];
o.src = '//' + u;
if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
s.parentNode.insertBefore(o, s);
}
Usage:
async('snapabug.appspot.com/snapabug.js', function() {
SnapABug.init('XXXXX-XXXXX-XXXXX-XXXXX-XXXXX');
});
jsFiddle
A more recent snippet:
async function loadAsync(src) {
const script = document.createElement('script');
script.src = src;
return new Promise((resolve, reject) => {
script.onreadystatechange = function () {
if (script.readyState === 'loaded' || script.readyState === 'complete') {
script.onreadystatechange = null;
resolve(true);
}
};
document.getElementsByTagName('head')[0].appendChild(script);
});
}
utilisation
loadAsync(`https://....js`).then(_ => {
// ... script loaded here
})
James Kyle's answer doesn't take IE9 into account. Here is a modified version of the code I found in the link proposed in the comments. Modify the var baseUrl so it can find the script accordingly.
//for requiring a script loaded asynchronously.
function loadAsync(src, callback, relative){
var baseUrl = "/resources/script/";
var script = document.createElement('script');
if(relative === true){
script.src = baseUrl + src;
}else{
script.src = src;
}
if(callback !== null){
if (script.readyState) { // IE, incl. IE9
script.onreadystatechange = function() {
if (script.readyState === "loaded" || script.readyState === "complete") {
script.onreadystatechange = null;
callback();
}
};
} else {
script.onload = function() { // Other browsers
callback();
};
}
}
document.getElementsByTagName('head')[0].appendChild(script);
}
utilisation:
loadAsync('https://www.gstatic.com/charts/loader.js' , function(){
chart.loadCharts();
});
// OR relative path
loadAsync('fastclick.js', null, true);
The other answers works well, but aren't super readable or require Promises. Here is my two cents:
function loadScript(src, callback) {
var script = document.createElement('script');
script.setAttribute('src', src);
script.addEventListener('load', callback);
document.head.appendChild(script);
},
I am trying to load an external javascript file from within javascript but I cannot seem to get it to work. Am I doing something wrong?
sample file of my work
function loadJs() {
var fileref=document.createElement('script')
fileref.setAttribute("type","text/javascript")
fileref.setAttribute("src", "https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js")
document.body.appendChild(fileref); }
Perhaps you are trying to access the jQuery API before it is fully loaded. You can add a callback parameter to the loadJs function like this:
function loadJs(src, callback) {
var s = document.createElement('script');
document.getElementsByTagName('head')[0].appendChild(s);
s.onload = function() {
//callback if existent.
if (typeof callback == "function") callback();
callback = null;
}
s.onreadystatechange = function() {
if (s.readyState == 4 || s.readyState == "complete") {
if (typeof callback == "function") callback();
callback = null; // Wipe callback, to prevent multiple calls.
}
}
s.src = src;
}
loadJs('https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js', function() {
$('body').append('<p>It works!</p>');
});
Tested in chrome, FF, ie8. Fiddle: http://jsfiddle.net/Umwbx/2/
Use code similar to this:
function loadJs() {
var s = document.createElement('script');
var c = document.getElementsByTagName('script')[0];
s.type = 'text/javascript';
s.src = 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js';
c.parentNode.insertBefore(s, c);
}