When loading a CSS/JS file from CDN or any external server, it is possible (even with low probability) to miss the file due to external failure. In this case, the html page will be corrupted in the lack of appropriate CSS and JS.
Is there a practical approach to load a local version upon CDN failure?
<link rel="stylesheet" href="http://cdn.com/style.css" type="text/css" />
IF (not loaded style.css){
<link rel="stylesheet" href="/css/style.css" type="text/css" media="screen" />
}
It would be easier to do this for JS, as we can test a JS function (provided in the JS file); then, loading the local file upon failure. For example, testing, if jQuery library is available.
However, I am curious to know if there is a practical method for this!
I would do it this way.
Create a class within your stylesheet ui-helper-hidden and then add a div as the first element on your page;
<body><div class="ui-helper-hidden"></div><!-- rest of html --></body>
After you have checked to make sure your CDN javascript file has been loaded, then use this bit of code note i am using jquery
<script>
// CSS Fallback
$(function () {
if ($('.ui-helper-hidden:first').is(':visible') === true) {
$('<link rel="stylesheet" type="text/css" href="/pathtocss/nameofstylesheet.css" />').appendTo('head');
}
});
</script>
This will check to see if the element which should be hidden is or not. If it isnt hidden, then you know your css file has not loaded from the CDN.
I use this method for jQuery and jQuery UI via a CDN
For Javascript, you can listen for the onload and onerror events when building a dynamic script. However, in those same pages, it shows otherwise for CSS.
The only reliable way to dynamically load CSS is to do it via AJAX. You could load the styles via dynamic link tags but without those events, you won't know if they have been loaded at all. You could poll for the styles, but it's hackish IMO.
Another way to do it is make the server read those CDN files. If they're good, print the urls for the links. But if those links are dead, make it print the local urls instead. This would be more reliable, and offloads your logic to the server. This assumes that you have access to the server.
Or better, use the local versions in the first place! With good caching, bandwidth won't be an issue
Related
I have a files that is cached in some cdn.
in my local environment you can read the markup in browser as
<script src="/js/cool.js"></script>
but once it in production it goes like:
<script src="/js/cool.js.pagespeed.ce.l2D9mD1nmX.js"></script>
.pagespeed.ce.l2D9mD1nmX.js means it was being cached
now i was wondering if its advisable or a practice to preload a file that is already being cached in cdn?
because i am doing
<link rel="preload" type="application/javascript" href="/js/cool.js" as="script">
in my <head> and this will give me warning of :
The resource (the preload file) was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate as value and it is preloaded intentionally.
because technically /js/cool.js is no longer existing in the dom
Use a preload plugin in your module bundler.
Q. Is it advisable or a practice to preload a file that is already being cached in cdn?
A. Yes, it is, assuming your cool.js actually needs to be preloaded.
Now, the warning is actually expected. See, cool.js.pagespeed.ce.l2D9mD1nmX.js and cool.js are two different files on the server. Your file cool.js.pagespeed.ce.l2D9mD1nmX.js is actually a clone of code.js produced by your module-bundler for certain reasons. What you are doing is preloading cool.js, the code of which never gets executed in production and thus the warning - ...preloaded using link preload but not used within a few seconds from the window's load event. But, what you actually need to do is preload cool.js.pagespeed.ce.l2D9mD1nmX.js. Assuming you are using webpack as your module-bundler, you need a plugin called preload-webpack-plugin to achieve the same.
I am developing a web page with the javascript bootstrap library for UI and layout design. It will run off an Android tablet.
It may work in an offiline environment and it cannot access the local file system. It means <script src='{Url to js file}'></script> won't work.
How can it access the bootstrap css and js files? Can then be included as inline codes in the html?
CSS can be included in following ways in an HTML
Using CDN (Internet)
Using local file system(external stylesheets)
using internal stylesheets (<style>)
Placing Inline styles
Considering the above, you have only one case left, i.e. include all styles in tag and make it work. You may apply checks that internal stylesheets should work only when it's offline mode.
You can use local file:
you have tow ways to do it:
download the files and put them in your project folder and creat links/script to those files:
<script src="folderInProject/bootstrap.js"></script>
the links below contain the code :
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js"></script>
simply go to the url and copy the code inside to your local js/css files:
for example go to this link and copy the code:
https://maxcdn.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css
I have searched for a source which describes the steps for implementing jQuery into an expression engine site and have yet to find a good one.
I am trying to initiate a simple slider effect for some images, using sliderPro. sliderPro has number of assets which I have uploaded into my expression js directory. Using the documented method on EE, this is how I'm calling those files:
{exp:jquery:script_tag ui='core'}
{exp:jquery:script_tag effect='sliderPro'}
{exp:jquery:script_tag file='src/js/'}
{exp:jquery:script_tag file='src/css/'}
{exp:jquery:script_tag file='lib/sliderPro'}
{exp:jquery:script_tag file='lib/fancybox/'}
I used all the html markup from silderPro: classes and ids for my images. When inspect Element, I do not see the passing of any of the jQuery functions? If someone could either identify what I'm missing, problem(s), or direct me to a resource which walks through the steps for implementing jQuery into an EE site, I would appreciate it. Thanks.
Call me old fashioned, but what's the point of using ExpressionEngine tags for this? Far better performance (no extra database calls, no server side file lookups, utilise mutliple remote library synchronous in browser), better control and far less grief to just insert the html tags yourself - far outweights the benefit of hiding the URL to the JS resource.
According to the docs, your second line looks wrong and should be:
{exp:jquery:script_tag plugin='sliderPro'}
The effect parameter would be requesting part of the JQuery UI library, not a third-party library. Also you specify 3 folders in the script tags, the documentation states specific files without extension - not folders. I can only assume it's not working for you because all the required JS lines are not being inserted into the page. If the above changes still don't work, what does the html output result in? Are there any JS errors in the console?
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link rel="stylesheet" href="/assets/fancybox/jquery.fancybox.css?v=2.1.5" type="text/css" media="screen" />
<script type="text/javascript" src="/assets/fancybox/jquery.fancybox.pack.js?v=2.1.5"></script>
<link rel="stylesheet" type="text/css" href="css/slider-pro.min.css" media="screen">
<script type="text/javascript" src="js/jquery.sliderPro.min.js"></script>
I am currently looking at changing from using css to LESS in my current project. A few things to mention before I get to the question are:
1) The project is purely clientside (Html/Js/Css) so there is no server side component for the website (although there is a web service it calls via CORS)
2) I load almost everything via resource loading frameworks, currently I am using yepnope
So given the above I need to be able to get the LESS styles to be processed clientside, but as I am using a resource loader and more css/less could be loaded after the initial page load has happened I was wondering if:
1) Does Less work with content loaders when using client side processing? As it says:
Client-side usage
Link your .less stylesheets with the rel set to “stylesheet/less”:
<link rel="stylesheet/less" type="text/css" href="styles.less">
Then download less.js from the top of the page, and include it in the <head> element of your page, like so:
<script src="less.js" type="text/javascript"></script>
Make sure you include your stylesheets before the script.
I think I may be able to tell yepnope how to handle less files and give them the required element attributes. If I can providing the less resources are brought in before the less javascript will it be ok?
2) Is there any manual way to tell it what to process in javascript?
This would cover the case where everything has been loaded for the current page, the user clicks a button which dynamically loads a new template which is displayed in the current page, this may require new less resources to be loaded, but the less.js file has already been included.
Hopefully the above gives you some context as to what I am trying to do and what the 2 questions are.
Yes you can.
Reading this post Load less.js rules dynamically and adjusting it a bit:
less.sheets.push(document.getElementById('new-style-1'));
// Add the new less file to the head of your document
var newLessStylesheet = $("<link />").attr("id", "new-style-1").attr("href", "/stylesheets/style.less").attr("type", 'text/less');
$("head").append(newLessStylesheet);
// Have less refresh the stylesheets
less.refresh(true);
You could also generate all the CSS in your development environment and put it in one file.
There are lots of options. The easiests way would be to use an application. You could use apps like http://incident57.com/less/ for Mac. You can even compile online: Search for something as "lessphp".
Is there a solution out there where I can have JavaScript/jQuery autoload dependent files when needed? For example, consider this scenario:
I have an autoloader script listening for when a particular script needs to be loaded.
A jQuery dialog() plugin is called.
The autoloader is told to listen for when this plugin is called, and loads the jQuery UI.
If more dialogs are called in the future, the required script will not be loaded.
Is this too much effort for simply trying to limit bandwidth? Should I just include all of the core files in one superpackage and be done with it?
Thank you for your time.
Yes you should inclde all of the scripts in one file. Or at least most of them groupped like this: jquery.js, global.js (that's where frequently - on more than one, two pages - used scripts should be) and page_specyfic.js.
Imagine that a dialog() is called and the user has to wait for .js to download and plugins to initialise.
Savings in bandwith (if any) wouldn't be worth harming the users expirience.
There are many examples of on demand script loading out there. For example remy sharp has a code sample on his blog that you could either use as is or turn into a jQuery plugin. Unfortunately it may not work in all browsers.
There is also the jQuery Lazy Plugin Loader which loads jQuery plugins on demand rather than up-front. To use it you would need to set up lazy loading for each piece of jQuery UI you are using as follows (name will be the function name for each piece you use):
$.lazy([{
src: 'jquery-ui-1.8.14.custom.min.js',
name: 'dialog'
}]);
You can also use the techniques in this question about loading jQuery itself on demand. For example you can dynamically create a script tag at the time needed to load jQuery UI.
Finally since you are talking about jQuery UI consider getting it from Google's CDN, which is likely cached in the user's browser anyway.
You can try this new jquery plugin. Works like yeapnope.js but more make sense.
http://plugins.jquery.com/plugin-tags/autoloader
$(document).autoLoader(
{
test: $.ui,
loadScript: "https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/jquery- ui.min.js",
complete: function(){
console.log($.ui);
}
}
);
I wouldn't worry too much. The files are cached. Once one page in your site loads the jquery UI (or any other include file like CSS), the next time it's needed it will be in the user's browser cache, never to be loaded again for days/weeks
Sounds like you want a script loader.
You can't generally do synchronous loading of scripts across browsers, though, so script loaders are necessarily asynchronous. What you're asking for isn't exactly possible since the script needs to load, call a callback, and then continue. You wouldn't want to call some plugin and not know whether it is executing synchronously or not, that gets you into a world of problems.
I recommend you look at DeferJS, a script loader for jQuery:
https://github.com/BorisMoore/jsdefer
From your comments, part of your wish seems to be to keep your code organized. I would recommend RequireJs. It lets you break your code up into clearly separated modules with explicit dependencies. Then when you want to go to production, there's a build tool that will merge them all back together into the (request/bandwidth saving) 2-3 files you want to serve.
Yeah, I have also thought about implementing something like this. I am not sure if it would be worthwhile or not in the end but there are quite a few libraries to do this for you like ensure
you could try something like this but it would be a pain. basically you are checking the type of error caught and message if dialog (the function you are trying to call doesn't exist) load the function and try calling the method again. Like I said it would be a pain to do this everywhere unless some elegant solution was thought of.
function loadDialog() {
$('#myDialog').dialog({});
}
try {
loadDialog()
} catch(e) {
if (e && e.type && e.type=='not_defined' && e.message == 'dialog is not defined') {
//load jQuery plugins...
loadDialog();
}
}
This is a follow-up post for a comment above:
<link rel="stylesheet" href="../system/stylesheets/universal.css" />
<link rel="stylesheet" href="../system/stylesheets/jquery-ui.min.css" />
<link rel="stylesheet" href="../system/stylesheets/uploadify.css" />
<link rel="stylesheet" href="system/stylesheets/style.css" />
<script src="../system/javascripts/swfobject.js"></script>
<script src="../system/javascripts/jquery.min.js"></script>
<script src="../system/javascripts/jquery-ui.min.js"></script>
<script src="../system/javascripts/global.jquery.js"></script>
<script src="../system/javascripts/analog.jquery.js"></script>
<script src="../system/javascripts/funtip.jquery.js"></script>
<script src="../system/javascripts/uploadify.jquery.js"></script>
<script src="system/javascripts/install.jquery.js"></script>
<link rel="stylesheet" href="system/templates/stylesheets/style.css" />
<script>
$(document).ready(function() {
$(':text, :password, textarea').funtip();
});
</script>