I want to give a minimal js code to random websites so that they can add a widget.
The code needs to run after the main page loads and include a parameter with the domain name. ( I don't want to hardcode it)
One option is to add this code just before the </body> (so It will run after the page loads):
<script type="text/javascript" id="widget_script">
document.getElementById('widget_script').src=location.protocol.toLowerCase()+"//mywebsite.com/?u="+encodeURIComponent(window.location.host);
</script>
This works in IE but not in Firefox. I see with Firebug that the src property is created correctly but the script from my site is not loaded.
My question is : what is the best way to do that ? (preferably by putting minimal lines on the header part.)
To further clarify the question: If I put a script on the header part, how do I make it run after it is loaded and the main page is loaded? If I use onload event in my script I may miss it because it may load after the onload event was fired.
You can try to statically include the script with document.write (is an older technique and not recommended to use as it can cause problems with more modern libraries):
var url = location.protocol.toLowerCase() +
"//mywebsite.com/?u="+encodeURIComponent(window.location.host);
document.write('<script src="', url, '" type="text/javascript"><\/script>');
Or with dynamically created DOM element:
var dynamicInclude = document.createElement("script");
dynamicInclude.src = url;
dynamicInclude.type = "text/javascript";
document.getElementsByTagName("head")[0].appendChild(dynamicInclude);
Later edit:
To ensure the script is run after onload this can be used:
var oldWindowOnload = window.onload;
window.onload = function() {
oldWindowOnload();
var url = location.protocol.toLowerCase() +
"//mywebsite.com/?u="+encodeURIComponent(window.location.host);
var dynamicInclude = document.createElement("script");
dynamicInclude.src = url;
dynamicInclude.type = "text/javascript";
document.getElementsByTagName("head")[0].appendChild(dynamicInclude);
}
I do not believe it can be shorter than this, apart from shorter variable names :)
Why not use the getScript method of jQuery to do the loading? If you don't want to be dependant on jQuery, you can trace through the source to learn how they tackled it.
Viewing a widely used library is always going to show you solutions to problems you didn't know you had. For example, you can see how jQuery manages to generate a callback when the script is loaded, and how it avoids a purported memory leak in IE.
You probably want to be implementing the non-blocking script technique. Essentially instead of modifying the src of a script, you're going to create and append a whole new script element.
Good write up here and there are standard ways to do this in YUI and jQuery. It's quite straightforward with only one gotcha which is well understood (and documented at that link).
Oh and this:
One option is to add this code just
before the </body> (so It will run
after the page loads):
...is not technically true: you're just making your script the last thing in the loading order.
Related
I have some javascript that is not required for my initial page load. I need to load it based on some condition that will be evaluated client-side.
$(document).ready(function() {
let someCondition = true; // someCondition is dynamic
if (someCondition) {
var element = document.createElement('script');
element.src = 'https://raw.githubusercontent.com/Useless-Garbage-Institute/useless-garbage/master/index.js';
element.defer = true; // does this make a difference?
element.onload = function() {
// do some library dependent stuff here
document.getElementById("loading").textContent = "Loaded";
};
document.body.appendChild(element);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1 id="loading">Loading...</h1>
Does it make a difference (in terms of how browser will treat the script tag), if a new tag created using javascript, after document is ready, has 'defer' attribute or not? I think there is no difference, but how can I say for sure?
I believe I understand how deferred scripts behave when script tag is part of the initial html (as described here). Also, this question is not about whether element.defer=true can be used or not (subject of this question).
No that doesn't make any difference, the defer attribute is ignored in case of "non-parser-inserted" scripts:
<script defer src="data:text/javascript,console.log('inline defer')"></script>
<script>
const script = document.createElement("script");
script.src = "data:text/javascript,console.log('dynamic defer')";
script.defer = true;
document.body.append(script);
</script>
<!-- force delaying of parsing -->
<script src="https://deelay.me/5000/https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Look at your browser's console or pay attention to the logs timestamps to see that the dynamically inserted script actually did execute while we were waiting for the delayed script to be fetched.
There's a difference between adding them to the function and adding directly the CDN ( especially in your case ).
Let's look at the code execution of the above-mentioned code first,
You have added the jquery CDN first ( without defer ) so that loads first.
$(document).ready will be fired once after the complete load of jquery.
There'll be the creation and insertion of a new script tag to the dom.
Download the https://raw.githubusercontent.com/Useless-Garbage-Institute/useless-garbage/master/index.js asynchronously.
Let's look at another approach: adding CDN to the code:
Your DOM will have 2 script tags.
Both will start loading based on the type of load parallelly ( defer async etc ).
Notice you are not waiting for the dom ready event to load the second script.
I suggest adding only the main JS part in a js file and adding it to the CDN. Others can wait load with the delay.
In case you are really needed with a js src, then don't load it the first way since it waits for the complete page load.
I suggest you read and look at web-vitals and SEO for this.
and for your other question, yes you can add defer attribute with element.defer=true to the elements while creating and loading to DOM.
Hope this answer helps you!
Feel free to comment if you get any errors or doubts.
I think the JQuery Arrive lib will solve your case.
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.
I'm a javascript beginner, and I'm trying to figure out why this code works when written in the head, but not when it's being referenced from an external file.
in the head of my html document, I'm referencing the javascript file "quote.js" as follows.
<script type="text/javascript" language="JavaScript" src="/js/quote.js"> </script>
the contents of quote.js are as follows
var textarray = [
"Be Good.",
"Our future depends powerfully on how well we understand the cosmos.",
"Bottomless wonders spring from simple rules... repeated without end.",
"All our science, measured against reality, is primitive and childlike — and yet, it is the most precious thing we have.",
"To use violence is to already be defeated."
];
function RndText() {
var rannum= Math.floor(Math.random()*textarray.length);
document.getElementById('ShowText').innerHTML=textarray[rannum];
}
window.onload = function() { RndText(); }
finally, the div I'm replacing in the body is as follows...
<div id = "ShowText"></div>
it's probably a stupid mistake, but I've been trying to track it down for a while now, and I'm missing something. When I write the contents of quote.js in my html head, it works fine. Any ideas? Thanks in advance.
If the code works in the head, but not when included, it's likely to be a problem with the path to the script. Double check that /js/quote.js is an appropriate location. It may need to be js/quote.js, you you may have a typo. In browsers like FireFox and Chrome, if you view the source code of your page you can click on the path to files like this and it loads the included file or shows you an error if the file is not found.
If you can share a link to the page, I can tell you with more certainty exactly what the problem is.
Also, you don't have to the language attribute if you're using XHTML, but that's not causing the problem.
Perhaps the code is running before the DOM is ready
Instead of onload use the event DOMContentLoaded
document.addEventListener('DOMContentLoaded', function () {
//code here
}, false);
Is there a cross-browser way to associate an onload event to a static script tag, in a html document?
The following will not work in IE 7 and IE 8:
<script onload="DoThat" type="text/javascript" src="..."></script>
Some background
I have found a way to accomplish this with a dynamic script tag and if statements. It is for example explained in this MSDN article.
My issue is that I need to locate the current script tag, because I am building widgets that insert DOM elements in-place. In the past I have found some workarounds to do this, but they all have their downside. I am hoping that using the "this" keyword on a script onload event will help.
As far as I understand, you need your code to know what script element it belongs to. You would then insert necessary HTML widget content right next to this script or append to the script parent, etc. If so then I would propose you another approach.
Inside you widget javascript file you place next line of code:
var scripts = document.getElementsByTagName('script'),
thisScript = scripts[scripts.length - 1];
thisScript is the DOM element you are looking for, current script element this code located in. So simple.
Why it works.
Detail explanation http://feather.elektrum.org/book/src.html. Just an abstract:
When a script with a src is loaded in a page, the rest of the page is not yet written. The implication is that no matter how many scripts a page contains, the one currently starting to execute is the last one
This is fairly simple and effective technic, although but not so many people know about this possibility. It's reliable and 100% browser compatible. By the way, Google uses this code to render +1 buttons:
<script type="text/javascript" src="https://apis.google.com/js/plusone.js">
{lang: 'dk'}
</script>
So how do think how the api script could get this hash parameters? In my example it would be thisScript.innerHTML
I'm not sure if I caught your needs.
Anyway to locate the current script you can try this example
<script src="jquery-1.7.1.min.js" varTest="valueTest" id="myID"></script>
<script>
var scripts = document.getElementsByTagName('script');
var pattern = 'jquery-1.7.1.min.js';
var i = undefined;
for (var x=0; x<scripts.length; x++)
{
if (scripts[x].src.match(pattern)) { i = x; break; }
}
if (typeof(i) != 'undefined')
{
// Do any association needed here
var current_script = scripts[i];
// Just for test
console.log(current_script.attributes);
}
</script>
It seems this is a known problem and has been asked several times before here in SO however I do not see anything specific to jQTouch so I thought I would give it a try.
jQT will dynamically load pages when a link is clicked. In this page I would like to include something like
<script>
$.include('javascriptfile.js', function() {alert('do something with results of this file to an already existing div element');};
</script>
The $.include is a jquery plugin I found that mimics the $.load with a few more smarts added to it. Tested to work on FF but not in Chrome or most importantly, Safari.
The alert is never displayed. FireBug never shows the javascript even being loaded. If I put an alert before the $.include I still do not see anything.
I have tried an onclick/ontap event that would then run this code that was included in the head tag, no luck.
Edit: I am using the r148 revision of jQT. This was working prior to moving to this version, i believe.
Did you try to add the javascript file using one of these two methods:
Static Way:
<script type="text/javascript">
function staticLoadScript(url){
document.write('<script src="', url, '" type="text/JavaScript"><\/script>');
}
staticLoadScript("javascriptfile.js");
modifyDivFn(processFnInFile());
</script>
Dynamic way:
<script type="text/javascript">
function dhtmlLoadScript(url){
var e = document.createElement("script");
e.src = url;
e.type="text/javascript";
document.getElementsByTagName("head")[0].appendChild(e);
}
onload = function(){
dhtmlLoadScript("javascriptfile.js");
modifyDivFn(processFnInFile());
}
</script>
After the include you can call a function that does the processing you want (that being processFnInFile()) which result will be passed to modifyDivFn (and modify the div you want.) You could do this in one function, just to illustrate the idea.
Source: Dynamically Loading Javascript Files
Well Geries, I appreciate your help but ultimately the answer required a drastic rethinking of how I was using JQTouch. The solution was to move everything to an onclick event and make all the hrefs link to #. This might be what you were talking about Geries.
In the onclick function I do the logic, preloading, loading of the page through my own GET through jquery, then use the public object jQT.goTo(div, transition). This seems to get around the WebKit bugs or whatever I was running into and this now owrks on FireFox, Chrome, Safari, iPhone, and the lot.
I do run into a few animation issues with JQT but I think these are known issues that I hope Stark and the gang at JQTouch are working on.