How to append timestamp to the javascript file in <script> tag url to avoid caching
Method 1
Lots of extensions can be added this way including Asynchronous inclusion and script deferring. Lots of ad networks and hi traffic sites use this approach.
<script type="text/javascript">
(function(){
var randomh=Math.random();
var e = document.getElementsByTagName("script")[0];
var d = document.createElement("script");
d.src = "//site.com/js.js?x="+randomh+"";
d.type = "text/javascript";
d.async = true;
d.defer = true;
e.parentNode.insertBefore(d,e);
})();
</script>
Method 2 (AJZane's comment)
Small and robust inclusion. You can see exactly where JavaScript is fired and it is less customisable (to the point) than Method 1.
<script>document.write("<script type='text/javascript' src='//site.com
/js.js?v=" + Date.now() + "'><\/script>");</script>
Insert the script dynamically via document.createElement('script')
, then when you set the URL, you can use new Date().getTime()
to append the extra parameter.
If you are worried about your javascript executing before the script is loaded, you can use the onload
callback of the script element (note that there are a few hoops to jump for IE though)
If you choose to use dates or a random numbers to append to your URI, you will provide opportunities for the end user to be served the same cached file and may potentially expose unintended security risks. An explicit versioning system would not. Here's why:
Why "Random" and Random Numbers are both BAD
For random numbers, you have no guarantee that same random number hasn't been generated and served to that user before. The likelihood of generating the same string is greater with smaller "random" number sets, or poor algorithms that provide the same results more often than others. In general, if you are relying on the JavaScript random method, keep in mind it's pseudo-random, and could have security implications as well if you are trying to rely on uniqueness for say a patch in one of your scripts for XSS vulnerabilities or something similar. We don't want Johnny to get served the old cached and unpatched JS file with an AJAX call to a no-longer trusted 3rd-party script the day Mr. Hacker happened to be visiting.
Why dates or timestamps are bad too, but not as bad
Regarding Dates as "unique" identifiers, JavaScript would be generating the Date object from the client's end. Depending on the date format, your chances for unintended caching may vary. Date alone (20160101) allows a full day's worth of potential caching issues because a visit in the morning results in foo.js?date=20160101, and so does a visit in the evening. Instead, if you specify down to the second (20160101000000) your odds of an end user calling the same GET parameter go down, but still exist.
A few rare but possible exceptions:
Clocks get reset (fall behind) once a year in most time zones
Computers that reset their local time on reboot for one reason or another
Automatic network time syncing causing your clock to adjust backwards a few seconds/minutes whenever your local time is off from the server time
Adjusting time zones settings when traveling (The astronauts on the IIS travel through a zone every few minutes...let's not degrade their browsing experience :P)
The user likes resetting their system clock to mess with you
Why incremental or unique versioning is good :)
For a fontend only solution, my suggestion would be to set an explicit version, which could be simply hard-coded by you or your team members every time you change the file. Manually doing exactly as you had done in your same code of your question would be a good practice.
You or your team should be the only ones editing your JS files, so the key take away isn't that your file needs to be served fresh every time, I just needs to be served fresh when it changes. Browser caching isn't a bad thing in your case, but you do need to tell the end user WHEN it should update. Essentially, when your file is updated, you want to ensure the client gets the updated copy. With this, you also have the added bonus of being able to revert to previous versions of your code without worry of client caching issues. The only drawback is you need to use due diligence to make sure you actually update the version number when you update your JS files. Keep in mind just because something isn't automated, doesn't mean it is necessarily bad practice or poor form. Make your solution work for your situation and the resources you have available.
I suggest using a form like Semantic Versioning's rules to easily identify backwards or breaking compatibility by looking at the file name (assuming nobody in the development process fudged up their version numbering) if possible. Unless you have an odd use case, there is no good reason to force a fresh copy down to the client every time.
Automated version incrementing on the client side with Local Storage
If what you were after was frontend way to automate the generation of a unique version number for you so you don't have to explicitly set it, then you would have to implement some sort of local storage method to keep track of, and auto increment your file versions. The solution I've shown below would lose the ability for Semantic versioning, and also has the potential to be reset if the user knows how to clear Local Storage. However, considering your options are limited to client-side only solutions, this may be your best bet:
<script type="text/javascript">
(function(){
/**
* Increment and return the local storage version for a given JavaScript file by name
* @param {string} scriptName Name of JavaScript file to be versioned (including .js file extension)
* @return {integer} New incremented version number of file to pass to .js GET parameter
*/
var incrementScriptVer = function(scriptName){
var version = parseInt(localStorage.getItem(scriptName));
// Simple validation that our item is an integer
if(version > 0){
version += 1;
} else {
// Default to 1
version = 1;
}
localStorage.setItem(scriptName, version);
return version;
};
// Set your scripts that you want to be versioned here
var scripts = ['foo.js', 'bar.js', 'baz.js'];
// Loop through each script name and append our new version number
scripts.map(function(script){
var currentScriptVer = incrementScriptVer(script);
document.write("<script language='text/javascript' type='text/javascript' src='http://yoursite.com/path/to/js/" + script + "?version=" + currentScriptVer + " '><\/script>");
});
})();
</script>
I'm going to mention for completeness, if you are converting from an old system of generating "random" numbered or dated GET variables, to an incrementing versioned system, be sure that you will not step over any potentially randomly generated files names with your new versioning system. If in doubt, add a prefix to your GET variable when changing methods, or simply add a new GET variable all together. Example: "foo.js?version=my_prefix_121216" or "foo.js?version=121216&version_system=incremental"
Automated versioning via AJAX calls and other methods (if backend development is a possiblity)
Personally, I like to stay away from local storage options. If the option is available, it would be the "best" solution. Try to get a backend developer make an endpoint to track JS file versions, you could always use the response to that endpoint determine your version number. If you are already using version control like Git, you could optionally have on of your Dev Ops team bind your versioning to your commit versions for some pretty sweet integration as well.
A jQuery solution to a RESTful GET endpoint might look like:
var script = "foo.js";
// Pretend this endpoint returns a valid JSON object with something like { "version": "1.12.20" }
$.ajax({
url: "//yoursite.com/path/to/your/file/version/endpoint/" + script
}).done(function(data) {
var currentScriptVer = data.version;
document.write("<script language='text/javascript' type='text/javascript' src='http://yoursite.com/path/to/js/" + script + "?version=" + currentScriptVer + " '><\/script>");
});